mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
More OPA conformant semantics (#62)
Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
This commit is contained in:
committed by
GitHub
parent
8a73b4bef9
commit
73ee18f002
+21
-3
@@ -10,33 +10,50 @@ bitsor
|
||||
bitsshiftleft
|
||||
bitsshiftright
|
||||
bitsxor
|
||||
casts
|
||||
comparisonexpr
|
||||
completedoc
|
||||
compositebasedereference
|
||||
dataderef
|
||||
comprehensions
|
||||
containskeyword
|
||||
cryptohmacequal
|
||||
cryptohmacmd5
|
||||
cryptohmacmd5
|
||||
cryptohmacsha1
|
||||
cryptohmacsha256
|
||||
cryptohmacsha512
|
||||
cryptomd5
|
||||
cryptsha1
|
||||
cryptosha1
|
||||
cryptosha256
|
||||
dataderef
|
||||
disjunction
|
||||
elsekeyword
|
||||
embeddedvirtualdoc
|
||||
evaltermexpr
|
||||
every
|
||||
example
|
||||
fix1863
|
||||
functionerrors
|
||||
functions
|
||||
globmatch
|
||||
globquotemeta
|
||||
helloworld
|
||||
indexing
|
||||
indirectreferences
|
||||
intersection
|
||||
invalidkeyerror
|
||||
jsonfilteridempotent
|
||||
jwtencodesignheadererrors
|
||||
jwtencodesignpayloaderrors
|
||||
nestedreferences
|
||||
objectfilter
|
||||
objectfilteridempotent
|
||||
objectfilternonstringkey
|
||||
objectget
|
||||
objectkeys
|
||||
objectremove
|
||||
objectremoveidempotent
|
||||
objectremovenonstringkey
|
||||
partialdocconstants
|
||||
partialsetdoc
|
||||
rand
|
||||
regexfind
|
||||
@@ -57,6 +74,7 @@ trimprefix
|
||||
trimright
|
||||
trimspace
|
||||
trimsuffix
|
||||
type
|
||||
typebuiltin
|
||||
typenamebuiltin
|
||||
undos
|
||||
|
||||
@@ -16,18 +16,33 @@ const OPA_REPO: &str = "https://github.com/open-policy-agent/opa";
|
||||
const OPA_BRANCH: &str = "v0.58.0";
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
struct TestCase {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
data: Option<Value>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
input: Option<Value>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
input_term: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
modules: Option<Vec<String>>,
|
||||
note: String,
|
||||
query: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
sort_bindings: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
want_result: Option<Value>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
skip: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
error: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
traces: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
strict_error: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
want_error: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
want_error_code: Option<String>,
|
||||
}
|
||||
|
||||
@@ -45,11 +60,18 @@ fn eval_test_case(case: &TestCase) -> Result<Value> {
|
||||
if let Some(input) = &case.input {
|
||||
engine.set_input(input.clone());
|
||||
}
|
||||
if let Some(input_term) = &case.input_term {
|
||||
let input = Value::from_json_str(&input_term)?;
|
||||
engine.set_input(input);
|
||||
}
|
||||
if let Some(modules) = &case.modules {
|
||||
for (idx, rego) in modules.iter().enumerate() {
|
||||
engine.add_policy(format!("rego{idx}.rego"), rego.clone())?;
|
||||
}
|
||||
}
|
||||
|
||||
engine.set_strict_builtin_errors(case.strict_error.unwrap_or_default());
|
||||
|
||||
let query_results = engine.eval_query(case.query.clone(), true)?;
|
||||
|
||||
let mut values = vec![];
|
||||
@@ -182,9 +204,11 @@ fn run_opa_tests(opa_tests_dir: String, folders: &[String]) -> Result<()> {
|
||||
println!("\nOPA TESTSUITE STATUS");
|
||||
println!(" {:40} {:4} {:4}", "FOLDER", "PASS", "FAIL");
|
||||
let (mut npass, mut nfail) = (0, 0);
|
||||
let mut passing = vec![];
|
||||
for (dir, (pass, fail)) in status {
|
||||
if fail == 0 {
|
||||
println!("\x1b[32m {dir:40}: {pass:4} {fail:4}\x1b[0m");
|
||||
passing.push(dir);
|
||||
} else {
|
||||
println!("\x1b[31m {dir:40}: {pass:4} {fail:4}\x1b[0m");
|
||||
}
|
||||
@@ -193,6 +217,8 @@ fn run_opa_tests(opa_tests_dir: String, folders: &[String]) -> Result<()> {
|
||||
}
|
||||
println!();
|
||||
|
||||
std::fs::write("target/opa.passing", passing.join("\n"))?;
|
||||
|
||||
if npass == 0 && nfail == 0 {
|
||||
bail!("no matching tests found.");
|
||||
} else if nfail == 0 {
|
||||
|
||||
@@ -569,12 +569,14 @@ fn match_rule(r: &Rule, v: &Value) -> Result<()> {
|
||||
Rule::Default {
|
||||
span,
|
||||
refr,
|
||||
args,
|
||||
op,
|
||||
value,
|
||||
} => {
|
||||
let obj = &v["default"];
|
||||
match_span_opt(span, &obj["span"])?;
|
||||
match_expr(refr, &obj["refr"])?;
|
||||
match_vec(span /*dummy*/, args, &obj["args"])?;
|
||||
match_assign_op(span, op, &obj["op"])?;
|
||||
match_expr(value, &obj["value"])
|
||||
}
|
||||
|
||||
@@ -37,9 +37,12 @@ cases:
|
||||
r = 1
|
||||
rrr = "fun"
|
||||
scopes:
|
||||
- locals: ["p", "y", "q", "x", "a", "b", "idx"]
|
||||
- locals: ["x"]
|
||||
unscoped: ["p", "y", "q", "a", "b", "idx"]
|
||||
inputs: ["r", "rrr"]
|
||||
- locals: ["k", "r1"]
|
||||
- locals: []
|
||||
unscoped: ["k", "r1"]
|
||||
inputs: ["a", "idx", "rrr"]
|
||||
- locals: ["q", "idx1", "t"]
|
||||
- locals: ["q"]
|
||||
unscoped: ["idx1", "t"]
|
||||
inputs: ["rrr"]
|
||||
|
||||
@@ -12,6 +12,7 @@ use std::collections::BTreeSet;
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||
struct Scope {
|
||||
pub locals: BTreeSet<String>,
|
||||
pub unscoped: BTreeSet<String>,
|
||||
pub inputs: BTreeSet<String>,
|
||||
}
|
||||
|
||||
@@ -27,8 +28,11 @@ struct YamlTest {
|
||||
cases: Vec<TestCase>,
|
||||
}
|
||||
|
||||
fn to_string_set(s: &BTreeSet<SourceStr>) -> BTreeSet<String> {
|
||||
s.iter().map(|s| s.to_string()).collect()
|
||||
fn to_string_set<'a, I>(itr: I) -> BTreeSet<String>
|
||||
where
|
||||
I: std::iter::Iterator<Item = &'a SourceStr>,
|
||||
{
|
||||
itr.map(|s| s.to_string()).collect()
|
||||
}
|
||||
|
||||
fn analyze_file(regos: &[String], expected_scopes: &[Scope]) -> Result<()> {
|
||||
@@ -55,8 +59,18 @@ fn analyze_file(regos: &[String], expected_scopes: &[Scope]) -> Result<()> {
|
||||
if idx > expected_scopes.len() {
|
||||
bail!("extra scope generated.")
|
||||
}
|
||||
assert_eq!(to_string_set(&scope.locals), expected_scopes[idx].locals);
|
||||
assert_eq!(to_string_set(&scope.inputs), expected_scopes[idx].inputs);
|
||||
assert_eq!(
|
||||
to_string_set(scope.locals.keys()),
|
||||
expected_scopes[idx].locals
|
||||
);
|
||||
assert_eq!(
|
||||
to_string_set(scope.unscoped.iter()),
|
||||
expected_scopes[idx].unscoped
|
||||
);
|
||||
assert_eq!(
|
||||
to_string_set(scope.inputs.iter()),
|
||||
expected_scopes[idx].inputs
|
||||
);
|
||||
println!("scope {idx} matched.")
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user