mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
fix(interpreter,rvm): correct partial object rule iteration and classification (#718)
Partial object rules with dynamic keys (e.g. `violations[k] if { ... }`)
only produced a single entry instead of collecting all bindings. Two
independent bugs caused this:
1. Interpreter: the early-return optimization in eval_output_expr_in_loop
checked whether the rule_ref was constant but never verified whether
the key expression was also constant. A variable key like `k` was
treated as constant output, causing the loop to exit after the first
iteration. Fixed by gating early-return on key_expr constness.
2. RVM: compute_rule_type incorrectly classified `p[k] if { ... }` as
PartialSet instead of PartialObject. OPA v1 semantics define this
form as a partial object (key -> true). Fixed the classification and
added compiler error guards for patterns the RVM codegen cannot yet
handle (constant keys, nested bracket keys), ensuring graceful
fallback to the interpreter.
The OPA test harness now skips RVM validation per-case when partial
object compiler errors are raised, rather than blanket-skipping entire
folders. This preserves RVM coverage for unrelated tests in the same
folders.
Closes #712
Co-authored-by: Anand Krishnamoorthi <anakrish@microsoft.com>
This commit is contained in:
@@ -1782,6 +1782,7 @@ impl Interpreter {
|
||||
|
||||
let mut comps = self.eval_rule_ref(&rule_ref)?;
|
||||
if let Some(ke) = &key_expr {
|
||||
is_const_rule = is_const_rule && Self::is_simple_literal(ke)?;
|
||||
comps.push(self.eval_expr(ke)?);
|
||||
}
|
||||
let output = if let Some(oe) = &output_expr {
|
||||
|
||||
@@ -63,6 +63,14 @@ pub enum CompilerError {
|
||||
#[error("Invalid function expression with package")]
|
||||
InvalidFunctionExpressionWithPackage,
|
||||
|
||||
#[error("partial object rules with constant keys are not yet supported by the RVM compiler")]
|
||||
PartialObjectConstantKeyUnsupported,
|
||||
|
||||
#[error(
|
||||
"partial object rules with nested bracket keys are not yet supported by the RVM compiler"
|
||||
)]
|
||||
PartialObjectNestedKeyUnsupported,
|
||||
|
||||
#[error("Compilation error: {message}")]
|
||||
General { message: String },
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ impl<'a> Compiler<'a> {
|
||||
crate::ast::Expr::RefBrack { .. } if assign.is_some() => {
|
||||
RuleType::PartialObject
|
||||
}
|
||||
crate::ast::Expr::RefBrack { .. } => RuleType::PartialSet,
|
||||
crate::ast::Expr::RefBrack { .. } => RuleType::PartialObject,
|
||||
_ => RuleType::Complete,
|
||||
},
|
||||
_ => RuleType::Complete,
|
||||
@@ -88,6 +88,54 @@ impl<'a> Compiler<'a> {
|
||||
})
|
||||
}
|
||||
|
||||
fn validate_partial_object_shape(&self, refr: &ExprRef) -> Result<()> {
|
||||
let Expr::RefBrack {
|
||||
refr: prefix,
|
||||
index,
|
||||
..
|
||||
} = refr.as_ref()
|
||||
else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
if Self::has_unsupported_bracket_prefix(prefix) {
|
||||
return Err(CompilerError::PartialObjectNestedKeyUnsupported.at(refr.span()));
|
||||
}
|
||||
|
||||
if Self::is_simple_literal(index) {
|
||||
return Err(CompilerError::PartialObjectConstantKeyUnsupported.at(index.span()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn has_unsupported_bracket_prefix(expr: &ExprRef) -> bool {
|
||||
match expr.as_ref() {
|
||||
Expr::RefBrack { refr, index, .. } => {
|
||||
!Self::is_string_literal(index) || Self::has_unsupported_bracket_prefix(refr)
|
||||
}
|
||||
Expr::RefDot { refr, .. } => Self::has_unsupported_bracket_prefix(refr),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_string_literal(expr: &ExprRef) -> bool {
|
||||
matches!(expr.as_ref(), Expr::String { .. } | Expr::RawString { .. })
|
||||
}
|
||||
|
||||
fn is_simple_literal(expr: &ExprRef) -> bool {
|
||||
match expr.as_ref() {
|
||||
Expr::String { .. }
|
||||
| Expr::RawString { .. }
|
||||
| Expr::Number { .. }
|
||||
| Expr::Bool { .. }
|
||||
| Expr::Null { .. } => true,
|
||||
// Unary expressions like `-1` are constant literals too.
|
||||
Expr::UnaryExpr { expr, .. } => Self::is_simple_literal(expr),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn get_or_assign_rule_index(&mut self, rule_path: &str) -> Result<u16> {
|
||||
if let Some(&index) = self.rule_index_map.get(rule_path) {
|
||||
return Ok(index);
|
||||
@@ -345,6 +393,10 @@ impl<'a> Compiler<'a> {
|
||||
|
||||
let (key_expr, value_expr) = match head {
|
||||
RuleHead::Compr { refr, assign, .. } => {
|
||||
if rule_type == RuleType::PartialObject {
|
||||
self.validate_partial_object_shape(refr)?;
|
||||
}
|
||||
|
||||
self.rule_definition_function_params[rule_index as usize].push(None);
|
||||
self.rule_definition_destructuring_patterns[rule_index as usize]
|
||||
.push(None);
|
||||
|
||||
150
tests/interpreter/cases/rule/partial_object_v1.yaml
Normal file
150
tests/interpreter/cases/rule/partial_object_v1.yaml
Normal file
@@ -0,0 +1,150 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
cases:
|
||||
- note: constant_key_partial_object_v1
|
||||
data: {}
|
||||
input:
|
||||
enabled: true
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
import rego.v1
|
||||
|
||||
p["fixed"] if {
|
||||
input.enabled
|
||||
}
|
||||
query: data.test
|
||||
want_result:
|
||||
p:
|
||||
fixed: true
|
||||
|
||||
- note: multilevel_partial_object_v1
|
||||
data: {}
|
||||
input:
|
||||
nested:
|
||||
app:
|
||||
read: 1
|
||||
write: 2
|
||||
ops:
|
||||
deploy: 3
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
import rego.v1
|
||||
|
||||
p[a][b] if {
|
||||
some a, obj in input.nested
|
||||
some b, _ in obj
|
||||
}
|
||||
query: data.test
|
||||
want_result:
|
||||
p:
|
||||
app:
|
||||
read: true
|
||||
write: true
|
||||
ops:
|
||||
deploy: true
|
||||
|
||||
- note: constant_key_partial_object_explicit_value_v1
|
||||
data: {}
|
||||
input:
|
||||
enabled: true
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
import rego.v1
|
||||
|
||||
p["fixed"] := 7 if {
|
||||
input.enabled
|
||||
}
|
||||
query: data.test
|
||||
want_result:
|
||||
p:
|
||||
fixed: 7
|
||||
|
||||
- note: multilevel_partial_object_explicit_value_v1
|
||||
data: {}
|
||||
input:
|
||||
nested:
|
||||
app:
|
||||
read: 1
|
||||
write: 2
|
||||
ops:
|
||||
deploy: 3
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
import rego.v1
|
||||
|
||||
p[a][b] := v if {
|
||||
some a, obj in input.nested
|
||||
some b, v in obj
|
||||
}
|
||||
query: data.test
|
||||
want_result:
|
||||
p:
|
||||
app:
|
||||
read: 1
|
||||
write: 2
|
||||
ops:
|
||||
deploy: 3
|
||||
|
||||
- note: issue_712_reproducer_v0_partial_set
|
||||
data: {}
|
||||
input:
|
||||
servers:
|
||||
FOO: 1
|
||||
BAR: 2
|
||||
BAZ: 3
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
import future.keywords.in
|
||||
|
||||
violations[k] {
|
||||
some k, _ in input.servers
|
||||
}
|
||||
query: data.test.violations
|
||||
want_result:
|
||||
set!: ["BAR", "BAZ", "FOO"]
|
||||
|
||||
- note: issue_712_reproducer_v1_partial_object
|
||||
data: {}
|
||||
input:
|
||||
servers:
|
||||
FOO: 1
|
||||
BAR: 2
|
||||
BAZ: 3
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
import rego.v1
|
||||
|
||||
violations[k] if {
|
||||
some k, _ in input.servers
|
||||
}
|
||||
query: data.test.violations
|
||||
want_result:
|
||||
BAR: true
|
||||
BAZ: true
|
||||
FOO: true
|
||||
|
||||
- note: issue_712_reproducer_v1_contains_partial_set
|
||||
data: {}
|
||||
input:
|
||||
servers:
|
||||
FOO: 1
|
||||
BAR: 2
|
||||
BAZ: 3
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
import rego.v1
|
||||
|
||||
violations contains k if {
|
||||
some k, _ in input.servers
|
||||
}
|
||||
query: data.test.violations
|
||||
want_result:
|
||||
set!: ["BAR", "BAZ", "FOO"]
|
||||
16
tests/opa.rs
16
tests/opa.rs
@@ -275,6 +275,14 @@ fn is_with_keyword_unsupported_error(err: &anyhow::Error) -> bool {
|
||||
})
|
||||
}
|
||||
|
||||
fn is_partial_object_unsupported_error(err: &anyhow::Error) -> bool {
|
||||
err.chain().any(|cause| {
|
||||
let msg = cause.to_string();
|
||||
msg.contains("partial object rules with constant keys are not yet supported")
|
||||
|| msg.contains("partial object rules with nested bracket keys are not yet supported")
|
||||
})
|
||||
}
|
||||
|
||||
fn maybe_verify_rvm_case(case: &TestCase, is_rego_v0_test: bool, actual: &Value) -> Result<()> {
|
||||
if case.note == "defaultkeyword/function with var arg, ref head query" {
|
||||
println!(
|
||||
@@ -308,6 +316,14 @@ fn maybe_verify_rvm_case(case: &TestCase, is_rego_v0_test: bool, actual: &Value)
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if is_partial_object_unsupported_error(&err) {
|
||||
println!(
|
||||
" skipping RVM check for '{}' (partial object pattern unsupported)",
|
||||
case.note
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
return Err(err);
|
||||
}
|
||||
};
|
||||
|
||||
939
tests/rvm/rego/cases/partial_object_rules.yaml
Normal file
939
tests/rvm/rego/cases/partial_object_rules.yaml
Normal file
@@ -0,0 +1,939 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
cases:
|
||||
- note: partial_object_variable_key_collects_all_bindings
|
||||
data: {}
|
||||
input:
|
||||
items:
|
||||
FOO: 1
|
||||
BAR: 2
|
||||
BAZ: 3
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
p[k] if {
|
||||
some k, _ in input.items
|
||||
}
|
||||
query: data.test.p
|
||||
want_result:
|
||||
BAR: true
|
||||
BAZ: true
|
||||
FOO: true
|
||||
|
||||
- note: partial_object_explicit_value_collects_all_bindings
|
||||
data: {}
|
||||
input:
|
||||
items:
|
||||
FOO: 1
|
||||
BAR: 2
|
||||
BAZ: 3
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
p[k] := v if {
|
||||
some k, v in input.items
|
||||
}
|
||||
query: data.test.p
|
||||
want_result:
|
||||
BAR: 2
|
||||
BAZ: 3
|
||||
FOO: 1
|
||||
|
||||
- note: partial_object_dynamic_expression_key_collects_all_bindings
|
||||
data: {}
|
||||
input:
|
||||
items:
|
||||
FOO: 1
|
||||
BAR: 2
|
||||
BAZ: 3
|
||||
aliases:
|
||||
FOO: alias-foo
|
||||
BAR: alias-bar
|
||||
BAZ: alias-baz
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
p[input.aliases[k]] := v if {
|
||||
some k, v in input.items
|
||||
}
|
||||
query: data.test.p
|
||||
want_result:
|
||||
alias-bar: 2
|
||||
alias-baz: 3
|
||||
alias-foo: 1
|
||||
|
||||
- note: partial_object_undefined_key_skipped
|
||||
# TODO(#719): RVM incorrectly materializes undefined keys instead of
|
||||
# skipping iterations where the key is undefined.
|
||||
skip: true
|
||||
data: {}
|
||||
input:
|
||||
items:
|
||||
FOO: 1
|
||||
BAR: 2
|
||||
aliases:
|
||||
FOO: alias-foo
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
p[input.aliases[k]] := v if {
|
||||
some k, v in input.items
|
||||
}
|
||||
query: data.test.p
|
||||
want_result:
|
||||
alias-foo: 1
|
||||
|
||||
- note: partial_object_duplicate_key_last_wins
|
||||
# TODO(#719): regorus silently overwrites conflicting keys instead of
|
||||
# erroring when the same key is produced with different values.
|
||||
skip: true
|
||||
data: {}
|
||||
input: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
p[k] := v if {
|
||||
some k, v in {"a": 1}
|
||||
}
|
||||
|
||||
p[k] := v if {
|
||||
some k, v in {"a": 2}
|
||||
}
|
||||
query: data.test.p
|
||||
want_error: "conflict"
|
||||
|
||||
- note: partial_object_duplicate_key_same_value_ok
|
||||
data: {}
|
||||
input: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
p[k] if {
|
||||
some k, _ in {"a": 1}
|
||||
}
|
||||
|
||||
p[k] if {
|
||||
some k, _ in {"a": 2}
|
||||
}
|
||||
query: data.test.p
|
||||
want_result:
|
||||
a: true
|
||||
|
||||
- note: partial_object_single_element_input
|
||||
data: {}
|
||||
input:
|
||||
items:
|
||||
ONLY: 1
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
p[k] if {
|
||||
some k, _ in input.items
|
||||
}
|
||||
query: data.test.p
|
||||
want_result:
|
||||
ONLY: true
|
||||
|
||||
- note: partial_object_static_bracket_prefix_collects_all_bindings
|
||||
data: {}
|
||||
input:
|
||||
items:
|
||||
FOO: 1
|
||||
BAR: 2
|
||||
BAZ: 3
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
p["a"][k] := v if {
|
||||
some k, v in input.items
|
||||
}
|
||||
query: data.test.p.a
|
||||
want_result:
|
||||
BAR: 2
|
||||
BAZ: 3
|
||||
FOO: 1
|
||||
|
||||
- note: partial_object_constant_key_unsupported_in_rvm
|
||||
data: {}
|
||||
input:
|
||||
enabled: true
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
p["fixed"] if {
|
||||
input.enabled
|
||||
}
|
||||
query: data.test.p.fixed
|
||||
want_error: "partial object rules with constant keys are not yet supported by the RVM compiler"
|
||||
allow_interpreter_success: true
|
||||
|
||||
- note: partial_object_constant_key_explicit_value_unsupported_in_rvm
|
||||
data: {}
|
||||
input:
|
||||
enabled: true
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
p["fixed"] := 7 if {
|
||||
input.enabled
|
||||
}
|
||||
query: data.test.p.fixed
|
||||
want_error: "partial object rules with constant keys are not yet supported by the RVM compiler"
|
||||
allow_interpreter_success: true
|
||||
|
||||
- note: partial_object_multiple_bodies_collects_all_bindings
|
||||
data: {}
|
||||
input:
|
||||
items:
|
||||
FOO: 1
|
||||
BAR: 2
|
||||
BAZ: 3
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
p[k] if {
|
||||
some k, _ in input.items
|
||||
k in {"FOO", "BAR"}
|
||||
}
|
||||
|
||||
p[k] if {
|
||||
some k, _ in input.items
|
||||
k == "BAZ"
|
||||
}
|
||||
query: data.test.p
|
||||
want_result:
|
||||
BAR: true
|
||||
BAZ: true
|
||||
FOO: true
|
||||
|
||||
- note: partial_set_contains_collects_all_bindings
|
||||
data: {}
|
||||
input:
|
||||
items:
|
||||
FOO: 1
|
||||
BAR: 2
|
||||
BAZ: 3
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
p contains k if {
|
||||
some k, _ in input.items
|
||||
}
|
||||
query: data.test.p
|
||||
want_result:
|
||||
set!: ["BAR", "BAZ", "FOO"]
|
||||
|
||||
- note: issue_712_reproducer_v1_partial_object
|
||||
data: {}
|
||||
input:
|
||||
servers:
|
||||
FOO: 1
|
||||
BAR: 2
|
||||
BAZ: 3
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
import rego.v1
|
||||
|
||||
violations[k] if {
|
||||
some k, _ in input.servers
|
||||
}
|
||||
query: data.test.violations
|
||||
want_result:
|
||||
BAR: true
|
||||
BAZ: true
|
||||
FOO: true
|
||||
|
||||
- note: issue_712_reproducer_v1_contains_partial_set
|
||||
data: {}
|
||||
input:
|
||||
servers:
|
||||
FOO: 1
|
||||
BAR: 2
|
||||
BAZ: 3
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
import rego.v1
|
||||
|
||||
violations contains k if {
|
||||
some k, _ in input.servers
|
||||
}
|
||||
query: data.test.violations
|
||||
want_result:
|
||||
set!: ["BAR", "BAZ", "FOO"]
|
||||
|
||||
- note: partial_object_multilevel_key_unsupported_in_rvm
|
||||
data: {}
|
||||
input:
|
||||
nested:
|
||||
app:
|
||||
read: 1
|
||||
write: 2
|
||||
ops:
|
||||
deploy: 3
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
p[a][b] if {
|
||||
some a, obj in input.nested
|
||||
some b, _ in obj
|
||||
}
|
||||
|
||||
main := p
|
||||
query: data.test.main
|
||||
want_error: "partial object rules with nested bracket keys are not yet supported by the RVM compiler"
|
||||
allow_interpreter_success: true
|
||||
|
||||
- note: partial_object_multilevel_key_explicit_value_unsupported_in_rvm
|
||||
data: {}
|
||||
input:
|
||||
nested:
|
||||
app:
|
||||
read: 1
|
||||
write: 2
|
||||
ops:
|
||||
deploy: 3
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
p[a][b] := v if {
|
||||
some a, obj in input.nested
|
||||
some b, v in obj
|
||||
}
|
||||
|
||||
main := p
|
||||
query: data.test.main
|
||||
want_error: "partial object rules with nested bracket keys are not yet supported by the RVM compiler"
|
||||
allow_interpreter_success: true
|
||||
|
||||
- note: partial_object_hidden_dynamic_prefix_unsupported_in_rvm
|
||||
data: {}
|
||||
input:
|
||||
nested:
|
||||
app:
|
||||
q:
|
||||
read: 1
|
||||
write: 2
|
||||
ops:
|
||||
q:
|
||||
deploy: 3
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
p[a].q[b] if {
|
||||
some a, obj in input.nested
|
||||
some b, _ in obj.q
|
||||
}
|
||||
|
||||
main := p
|
||||
query: data.test.main
|
||||
want_error: "partial object rules with nested bracket keys are not yet supported by the RVM compiler"
|
||||
allow_interpreter_success: true
|
||||
|
||||
- note: partial_object_array_iteration_collects_all_bindings
|
||||
data: {}
|
||||
input:
|
||||
items: ["FOO", "BAR", "BAZ"]
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
p[v] if {
|
||||
some _, v in input.items
|
||||
}
|
||||
query: data.test.p
|
||||
want_result:
|
||||
BAR: true
|
||||
BAZ: true
|
||||
FOO: true
|
||||
|
||||
- note: partial_object_empty_input_is_empty_object
|
||||
data: {}
|
||||
input:
|
||||
items: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
p[k] if {
|
||||
some k, _ in input.items
|
||||
}
|
||||
query: data.test.p
|
||||
want_result: {}
|
||||
|
||||
- note: partial_object_duplicate_paths_same_key_same_value_deduplicates
|
||||
data: {}
|
||||
input:
|
||||
pairs:
|
||||
- alias: shared
|
||||
value: 1
|
||||
- alias: alpha
|
||||
value: 10
|
||||
- alias: shared
|
||||
value: 1
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
p[entry.alias] := entry.value if {
|
||||
some entry in input.pairs
|
||||
}
|
||||
query: data.test.p
|
||||
want_result:
|
||||
alpha: 10
|
||||
shared: 1
|
||||
|
||||
- note: partial_object_duplicate_paths_same_key_different_values_conflict
|
||||
# TODO(#719): regorus silently overwrites conflicting keys instead of
|
||||
# erroring when the same key is produced with different values.
|
||||
skip: true
|
||||
data: {}
|
||||
input:
|
||||
pairs:
|
||||
- alias: shared
|
||||
value: 1
|
||||
- alias: shared
|
||||
value: 2
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
p[entry.alias] := entry.value if {
|
||||
some entry in input.pairs
|
||||
}
|
||||
query: data.test.p
|
||||
want_error: "conflict"
|
||||
|
||||
- note: partial_object_undefined_key_skips_iteration
|
||||
# TODO(#719): RVM incorrectly materializes undefined keys instead of
|
||||
# skipping iterations where the key is undefined.
|
||||
skip: true
|
||||
data: {}
|
||||
input:
|
||||
items:
|
||||
FOO: 1
|
||||
BAR: 2
|
||||
BAZ: 3
|
||||
aliases:
|
||||
FOO: alias-foo
|
||||
BAZ: alias-baz
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
p[input.aliases[k]] := v if {
|
||||
some k, v in input.items
|
||||
}
|
||||
query: data.test.p
|
||||
want_result:
|
||||
alias-baz: 3
|
||||
alias-foo: 1
|
||||
|
||||
- note: partial_object_undefined_value_skips_iteration
|
||||
# TODO(#719): RVM incorrectly materializes undefined values instead of
|
||||
# skipping iterations where the value is undefined.
|
||||
skip: true
|
||||
data: {}
|
||||
input:
|
||||
keys: ["FOO", "BAR", "BAZ"]
|
||||
values:
|
||||
FOO: 1
|
||||
BAZ: 3
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
p[k] := input.values[k] if {
|
||||
some _, k in input.keys
|
||||
}
|
||||
query: data.test.p
|
||||
want_result:
|
||||
BAZ: 3
|
||||
FOO: 1
|
||||
|
||||
- note: partial_object_mixed_undefined_key_value_cases_skip_bad_iterations
|
||||
# TODO(#719): RVM incorrectly materializes undefined keys/values instead of
|
||||
# skipping iterations where the key or value is undefined.
|
||||
skip: true
|
||||
data: {}
|
||||
input:
|
||||
rows:
|
||||
- src: keep
|
||||
- src: missing_alias
|
||||
- src: missing_value
|
||||
- src: missing_both
|
||||
aliases:
|
||||
keep: alias-keep
|
||||
missing_value: alias-no-value
|
||||
values:
|
||||
keep: 1
|
||||
missing_alias: 2
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
p[input.aliases[row.src]] := input.values[row.src] if {
|
||||
some row in input.rows
|
||||
}
|
||||
query: data.test.p
|
||||
want_result:
|
||||
alias-keep: 1
|
||||
|
||||
- note: partial_object_and_partial_set_same_name_conflict
|
||||
data: {}
|
||||
input:
|
||||
items:
|
||||
FOO: 1
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
p[k] if {
|
||||
some k, _ in input.items
|
||||
}
|
||||
|
||||
p contains "shadow" if {
|
||||
true
|
||||
}
|
||||
query: data.test.p
|
||||
want_error: "has multiple types"
|
||||
|
||||
- note: partial_object_complete_rule_conflicts_with_partial_object
|
||||
data: {}
|
||||
input:
|
||||
items:
|
||||
a: 1
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
p := {"fixed": 1}
|
||||
|
||||
p[k] := v if {
|
||||
some k, v in input.items
|
||||
}
|
||||
query: data.test.p
|
||||
want_error: "multiple types"
|
||||
|
||||
- note: partial_object_partial_set_conflicts_with_partial_object
|
||||
data: {}
|
||||
input:
|
||||
items:
|
||||
a: 1
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
p contains k if {
|
||||
some k, _ in input.items
|
||||
}
|
||||
|
||||
p[k] := 1 if {
|
||||
some k, _ in input.items
|
||||
}
|
||||
query: data.test.p
|
||||
want_error: "multiple types"
|
||||
|
||||
- note: partial_object_large_range_counts_all_entries
|
||||
data: {}
|
||||
input: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
p[key] := n if {
|
||||
n := numbers.range(0, 255)[_]
|
||||
key := sprintf("k-%d", [n])
|
||||
}
|
||||
|
||||
main := count(p)
|
||||
query: data.test.main
|
||||
want_result: 256
|
||||
|
||||
- note: partial_object_rbac_duplicate_actions_deduplicate
|
||||
data:
|
||||
role_permissions:
|
||||
reader: ["read", "list"]
|
||||
writer: ["read", "write"]
|
||||
auditor: ["read", "list"]
|
||||
input:
|
||||
user_roles: ["reader", "writer", "auditor"]
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
allowed_actions[action] if {
|
||||
some role in input.user_roles
|
||||
some action in data.role_permissions[role]
|
||||
}
|
||||
query: data.test.allowed_actions
|
||||
want_result:
|
||||
list: true
|
||||
read: true
|
||||
write: true
|
||||
|
||||
- note: partial_object_violations_real_world_pattern
|
||||
data: {}
|
||||
input:
|
||||
spec:
|
||||
containers:
|
||||
- name: api
|
||||
securityContext:
|
||||
readOnlyRootFilesystem: false
|
||||
- name: worker
|
||||
securityContext:
|
||||
readOnlyRootFilesystem: true
|
||||
- name: sidecar
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
violations[msg] if {
|
||||
some container in input.spec.containers
|
||||
not container.securityContext.readOnlyRootFilesystem
|
||||
msg := sprintf("Container %s must use readOnlyRootFilesystem", [container.name])
|
||||
}
|
||||
query: data.test.violations
|
||||
want_result:
|
||||
Container api must use readOnlyRootFilesystem: true
|
||||
Container sidecar must use readOnlyRootFilesystem: true
|
||||
|
||||
- note: partial_object_resource_mapping_filters_valid_resources
|
||||
data: {}
|
||||
input:
|
||||
resources:
|
||||
svc-api:
|
||||
cpu: 1
|
||||
job-cleanup:
|
||||
cpu: 2
|
||||
svc-worker:
|
||||
cpu: 4
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
valid_resource(name) if {
|
||||
startswith(name, "svc-")
|
||||
}
|
||||
|
||||
resources[name] := config if {
|
||||
some name, config in input.resources
|
||||
valid_resource(name)
|
||||
}
|
||||
query: data.test.resources
|
||||
want_result:
|
||||
svc-api:
|
||||
cpu: 1
|
||||
svc-worker:
|
||||
cpu: 4
|
||||
|
||||
- note: partial_object_computed_concat_key_constant_body
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
p[concat("", ["edge", "-", "key"])] if {
|
||||
true
|
||||
}
|
||||
query: data.test.p
|
||||
want_result:
|
||||
edge-key: true
|
||||
|
||||
- note: partial_object_duplicate_computed_key_same_value_merges
|
||||
data: {}
|
||||
input:
|
||||
items:
|
||||
A: 0
|
||||
a: 0
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
p[lower(k)] := 1 if {
|
||||
some k, _ in input.items
|
||||
}
|
||||
query: data.test.p
|
||||
want_result:
|
||||
a: 1
|
||||
|
||||
- note: partial_object_function_key_and_object_value
|
||||
data: {}
|
||||
input:
|
||||
items:
|
||||
a: 1
|
||||
b: 2
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
f(x) := concat(":", [x, "suffix"])
|
||||
|
||||
p[f(k)] := {"nested": v + 1} if {
|
||||
some k, v in input.items
|
||||
}
|
||||
query: data.test.p
|
||||
want_result:
|
||||
"a:suffix":
|
||||
nested: 2
|
||||
"b:suffix":
|
||||
nested: 3
|
||||
|
||||
- note: partial_object_array_index_key_uses_selected_elements
|
||||
data: {}
|
||||
input:
|
||||
keys: ["alpha", "beta"]
|
||||
values: [10, 20]
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
p[input.keys[i]] := v if {
|
||||
some i, v in input.values
|
||||
}
|
||||
query: data.test.p
|
||||
want_result:
|
||||
alpha: 10
|
||||
beta: 20
|
||||
|
||||
- note: partial_object_computed_empty_and_special_string_keys
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
p[concat("", [""])] := "empty" if {
|
||||
true
|
||||
}
|
||||
|
||||
p[concat("", ["a/b?c#d"])] := "special" if {
|
||||
true
|
||||
}
|
||||
query: data.test.p
|
||||
want_result:
|
||||
"": "empty"
|
||||
a/b?c#d: "special"
|
||||
|
||||
- note: partial_object_not_filters_blocked_entries
|
||||
data: {}
|
||||
input:
|
||||
items:
|
||||
allowed: true
|
||||
blocked: true
|
||||
blocked:
|
||||
blocked: true
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
p[k] if {
|
||||
some k, _ in input.items
|
||||
not input.blocked[k]
|
||||
}
|
||||
query: data.test.p
|
||||
want_result:
|
||||
allowed: true
|
||||
|
||||
- note: partial_object_dot_bracket_object_value_collects_all_bindings
|
||||
data: {}
|
||||
input:
|
||||
items:
|
||||
a: 1
|
||||
b: 2
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
p.config[k] := {"nested": v} if {
|
||||
some k, v in input.items
|
||||
}
|
||||
query: data.test.p.config
|
||||
want_result:
|
||||
a:
|
||||
nested: 1
|
||||
b:
|
||||
nested: 2
|
||||
|
||||
- note: partial_object_dynamic_prefix_static_suffix_unsupported_in_rvm
|
||||
data: {}
|
||||
input:
|
||||
items:
|
||||
a: 1
|
||||
b: 2
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
p[k]["fixed"] := upper(k) if {
|
||||
some k, _ in input.items
|
||||
}
|
||||
|
||||
main := p
|
||||
query: data.test.main
|
||||
want_error: "partial object rules with nested bracket keys are not yet supported by the RVM compiler"
|
||||
allow_interpreter_success: true
|
||||
|
||||
- note: partial_object_literal_prefix_nested_unsupported_in_rvm
|
||||
data: {}
|
||||
input:
|
||||
items:
|
||||
a: 1
|
||||
b: 2
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
p[1][k] := v if {
|
||||
some k, v in input.items
|
||||
}
|
||||
|
||||
main := p
|
||||
query: data.test.main
|
||||
want_error: "partial object rules with nested bracket keys are not yet supported by the RVM compiler"
|
||||
allow_interpreter_success: true
|
||||
|
||||
- note: partial_object_three_level_nested_dynamic_unsupported_in_rvm
|
||||
data: {}
|
||||
input:
|
||||
nested:
|
||||
app:
|
||||
read: 1
|
||||
write: 2
|
||||
ops:
|
||||
deploy: 3
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
p["root"][a][b] := v if {
|
||||
some a, obj in input.nested
|
||||
some b, v in obj
|
||||
}
|
||||
|
||||
main := p
|
||||
query: data.test.main
|
||||
want_error: "partial object rules with nested bracket keys are not yet supported by the RVM compiler"
|
||||
allow_interpreter_success: true
|
||||
|
||||
- note: partial_object_with_body_unsupported_in_rvm
|
||||
data: {}
|
||||
input:
|
||||
enabled: false
|
||||
items:
|
||||
a: 1
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
gate if {
|
||||
input.enabled
|
||||
}
|
||||
|
||||
p[k] := v if {
|
||||
some k, v in input.items
|
||||
data.test.gate with input as {"enabled": true}
|
||||
}
|
||||
query: data.test.p
|
||||
want_error: "the `with` keyword is not supported by the compiler yet"
|
||||
allow_interpreter_success: true
|
||||
|
||||
- note: partial_object_every_vacuous_truth_collects_empty_arrays
|
||||
# TODO(#719): RVM currently includes the failing `bad` group here, while the
|
||||
# interpreter returns only `empty` and `ok` (expected per vacuous truth
|
||||
# semantics).
|
||||
skip: true
|
||||
data: {}
|
||||
input:
|
||||
groups:
|
||||
ok: [1, 2]
|
||||
bad: [1, 0]
|
||||
empty: []
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
p[k] if {
|
||||
some k, arr in input.groups
|
||||
every v in arr {
|
||||
v > 0
|
||||
}
|
||||
}
|
||||
query: data.test.p
|
||||
want_result:
|
||||
empty: true
|
||||
ok: true
|
||||
|
||||
- note: partial_object_join_var_multiple_bindings
|
||||
data:
|
||||
a: ["1", "2", "3", "4"]
|
||||
g:
|
||||
a: ["1", "0", "0", "0"]
|
||||
b: ["0", "2", "0", "0"]
|
||||
c: ["0", "0", "0", "4"]
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
p[k] := v if {
|
||||
data.a[i] = v
|
||||
data.g[k][i] = v
|
||||
}
|
||||
query: data.test.p
|
||||
want_result:
|
||||
a: "1"
|
||||
b: "2"
|
||||
c: "4"
|
||||
|
||||
- note: partial_object_composite_value
|
||||
data:
|
||||
g:
|
||||
a: [1, 0, 0, 0]
|
||||
b: [0, 2, 0, 0]
|
||||
c: [0, 0, 0, 4]
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
p[k] := [i, {"v2": v}] if {
|
||||
data.g[k] = x
|
||||
x[i] = v
|
||||
v != 0
|
||||
}
|
||||
query: data.test.p
|
||||
want_result:
|
||||
a: [0, {v2: 1}]
|
||||
b: [1, {v2: 2}]
|
||||
c: [3, {v2: 4}]
|
||||
|
||||
- note: partial_object_true_semantics_dedupes_duplicate_keys
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
p[k] if {
|
||||
ks := ["a", "b", "c", "a"]
|
||||
ks[_] = k
|
||||
}
|
||||
query: data.test.p
|
||||
want_result:
|
||||
a: true
|
||||
b: true
|
||||
c: true
|
||||
Reference in New Issue
Block a user