mirror of
https://github.com/open-policy-agent/opa.git
synced 2026-08-12 19:32:48 -06:00
Fix test cases to work with new syntax
This commit is contained in:
+7
-5
@@ -32,7 +32,7 @@ func TestCompare(t *testing.T) {
|
||||
{`{1: 2, 3: 4, 5: 6}`, `{1: 2, 3: 4}`, 1},
|
||||
|
||||
// Array comprehensions
|
||||
{`[ null | true ]`, `[ false | null ]`, -1},
|
||||
{`[null | true]`, `[false | null]`, -1},
|
||||
|
||||
// Expressions
|
||||
{`a = b`, `b = a`, -1},
|
||||
@@ -44,8 +44,8 @@ func TestCompare(t *testing.T) {
|
||||
{`a = b with input.foo as bar`, `a = b with input.foo as bar with input.baz as qux`, -1},
|
||||
|
||||
// Body
|
||||
{`a = b`, `a = b, b = a`, -1},
|
||||
{`a = b, b = a`, `a = b`, 1},
|
||||
{`a = b`, `a = b; b = a`, -1},
|
||||
{`a = b; b = a`, `a = b`, 1},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
var a, b interface{}
|
||||
@@ -72,9 +72,11 @@ func TestCompareModule(t *testing.T) {
|
||||
}
|
||||
|
||||
a = MustParseModule(`package a.b.c
|
||||
import input.x.y`)
|
||||
|
||||
import input.x.y`)
|
||||
b = MustParseModule(`package a.b.c
|
||||
import input.x.z`)
|
||||
|
||||
import input.x.z`)
|
||||
result = Compare(a, b)
|
||||
|
||||
if result != -1 {
|
||||
|
||||
+15
-15
@@ -47,9 +47,9 @@ type Compiler struct {
|
||||
// following module:
|
||||
//
|
||||
// package ex
|
||||
// p[1] :- true
|
||||
// p[2] :- true
|
||||
// q :- true
|
||||
// p[1] { true }
|
||||
// p[2] { true }
|
||||
// q = true
|
||||
//
|
||||
// root
|
||||
// |
|
||||
@@ -207,8 +207,8 @@ func (c *Compiler) Failed() bool {
|
||||
//
|
||||
// package a.b.c
|
||||
//
|
||||
// p[k] = v :- ... # rule1
|
||||
// p[k1] = v1 :- ... # rule2
|
||||
// p[k] = v { ... } # rule1
|
||||
// p[k1] = v1 { ... } # rule2
|
||||
//
|
||||
// The following calls yield the rules on the right.
|
||||
//
|
||||
@@ -235,8 +235,8 @@ func (c *Compiler) GetRulesExact(ref Ref) (rules []*Rule) {
|
||||
//
|
||||
// package a.b.c
|
||||
//
|
||||
// p[k] = v :- ... # rule1
|
||||
// p[k1] = v1 :- ... # rule2
|
||||
// p[k] = v { ... } # rule1
|
||||
// p[k1] = v1 { ... } # rule2
|
||||
//
|
||||
// The following calls yield the rules on the right.
|
||||
//
|
||||
@@ -266,9 +266,9 @@ func (c *Compiler) GetRulesForVirtualDocument(ref Ref) (rules []*Rule) {
|
||||
//
|
||||
// package a.b.c
|
||||
//
|
||||
// p[x] = y :- ... # rule1
|
||||
// p[k] = v :- ... # rule2
|
||||
// q :- ... # rule3
|
||||
// p[x] = y { ... } # rule1
|
||||
// p[k] = v { ... } # rule2
|
||||
// q { ... } # rule3
|
||||
//
|
||||
// The following calls yield the rules on the right.
|
||||
//
|
||||
@@ -306,8 +306,8 @@ func (c *Compiler) GetRulesWithPrefix(ref Ref) (rules []*Rule) {
|
||||
//
|
||||
// package a.b.c
|
||||
//
|
||||
// p[x] = y :- q[x] = y, ... # rule1
|
||||
// q[x] = y :- ... # rule2
|
||||
// p[x] = y { q[x] = y; ... } # rule1
|
||||
// q[x] = y { ... } # rule2
|
||||
//
|
||||
// The following calls yield the rules on the right.
|
||||
//
|
||||
@@ -516,7 +516,7 @@ func (c *Compiler) getExports() *util.HashMap {
|
||||
//
|
||||
// package a.b
|
||||
// import data.foo.bar
|
||||
// p[x] :- bar[_] = x
|
||||
// p[x] { bar[_] = x }
|
||||
//
|
||||
// The reference "bar[_]" would be resolved to "data.foo.bar[_]".
|
||||
func (c *Compiler) resolveAllRefs() {
|
||||
@@ -574,11 +574,11 @@ func (c *Compiler) resolveAllRefs() {
|
||||
//
|
||||
// For instance, given the following rule:
|
||||
//
|
||||
// p[{"foo": data.foo[i]}] :- i < 100
|
||||
// p[{"foo": data.foo[i]}] { i < 100 }
|
||||
//
|
||||
// The rule would be re-written as:
|
||||
//
|
||||
// p[__local0__] :- i < 100, __local0__ = {"foo": data.foo[i]}
|
||||
// p[__local0__] { i < 100; __local0__ = {"foo": data.foo[i]} }
|
||||
func (c *Compiler) rewriteRefsInHead() {
|
||||
for _, mod := range c.Modules {
|
||||
generator := newLocalVarGenerator(mod)
|
||||
|
||||
+263
-331
@@ -29,11 +29,11 @@ func TestRuleTree(t *testing.T) {
|
||||
|
||||
mods := getCompilerTestModules()
|
||||
|
||||
mods["mod-incr"] = MustParseModule(`
|
||||
package a.b.c
|
||||
s[1] :- true
|
||||
s[2] :- true
|
||||
`)
|
||||
mods["mod-incr"] = MustParseModule(`package a.b.c
|
||||
|
||||
s[1] { true }
|
||||
s[2] { true }`,
|
||||
)
|
||||
|
||||
tree := NewRuleTree(NewModuleTree(mods))
|
||||
expectedNumRules := 18
|
||||
@@ -67,14 +67,14 @@ func TestCompilerExample(t *testing.T) {
|
||||
func TestCompilerCheckSafetyHead(t *testing.T) {
|
||||
c := NewCompiler()
|
||||
c.Modules = getCompilerTestModules()
|
||||
c.Modules["newMod"] = MustParseModule(`
|
||||
package a.b
|
||||
unboundKey[x] = y :- q[y] = {"foo": [1,2,[{"bar": y}]]}
|
||||
unboundVal[y] = x :- q[y] = {"foo": [1,2,[{"bar": y}]]}
|
||||
unboundCompositeVal[y] = [{"foo": x, "bar": y}] :- q[y] = {"foo": [1,2,[{"bar": y}]]}
|
||||
unboundCompositeKey[[{"x": x}]] :- q[y]
|
||||
unboundBuiltinOperator = eq :- x = 1
|
||||
`)
|
||||
c.Modules["newMod"] = MustParseModule(`package a.b
|
||||
|
||||
unboundKey[x] = y { q[y] = {"foo": [1, 2, [{"bar": y}]]} }
|
||||
unboundVal[y] = x { q[y] = {"foo": [1, 2, [{"bar": y}]]} }
|
||||
unboundCompositeVal[y] = [{"foo": x, "bar": y}] { q[y] = {"foo": [1, 2, [{"bar": y}]]} }
|
||||
unboundCompositeKey[[{"x": x}]] { q[y] }
|
||||
unboundBuiltinOperator = eq { x = 1 }`,
|
||||
)
|
||||
compileStages(c, "", "checkSafetyHead")
|
||||
|
||||
makeErrMsg := func(rule, v string) string {
|
||||
@@ -110,20 +110,20 @@ func TestCompilerCheckSafetyBodyReordering(t *testing.T) {
|
||||
body string
|
||||
expected string
|
||||
}{
|
||||
{"noop", "x = 1, x != 0", "x = 1, x != 0"},
|
||||
{"var/ref", "a[i] = x, a = [1,2,3,4]", "a = [1,2,3,4], a[i] = x"},
|
||||
{"var/ref (nested)", "a = [1,2,3,4], a[b[i]] = x, b = [0,0,0,0]", "a = [1,2,3,4], b = [0,0,0,0], a[b[i]] = x"},
|
||||
{"noop", `x = 1; x != 0`, `x = 1; x != 0`},
|
||||
{"var/ref", `a[i] = x; a = [1, 2, 3, 4]`, `a = [1, 2, 3, 4]; a[i] = x`},
|
||||
{"var/ref (nested)", `a = [1, 2, 3, 4]; a[b[i]] = x; b = [0, 0, 0, 0]`, `a = [1, 2, 3, 4]; b = [0, 0, 0, 0]; a[b[i]] = x`},
|
||||
{"negation",
|
||||
"a = [true, false], b = [true, false], not a[i], b[i]",
|
||||
"a = [true, false], b = [true, false], b[i], not a[i]"},
|
||||
{"built-in", "x != 0, count([1,2,3], x)", "count([1,2,3], x), x != 0"},
|
||||
{"var/var 1", "x = y, z = 1, y = z", "z = 1, y = z, x = y"},
|
||||
{"var/var 2", "x = y, 1 = z, z = y", "1 = z, z = y, x = y"},
|
||||
{"var/var 3", "x != 0, y = x, y = 1", "y = 1, y = x, x != 0"},
|
||||
{"array compr/var", "x != 0, [y | y = 1] = x", "[y | y = 1] = x, x != 0"},
|
||||
{"array compr/array", "[1] != [x], [y | y = 1] = [x]", "[y | y = 1] = [x], [1] != [x]"},
|
||||
{"with", "data.a.b.d.t with input as x, x = 1", "x = 1, data.a.b.d.t with input as x"},
|
||||
{"with-2", "data.a.b.d.t with input.x as x, x = 1", "x = 1, data.a.b.d.t with input.x as x"},
|
||||
`a = [true, false]; b = [true, false]; not a[i]; b[i]`,
|
||||
`a = [true, false]; b = [true, false]; b[i]; not a[i]`},
|
||||
{"built-in", `x != 0; count([1, 2, 3], x)`, `count([1, 2, 3], x); x != 0`},
|
||||
{"var/var 1", `x = y; z = 1; y = z`, `z = 1; y = z; x = y`},
|
||||
{"var/var 2", `x = y; 1 = z; z = y`, `1 = z; z = y; x = y`},
|
||||
{"var/var 3", `x != 0; y = x; y = 1`, `y = 1; y = x; x != 0`},
|
||||
{"array compr/var", `x != 0; [y | y = 1] = x`, `[y | y = 1] = x; x != 0`},
|
||||
{"array compr/array", `[1] != [x]; [y | y = 1] = [x]`, `[y | y = 1] = [x]; [1] != [x]`},
|
||||
{"with", `data.a.b.d.t with input as x; x = 1`, `x = 1; data.a.b.d.t with input as x`},
|
||||
{"with-2", `data.a.b.d.t with input.x as x; x = 1`, `x = 1; data.a.b.d.t with input.x as x`},
|
||||
{"with-nop", "data.somedoc[x] with input as true", "data.somedoc[x] with input as true"},
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ func TestCompilerCheckSafetyBodyReordering(t *testing.T) {
|
||||
c.Modules = getCompilerTestModules()
|
||||
c.Modules["reordering"] = MustParseModule(fmt.Sprintf(
|
||||
`package test
|
||||
p :- %s`, tc.body))
|
||||
p { %s }`, tc.body))
|
||||
|
||||
compileStages(c, "", "checkSafetyBody")
|
||||
|
||||
@@ -154,55 +154,27 @@ func TestCompilerCheckSafetyBodyReorderingClosures(t *testing.T) {
|
||||
c := NewCompiler()
|
||||
c.Modules = map[string]*Module{
|
||||
"mod": MustParseModule(
|
||||
`
|
||||
package compr
|
||||
`package compr
|
||||
|
||||
import data.b
|
||||
import data.c
|
||||
import data.b
|
||||
import data.c
|
||||
|
||||
p :- v = [null | true], # leave untouched
|
||||
xs = [x | a[i] = x, a = [y | y != 1, y = c[j]]], # close over 'i' and 'j', 2-level reorder
|
||||
xs[j] > 0,
|
||||
z = [true | data.a.b.d.t with input as i2, i2 = i], # close over 'i'
|
||||
b[i] = j
|
||||
|
||||
# test that reordering is not performed when closing over different globals, e.g.,
|
||||
# built-ins, data, imports.
|
||||
q :- _ = [x | x = b[i]],
|
||||
_ = b[j],
|
||||
|
||||
_ = [x | x = true, x != false],
|
||||
true != false,
|
||||
|
||||
_ = [x | data.foo[_] = x],
|
||||
data.foo[_] = _
|
||||
`),
|
||||
p = true { v = [null | true]; xs = [x | a[i] = x; a = [y | y != 1; y = c[j]]]; xs[j] > 0; z = [true | data.a.b.d.t with input as i2; i2 = i]; b[i] = j }
|
||||
q = true { _ = [x | x = b[i]]; _ = b[j]; _ = [x | x = true; x != false]; true != false; _ = [x | data.foo[_] = x]; data.foo[_] = _ }`,
|
||||
),
|
||||
}
|
||||
|
||||
compileStages(c, "", "checkSafetyBody")
|
||||
assertNotFailed(t, c)
|
||||
|
||||
result1 := c.Modules["mod"].Rules[0].Body
|
||||
expected1 := MustParseBody(`
|
||||
v = [null | true],
|
||||
data.b[i] = j,
|
||||
xs = [x | a = [y | y = data.c[j], y != 1], a[i] = x],
|
||||
z = [true | i2 = i, data.a.b.d.t with input as i2],
|
||||
xs[j] > 0
|
||||
`)
|
||||
expected1 := MustParseBody(`v = [null | true]; data.b[i] = j; xs = [x | a = [y | y = data.c[j]; y != 1]; a[i] = x]; z = [true | i2 = i; data.a.b.d.t with input as i2]; xs[j] > 0`)
|
||||
if !result1.Equal(expected1) {
|
||||
t.Errorf("Expected reordered body to be equal to:\n%v\nBut got:\n%v", expected1, result1)
|
||||
}
|
||||
|
||||
result2 := c.Modules["mod"].Rules[1].Body
|
||||
expected2 := MustParseBody(`
|
||||
_ = [x | x = data.b[i]],
|
||||
_ = data.b[j],
|
||||
_ = [x | x = true, x != false],
|
||||
true != false,
|
||||
_ = [x | data.foo[_] = x],
|
||||
data.foo[_] = _
|
||||
`)
|
||||
expected2 := MustParseBody(`_ = [x | x = data.b[i]]; _ = data.b[j]; _ = [x | x = true; x != false]; true != false; _ = [x | data.foo[_] = x]; data.foo[_] = _`)
|
||||
if !result2.Equal(expected2) {
|
||||
t.Errorf("Expected pre-ordered body to equal:\n%v\nBut got:\n%v", expected2, result2)
|
||||
}
|
||||
@@ -213,67 +185,41 @@ func TestCompilerCheckSafetyBodyErrors(t *testing.T) {
|
||||
|
||||
c.Modules = getCompilerTestModules()
|
||||
c.Modules = map[string]*Module{
|
||||
"newMod": MustParseModule(`
|
||||
package a.b
|
||||
"newMod": MustParseModule(`package a.b
|
||||
|
||||
import input.aref.b.c as foo
|
||||
import input.avar as bar
|
||||
import data.m.n as baz
|
||||
import input.aref.b.c as foo
|
||||
import input.avar as bar
|
||||
import data.m.n as baz
|
||||
|
||||
# a would be unbound
|
||||
unboundRef1 = true :- a.b.c = "foo"
|
||||
|
||||
# a would be unbound
|
||||
unboundRef2 = true :- {"foo": [{"bar": a.b.c}]} = {"foo": [{"bar": "baz"}]}
|
||||
|
||||
# i will be bound even though it's in a non-output position
|
||||
inputPosRef = true :- a = [1,2,3,4], a[i] != 100
|
||||
|
||||
# i and x would be unbound
|
||||
unboundNegated1 = true :- a = [1,2,3,4], not a[i] = x
|
||||
|
||||
# i and x would be unbound even though x appears in head
|
||||
unboundNegated2[x] :- a = [1,2,3,4], not a[i] = x
|
||||
|
||||
# x, i, and j would be unbound even though they appear in other expressions
|
||||
unboundNegated3[x] = true :- a = [1,2,3,4], b = [1,2,3,4], not a[i] = x, not b[j] = x
|
||||
|
||||
# i and j would be unbound even though they are in embedded references
|
||||
unboundNegated4 = true :- a = [{"foo": ["bar", "baz"]}], not a[0].foo = [a[0].foo[i], a[0].foo[j]]
|
||||
|
||||
# x would be unbound as input to count
|
||||
unsafeBuiltin :- count([1,2,x], x)
|
||||
unsafeBuiltinOperator :- count(eq, 1)
|
||||
|
||||
# i and x would be bound in the last expression so the third expression is safe
|
||||
negatedSafe = true :- a = [1,2,3,4], b = [1,2,3,4], not a[i] = x, b[i] = x
|
||||
|
||||
# x would be unbound because it does not appear in the target position of any expression
|
||||
unboundNoTarget = true :- x > 0, x <= 3, x != 2
|
||||
|
||||
unboundArrayComprBody1 :- _ = [x | x = data.a[_], y > 1]
|
||||
unboundArrayComprBody2 :- _ = [x | x = a[_], a = [y | y = data.a[_], z > 1]]
|
||||
unboundArrayComprBody3 :- _ = [v | v = [x | x = data.a[_]], x > 1]
|
||||
unboundArrayComprTerm1 :- _ = [u | true]
|
||||
unboundArrayComprTerm2 :- _ = [v | v = [w | w != 0]]
|
||||
unboundArrayComprTerm3 :- _ = [x[i] | x = []]
|
||||
unboundArrayComprMixed1 :- _ = [x | y = [a | a = z[i]]]
|
||||
unboundBuiltinOperatorArrayCompr :- 1 = 1, [true | eq != 2]
|
||||
|
||||
unsafeClosure1 :- x = [x | x = 1]
|
||||
unsafeClosure2 :- x = y, x = [y | y = 1]
|
||||
|
||||
unsafeNestedHead :- count(baz[i].attr[bar[dead.beef]], n)
|
||||
|
||||
negatedImport1 = true :- not foo
|
||||
negatedImport2 = true :- not bar
|
||||
negatedImport3 = true :- not baz
|
||||
|
||||
rewriteUnsafe[{"foo": dead[i]}] :- true # dead is not imported
|
||||
|
||||
unsafeWithValue1 :- data.a.b.d.t with input as x
|
||||
unsafeWithValue2 :- x = data.a.b.d.t with input as x
|
||||
`)}
|
||||
unboundRef1 = true { a.b.c = "foo" }
|
||||
unboundRef2 = true { {"foo": [{"bar": a.b.c}]} = {"foo": [{"bar": "baz"}]} }
|
||||
inputPosRef = true { a = [1, 2, 3, 4]; a[i] != 100 }
|
||||
unboundNegated1 = true { a = [1, 2, 3, 4]; not a[i] = x }
|
||||
unboundNegated2[x] { a = [1, 2, 3, 4]; not a[i] = x }
|
||||
unboundNegated3[x] = true { a = [1, 2, 3, 4]; b = [1, 2, 3, 4]; not a[i] = x; not b[j] = x }
|
||||
unboundNegated4 = true { a = [{"foo": ["bar", "baz"]}]; not a[0].foo = [a[0].foo[i], a[0].foo[j]] }
|
||||
unsafeBuiltin = true { count([1, 2, x], x) }
|
||||
unsafeBuiltinOperator = true { count(eq, 1) }
|
||||
negatedSafe = true { a = [1, 2, 3, 4]; b = [1, 2, 3, 4]; not a[i] = x; b[i] = x }
|
||||
unboundNoTarget = true { x > 0; x <= 3; x != 2 }
|
||||
unboundArrayComprBody1 = true { _ = [x | x = data.a[_]; y > 1] }
|
||||
unboundArrayComprBody2 = true { _ = [x | x = a[_]; a = [y | y = data.a[_]; z > 1]] }
|
||||
unboundArrayComprBody3 = true { _ = [v | v = [x | x = data.a[_]]; x > 1] }
|
||||
unboundArrayComprTerm1 = true { _ = [u | true] }
|
||||
unboundArrayComprTerm2 = true { _ = [v | v = [w | w != 0]] }
|
||||
unboundArrayComprTerm3 = true { _ = [x[i] | x = []] }
|
||||
unboundArrayComprMixed1 = true { _ = [x | y = [a | a = z[i]]] }
|
||||
unboundBuiltinOperatorArrayCompr = true { 1 = 1; [true | eq != 2] }
|
||||
unsafeClosure1 = true { x = [x | x = 1] }
|
||||
unsafeClosure2 = true { x = y; x = [y | y = 1] }
|
||||
unsafeNestedHead = true { count(baz[i].attr[bar[dead.beef]], n) }
|
||||
negatedImport1 = true { not foo }
|
||||
negatedImport2 = true { not bar }
|
||||
negatedImport3 = true { not baz }
|
||||
rewriteUnsafe[{"foo": dead[i]}] { true }
|
||||
unsafeWithValue1 = true { data.a.b.d.t with input as x }
|
||||
unsafeWithValue2 = true { x = data.a.b.d.t with input as x }`,
|
||||
)}
|
||||
compileStages(c, "", "checkSafetyBody")
|
||||
|
||||
makeErrMsg := func(rule string, varName string) string {
|
||||
@@ -331,16 +277,15 @@ func TestCompilerCheckWithModifiers(t *testing.T) {
|
||||
|
||||
c := NewCompiler()
|
||||
c.Modules = getCompilerTestModules()
|
||||
c.Modules["with-modifiers"] = MustParseModule(`
|
||||
package badwith
|
||||
c.Modules["with-modifiers"] = MustParseModule(`package badwith
|
||||
|
||||
import data.a.b.d.t as req_dep
|
||||
import data.a.b.d.t as req_dep
|
||||
|
||||
p = true
|
||||
ref_in_value :- req_dep with input as p
|
||||
closure_in_value :- req_dep with input as [null | null]
|
||||
data_target :- req_dep with data.p as "foo"
|
||||
`)
|
||||
p = true { true }
|
||||
ref_in_value = true { req_dep with input as p }
|
||||
closure_in_value = true { req_dep with input as [null | null] }
|
||||
data_target = true { req_dep with data.p as "foo" }`,
|
||||
)
|
||||
|
||||
compileStages(c, "", "checkWithModifiers")
|
||||
|
||||
@@ -357,18 +302,18 @@ func TestCompilerCheckWithModifiers(t *testing.T) {
|
||||
func TestCompilerCheckBuiltins(t *testing.T) {
|
||||
c := NewCompiler()
|
||||
c.Modules = map[string]*Module{
|
||||
"mod": MustParseModule(`
|
||||
package badbuiltin
|
||||
p :- count(1)
|
||||
q :- count([1,2,3], x, 1)
|
||||
r :- [ x | deadbeef(1,2,x) ]
|
||||
`),
|
||||
"mod": MustParseModule(`package badbuiltin
|
||||
|
||||
p = true { count(1) }
|
||||
q = true { count([1, 2, 3], x, 1) }
|
||||
r = true { [x | deadbeef(1, 2, x)] }`,
|
||||
),
|
||||
}
|
||||
compileStages(c, "", "checkBuiltins")
|
||||
|
||||
expected := []string{
|
||||
"p: wrong number of arguments (expression count(1) must specify 2 arguments to built-in function count)",
|
||||
"q: wrong number of arguments (expression count([1,2,3], x, 1) must specify 2 arguments to built-in function count)",
|
||||
"q: wrong number of arguments (expression count([1, 2, 3], x, 1) must specify 2 arguments to built-in function count)",
|
||||
"r: unknown built-in function deadbeef",
|
||||
}
|
||||
|
||||
@@ -378,26 +323,24 @@ func TestCompilerCheckBuiltins(t *testing.T) {
|
||||
func TestCompilerCheckRuleConflicts(t *testing.T) {
|
||||
|
||||
c := getCompilerWithParsedModules(map[string]string{
|
||||
"mod1.rego": `
|
||||
package badrules
|
||||
p[x] :- x = 1
|
||||
p[x] = y :- x = y, x = "a"
|
||||
q[1] :- true
|
||||
q = {1,2,3} :- true
|
||||
r[x] = y :- x = y, x = "a"
|
||||
r[x] = y :- x = y, x = "a"
|
||||
`,
|
||||
"mod2.rego": `
|
||||
package badrules.r
|
||||
q[1] :- true
|
||||
`,
|
||||
"mod3.rego": `
|
||||
package badrules.defkw
|
||||
"mod1.rego": `package badrules
|
||||
|
||||
default foo = 1
|
||||
default foo = 2
|
||||
foo = 3
|
||||
`,
|
||||
p[x] { x = 1 }
|
||||
p[x] = y { x = y; x = "a" }
|
||||
q[1] { true }
|
||||
q = {1, 2, 3} { true }
|
||||
r[x] = y { x = y; x = "a" }
|
||||
r[x] = y { x = y; x = "a" }`,
|
||||
|
||||
"mod2.rego": `package badrules.r
|
||||
|
||||
q[1] { true }`,
|
||||
|
||||
"mod3.rego": `package badrules.defkw
|
||||
|
||||
default foo = 1
|
||||
default foo = 2
|
||||
foo = 3 { true }`,
|
||||
})
|
||||
|
||||
compileStages(c, "", "checkRuleConflicts")
|
||||
@@ -416,16 +359,15 @@ func TestCompilerCheckRuleConflicts(t *testing.T) {
|
||||
func TestCompilerImportsResolved(t *testing.T) {
|
||||
|
||||
modules := map[string]*Module{
|
||||
"mod1": MustParseModule(`
|
||||
package ex
|
||||
"mod1": MustParseModule(`package ex
|
||||
|
||||
import data
|
||||
import input
|
||||
import data.foo
|
||||
import input.bar
|
||||
import data.abc as baz
|
||||
import input.abc as qux
|
||||
`),
|
||||
import data
|
||||
import input
|
||||
import data.foo
|
||||
import input.bar
|
||||
import data.abc as baz
|
||||
import input.abc as qux`,
|
||||
),
|
||||
}
|
||||
|
||||
c := NewCompiler()
|
||||
@@ -443,11 +385,12 @@ func TestCompilerResolveAllRefs(t *testing.T) {
|
||||
c := NewCompiler()
|
||||
c.Modules = getCompilerTestModules()
|
||||
c.Modules["head"] = MustParseModule(`package head
|
||||
import data.doc1 as bar
|
||||
import input.x.y.foo
|
||||
import input.qux as baz
|
||||
p[foo[bar[i]]] = {"baz": baz} :- true
|
||||
`)
|
||||
|
||||
import data.doc1 as bar
|
||||
import input.x.y.foo
|
||||
import input.qux as baz
|
||||
|
||||
p[foo[bar[i]]] = {"baz": baz} { true }`)
|
||||
compileStages(c, "", "resolveAllRefs")
|
||||
assertNotFailed(t, c)
|
||||
|
||||
@@ -525,35 +468,27 @@ func TestCompilerResolveAllRefs(t *testing.T) {
|
||||
func TestCompilerRewriteTermsInHead(t *testing.T) {
|
||||
c := NewCompiler()
|
||||
c.Modules["head"] = MustParseModule(`package head
|
||||
import data.doc1 as bar
|
||||
import data.doc2 as corge
|
||||
import input.x.y.foo
|
||||
import input.qux as baz
|
||||
p[foo[bar[i]]] = {"baz": baz, "corge": corge} :- true
|
||||
q = [true | true] :- true
|
||||
`)
|
||||
|
||||
import data.doc1 as bar
|
||||
import data.doc2 as corge
|
||||
import input.x.y.foo
|
||||
import input.qux as baz
|
||||
|
||||
p[foo[bar[i]]] = {"baz": baz, "corge": corge} { true }
|
||||
q = [true | true] { true }`)
|
||||
|
||||
compileStages(c, "", "rewriteRefsInHead")
|
||||
assertNotFailed(t, c)
|
||||
|
||||
rule1 := c.Modules["head"].Rules[0]
|
||||
|
||||
expected1 := MustParseRule(`
|
||||
p[__local0__] = __local1__ :-
|
||||
true,
|
||||
__local0__ = input.x.y.foo[data.doc1[i]],
|
||||
__local1__ = {"baz": input.qux, "corge": data.doc2}
|
||||
`)
|
||||
expected1 := MustParseRule(`p[__local0__] = __local1__ { true; __local0__ = input.x.y.foo[data.doc1[i]]; __local1__ = {"baz": input.qux, "corge": data.doc2} }`)
|
||||
|
||||
assertRulesEqual(t, rule1, expected1)
|
||||
|
||||
rule2 := c.Modules["head"].Rules[1]
|
||||
|
||||
expected2 := MustParseRule(`
|
||||
q = __local2__ :-
|
||||
true,
|
||||
__local2__ = [true | true]
|
||||
`)
|
||||
expected2 := MustParseRule(`q = __local2__ { true; __local2__ = [true | true] }`)
|
||||
|
||||
assertRulesEqual(t, rule2, expected2)
|
||||
}
|
||||
@@ -585,51 +520,51 @@ func TestCompilerSetRuleGraph(t *testing.T) {
|
||||
func TestCompilerCheckRecursion(t *testing.T) {
|
||||
c := NewCompiler()
|
||||
c.Modules = map[string]*Module{
|
||||
"newMod1": MustParseModule(`
|
||||
package rec
|
||||
s = true :- t
|
||||
t = true :- s
|
||||
a = true :- b
|
||||
b = true :- c
|
||||
c = true :- d, e
|
||||
d = true :- true
|
||||
e = true :- a`),
|
||||
"newMod2": MustParseModule(`
|
||||
package rec
|
||||
x = true :- s
|
||||
`),
|
||||
"newMod3": MustParseModule(`
|
||||
package rec2
|
||||
import data.rec.x
|
||||
y = true :- x
|
||||
`),
|
||||
"newMod4": MustParseModule(`
|
||||
package rec3
|
||||
p[x] = y :- data.rec4[x][y] = z
|
||||
`),
|
||||
"newMod5": MustParseModule(`
|
||||
package rec4
|
||||
import data.rec3.p
|
||||
q[x] = y :- p[x] = y
|
||||
`),
|
||||
"newMod6": MustParseModule(`
|
||||
package rec5
|
||||
acp[x] :- acq[x]
|
||||
acq[x] :- a = [x | acp[x]], a[i] = x
|
||||
`),
|
||||
"newMod7": MustParseModule(`
|
||||
package rec6
|
||||
np[x] = y :- data.a[data.b.c[nq[x]]] = y
|
||||
nq[x] = y :- data.d[data.e[x].f[np[y]]]
|
||||
`),
|
||||
"newMod8": MustParseModule(`
|
||||
package rec7
|
||||
prefix :- data.rec7
|
||||
`),
|
||||
"newMod9": MustParseModule(`
|
||||
package rec8
|
||||
dataref :- data
|
||||
`),
|
||||
"newMod1": MustParseModule(`package rec
|
||||
|
||||
s = true { t }
|
||||
t = true { s }
|
||||
a = true { b }
|
||||
b = true { c }
|
||||
c = true { d; e }
|
||||
d = true { true }
|
||||
e = true { a }`),
|
||||
"newMod2": MustParseModule(`package rec
|
||||
|
||||
x = true { s }`,
|
||||
),
|
||||
"newMod3": MustParseModule(`package rec2
|
||||
|
||||
import data.rec.x
|
||||
|
||||
y = true { x }`),
|
||||
"newMod4": MustParseModule(`package rec3
|
||||
|
||||
p[x] = y { data.rec4[x][y] = z }`,
|
||||
),
|
||||
"newMod5": MustParseModule(`package rec4
|
||||
|
||||
import data.rec3.p
|
||||
|
||||
q[x] = y { p[x] = y }`),
|
||||
"newMod6": MustParseModule(`package rec5
|
||||
|
||||
acp[x] { acq[x] }
|
||||
acq[x] { a = [x | acp[x]]; a[i] = x }`,
|
||||
),
|
||||
"newMod7": MustParseModule(`package rec6
|
||||
|
||||
np[x] = y { data.a[data.b.c[nq[x]]] = y }
|
||||
nq[x] = y { data.d[data.e[x].f[np[y]]] }`,
|
||||
),
|
||||
"newMod8": MustParseModule(`package rec7
|
||||
|
||||
prefix = true { data.rec7 }`,
|
||||
),
|
||||
"newMod9": MustParseModule(`package rec8
|
||||
|
||||
dataref = true { data }`,
|
||||
),
|
||||
}
|
||||
|
||||
compileStages(c, "", "checkRecursion")
|
||||
@@ -673,11 +608,11 @@ func TestCompilerGetRulesExact(t *testing.T) {
|
||||
mods := getCompilerTestModules()
|
||||
|
||||
// Add incrementally defined rules.
|
||||
mods["mod-incr"] = MustParseModule(`
|
||||
package a.b.c
|
||||
p[1] :- true
|
||||
p[2] :- true
|
||||
`)
|
||||
mods["mod-incr"] = MustParseModule(`package a.b.c
|
||||
|
||||
p[1] { true }
|
||||
p[2] { true }`,
|
||||
)
|
||||
|
||||
c := NewCompiler()
|
||||
c.Compile(mods)
|
||||
@@ -731,11 +666,11 @@ func TestCompilerGetRulesForVirtualDocument(t *testing.T) {
|
||||
mods := getCompilerTestModules()
|
||||
|
||||
// Add incrementally defined rules.
|
||||
mods["mod-incr"] = MustParseModule(`
|
||||
package a.b.c
|
||||
p[1] :- true
|
||||
p[2] :- true
|
||||
`)
|
||||
mods["mod-incr"] = MustParseModule(`package a.b.c
|
||||
|
||||
p[1] { true }
|
||||
p[2] { true }`,
|
||||
)
|
||||
|
||||
c := NewCompiler()
|
||||
c.Compile(mods)
|
||||
@@ -793,12 +728,12 @@ func TestCompilerGetRulesWithPrefix(t *testing.T) {
|
||||
mods := getCompilerTestModules()
|
||||
|
||||
// Add incrementally defined rules.
|
||||
mods["mod-incr"] = MustParseModule(`
|
||||
package a.b.c
|
||||
p[1] :- true
|
||||
p[2] :- true
|
||||
q[3] :- true
|
||||
`)
|
||||
mods["mod-incr"] = MustParseModule(`package a.b.c
|
||||
|
||||
p[1] { true }
|
||||
p[2] { true }
|
||||
q[3] { true }`,
|
||||
)
|
||||
|
||||
c := NewCompiler()
|
||||
c.Compile(mods)
|
||||
@@ -858,13 +793,11 @@ func TestCompilerGetRulesWithPrefix(t *testing.T) {
|
||||
|
||||
func TestCompilerGetRules(t *testing.T) {
|
||||
compiler := getCompilerWithParsedModules(map[string]string{
|
||||
"mod1": `
|
||||
package a.b.c
|
||||
"mod1": `package a.b.c
|
||||
|
||||
p[x] = y :- q[x] = y # rule1
|
||||
q["a"] = 1 :- true # rule2
|
||||
q["b"] = 2 :- true # rule3
|
||||
`,
|
||||
p[x] = y { q[x] = y }
|
||||
q["a"] = 1 { true }
|
||||
q["b"] = 2 { true }`,
|
||||
})
|
||||
|
||||
compileStages(compiler, "", "")
|
||||
@@ -925,30 +858,34 @@ func TestCompilerLazyLoadingError(t *testing.T) {
|
||||
|
||||
func TestCompilerLazyLoading(t *testing.T) {
|
||||
|
||||
mod1 := MustParseModule(`
|
||||
package a.b.c
|
||||
import data.x.z1 as z2
|
||||
p :- q, r
|
||||
q :- z2`)
|
||||
mod1 := MustParseModule(`package a.b.c
|
||||
|
||||
mod2 := MustParseModule(`
|
||||
package a.b.c
|
||||
r = true`)
|
||||
import data.x.z1 as z2
|
||||
|
||||
p = true { q; r }
|
||||
q = true { z2 }`)
|
||||
|
||||
mod2 := MustParseModule(`package a.b.c
|
||||
|
||||
r = true { true }`)
|
||||
|
||||
mod3 := MustParseModule(`package x
|
||||
import data.foo.bar
|
||||
import input.input
|
||||
z1 :- [ localvar | count(bar.baz.qux, localvar) ]`)
|
||||
|
||||
mod4 := MustParseModule(`
|
||||
package foo.bar.baz
|
||||
qux = grault :- true`)
|
||||
import data.foo.bar
|
||||
import input.input
|
||||
|
||||
mod5 := MustParseModule(`
|
||||
package foo.bar.baz
|
||||
import data.d.e.f
|
||||
deadbeef = f :- true
|
||||
grault = deadbeef :- true`)
|
||||
z1 = true { [localvar | count(bar.baz.qux, localvar)] }`)
|
||||
|
||||
mod4 := MustParseModule(`package foo.bar.baz
|
||||
|
||||
qux = grault { true }`)
|
||||
|
||||
mod5 := MustParseModule(`package foo.bar.baz
|
||||
|
||||
import data.d.e.f
|
||||
|
||||
deadbeef = f { true }
|
||||
grault = deadbeef { true }`)
|
||||
|
||||
// testLoader will return 4 rounds of parsed modules.
|
||||
rounds := []map[string]*Module{
|
||||
@@ -965,30 +902,30 @@ func TestCompilerLazyLoading(t *testing.T) {
|
||||
// collection.
|
||||
},
|
||||
func(partial map[string]*Module) {
|
||||
p := MustParseRule(`p :- data.a.b.c.q, data.a.b.c.r`)
|
||||
p := MustParseRule(`p = true { data.a.b.c.q; data.a.b.c.r }`)
|
||||
if !partial["mod1"].Rules[0].Equal(p) {
|
||||
t.Errorf("Expected %v but got %v", p, partial["mod1"].Rules[0])
|
||||
}
|
||||
q := MustParseRule(`q :- data.x.z1`)
|
||||
q := MustParseRule(`q = true { data.x.z1 }`)
|
||||
if !partial["mod1"].Rules[1].Equal(q) {
|
||||
t.Errorf("Expected %v but got %v", q, partial["mod1"].Rules[0])
|
||||
}
|
||||
},
|
||||
func(partial map[string]*Module) {
|
||||
z1 := MustParseRule(`z1 :- [ localvar | count(data.foo.bar.baz.qux, localvar) ]`)
|
||||
z1 := MustParseRule(`z1 = true { [localvar | count(data.foo.bar.baz.qux, localvar)] }`)
|
||||
if !partial["mod3"].Rules[0].Equal(z1) {
|
||||
t.Errorf("Expected %v but got %v", z1, partial["mod3"].Rules[0])
|
||||
}
|
||||
},
|
||||
func(partial map[string]*Module) {
|
||||
qux := MustParseRule(`qux = grault :- true`)
|
||||
qux := MustParseRule(`qux = grault { true }`)
|
||||
if !partial["mod4"].Rules[0].Equal(qux) {
|
||||
t.Errorf("Expected %v but got %v", qux, partial["mod4"].Rules[0])
|
||||
}
|
||||
},
|
||||
func(partial map[string]*Module) {
|
||||
grault := MustParseRule("qux = data.foo.bar.baz.grault :- true") // rewrite has not happened yet
|
||||
f := MustParseRule("deadbeef = data.d.e.f :- true")
|
||||
grault := MustParseRule(`qux = data.foo.bar.baz.grault { true }`) // rewrite has not happened yet
|
||||
f := MustParseRule(`deadbeef = data.d.e.f { true }`)
|
||||
if !partial["mod4"].Rules[0].Equal(grault) {
|
||||
t.Errorf("Expected %v but got %v", grault, partial["mod4"].Rules[0])
|
||||
}
|
||||
@@ -1026,17 +963,17 @@ func TestQueryCompiler(t *testing.T) {
|
||||
input string
|
||||
expected interface{}
|
||||
}{
|
||||
{"exports resolved", "z", "package a.b.c", nil, "", "data.a.b.c.z"},
|
||||
{"imports resolved", "z", "package a.b.c.d", []string{"import data.a.b.c.z"}, "", "data.a.b.c.z"},
|
||||
{"exports resolved", "z", `package a.b.c`, nil, "", "data.a.b.c.z"},
|
||||
{"imports resolved", "z", `package a.b.c.d`, []string{"import data.a.b.c.z"}, "", "data.a.b.c.z"},
|
||||
{"unsafe vars", "z", "", nil, "", fmt.Errorf("1 error occurred: 1:1: z is unsafe (variable z must appear in the output position of at least one non-negated expression)")},
|
||||
{"safe vars", "data, abc", "package ex", []string{"import input.xyz as abc"}, `{}`, "data, input.xyz"},
|
||||
{"reorder", "x != 1, x = 0", "", nil, "", "x = 0, x != 1"},
|
||||
{"safe vars", `data; abc`, `package ex`, []string{"import input.xyz as abc"}, `{}`, `data; input.xyz`},
|
||||
{"reorder", `x != 1; x = 0`, "", nil, "", `x = 0; x != 1`},
|
||||
{"bad builtin", "deadbeef(1,2,3)", "", nil, "", fmt.Errorf("1 error occurred: 1:1: unknown built-in function deadbeef")},
|
||||
{"bad with target", "x = 1 with data.p as null", "", nil, "", fmt.Errorf("1 error occurred: 1:7: with target must be input (got data.p as target)")},
|
||||
// wrapping refs in extra terms to cover error handling
|
||||
{"undefined input", "[[ true | [data.a.b.d.t, true]], true]", "", nil, "", fmt.Errorf("4:14: input document undefined")},
|
||||
{"conflicting input", "[ true | data.a.b.d.t with input as 1 ]", "", nil, "2", fmt.Errorf("1:10: conflicting input document")},
|
||||
{"conflicting input-2", "sum([1 | data.a.b.d.t with input as 2], x) with input as 3", "", nil, "", fmt.Errorf("1:10: conflicting input document")},
|
||||
{"undefined input", `[[true | [data.a.b.d.t, true]], true]`, "", nil, "", fmt.Errorf("5:12: input document undefined")},
|
||||
{"conflicting input", `[true | data.a.b.d.t with input as 1]`, "", nil, "2", fmt.Errorf("1:9: conflicting input document")},
|
||||
{"conflicting input-2", `sum([1 | data.a.b.d.t with input as 2], x) with input as 3`, "", nil, "", fmt.Errorf("1:10: conflicting input document")},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
@@ -1105,61 +1042,56 @@ func compileStages(c *Compiler, from string, to string) {
|
||||
|
||||
func getCompilerTestModules() map[string]*Module {
|
||||
|
||||
mod1 := MustParseModule(`
|
||||
package a.b.c
|
||||
mod1 := MustParseModule(`package a.b.c
|
||||
|
||||
import data.x.y.z as foo
|
||||
import data.g.h.k
|
||||
import data.x.y.z as foo
|
||||
import data.g.h.k
|
||||
|
||||
p[x] :- q[x], not r[x]
|
||||
q[x] :- foo[i] = x
|
||||
z = 400
|
||||
`)
|
||||
p[x] { q[x]; not r[x] }
|
||||
q[x] { foo[i] = x }
|
||||
z = 400 { true }`,
|
||||
)
|
||||
|
||||
mod2 := MustParseModule(`
|
||||
package a.b.c
|
||||
import data.bar
|
||||
import data.x.y.p
|
||||
r[x] :- bar[x] = 100, p = 101
|
||||
`)
|
||||
mod2 := MustParseModule(`package a.b.c
|
||||
|
||||
mod3 := MustParseModule(`
|
||||
package a.b.d
|
||||
import input.x as y
|
||||
t = true :- input = {y.secret: [{y.keyid}]}
|
||||
x = false :- true
|
||||
`)
|
||||
import data.bar
|
||||
import data.x.y.p
|
||||
|
||||
mod4 := MustParseModule(`
|
||||
package a.b.empty
|
||||
`)
|
||||
r[x] { bar[x] = 100; p = 101 }`)
|
||||
|
||||
mod5 := MustParseModule(`
|
||||
package a.b.compr
|
||||
mod3 := MustParseModule(`package a.b.d
|
||||
|
||||
import input.x as y
|
||||
import input.a.b.c.q
|
||||
import input.x as y
|
||||
|
||||
p :- [y.a | true]
|
||||
r :- [q.a | true]
|
||||
s :- [true | y.a = 0]
|
||||
t :- [true | q[i] = 1]
|
||||
u :- [true | _ = [y.a | true]]
|
||||
v :- [true | _ = [ true | q[i] = 1]]
|
||||
`)
|
||||
t = true { input = {y.secret: [{y.keyid}]} }
|
||||
x = false { true }`)
|
||||
|
||||
mod6 := MustParseModule(`
|
||||
package a.b.nested
|
||||
mod4 := MustParseModule(`package a.b.empty`)
|
||||
|
||||
import data.x
|
||||
import data.z
|
||||
import input.x as y
|
||||
mod5 := MustParseModule(`package a.b.compr
|
||||
|
||||
p :- x[y[i].a[z.b[j]]]
|
||||
q :- x = v, v[y[i]]
|
||||
r = 1 :- true
|
||||
s :- x[r]
|
||||
`)
|
||||
import input.x as y
|
||||
import input.a.b.c.q
|
||||
|
||||
p = true { [y.a | true] }
|
||||
r = true { [q.a | true] }
|
||||
s = true { [true | y.a = 0] }
|
||||
t = true { [true | q[i] = 1] }
|
||||
u = true { [true | _ = [y.a | true]] }
|
||||
v = true { [true | _ = [true | q[i] = 1]] }`,
|
||||
)
|
||||
|
||||
mod6 := MustParseModule(`package a.b.nested
|
||||
|
||||
import data.x
|
||||
import data.z
|
||||
import input.x as y
|
||||
|
||||
p = true { x[y[i].a[z.b[j]]] }
|
||||
q = true { x = v; v[y[i]] }
|
||||
r = 1 { true }
|
||||
s = true { x[r] }`,
|
||||
)
|
||||
|
||||
return map[string]*Module{
|
||||
"mod1": mod1,
|
||||
|
||||
+13
-23
@@ -13,18 +13,13 @@ import (
|
||||
func ExampleCompiler_Compile() {
|
||||
|
||||
// Define an input module that will be compiled.
|
||||
exampleModule := `
|
||||
exampleModule := `package opa.example
|
||||
|
||||
package opa.example
|
||||
import data.foo
|
||||
import input.bar
|
||||
|
||||
import data.foo
|
||||
import input.bar
|
||||
|
||||
p[x] :- foo[x], not bar[x], x >= min_x
|
||||
|
||||
min_x = 100
|
||||
|
||||
`
|
||||
p[x] { foo[x]; not bar[x]; x >= min_x }
|
||||
min_x = 100 { true }`
|
||||
|
||||
// Parse the input module to obtain the AST representation.
|
||||
mod, err := ast.ParseModule("my_module", exampleModule)
|
||||
@@ -57,18 +52,13 @@ func ExampleCompiler_Compile() {
|
||||
func ExampleQueryCompiler_Compile() {
|
||||
|
||||
// Define an input module that will be compiled.
|
||||
exampleModule := `
|
||||
exampleModule := `package opa.example
|
||||
|
||||
package opa.example
|
||||
import data.foo
|
||||
import input.bar
|
||||
|
||||
import data.foo
|
||||
import input.bar
|
||||
|
||||
p[x] :- foo[x], not bar[x], x >= min_x
|
||||
|
||||
min_x = 100
|
||||
|
||||
`
|
||||
p[x] { foo[x]; not bar[x]; x >= min_x }
|
||||
min_x = 100 { true }`
|
||||
|
||||
// Parse the input module to obtain the AST representation.
|
||||
mod, err := ast.ParseModule("my_module", exampleModule)
|
||||
@@ -98,13 +88,13 @@ func ExampleQueryCompiler_Compile() {
|
||||
// ast.Parse<X> functions that return meaningful error messages
|
||||
// instead.
|
||||
ast.NewQueryContext().
|
||||
WithPackage(ast.MustParsePackage("package opa.example")).
|
||||
WithPackage(ast.MustParsePackage(`package opa.example`)).
|
||||
WithImports(ast.MustParseImports("import input.query_arg")).
|
||||
WithInput(ast.MustParseTerm(`{"query_arg": 1000, "bar": [1,2,3]}`).Value),
|
||||
)
|
||||
|
||||
// Parse the input query to obtain the AST representation.
|
||||
query, err := ast.ParseBody("p[x], x < query_arg")
|
||||
query, err := ast.ParseBody(`p[x]; x < query_arg`)
|
||||
if err != nil {
|
||||
fmt.Println("Parse error:", err)
|
||||
}
|
||||
@@ -118,5 +108,5 @@ func ExampleQueryCompiler_Compile() {
|
||||
|
||||
// Output:
|
||||
//
|
||||
// Compiled: data.opa.example.p[x], x < input.query_arg
|
||||
// Compiled: data.opa.example.p[x]; x < input.query_arg
|
||||
}
|
||||
|
||||
+685
-620
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -118,7 +118,7 @@ func MustParseTerm(input string) *Term {
|
||||
|
||||
// ParseRuleFromBody attempts to return a rule from a body. Equality expressions
|
||||
// of the form <var> = <term> can be converted into rules of the form <var> =
|
||||
// <term> :- true. This is a concise way of defining constants inside modules.
|
||||
// <term> { true }. This is a concise way of defining constants inside modules.
|
||||
func ParseRuleFromBody(body Body) (*Rule, error) {
|
||||
|
||||
if len(body) != 1 {
|
||||
|
||||
+155
-148
@@ -15,24 +15,24 @@ import (
|
||||
var _ = fmt.Printf
|
||||
|
||||
const (
|
||||
testModule = `
|
||||
package opa.examples # this policy belongs the opa.examples package
|
||||
testModule = `package opa.examples
|
||||
|
||||
import data.servers # import the data.servers document to refer to it as "servers" instead of "data.servers"
|
||||
import data.networks # same but for data.networks
|
||||
import data.ports # same but for data.ports
|
||||
import data.servers
|
||||
import data.networks
|
||||
import data.ports
|
||||
|
||||
violations[server] :- # a server exists in the violations set if:
|
||||
server = servers[i], # the server exists in the servers collection
|
||||
server.protocols[j] = "http", # and the server has http in its protocols collection
|
||||
public_servers[server] # and the server exists in the public_servers set
|
||||
violations[server] {
|
||||
server = servers[i]
|
||||
server.protocols[j] = "http"
|
||||
public_servers[server]
|
||||
}
|
||||
|
||||
public_servers[server] :- # a server exists in the public_servers set if:
|
||||
server = servers[i], # the server exists in the servers collection
|
||||
server.ports[j] = ports[k].id, # and the server is connected to a port in the ports collection
|
||||
ports[k].networks[l] = networks[m].id, # and the port is connected to a network in the networks collection
|
||||
networks[m].public = true # and the network is public
|
||||
`
|
||||
public_servers[server] {
|
||||
server = servers[i]
|
||||
server.ports[j] = ports[k].id
|
||||
ports[k].networks[l] = networks[m].id
|
||||
networks[m].public = true
|
||||
}`
|
||||
)
|
||||
|
||||
func TestNumberTerms(t *testing.T) {
|
||||
@@ -126,11 +126,11 @@ func TestVarTerms(t *testing.T) {
|
||||
assertParseOneTerm(t, "var", "foo0", VarTerm("foo0"))
|
||||
assertParseOneTerm(t, "import prefix", "imports", VarTerm("imports"))
|
||||
assertParseOneTerm(t, "not prefix", "not_foo", VarTerm("not_foo"))
|
||||
assertParseOneTerm(t, "package prefix", "packages", VarTerm("packages"))
|
||||
assertParseOneTerm(t, `package prefix`, "packages", VarTerm("packages"))
|
||||
assertParseError(t, "non-var", "foo-bar")
|
||||
assertParseError(t, "non-var2", "foo-7")
|
||||
assertParseError(t, "not keyword", "not")
|
||||
assertParseError(t, "package keyword", "package")
|
||||
assertParseError(t, `package keyword`, "package")
|
||||
assertParseError(t, "import keyword", "import")
|
||||
}
|
||||
|
||||
@@ -167,6 +167,7 @@ func TestObjectWithScalars(t *testing.T) {
|
||||
func TestObjectWithVars(t *testing.T) {
|
||||
assertParseOneTerm(t, "var keys", "{foo: \"bar\", bar: 64}", ObjectTerm(Item(VarTerm("foo"), StringTerm("bar")), Item(VarTerm("bar"), IntNumberTerm(64))))
|
||||
assertParseOneTerm(t, "nested var keys", "{baz: {foo: \"bar\", bar: qux}}", ObjectTerm(Item(VarTerm("baz"), ObjectTerm(Item(VarTerm("foo"), StringTerm("bar")), Item(VarTerm("bar"), VarTerm("qux"))))))
|
||||
assertParseOneTerm(t, "trailing comma", "{foo: \"bar\", bar: 64, }", ObjectTerm(Item(VarTerm("foo"), StringTerm("bar")), Item(VarTerm("bar"), IntNumberTerm(64))))
|
||||
}
|
||||
|
||||
func TestObjectFail(t *testing.T) {
|
||||
@@ -183,6 +184,7 @@ func TestArrayWithScalars(t *testing.T) {
|
||||
assertParseOneTerm(t, "bool", "[true, false, true]", ArrayTerm(BooleanTerm(true), BooleanTerm(false), BooleanTerm(true)))
|
||||
assertParseOneTerm(t, "string", "[\"foo\", \"bar\"]", ArrayTerm(StringTerm("foo"), StringTerm("bar")))
|
||||
assertParseOneTerm(t, "mixed", "[null, true, 42]", ArrayTerm(NullTerm(), BooleanTerm(true), IntNumberTerm(42)))
|
||||
assertParseOneTerm(t, "trailing comma", "[null, true, ]", ArrayTerm(NullTerm(), BooleanTerm(true)))
|
||||
}
|
||||
|
||||
func TestArrayWithVars(t *testing.T) {
|
||||
@@ -193,7 +195,6 @@ func TestArrayWithVars(t *testing.T) {
|
||||
func TestArrayFail(t *testing.T) {
|
||||
assertParseError(t, "non-terminated 1", "[foo, bar")
|
||||
assertParseError(t, "non-terminated 2", "[foo, bar, ")
|
||||
assertParseError(t, "missing element", "[foo, bar, ]")
|
||||
assertParseError(t, "missing separator", "[foo bar]")
|
||||
assertParseError(t, "missing start", "foo, bar, baz]")
|
||||
}
|
||||
@@ -203,6 +204,7 @@ func TestSetWithScalars(t *testing.T) {
|
||||
assertParseOneTerm(t, "bool", "{true, false, true}", SetTerm(BooleanTerm(true), BooleanTerm(false), BooleanTerm(true)))
|
||||
assertParseOneTerm(t, "string", "{\"foo\", \"bar\"}", SetTerm(StringTerm("foo"), StringTerm("bar")))
|
||||
assertParseOneTerm(t, "mixed", "{null, true, 42}", SetTerm(NullTerm(), BooleanTerm(true), IntNumberTerm(42)))
|
||||
assertParseOneTerm(t, "trailing comma", "{null, true,}", SetTerm(NullTerm(), BooleanTerm(true)))
|
||||
}
|
||||
|
||||
func TestSetWithVars(t *testing.T) {
|
||||
@@ -214,7 +216,6 @@ func TestSetFail(t *testing.T) {
|
||||
assertParseError(t, "non-terminated 1", "set(")
|
||||
assertParseError(t, "non-terminated 2", "{foo, bar")
|
||||
assertParseError(t, "non-terminated 3", "{foo, bar, ")
|
||||
assertParseError(t, "missing element", "{foo, bar, }")
|
||||
assertParseError(t, "missing separator", "{foo bar}")
|
||||
assertParseError(t, "missing start", "foo, bar, baz}")
|
||||
}
|
||||
@@ -239,10 +240,7 @@ func TestCompositesWithRefs(t *testing.T) {
|
||||
|
||||
func TestArrayComprehensions(t *testing.T) {
|
||||
|
||||
input := `[
|
||||
{"x": [a[i] | xs = [{"a": ["baz", j]} | q[p], p.a != "bar", j = "foo"],
|
||||
xs[j].a[k] = "foo"]}
|
||||
]`
|
||||
input := `[{"x": [a[i] | xs = [{"a": ["baz", j]} | q[p]; p.a != "bar"; j = "foo"]; xs[j].a[k] = "foo"]}]`
|
||||
|
||||
expected := ArrayTerm(
|
||||
ObjectTerm(Item(
|
||||
@@ -274,15 +272,6 @@ func TestArrayComprehensions(t *testing.T) {
|
||||
|
||||
assertParseOneTerm(t, "nested", input, expected)
|
||||
|
||||
inputNewLines := `[
|
||||
{"x": [a[i] | xs = [{"a": ["baz", j]} | q[p]
|
||||
p.a != "bar"
|
||||
j = "foo"]
|
||||
xs[j].a[k] = "foo"]}
|
||||
]`
|
||||
|
||||
assertParseOneTerm(t, "nested newlines", inputNewLines, expected)
|
||||
|
||||
}
|
||||
|
||||
func TestInfixExpr(t *testing.T) {
|
||||
@@ -381,13 +370,53 @@ func TestExprWith(t *testing.T) {
|
||||
assertParseErrorEquals(t, "invalid import path", `data.foo with foo.bar as "x"`, "invalid path foo.bar: path must begin with input or data")
|
||||
}
|
||||
|
||||
func TestMultiLineBody(t *testing.T) {
|
||||
|
||||
input1 := `
|
||||
{
|
||||
x = 1
|
||||
y = 2
|
||||
z = [ i | [x,y] = arr
|
||||
arr[_] = i]
|
||||
}
|
||||
`
|
||||
|
||||
body1, err := ParseBody(input1)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected parse error on enclosed body: %v", err)
|
||||
}
|
||||
|
||||
expected1 := MustParseBody(`x = 1; y = 2; z = [i | [x,y] = arr; arr[_] = i]`)
|
||||
|
||||
if !body1.Equal(expected1) {
|
||||
t.Errorf("Expected enclosed body to equal %v but got: %v", expected1, body1)
|
||||
}
|
||||
|
||||
// Check that parser can handle multiple expressions w/o enclsoing braces.
|
||||
input2 := `
|
||||
x = 1
|
||||
y = 2
|
||||
z = [ i | [x,y] = arr
|
||||
arr[_] = i]
|
||||
`
|
||||
|
||||
body2, err := ParseBody(input2)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected parse error on enclosed body: %v", err)
|
||||
}
|
||||
|
||||
if !body2.Equal(expected1) {
|
||||
t.Errorf("Expected unenclosed body to equal %v but got: %v", expected1, body1)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPackage(t *testing.T) {
|
||||
ref1 := RefTerm(DefaultRootDocument, StringTerm("foo"))
|
||||
assertParsePackage(t, "single", "package foo", &Package{Path: ref1.Value.(Ref)})
|
||||
assertParsePackage(t, "single", `package foo`, &Package{Path: ref1.Value.(Ref)})
|
||||
ref2 := RefTerm(DefaultRootDocument, StringTerm("f00"), StringTerm("bar_baz"), StringTerm("qux"))
|
||||
assertParsePackage(t, "multiple", "package f00.bar_baz.qux", &Package{Path: ref2.Value.(Ref)})
|
||||
assertParsePackage(t, "multiple", `package f00.bar_baz.qux`, &Package{Path: ref2.Value.(Ref)})
|
||||
ref3 := RefTerm(DefaultRootDocument, StringTerm("foo"), StringTerm("bar baz"))
|
||||
assertParsePackage(t, "space", "package foo[\"bar baz\"]", &Package{Path: ref3.Value.(Ref)})
|
||||
assertParsePackage(t, "space", `package foo["bar baz"]`, &Package{Path: ref3.Value.(Ref)})
|
||||
assertParseError(t, "non-ground ref", "package foo[x]")
|
||||
assertParseError(t, "non-string value", "package foo.bar[42].baz")
|
||||
}
|
||||
@@ -429,21 +458,21 @@ func TestIsValidImportPath(t *testing.T) {
|
||||
|
||||
func TestRule(t *testing.T) {
|
||||
|
||||
assertParseRule(t, "identity", "p = true :- true", &Rule{
|
||||
assertParseRule(t, "identity", `p = true { true }`, &Rule{
|
||||
Head: NewHead(Var("p"), nil, BooleanTerm(true)),
|
||||
Body: NewBody(
|
||||
&Expr{Terms: BooleanTerm(true)},
|
||||
),
|
||||
})
|
||||
|
||||
assertParseRule(t, "set", "p[x] :- x = 42", &Rule{
|
||||
assertParseRule(t, "set", `p[x] { x = 42 }`, &Rule{
|
||||
Head: NewHead(Var("p"), VarTerm("x")),
|
||||
Body: NewBody(
|
||||
Equality.Expr(VarTerm("x"), IntNumberTerm(42)),
|
||||
),
|
||||
})
|
||||
|
||||
assertParseRule(t, "object", "p[x] = y :- x = 42, y = \"hello\"", &Rule{
|
||||
assertParseRule(t, "object", `p[x] = y { x = 42; y = "hello" }`, &Rule{
|
||||
Head: NewHead(Var("p"), VarTerm("x"), VarTerm("y")),
|
||||
Body: NewBody(
|
||||
Equality.Expr(VarTerm("x"), IntNumberTerm(42)),
|
||||
@@ -451,7 +480,7 @@ func TestRule(t *testing.T) {
|
||||
),
|
||||
})
|
||||
|
||||
assertParseRule(t, "constant composite", "p = [{\"foo\": [1,2,3,4]}] :- true", &Rule{
|
||||
assertParseRule(t, "constant composite", `p = [{"foo": [1, 2, 3, 4]}] { true }`, &Rule{
|
||||
Head: NewHead(Var("p"), nil, ArrayTerm(
|
||||
ObjectTerm(Item(StringTerm("foo"), ArrayTerm(IntNumberTerm(1), IntNumberTerm(2), IntNumberTerm(3), IntNumberTerm(4)))))),
|
||||
Body: NewBody(
|
||||
@@ -459,14 +488,14 @@ func TestRule(t *testing.T) {
|
||||
),
|
||||
})
|
||||
|
||||
assertParseRule(t, "true", "p :- true", &Rule{
|
||||
assertParseRule(t, "true", `p = true { true }`, &Rule{
|
||||
Head: NewHead(Var("p"), nil, BooleanTerm(true)),
|
||||
Body: NewBody(
|
||||
&Expr{Terms: BooleanTerm(true)},
|
||||
),
|
||||
})
|
||||
|
||||
assertParseRule(t, "composites in head", `p[[{"x": [a,b]}]] :- a = 1, b = 2`, &Rule{
|
||||
assertParseRule(t, "composites in head", `p[[{"x": [a, b]}]] { a = 1; b = 2 }`, &Rule{
|
||||
Head: NewHead(Var("p"), ArrayTerm(
|
||||
ObjectTerm(
|
||||
Item(StringTerm("x"), ArrayTerm(VarTerm("a"), VarTerm("b"))),
|
||||
@@ -478,21 +507,21 @@ func TestRule(t *testing.T) {
|
||||
),
|
||||
})
|
||||
|
||||
assertParseRule(t, "refs in head", "p = data.foo[x] :- x = 1", &Rule{
|
||||
assertParseRule(t, "refs in head", `p = data.foo[x] { x = 1 }`, &Rule{
|
||||
Head: NewHead(Var("p"), nil, &Term{
|
||||
Value: MustParseRef("data.foo[x]"),
|
||||
}),
|
||||
Body: MustParseBody("x = 1"),
|
||||
})
|
||||
|
||||
assertParseRule(t, "refs in head", "p[data.foo[x]] :- true", &Rule{
|
||||
assertParseRule(t, "refs in head", `p[data.foo[x]] { true }`, &Rule{
|
||||
Head: NewHead(Var("p"), &Term{
|
||||
Value: MustParseRef("data.foo[x]"),
|
||||
}),
|
||||
Body: MustParseBody("true"),
|
||||
})
|
||||
|
||||
assertParseRule(t, "refs in head", "p[data.foo[x]] = data.bar[y] :- true", &Rule{
|
||||
assertParseRule(t, "refs in head", `p[data.foo[x]] = data.bar[y] { true }`, &Rule{
|
||||
Head: NewHead(Var("p"), &Term{
|
||||
Value: MustParseRef("data.foo[x]"),
|
||||
}, &Term{
|
||||
@@ -501,55 +530,41 @@ func TestRule(t *testing.T) {
|
||||
Body: MustParseBody("true"),
|
||||
})
|
||||
|
||||
assertParseRule(t, "data", "data :- true", &Rule{
|
||||
assertParseRule(t, "data", `data = true { true }`, &Rule{
|
||||
Head: NewHead(Var("data"), nil, MustParseTerm("true")),
|
||||
Body: MustParseBody("true"),
|
||||
})
|
||||
|
||||
assertParseRule(t, "input", "input :- true", &Rule{
|
||||
assertParseRule(t, "input", `input = true { true }`, &Rule{
|
||||
Head: NewHead(Var("input"), nil, MustParseTerm("true")),
|
||||
Body: MustParseBody("true"),
|
||||
})
|
||||
|
||||
assertParseRule(t, "default", "default allow = false", &Rule{
|
||||
assertParseRule(t, "default", `default allow = false`, &Rule{
|
||||
Default: true,
|
||||
Head: NewHead(Var("allow"), nil, MustParseTerm("false")),
|
||||
Body: NewBody(NewExpr(BooleanTerm(true))),
|
||||
})
|
||||
|
||||
assertParseRule(t, "default w/ comprehension", "default widgets = [x | x = data.fooz[_]]", &Rule{
|
||||
assertParseRule(t, "default w/ comprehension", `default widgets = [x | x = data.fooz[_]]`, &Rule{
|
||||
Default: true,
|
||||
Head: NewHead(Var("widgets"), nil, MustParseTerm("[x | x = data.fooz[_]]")),
|
||||
Head: NewHead(Var("widgets"), nil, MustParseTerm(`[x | x = data.fooz[_]]`)),
|
||||
Body: NewBody(NewExpr(BooleanTerm(true))),
|
||||
})
|
||||
|
||||
assertParseRule(t, "one line with braces", "p[x] { x = data.a[_], count(x, 3) }", &Rule{
|
||||
assertParseRule(t, "one line with braces", `p[x] { x = data.a[_]; count(x, 3) }`, &Rule{
|
||||
Head: NewHead(Var("p"), VarTerm("x")),
|
||||
Body: MustParseBody("x = data.a[_], count(x, 3)"),
|
||||
Body: MustParseBody(`x = data.a[_]; count(x, 3)`),
|
||||
})
|
||||
|
||||
assertParseRule(t, "multiple lines with braces", `
|
||||
p[[x, y]] {
|
||||
assertParseRule(t, "multiple lines with braces", `p[[x, y]] { [data.a[0]] = [{"x": x}]; count(x, 3); sum(x, y); y > 100 }`,
|
||||
|
||||
[ data.a[0] ] = [
|
||||
{
|
||||
"x": x
|
||||
} # comment embedded
|
||||
]
|
||||
&Rule{
|
||||
Head: NewHead(Var("p"), MustParseTerm("[x, y]")),
|
||||
Body: MustParseBody(`[data.a[0]] = [{"x": x}]; count(x, 3); sum(x, y); y > 100`),
|
||||
})
|
||||
|
||||
# another comment
|
||||
|
||||
count(x, 3)
|
||||
sum(x, y), # comma can be included
|
||||
y > 100
|
||||
|
||||
}
|
||||
`, &Rule{
|
||||
Head: NewHead(Var("p"), MustParseTerm("[x, y]")),
|
||||
Body: MustParseBody(`[data.a[0]] = [{"x": x}], count(x, 3), sum(x, y), y > 100`),
|
||||
})
|
||||
|
||||
assertParseErrorEquals(t, "object composite key", "p[[x,y]] = z :- true", "object key must be one of string, var, ref not array")
|
||||
assertParseErrorEquals(t, "object composite key", "p[[x,y]] = z { true }", "object key must be one of string, var, ref not array")
|
||||
assertParseErrorEquals(t, "default ref value", "default p = [data.foo]", "default rule value cannot contain ref")
|
||||
assertParseErrorEquals(t, "default var value", "default p = [x]", "default rule value cannot contain var")
|
||||
assertParseErrorEquals(t, "empty rule body", "p {}", "body must be non-empty")
|
||||
@@ -557,36 +572,34 @@ func TestRule(t *testing.T) {
|
||||
// TODO(tsandall): improve error checking here. This is a common mistake
|
||||
// and the current error message is not very good. Need to investigate if the
|
||||
// parser can be improved.
|
||||
assertParseError(t, "dangling comma", "p :- true, false,")
|
||||
assertParseError(t, "dangling semicolon", "p { true; false; }")
|
||||
}
|
||||
|
||||
func TestMultipleEnclosedBodies(t *testing.T) {
|
||||
|
||||
result, err := ParseModule("", `
|
||||
package ex
|
||||
result, err := ParseModule("", `package ex
|
||||
|
||||
p[x] = y {
|
||||
x = "a"
|
||||
y = 1
|
||||
} {
|
||||
x = "b"
|
||||
y = 2
|
||||
}
|
||||
p[x] = y {
|
||||
x = "a"
|
||||
y = 1
|
||||
} {
|
||||
x = "b"
|
||||
y = 2
|
||||
}
|
||||
|
||||
q = 1
|
||||
`)
|
||||
q = 1`,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected parse error: %v", err)
|
||||
}
|
||||
|
||||
expected := MustParseModule(`
|
||||
package ex
|
||||
expected := MustParseModule(`package ex
|
||||
|
||||
p[x] = y :- x = "a", y = 1
|
||||
p[x] = y :- x = "b", y = 2
|
||||
q = 1 :- true
|
||||
`)
|
||||
p[x] = y { x = "a"; y = 1 }
|
||||
p[x] = y { x = "b"; y = 2 }
|
||||
q = 1 { true }`,
|
||||
)
|
||||
|
||||
if !expected.Equal(result) {
|
||||
t.Fatal("Expected modules to be equal but got:\n\n", result, "\n\nExpected:\n\n", expected)
|
||||
@@ -607,66 +620,64 @@ func TestEmptyModule(t *testing.T) {
|
||||
|
||||
func TestComments(t *testing.T) {
|
||||
|
||||
testModule := `
|
||||
package a.b.c
|
||||
testModule := `package a.b.c
|
||||
|
||||
import input.e.f as g # end of line
|
||||
import input.h
|
||||
|
||||
# by itself
|
||||
|
||||
p[x] = y :- y = "foo",
|
||||
p[x] = y { y = "foo";
|
||||
# inside a rule
|
||||
x = "bar",
|
||||
x != y,
|
||||
x = "bar";
|
||||
x != y;
|
||||
q[x]
|
||||
}
|
||||
|
||||
import input.xyz.abc
|
||||
|
||||
q # interruptting
|
||||
[a] # the head of a rule
|
||||
:- m = [1,2,
|
||||
3],
|
||||
|
||||
[a] # the head of a rule
|
||||
|
||||
{ m = [1,2,
|
||||
3, ];
|
||||
a = m[i]
|
||||
|
||||
r[x] :- x = [ a | # inside comprehension
|
||||
a = z[i],
|
||||
}
|
||||
|
||||
r[x] { x = [ a | # inside comprehension
|
||||
a = z[i]
|
||||
b[i].a = a ]
|
||||
`
|
||||
|
||||
}`
|
||||
|
||||
assertParseModule(t, "module comments", testModule, &Module{
|
||||
Package: MustParseStatement("package a.b.c").(*Package),
|
||||
Package: MustParseStatement(`package a.b.c`).(*Package),
|
||||
Imports: []*Import{
|
||||
MustParseStatement("import input.e.f as g").(*Import),
|
||||
MustParseStatement("import input.h").(*Import),
|
||||
MustParseStatement("import input.xyz.abc").(*Import),
|
||||
},
|
||||
Rules: []*Rule{
|
||||
MustParseStatement("p[x] = y :- y = \"foo\", x = \"bar\", x != y, q[x]").(*Rule),
|
||||
MustParseStatement("q[a] :- m = [1,2,3], a = m[i]").(*Rule),
|
||||
MustParseStatement("r[x] :- x = [a | a = z[i], b[i].a = a]").(*Rule),
|
||||
MustParseStatement(`p[x] = y { y = "foo"; x = "bar"; x != y; q[x] }`).(*Rule),
|
||||
MustParseStatement(`q[a] { m = [1, 2, 3]; a = m[i] }`).(*Rule),
|
||||
MustParseStatement(`r[x] { x = [a | a = z[i]; b[i].a = a] }`).(*Rule),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestExample(t *testing.T) {
|
||||
assertParseModule(t, "example module", testModule, &Module{
|
||||
Package: MustParseStatement("package opa.examples").(*Package),
|
||||
Package: MustParseStatement(`package opa.examples`).(*Package),
|
||||
Imports: []*Import{
|
||||
MustParseStatement("import data.servers").(*Import),
|
||||
MustParseStatement("import data.networks").(*Import),
|
||||
MustParseStatement("import data.ports").(*Import),
|
||||
},
|
||||
Rules: []*Rule{
|
||||
MustParseStatement(`violations[server] :-
|
||||
server = servers[i],
|
||||
server.protocols[j] = "http",
|
||||
public_servers[server]`).(*Rule),
|
||||
MustParseStatement(`public_servers[server] :-
|
||||
server = servers[i],
|
||||
server.ports[j] = ports[k].id,
|
||||
ports[k].networks[l] = networks[m].id,
|
||||
networks[m].public = true`).(*Rule),
|
||||
MustParseStatement(`violations[server] { server = servers[i]; server.protocols[j] = "http"; public_servers[server] }`).(*Rule),
|
||||
MustParseStatement(`public_servers[server] { server = servers[i]; server.ports[j] = ports[k].id; ports[k].networks[l] = networks[m].id; networks[m].public = true }`).(*Rule),
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -677,7 +688,7 @@ func TestModuleParseErrors(t *testing.T) {
|
||||
package a # unexpected package
|
||||
1 = 2 # non-var head
|
||||
1 != 2 # non-equality expr
|
||||
x = y, x = 1 # multiple exprs
|
||||
x = y; x = 1 # multiple exprs
|
||||
`
|
||||
|
||||
mod, err := ParseModule("test.rego", input)
|
||||
@@ -705,8 +716,8 @@ func TestLocation(t *testing.T) {
|
||||
if expr.Location.Col != 5 {
|
||||
t.Errorf("Expected column of %v to be 5 but got: %v", expr, expr.Location.Col)
|
||||
}
|
||||
if expr.Location.Row != 9 {
|
||||
t.Errorf("Expected row of %v to be 9 but got: %v", expr, expr.Location.Row)
|
||||
if expr.Location.Row != 8 {
|
||||
t.Errorf("Expected row of %v to be 8 but got: %v", expr, expr.Location.Row)
|
||||
}
|
||||
if expr.Location.File != "test" {
|
||||
t.Errorf("Expected file of %v to be test but got: %v", expr, expr.Location.File)
|
||||
@@ -714,42 +725,37 @@ func TestLocation(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRuleFromBody(t *testing.T) {
|
||||
testModule := `
|
||||
package a.b.c
|
||||
testModule := `package a.b.c
|
||||
|
||||
pi = 3.14159
|
||||
# intersperse a regular rule
|
||||
p[x] :- x = 1
|
||||
greeting = "hello"
|
||||
cores = [{0: 1}, {1: 2}]
|
||||
wrapper = cores[0][1]
|
||||
pi = [3, 1, 4, x, y, z]
|
||||
`
|
||||
pi = 3.14159 { true }
|
||||
p[x] { x = 1 }
|
||||
greeting = "hello" { true }
|
||||
cores = [{0: 1}, {1: 2}] { true }
|
||||
wrapper = cores[0][1] { true }
|
||||
pi = [3, 1, 4, x, y, z] { true }`
|
||||
|
||||
assertParseModule(t, "rules from bodies", testModule, &Module{
|
||||
Package: MustParseStatement("package a.b.c").(*Package),
|
||||
Package: MustParseStatement(`package a.b.c`).(*Package),
|
||||
Rules: []*Rule{
|
||||
MustParseStatement("pi = 3.14159 :- true").(*Rule),
|
||||
MustParseStatement("p[x] :- x = 1").(*Rule),
|
||||
MustParseStatement("greeting = \"hello\" :- true").(*Rule),
|
||||
MustParseStatement("cores = [{0: 1}, {1: 2}] :- true").(*Rule),
|
||||
MustParseStatement("wrapper = cores[0][1] :- true").(*Rule),
|
||||
MustParseStatement("pi = [3, 1, 4, x, y, z] :- true").(*Rule),
|
||||
MustParseStatement(`pi = 3.14159 { true }`).(*Rule),
|
||||
MustParseStatement(`p[x] { x = 1 }`).(*Rule),
|
||||
MustParseStatement(`greeting = "hello" { true }`).(*Rule),
|
||||
MustParseStatement(`cores = [{0: 1}, {1: 2}] { true }`).(*Rule),
|
||||
MustParseStatement(`wrapper = cores[0][1] { true }`).(*Rule),
|
||||
MustParseStatement(`pi = [3, 1, 4, x, y, z] { true }`).(*Rule),
|
||||
},
|
||||
})
|
||||
|
||||
mockModule := `
|
||||
package ex
|
||||
mockModule := `package ex
|
||||
|
||||
input = {"foo": 1}
|
||||
data = {"bar": 2}
|
||||
`
|
||||
input = {"foo": 1} { true }
|
||||
data = {"bar": 2} { true }`
|
||||
|
||||
assertParseModule(t, "rule name: input/data", mockModule, &Module{
|
||||
Package: MustParsePackage("package ex"),
|
||||
Package: MustParsePackage(`package ex`),
|
||||
Rules: []*Rule{
|
||||
MustParseRule(`input = {"foo": 1} :- true`),
|
||||
MustParseRule(`data = {"bar": 2} :- true`),
|
||||
MustParseRule(`input = {"foo": 1} { true }`),
|
||||
MustParseRule(`data = {"bar": 2} { true }`),
|
||||
},
|
||||
})
|
||||
|
||||
@@ -816,7 +822,7 @@ func TestWildcards(t *testing.T) {
|
||||
RefTerm(VarTerm("a"), VarTerm("$1")),
|
||||
)))
|
||||
|
||||
assertParseOneExpr(t, "comprehension", "eq(_, [x | a = a[_]])", Equality.Expr(
|
||||
assertParseOneExpr(t, "comprehension", `_ = [x | a = a[_]]`, Equality.Expr(
|
||||
VarTerm("$0"),
|
||||
ArrayComprehensionTerm(
|
||||
VarTerm("x"),
|
||||
@@ -832,12 +838,13 @@ func TestWildcards(t *testing.T) {
|
||||
func TestNoMatchError(t *testing.T) {
|
||||
mod := `package test
|
||||
|
||||
p :- true,
|
||||
1 != 0, // <-- parse error: no match`
|
||||
p { true;
|
||||
1 != 0; # <-- parse error: no match
|
||||
}`
|
||||
|
||||
_, err := ParseModule("foo.rego", mod)
|
||||
|
||||
expected := "1 error occurred: foo.rego:4: no match found, unexpected '/'"
|
||||
expected := "1 error occurred: foo.rego:3: no match found, unexpected '{'"
|
||||
|
||||
if err.Error() != expected {
|
||||
t.Fatalf("Bad parse error, expected %v but got: %v", expected, err)
|
||||
@@ -845,13 +852,13 @@ func TestNoMatchError(t *testing.T) {
|
||||
|
||||
mod = `package test
|
||||
|
||||
p :- true// <-- parse error: no match`
|
||||
p { true // <-- parse error: no match`
|
||||
|
||||
_, err = ParseModule("foo.rego", mod)
|
||||
|
||||
loc := NewLocation(nil, "foo.rego", 3, 12)
|
||||
|
||||
if !reflect.DeepEqual(err.(Errors)[0].Location, loc) {
|
||||
if err.(Errors)[0].Location.File != "foo.rego" || err.(Errors)[0].Location.Row != 3 {
|
||||
t.Fatalf("Expected %v but got: %v", loc, err)
|
||||
}
|
||||
}
|
||||
|
||||
+30
-32
@@ -15,25 +15,25 @@ import (
|
||||
|
||||
func TestModuleJSONRoundTrip(t *testing.T) {
|
||||
|
||||
mod := MustParseModule(`
|
||||
package a.b.c
|
||||
import data.x.y as z
|
||||
import data.u.i
|
||||
p = [1,2,{"foo":3.14}] :- r[x] = 1, not q[x]
|
||||
r[y] = v :- i[1] = y, v = i[2]
|
||||
q[x] :- a=[true,false,null,{"x":[1,2,3]}], a[i] = x
|
||||
t = true :- xs = [{"x": a[i].a} | a[i].n = "bob", b[x]]
|
||||
big = 1e1000
|
||||
odd = -.1
|
||||
s = {1,2,3} :- true
|
||||
s = set() :- false
|
||||
empty_obj :- {}
|
||||
empty_arr :- []
|
||||
empty_set :- set()
|
||||
using_with :- plus(data.foo, 1, x) with input.foo as bar
|
||||
x = 2 :- input = null
|
||||
default allow = true
|
||||
`)
|
||||
mod := MustParseModule(`package a.b.c
|
||||
|
||||
import data.x.y as z
|
||||
import data.u.i
|
||||
|
||||
p = [1, 2, {"foo": 3.14}] { r[x] = 1; not q[x] }
|
||||
r[y] = v { i[1] = y; v = i[2] }
|
||||
q[x] { a = [true, false, null, {"x": [1, 2, 3]}]; a[i] = x }
|
||||
t = true { xs = [{"x": a[i].a} | a[i].n = "bob"; b[x]] }
|
||||
big = 1e+1000 { true }
|
||||
odd = -0.1 { true }
|
||||
s = {1, 2, 3} { true }
|
||||
s = set() { false }
|
||||
empty_obj = true { {} }
|
||||
empty_arr = true { [] }
|
||||
empty_set = true { set() }
|
||||
using_with = true { x = data.foo + 1 with input.foo as bar }
|
||||
x = 2 { input = null }
|
||||
default allow = true`)
|
||||
|
||||
bs, err := json.Marshal(mod)
|
||||
if err != nil {
|
||||
@@ -66,7 +66,7 @@ func TestPackageEquals(t *testing.T) {
|
||||
func TestPackageString(t *testing.T) {
|
||||
pkg1 := &Package{Path: RefTerm(VarTerm("foo"), StringTerm("bar"), StringTerm("baz")).Value.(Ref)}
|
||||
result1 := pkg1.String()
|
||||
expected1 := "package bar.baz"
|
||||
expected1 := `package bar.baz`
|
||||
if result1 != expected1 {
|
||||
t.Errorf("Expected %v but got %v", expected1, result1)
|
||||
}
|
||||
@@ -206,7 +206,7 @@ func TestExprEquals(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBodyIsGround(t *testing.T) {
|
||||
if MustParseBody(`a.b[0] = 1, a = [1,2,x]`).IsGround() {
|
||||
if MustParseBody(`a.b[0] = 1; a = [1, 2, x]`).IsGround() {
|
||||
t.Errorf("Expected body to be non-ground")
|
||||
}
|
||||
}
|
||||
@@ -424,23 +424,21 @@ func TestRuleString(t *testing.T) {
|
||||
Head: NewHead("p", nil, BooleanTerm(true)),
|
||||
}
|
||||
|
||||
assertRuleString(t, rule1, "p :- \"foo\" = \"bar\"")
|
||||
assertRuleString(t, rule2, "p[x] = y :- \"foo\" = x, not a.b[x], \"b\" = y")
|
||||
assertRuleString(t, rule3, "default p = true")
|
||||
assertRuleString(t, rule1, `p { "foo" = "bar" }`)
|
||||
assertRuleString(t, rule2, `p[x] = y { "foo" = x; not a.b[x]; "b" = y }`)
|
||||
assertRuleString(t, rule3, `default p = true`)
|
||||
}
|
||||
|
||||
func TestModuleString(t *testing.T) {
|
||||
|
||||
input := `
|
||||
package a.b.c
|
||||
input := `package a.b.c
|
||||
|
||||
import data.foo.bar
|
||||
import input.xyz
|
||||
import data.foo.bar
|
||||
import input.xyz
|
||||
|
||||
p :- not bar
|
||||
q :- xyz.abc = 2
|
||||
wildcard :- bar[_] = 1
|
||||
`
|
||||
p = true { not bar }
|
||||
q = true { xyz.abc = 2 }
|
||||
wildcard = true { bar[_] = 1 }`
|
||||
|
||||
mod := MustParseModule(input)
|
||||
|
||||
|
||||
+3
-15
@@ -128,19 +128,7 @@ func TestTermEqual(t *testing.T) {
|
||||
|
||||
func TestHash(t *testing.T) {
|
||||
|
||||
doc := `
|
||||
{
|
||||
"a": [
|
||||
[true, {"b": [null]}, {"c": "d"}]
|
||||
],
|
||||
"e": {
|
||||
100: a[i].b
|
||||
},
|
||||
"k": [ "foo" | true ],
|
||||
"s": {1,2,{3,4}},
|
||||
"big": 1e1000
|
||||
}
|
||||
`
|
||||
doc := `{"a": [[true, {"b": [null]}, {"c": "d"}]], "e": {100: a[i].b}, "k": ["foo" | true], "s": {1, 2, {3, 4}}, "big": 1e+1000}`
|
||||
|
||||
stmt1 := MustParseStatement(doc)
|
||||
stmt2 := MustParseStatement(doc)
|
||||
@@ -232,7 +220,7 @@ func TestTermString(t *testing.T) {
|
||||
assertToString(t, ObjectTerm().Value, "{}")
|
||||
assertToString(t, SetTerm().Value, "set()")
|
||||
assertToString(t, ArrayTerm(ObjectTerm(Item(VarTerm("foo"), ArrayTerm(RefTerm(VarTerm("bar"), VarTerm("i"))))), StringTerm("foo"), SetTerm(BooleanTerm(true), NullTerm()), FloatNumberTerm(42.1)).Value, "[{foo: [bar[i]]}, \"foo\", {true, null}, 42.1]")
|
||||
assertToString(t, ArrayComprehensionTerm(ArrayTerm(VarTerm("x")), NewBody(&Expr{Terms: RefTerm(VarTerm("a"), VarTerm("i"))})).Value, "[[x] | a[i]]")
|
||||
assertToString(t, ArrayComprehensionTerm(ArrayTerm(VarTerm("x")), NewBody(&Expr{Terms: RefTerm(VarTerm("a"), VarTerm("i"))})).Value, `[[x] | a[i]]`)
|
||||
}
|
||||
|
||||
func TestRefHasPrefix(t *testing.T) {
|
||||
@@ -347,7 +335,7 @@ func TestSetOperations(t *testing.T) {
|
||||
op string
|
||||
}{
|
||||
{`{1,2,3,4}`, `{1,3,5}`, `{2,4}`, "-"},
|
||||
{`{1,3,5}`, `{1,2,3,4}`, `{5}`, "-"},
|
||||
{`{1,3,5}`, `{1,2,3,4}`, `{5,}`, "-"},
|
||||
{`{1,2,3,4}`, `{1,3,5}`, `{1,3}`, "&"},
|
||||
{`{1,3,5}`, `{1,2,3,4}`, `{1,3}`, "&"},
|
||||
{`{1,2,3,4}`, `{1,3,5}`, `{1,2,3,4,5}`, "|"},
|
||||
|
||||
+22
-22
@@ -7,17 +7,17 @@ package ast
|
||||
import "testing"
|
||||
|
||||
func TestTransform(t *testing.T) {
|
||||
module := MustParseModule(`
|
||||
package ex["this"]
|
||||
import input.foo
|
||||
import data.bar["this"] as qux
|
||||
p :- "this" = "that"
|
||||
p = "this" :- false
|
||||
p["this"] :- false
|
||||
p[y] = {"this": ["this"]} :- false
|
||||
p :- ["this" | "this"]
|
||||
p = n :- count({"this", "that"}, n) with input.foo.this as {"this": true}
|
||||
`)
|
||||
module := MustParseModule(`package ex.this
|
||||
|
||||
import input.foo
|
||||
import data.bar.this as qux
|
||||
|
||||
p = true { "this" = "that" }
|
||||
p = "this" { false }
|
||||
p["this"] { false }
|
||||
p[y] = {"this": ["this"]} { false }
|
||||
p = true { ["this" | "this"] }
|
||||
p = n { count({"this", "that"}, n) with input.foo.this as {"this": true} }`)
|
||||
|
||||
result, err := Transform(&GenericTransformer{
|
||||
func(x interface{}) (interface{}, error) {
|
||||
@@ -37,17 +37,17 @@ func TestTransform(t *testing.T) {
|
||||
t.Fatalf("Expected module from transform but got: %v", result)
|
||||
}
|
||||
|
||||
expected := MustParseModule(`
|
||||
package ex["that"]
|
||||
import input.foo
|
||||
import data.bar["that"] as qux
|
||||
p :- "that" = "that"
|
||||
p = "that" :- false
|
||||
p["that"] :- false
|
||||
p[y] = {"that": ["that"]} :- false
|
||||
p :- ["that" | "that"]
|
||||
p = n :- count({"that"}, n) with input.foo.that as {"that": true}
|
||||
`)
|
||||
expected := MustParseModule(`package ex.that
|
||||
|
||||
import input.foo
|
||||
import data.bar.that as qux
|
||||
|
||||
p = true { "that" = "that" }
|
||||
p = "that" { false }
|
||||
p["that"] { false }
|
||||
p[y] = {"that": ["that"]} { false }
|
||||
p = true { ["that" | "that"] }
|
||||
p = n { count({"that"}, n) with input.foo.that as {"that": true} }`)
|
||||
|
||||
if !expected.Equal(resultMod) {
|
||||
t.Fatalf("Expected module:\n%v\n\nGot:\n%v\n", expected, resultMod)
|
||||
|
||||
+9
-12
@@ -17,15 +17,12 @@ func (vis *testVis) Visit(x interface{}) Visitor {
|
||||
|
||||
func TestVisitor(t *testing.T) {
|
||||
|
||||
rule := MustParseModule(`
|
||||
package a.b
|
||||
import input.x.y as z
|
||||
t[x] = y :-
|
||||
p[x] = {"foo": [y,2,{"bar": 3}]},
|
||||
not q[x],
|
||||
y = [ [x,z] | x = "x", z = "z" ],
|
||||
count({1,2,3}, n) with input.foo.bar as x
|
||||
`)
|
||||
rule := MustParseModule(`package a.b
|
||||
|
||||
import input.x.y as z
|
||||
|
||||
t[x] = y { p[x] = {"foo": [y, 2, {"bar": 3}]}; not q[x]; y = [[x, z] | x = "x"; z = "z"]; count({1, 2, 3}, n) with input.foo.bar as x }`,
|
||||
)
|
||||
vis := &testVis{}
|
||||
|
||||
Walk(vis, rule)
|
||||
@@ -103,7 +100,7 @@ func TestVisitor(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestWalkVars(t *testing.T) {
|
||||
x := MustParseBody("x = 1, data.abc[2] = y, y[z] = [q | q = 1]")
|
||||
x := MustParseBody(`x = 1; data.abc[2] = y; y[z] = [q | q = 1]`)
|
||||
found := NewVarSet()
|
||||
WalkVars(x, func(v Var) bool {
|
||||
found.Add(v)
|
||||
@@ -124,8 +121,8 @@ func TestVarVisitor(t *testing.T) {
|
||||
}{
|
||||
{"data.foo[x] = bar.baz[y]", VarVisitorParams{SkipRefHead: true}, "[eq, x, y]"},
|
||||
{"{x: y}", VarVisitorParams{SkipObjectKeys: true}, "[y]"},
|
||||
{"foo = [ x | data.a[i] = x ]", VarVisitorParams{SkipClosures: true}, "[eq, foo]"},
|
||||
{"x=1,y=2,plus(x,y,z),count([x,y,z],z)", VarVisitorParams{SkipBuiltinOperators: true}, "[x, y, z]"},
|
||||
{`foo = [x | data.a[i] = x]`, VarVisitorParams{SkipClosures: true}, "[eq, foo]"},
|
||||
{`x = 1; y = 2; z = x + y; count([x, y, z], z)`, VarVisitorParams{SkipBuiltinOperators: true}, "[x, y, z]"},
|
||||
{"foo with input.bar.baz as qux[corge]", VarVisitorParams{SkipWithTarget: true}, "[foo, qux, corge]"},
|
||||
}
|
||||
|
||||
|
||||
+12
-17
@@ -41,7 +41,7 @@ func ExampleRego_Eval_multipleBindings() {
|
||||
ctx := context.Background()
|
||||
|
||||
// Create query that produces multiple bindings for variable.
|
||||
rego := rego.New(rego.Query(`a = ["ex", "am", "ple"], x = a[_]`))
|
||||
rego := rego.New(rego.Query(`a = ["ex", "am", "ple"]; x = a[_]`))
|
||||
|
||||
// Run evaluation.
|
||||
rs, err := rego.Eval(ctx)
|
||||
@@ -70,11 +70,10 @@ func ExampleRego_Eval_singleDocument() {
|
||||
rego := rego.New(
|
||||
rego.Query("data.example.p"),
|
||||
rego.Module("example.rego",
|
||||
`
|
||||
package example
|
||||
`package example
|
||||
|
||||
p = ["hello", "world"]
|
||||
`))
|
||||
p = ["hello", "world"] { true }`,
|
||||
))
|
||||
|
||||
// Run evaluation.
|
||||
rs, err := rego.Eval(ctx)
|
||||
@@ -97,11 +96,10 @@ func ExampleRego_Eval_multipleDocuments() {
|
||||
rego := rego.New(
|
||||
rego.Query("data.example.p[x]"),
|
||||
rego.Module("example.rego",
|
||||
`
|
||||
package example
|
||||
`package example
|
||||
|
||||
p = {"hello": "alice", "goodbye": "bob"}
|
||||
`))
|
||||
p = {"hello": "alice", "goodbye": "bob"} { true }`,
|
||||
))
|
||||
|
||||
// Run evaluation.
|
||||
rs, err := rego.Eval(ctx)
|
||||
@@ -179,14 +177,11 @@ func ExampleRego_Eval_errors() {
|
||||
r := rego.New(
|
||||
rego.Query("data.example.p"),
|
||||
rego.Module("example_error.rego",
|
||||
`
|
||||
package example
|
||||
`package example
|
||||
|
||||
# variable 'x' is unsafe. This will not compile.
|
||||
p :- not q[x]
|
||||
|
||||
q = {1,2,3}
|
||||
`))
|
||||
p = true { not q[x] }
|
||||
q = {1, 2, 3} { true }`,
|
||||
))
|
||||
|
||||
_, err := r.Eval(ctx)
|
||||
|
||||
@@ -207,6 +202,6 @@ func ExampleRego_Eval_errors() {
|
||||
// Output:
|
||||
//
|
||||
// code: 2
|
||||
// row: 5
|
||||
// row: 3
|
||||
// filename: example_error.rego
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ func ExampleREPL_OneShot() {
|
||||
repl := repl.New(store, "", &buf, "json", "")
|
||||
|
||||
// Define a rule inside the REPL.
|
||||
repl.OneShot(ctx, "p :- a = [1, 2, 3, 4], a[_] > 3")
|
||||
repl.OneShot(ctx, "p { a = [1, 2, 3, 4]; a[_] > 3 }")
|
||||
|
||||
// Query the rule defined above.
|
||||
repl.OneShot(ctx, "p")
|
||||
|
||||
+1
-1
@@ -1279,7 +1279,7 @@ For example:
|
||||
> import input.params
|
||||
|
||||
# Define rule that refers to "params".
|
||||
> is_post :- params.method = "POST"
|
||||
> is_post { params.method = "POST" }
|
||||
|
||||
# Test evaluation.
|
||||
> is_post
|
||||
|
||||
+46
-39
@@ -28,11 +28,13 @@ func TestComplete(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
mod1 := ast.MustParseModule(`package a.b.c
|
||||
p = 1
|
||||
q = 2`)
|
||||
|
||||
p = 1 { true }
|
||||
q = 2 { true }`)
|
||||
|
||||
mod2 := ast.MustParseModule(`package a.b.d
|
||||
r = 3`)
|
||||
|
||||
r = 3 { true }`)
|
||||
|
||||
if err := storage.InsertPolicy(ctx, store, "mod1", mod1, nil, false); err != nil {
|
||||
panic(err)
|
||||
@@ -179,7 +181,7 @@ func TestShow(t *testing.T) {
|
||||
var buffer bytes.Buffer
|
||||
repl := newRepl(store, &buffer)
|
||||
|
||||
repl.OneShot(ctx, "package repl_test")
|
||||
repl.OneShot(ctx, `package repl_test`)
|
||||
repl.OneShot(ctx, "show")
|
||||
assertREPLText(t, buffer, "package repl_test\n")
|
||||
buffer.Reset()
|
||||
@@ -203,8 +205,8 @@ import data.foo as bar` + "\n"
|
||||
assertREPLText(t, buffer, expected)
|
||||
buffer.Reset()
|
||||
|
||||
repl.OneShot(ctx, "p[1] :- true")
|
||||
repl.OneShot(ctx, "p[2] :- true")
|
||||
repl.OneShot(ctx, `p[1] { true }`)
|
||||
repl.OneShot(ctx, `p[2] { true }`)
|
||||
repl.OneShot(ctx, "show")
|
||||
|
||||
expected = `package repl_test
|
||||
@@ -212,8 +214,8 @@ import data.foo as bar` + "\n"
|
||||
import input.xyz
|
||||
import data.foo as bar
|
||||
|
||||
p[1] :- true
|
||||
p[2] :- true` + "\n"
|
||||
p[1] { true }
|
||||
p[2] { true }` + "\n"
|
||||
assertREPLText(t, buffer, expected)
|
||||
buffer.Reset()
|
||||
|
||||
@@ -247,7 +249,7 @@ func TestUnset(t *testing.T) {
|
||||
|
||||
buffer.Reset()
|
||||
repl.OneShot(ctx, "p = 3.14")
|
||||
repl.OneShot(ctx, "p = 3 :- false")
|
||||
repl.OneShot(ctx, `p = 3 { false }`)
|
||||
repl.OneShot(ctx, "unset p")
|
||||
|
||||
err = repl.OneShot(ctx, "p")
|
||||
@@ -304,7 +306,7 @@ func TestOneShotEmptyBufferOneRule(t *testing.T) {
|
||||
store := newTestStore()
|
||||
var buffer bytes.Buffer
|
||||
repl := newRepl(store, &buffer)
|
||||
repl.OneShot(ctx, "p[x] :- data.a[i] = x")
|
||||
repl.OneShot(ctx, `p[x] { data.a[i] = x }`)
|
||||
expectOutput(t, buffer.String(), "")
|
||||
}
|
||||
|
||||
@@ -326,16 +328,20 @@ func TestOneShotBufferedRule(t *testing.T) {
|
||||
store := newTestStore()
|
||||
var buffer bytes.Buffer
|
||||
repl := newRepl(store, &buffer)
|
||||
repl.OneShot(ctx, "p[x] :- ")
|
||||
repl.OneShot(ctx, "p[x] { ")
|
||||
expectOutput(t, buffer.String(), "")
|
||||
repl.OneShot(ctx, "data.a[i]")
|
||||
repl.OneShot(ctx, "data.a[i].b.c[1]")
|
||||
expectOutput(t, buffer.String(), "")
|
||||
repl.OneShot(ctx, " = ")
|
||||
expectOutput(t, buffer.String(), "")
|
||||
repl.OneShot(ctx, "x")
|
||||
expectOutput(t, buffer.String(), "")
|
||||
repl.OneShot(ctx, "}")
|
||||
expectOutput(t, buffer.String(), "")
|
||||
repl.OneShot(ctx, "")
|
||||
expectOutput(t, buffer.String(), "")
|
||||
repl.OneShot(ctx, "p[2]")
|
||||
expectOutput(t, buffer.String(), "2\n")
|
||||
}
|
||||
|
||||
func TestOneShotJSON(t *testing.T) {
|
||||
@@ -395,7 +401,8 @@ func TestEvalData(t *testing.T) {
|
||||
var buffer bytes.Buffer
|
||||
repl := newRepl(store, &buffer)
|
||||
testmod := ast.MustParseModule(`package ex
|
||||
p = [1,2,3]`)
|
||||
|
||||
p = [1, 2, 3] { true }`)
|
||||
if err := storage.InsertPolicy(ctx, store, "test", testmod, nil, false); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -551,7 +558,7 @@ func TestEvalSingleTermMultiValue(t *testing.T) {
|
||||
|
||||
buffer.Reset()
|
||||
|
||||
repl.OneShot(ctx, "p[x] :- a = [1,2,3,4], a[_] = x")
|
||||
repl.OneShot(ctx, `p[x] { a = [1, 2, 3, 4]; a[_] = x }`)
|
||||
buffer.Reset()
|
||||
repl.OneShot(ctx, "p[x]")
|
||||
|
||||
@@ -592,10 +599,10 @@ func TestEvalSingleTermMultiValueSetRef(t *testing.T) {
|
||||
var buffer bytes.Buffer
|
||||
repl := newRepl(store, &buffer)
|
||||
repl.outputFormat = "json"
|
||||
repl.OneShot(ctx, "p[1] :- true")
|
||||
repl.OneShot(ctx, "p[2] :- true")
|
||||
repl.OneShot(ctx, "q = {3,4} :- true")
|
||||
repl.OneShot(ctx, "r = [x, y] :- x = {5,6}, y = [7,8]")
|
||||
repl.OneShot(ctx, `p[1] { true }`)
|
||||
repl.OneShot(ctx, `p[2] { true }`)
|
||||
repl.OneShot(ctx, `q = {3, 4} { true }`)
|
||||
repl.OneShot(ctx, `r = [x, y] { x = {5, 6}; y = [7, 8] }`)
|
||||
|
||||
repl.OneShot(ctx, "p[x]")
|
||||
expected := parseJSON(`[{"x": 1}, {"x": 2}]`)
|
||||
@@ -631,7 +638,7 @@ func TestEvalRuleCompileError(t *testing.T) {
|
||||
store := newTestStore()
|
||||
var buffer bytes.Buffer
|
||||
repl := newRepl(store, &buffer)
|
||||
repl.OneShot(ctx, "p[x] :- true")
|
||||
repl.OneShot(ctx, `p[x] { true }`)
|
||||
result := buffer.String()
|
||||
expected := "error: 1 error occurred: 1:1: p: x is unsafe (variable x must appear in at least one expression within the body of p)\n"
|
||||
if result != expected {
|
||||
@@ -639,7 +646,7 @@ func TestEvalRuleCompileError(t *testing.T) {
|
||||
return
|
||||
}
|
||||
buffer.Reset()
|
||||
repl.OneShot(ctx, "p = true :- true")
|
||||
repl.OneShot(ctx, `p = true { true }`)
|
||||
result = buffer.String()
|
||||
if result != "" {
|
||||
t.Errorf("Expected valid rule to compile (because state should be unaffected) but got: %v", result)
|
||||
@@ -652,12 +659,12 @@ func TestEvalBodyCompileError(t *testing.T) {
|
||||
var buffer bytes.Buffer
|
||||
repl := newRepl(store, &buffer)
|
||||
repl.outputFormat = "json"
|
||||
err := repl.OneShot(ctx, "x = 1, y > x")
|
||||
err := repl.OneShot(ctx, `x = 1; y > x`)
|
||||
if _, ok := err.(ast.Errors); !ok {
|
||||
t.Fatalf("Expected error message in output but got`: %v", buffer.String())
|
||||
}
|
||||
buffer.Reset()
|
||||
repl.OneShot(ctx, "x = 1, y = 2, y > x")
|
||||
repl.OneShot(ctx, `x = 1; y = 2; y > x`)
|
||||
var result2 []interface{}
|
||||
err = util.UnmarshalJSON(buffer.Bytes(), &result2)
|
||||
if err != nil {
|
||||
@@ -706,12 +713,12 @@ func TestEvalBodyInput(t *testing.T) {
|
||||
var buffer bytes.Buffer
|
||||
repl := newRepl(store, &buffer)
|
||||
|
||||
repl.OneShot(ctx, "package repl")
|
||||
repl.OneShot(ctx, `input["foo.bar"] = "hello" :- true`)
|
||||
repl.OneShot(ctx, `input["baz"] = data.a[0].b.c[2] :- true`)
|
||||
repl.OneShot(ctx, "package test")
|
||||
repl.OneShot(ctx, `package repl`)
|
||||
repl.OneShot(ctx, `input["foo.bar"] = "hello" { true }`)
|
||||
repl.OneShot(ctx, `input["baz"] = data.a[0].b.c[2] { true }`)
|
||||
repl.OneShot(ctx, `package test`)
|
||||
repl.OneShot(ctx, "import input.baz")
|
||||
repl.OneShot(ctx, `p :- input["foo.bar"] = "hello", baz = false`)
|
||||
repl.OneShot(ctx, `p = true { input["foo.bar"] = "hello"; baz = false }`)
|
||||
repl.OneShot(ctx, "p")
|
||||
|
||||
result := buffer.String()
|
||||
@@ -782,7 +789,7 @@ func TestEvalBodyWith(t *testing.T) {
|
||||
var buffer bytes.Buffer
|
||||
repl := newRepl(store, &buffer)
|
||||
|
||||
repl.OneShot(ctx, `p :- input.foo = "bar"`)
|
||||
repl.OneShot(ctx, `p = true { input.foo = "bar" }`)
|
||||
err := repl.OneShot(ctx, "p")
|
||||
|
||||
if err == nil || !strings.Contains(err.Error(), "input document undefined") {
|
||||
@@ -834,9 +841,9 @@ func TestEvalPackage(t *testing.T) {
|
||||
store := newTestStore()
|
||||
var buffer bytes.Buffer
|
||||
repl := newRepl(store, &buffer)
|
||||
repl.OneShot(ctx, "package foo.bar")
|
||||
repl.OneShot(ctx, "p = true :- true")
|
||||
repl.OneShot(ctx, "package baz.qux")
|
||||
repl.OneShot(ctx, `package foo.bar`)
|
||||
repl.OneShot(ctx, `p = true { true }`)
|
||||
repl.OneShot(ctx, `package baz.qux`)
|
||||
buffer.Reset()
|
||||
err := repl.OneShot(ctx, "p")
|
||||
if err.Error() != "1 error occurred: 1:1: p is unsafe (variable p must appear in the output position of at least one non-negated expression)" {
|
||||
@@ -857,9 +864,9 @@ func TestEvalTrace(t *testing.T) {
|
||||
var buffer bytes.Buffer
|
||||
repl := newRepl(store, &buffer)
|
||||
repl.OneShot(ctx, "trace")
|
||||
repl.OneShot(ctx, "data.a[i].b.c[j] = x, data.a[k].b.c[x] = 1")
|
||||
repl.OneShot(ctx, `data.a[i].b.c[j] = x; data.a[k].b.c[x] = 1`)
|
||||
expected := strings.TrimSpace(`
|
||||
Enter data.a[i].b.c[j] = x, data.a[k].b.c[x] = 1
|
||||
Enter data.a[i].b.c[j] = x; data.a[k].b.c[x] = 1
|
||||
| Eval data.a[i].b.c[j] = x
|
||||
| Eval data.a[k].b.c[true] = 1
|
||||
| Fail data.a[k].b.c[true] = 1
|
||||
@@ -867,8 +874,8 @@ Enter data.a[i].b.c[j] = x, data.a[k].b.c[x] = 1
|
||||
| Eval data.a[k].b.c[2] = 1
|
||||
| Fail data.a[0].b.c[2] = 1
|
||||
| Redo data.a[0].b.c[2] = 1
|
||||
| Exit data.a[i].b.c[j] = x, data.a[k].b.c[x] = 1
|
||||
Redo data.a[i].b.c[j] = x, data.a[k].b.c[x] = 1
|
||||
| Exit data.a[i].b.c[j] = x; data.a[k].b.c[x] = 1
|
||||
Redo data.a[i].b.c[j] = x; data.a[k].b.c[x] = 1
|
||||
| Redo data.a[0].b.c[1] = x
|
||||
| Eval data.a[k].b.c[false] = 1
|
||||
| Fail data.a[k].b.c[false] = 1
|
||||
@@ -901,12 +908,12 @@ func TestEvalTruth(t *testing.T) {
|
||||
var buffer bytes.Buffer
|
||||
repl := newRepl(store, &buffer)
|
||||
repl.OneShot(ctx, "truth")
|
||||
repl.OneShot(ctx, "data.a[i].b.c[j] = x, data.a[k].b.c[x] = 1")
|
||||
repl.OneShot(ctx, `data.a[i].b.c[j] = x; data.a[k].b.c[x] = 1`)
|
||||
expected := strings.TrimSpace(`
|
||||
Enter data.a[i].b.c[j] = x, data.a[k].b.c[x] = 1
|
||||
Enter data.a[i].b.c[j] = x; data.a[k].b.c[x] = 1
|
||||
| Redo data.a[0].b.c[0] = x
|
||||
| Redo data.a[0].b.c[2] = 1
|
||||
| Exit data.a[i].b.c[j] = x, data.a[k].b.c[x] = 1
|
||||
| Exit data.a[i].b.c[j] = x; data.a[k].b.c[x] = 1
|
||||
+---+---+---+---+
|
||||
| i | j | k | x |
|
||||
+---+---+---+---+
|
||||
@@ -935,7 +942,7 @@ func TestBuildHeader(t *testing.T) {
|
||||
func assertREPLText(t *testing.T, buf bytes.Buffer, expected string) {
|
||||
result := buf.String()
|
||||
if result != expected {
|
||||
t.Fatalf("Expected:\n%v\n\nGot:\n%v", expected, result)
|
||||
t.Fatalf("Expected:\n%v\n\nString:\n\n%v\nGot:\n%v\n\nString:\n\n%v", []byte(expected), expected, []byte(result), result)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,8 +42,8 @@ func TestLoadRego(t *testing.T) {
|
||||
|
||||
files := map[string]string{
|
||||
"/foo.rego": `package ex
|
||||
p :- true`,
|
||||
}
|
||||
|
||||
p = true { true }`}
|
||||
|
||||
withTempFS(files, func(rootDir string) {
|
||||
moduleFile := filepath.Join(rootDir, "foo.rego")
|
||||
|
||||
+11
-11
@@ -24,7 +24,7 @@ func TestEval(t *testing.T) {
|
||||
var buffer bytes.Buffer
|
||||
params.Output = &buffer
|
||||
params.OutputFormat = "json"
|
||||
params.Eval = `a = b, a = 1, c = 2, c > b`
|
||||
params.Eval = `a = b; a = 1; c = 2; c > b`
|
||||
rt := &Runtime{}
|
||||
rt.Start(params)
|
||||
expected := parseJSON(`[{"a": 1, "b": 1, "c": 2}]`)
|
||||
@@ -56,12 +56,12 @@ func TestInit(t *testing.T) {
|
||||
panic(err)
|
||||
}
|
||||
defer os.Remove(tmp2.Name())
|
||||
mod1 := `
|
||||
package a.b.c
|
||||
import data.foo
|
||||
p = true :- foo = "bar"
|
||||
p = true :- 1 = 2
|
||||
`
|
||||
mod1 := `package a.b.c
|
||||
|
||||
import data.foo
|
||||
|
||||
p = true { foo = "bar" }
|
||||
p = true { 1 = 2 }`
|
||||
if _, err := tmp2.Write([]byte(mod1)); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -78,10 +78,10 @@ func TestInit(t *testing.T) {
|
||||
|
||||
tmp4 := filepath.Join(tmp3, "existingPolicy")
|
||||
|
||||
err = ioutil.WriteFile(tmp4, []byte(`
|
||||
package a.b.c
|
||||
q = true :- false
|
||||
`), 0644)
|
||||
err = ioutil.WriteFile(tmp4, []byte(`package a.b.c
|
||||
|
||||
q = true { false }`,
|
||||
), 0644)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
+41
-49
@@ -48,45 +48,39 @@ type tr struct {
|
||||
func TestDataV1(t *testing.T) {
|
||||
|
||||
testMod1 := `package testmod
|
||||
p[x] :- q[x], not r[x]
|
||||
q[x] :- data.x.y[i] = x
|
||||
r[x] :- data.x.z[i] = x
|
||||
|
||||
import input.req1
|
||||
import input.req2 as reqx
|
||||
import input.req3.attr1
|
||||
g :- req1.a[0] = 1, reqx.b[i] = 1
|
||||
h :- attr1[i] > 1
|
||||
import input.req1
|
||||
import input.req2 as reqx
|
||||
import input.req3.attr1
|
||||
|
||||
gt1 :- req1 > 1
|
||||
arr = [1,2,3,4]
|
||||
|
||||
undef :- false
|
||||
`
|
||||
p[x] { q[x]; not r[x] }
|
||||
q[x] { data.x.y[i] = x }
|
||||
r[x] { data.x.z[i] = x }
|
||||
g = true { req1.a[0] = 1; reqx.b[i] = 1 }
|
||||
h = true { attr1[i] > 1 }
|
||||
gt1 = true { req1 > 1 }
|
||||
arr = [1, 2, 3, 4] { true }
|
||||
undef = true { false }`
|
||||
|
||||
testMod2 := `package testmod
|
||||
|
||||
p = [1,2,3,4]
|
||||
q = {"a": 1, "b": 2}
|
||||
`
|
||||
p = [1, 2, 3, 4] { true }
|
||||
q = {"a": 1, "b": 2} { true }`
|
||||
|
||||
testMod3 := `package testmod
|
||||
|
||||
p :- loopback with input as true
|
||||
loopback = input
|
||||
`
|
||||
p = true { loopback with input as true }
|
||||
loopback = input { true }`
|
||||
|
||||
testMod4 := `package testmod
|
||||
|
||||
p = true :- true
|
||||
p = false :- true
|
||||
`
|
||||
p = true { true }
|
||||
p = false { true }`
|
||||
|
||||
testMod5 := `package testmod.empty.mod`
|
||||
testMod6 := `package testmod.all.undefined
|
||||
|
||||
p :- false
|
||||
`
|
||||
p = true { false }`
|
||||
|
||||
tests := []struct {
|
||||
note string
|
||||
@@ -378,8 +372,8 @@ func TestDataGetExplainTruth(t *testing.T) {
|
||||
f := newFixture(t)
|
||||
|
||||
f.v1("PUT", "/policies/test", `package test
|
||||
p :- a = [1,2,3,4], a[_] = x, x > 1
|
||||
`, 204, "")
|
||||
|
||||
p = true { a = [1, 2, 3, 4]; a[_] = x; x > 1 }`, 204, "")
|
||||
|
||||
req := newReqV1("GET", "/data/test/p?explain=truth", "")
|
||||
f.reset()
|
||||
@@ -419,7 +413,7 @@ func TestDataPostExplain(t *testing.T) {
|
||||
|
||||
f.v1("PUT", "/policies/test", `package test
|
||||
|
||||
p = [1,2,3,4]`, 200, "")
|
||||
p = [1, 2, 3, 4] { true }`, 200, "")
|
||||
|
||||
req := newReqV1("POST", "/data/test/p?explain=full", "")
|
||||
f.reset()
|
||||
@@ -561,20 +555,18 @@ func TestPoliciesPutV1ParseError(t *testing.T) {
|
||||
t.Fatalf("Unexpected JSON decode error: %v", err)
|
||||
}
|
||||
|
||||
expected := ast.NewLocation(nil, "test", 4, 8)
|
||||
|
||||
if !reflect.DeepEqual(errs.Errors[0].Location, expected) {
|
||||
t.Fatalf("Expected error location to be %v but got: %v", expected, errs)
|
||||
if errs.Errors[0].Location.File != "test" || errs.Errors[0].Location.Row != 4 {
|
||||
t.Fatalf("Bad location: %v (expecfted test:4)", errs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPoliciesPutV1CompileError(t *testing.T) {
|
||||
f := newFixture(t)
|
||||
req := newReqV1("PUT", "/policies/test", `
|
||||
package a.b.c
|
||||
p[x] :- q[x]
|
||||
q[x] :- p[x]
|
||||
`)
|
||||
req := newReqV1("PUT", "/policies/test", `package a.b.c
|
||||
|
||||
p[x] { q[x] }
|
||||
q[x] { p[x] }`,
|
||||
)
|
||||
|
||||
f.server.Handler.ServeHTTP(f.recorder, req)
|
||||
|
||||
@@ -587,8 +579,6 @@ func TestPoliciesPutV1CompileError(t *testing.T) {
|
||||
t.Fatalf("Unexpected JSON decode error: %v", err)
|
||||
}
|
||||
|
||||
expected := ast.NewLocation(nil, "test", 3, 5)
|
||||
|
||||
if len(errs.Errors) != 2 {
|
||||
t.Fatalf("Expected exactly two errors but got %d: %v", len(errs.Errors), errs)
|
||||
}
|
||||
@@ -596,13 +586,14 @@ func TestPoliciesPutV1CompileError(t *testing.T) {
|
||||
found := false
|
||||
|
||||
for _, err := range errs.Errors {
|
||||
if reflect.DeepEqual(err.Location, expected) {
|
||||
if err.Location.File == "test" && err.Location.Row == 3 {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Fatalf("Missing expected error %v: %v", expected, errs)
|
||||
t.Fatalf("Missing expected error %v (expected test:3)", errs)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -726,7 +717,7 @@ func TestPoliciesDeleteV1(t *testing.T) {
|
||||
|
||||
func TestQueryV1(t *testing.T) {
|
||||
f := newFixture(t)
|
||||
get := newReqV1("GET", `/query?q=a=[1,2,3],a[i]=x`, "")
|
||||
get := newReqV1("GET", `/query?q=a=[1,2,3]%3Ba[i]=x`, "")
|
||||
f.server.Handler.ServeHTTP(f.recorder, get)
|
||||
|
||||
if f.recorder.Code != 200 {
|
||||
@@ -754,7 +745,7 @@ func TestQueryV1(t *testing.T) {
|
||||
|
||||
func TestQueryV1Explain(t *testing.T) {
|
||||
f := newFixture(t)
|
||||
get := newReqV1("GET", `/query?q=a=[1,2,3],a[i]=x&explain=full`, "")
|
||||
get := newReqV1("GET", `/query?q=a=[1,2,3]%3Ba[i]=x&explain=full`, "")
|
||||
f.server.Handler.ServeHTTP(f.recorder, get)
|
||||
|
||||
if f.recorder.Code != 200 {
|
||||
@@ -771,7 +762,7 @@ func TestQueryV1Explain(t *testing.T) {
|
||||
t.Fatalf("Expected exactly 10 trace events for full query but got %d", len(result.Explanation))
|
||||
}
|
||||
|
||||
get = newReqV1("GET", "/query?q=a=[1,2,3],a[_]=x,x>1&explain=truth", "")
|
||||
get = newReqV1("GET", "/query?q=a=[1,2,3]%3Ba[_]=x%3Bx>1&explain=truth", "")
|
||||
f.reset()
|
||||
f.server.Handler.ServeHTTP(f.recorder, get)
|
||||
|
||||
@@ -850,12 +841,13 @@ func TestQueryBindingIterationError(t *testing.T) {
|
||||
}
|
||||
|
||||
const (
|
||||
testMod = `
|
||||
package a.b.c
|
||||
import data.x.y as z
|
||||
import data.p
|
||||
q[x] :- p[x], not r[x]
|
||||
r[x] :- z[x] = 4`
|
||||
testMod = `package a.b.c
|
||||
|
||||
import data.x.y as z
|
||||
import data.p
|
||||
|
||||
q[x] { p[x]; not r[x] }
|
||||
r[x] { z[x] = 4 }`
|
||||
)
|
||||
|
||||
type fixture struct {
|
||||
|
||||
@@ -170,7 +170,7 @@ func ExampleStorage_Open() {
|
||||
|
||||
package opa.example
|
||||
|
||||
p :- q.r != 0
|
||||
p { q.r != 0 }
|
||||
|
||||
`
|
||||
|
||||
|
||||
@@ -207,18 +207,14 @@ func TestPolicyStoreUpdate(t *testing.T) {
|
||||
}
|
||||
|
||||
const (
|
||||
testMod1 = `
|
||||
package a.b
|
||||
testMod1 = `package a.b
|
||||
|
||||
p = true :- q
|
||||
q = true :- true
|
||||
`
|
||||
p = true { q }
|
||||
q = true { true }`
|
||||
|
||||
testMod2 = `
|
||||
package a.b
|
||||
testMod2 = `package a.b
|
||||
|
||||
p = true :- false
|
||||
`
|
||||
p = true { false }`
|
||||
)
|
||||
|
||||
type fixture struct {
|
||||
|
||||
@@ -182,16 +182,15 @@ func generateRequestPath(i int) string {
|
||||
return fmt.Sprintf("/api/v1/resourcetype-%d/somefakeresourceid000000111111", i)
|
||||
}
|
||||
|
||||
const policy = `
|
||||
package restauthz
|
||||
const policy = `package restauthz
|
||||
|
||||
import data.restauthz.tokens
|
||||
|
||||
default allow = false
|
||||
|
||||
allow :-
|
||||
tokens[input.token_id] = token,
|
||||
token.authz_profiles[_] = authz,
|
||||
re_match(authz.path, input.path),
|
||||
authz.methods[_] = input.method
|
||||
`
|
||||
allow {
|
||||
tokens[input.token_id] = token
|
||||
token.authz_profiles[_] = authz
|
||||
re_match(authz.path, input.path)
|
||||
authz.methods[_] = input.method
|
||||
}`
|
||||
|
||||
@@ -363,7 +363,7 @@ used_nonzero_cpu[node_id] = used {
|
||||
|
||||
pods_on_node[node_id] = pds {
|
||||
node_name = nodes[node_id].metadata.name
|
||||
pds = [p | pods[i].spec.nodeName = node_name, p = pods[i]]
|
||||
pds = [p | pods[i].spec.nodeName = node_name; p = pods[i]]
|
||||
}
|
||||
|
||||
hollow_node {
|
||||
@@ -509,7 +509,8 @@ rcs_for_pod[pod_id] = rc_ids {
|
||||
}
|
||||
|
||||
selector_matches[[pod_id, rc_id]] {
|
||||
pods[pod_id], rcs[rc_id]
|
||||
pods[pod_id]
|
||||
rcs[rc_id]
|
||||
x = [pod_id, rc_id]
|
||||
not selector_not_matches[x]
|
||||
}
|
||||
|
||||
+7
-10
@@ -26,7 +26,7 @@ func ExampleEval() {
|
||||
compiler := ast.NewCompiler()
|
||||
|
||||
// Define a dummy query and some data that the query will execute against.
|
||||
query, err := compiler.QueryCompiler().Compile(ast.MustParseBody("data.a[_] = x, x >= 2"))
|
||||
query, err := compiler.QueryCompiler().Compile(ast.MustParseBody(`data.a[_] = x; x >= 2`))
|
||||
if err != nil {
|
||||
// Handle error.
|
||||
}
|
||||
@@ -99,15 +99,12 @@ func ExampleQuery() {
|
||||
compiler := ast.NewCompiler()
|
||||
|
||||
// Define a dummy module with rules that produce documents that we will query below.
|
||||
module, err := ast.ParseModule("my_module.rego", `
|
||||
module, err := ast.ParseModule("my_module.rego", `package opa.example
|
||||
|
||||
package opa.example
|
||||
|
||||
p[x] :- q[x], not r[x]
|
||||
q[y] :- a = [1,2,3], y = a[_]
|
||||
r[z] :- b = [2,4], z = b[_]
|
||||
|
||||
`)
|
||||
p[x] { q[x]; not r[x] }
|
||||
q[y] { a = [1, 2, 3]; y = a[_] }
|
||||
r[z] { b = [2, 4]; z = b[_] }`,
|
||||
)
|
||||
|
||||
mods := map[string]*ast.Module{
|
||||
"my_module": module,
|
||||
@@ -202,7 +199,7 @@ func ExampleRegisterFunctionalBuiltin1() {
|
||||
// queries. Our custom built-in converts strings to upper case but is not
|
||||
// defined for the input "magic".
|
||||
compiler := ast.NewCompiler()
|
||||
query, err := compiler.QueryCompiler().Compile(ast.MustParseBody(`selective_upper("custom", x), not selective_upper("magic", "MAGIC")`))
|
||||
query, err := compiler.QueryCompiler().Compile(ast.MustParseBody(`selective_upper("custom", x); not selective_upper("magic", "MAGIC")`))
|
||||
if err != nil {
|
||||
// Handle error.
|
||||
}
|
||||
|
||||
@@ -17,16 +17,15 @@ import (
|
||||
|
||||
func TestTruth(t *testing.T) {
|
||||
|
||||
module := `
|
||||
package test
|
||||
p :- q[x], r[x]
|
||||
q[x] :- a = [1,2,3,4], x = a[_]
|
||||
r[z] :- z = 3
|
||||
r[a] :- a = 4
|
||||
`
|
||||
module := `package test
|
||||
|
||||
q := ast.MustParseRule(`q[x] :- a = [1,2,3,4], x = a[_]`)
|
||||
ra := ast.MustParseRule(`r[a] :- a = 4`)
|
||||
p = true { q[x]; r[x] }
|
||||
q[x] { a = [1, 2, 3, 4]; x = a[_] }
|
||||
r[z] { z = 3 }
|
||||
r[a] { a = 4 }`
|
||||
|
||||
q := ast.MustParseRule(`q[x] { a = [1, 2, 3, 4]; x = a[_] }`)
|
||||
ra := ast.MustParseRule(`r[a] { a = 4 }`)
|
||||
|
||||
runTruthTestCase(t, "", module, 14, map[int]*topdown.Event{
|
||||
6: &topdown.Event{
|
||||
@@ -54,33 +53,30 @@ func TestTruth(t *testing.T) {
|
||||
|
||||
func TestTruthAllPaths(t *testing.T) {
|
||||
|
||||
module := `
|
||||
package test
|
||||
p :- q = {"a": 1, "d": 1}
|
||||
q[k] = 1 :- a = ["a", "b", "c", "d"], a[_] = k, r[k]
|
||||
r[x] :- x = "d"
|
||||
r[y] :- y = "a"
|
||||
`
|
||||
module := `package test
|
||||
|
||||
p = true { q = {"a": 1, "d": 1} }
|
||||
q[k] = 1 { a = ["a", "b", "c", "d"]; a[_] = k; r[k] }
|
||||
r[x] { x = "d" }
|
||||
r[y] { y = "a" }`
|
||||
|
||||
runTruthTestCaseIdentity(t, module)
|
||||
}
|
||||
|
||||
func TestTruthAllPathsComprehension(t *testing.T) {
|
||||
module := `
|
||||
package test
|
||||
p :- x = [y | a=[1,2,3,4], a[_] = y, y != 2], count(x, 3)
|
||||
`
|
||||
module := `package test
|
||||
|
||||
p = true { x = [y | a = [1, 2, 3, 4]; a[_] = y; y != 2]; count(x, 3) }`
|
||||
|
||||
runTruthTestCaseIdentity(t, module)
|
||||
}
|
||||
|
||||
func TestTruthAllPathsNegation(t *testing.T) {
|
||||
|
||||
module := `
|
||||
package test
|
||||
p :- not q
|
||||
q :- a = [1,2,3], a[_] = 100
|
||||
`
|
||||
module := `package test
|
||||
|
||||
p = true { not q }
|
||||
q = true { a = [1, 2, 3]; a[_] = 100 }`
|
||||
|
||||
runTruthTestCaseIdentity(t, module)
|
||||
}
|
||||
@@ -108,21 +104,14 @@ func TestExample(t *testing.T) {
|
||||
}
|
||||
`
|
||||
|
||||
module := `
|
||||
package test
|
||||
module := `package test
|
||||
|
||||
import data.servers
|
||||
import data.networks
|
||||
import data.ports
|
||||
import data.servers
|
||||
import data.networks
|
||||
import data.ports
|
||||
|
||||
p :- public_servers[x]
|
||||
|
||||
public_servers[server] :-
|
||||
server = servers[server_index],
|
||||
server.ports[port_index] = ports[i].id,
|
||||
ports[i].networks[network_index] = networks[j].id,
|
||||
networks[j].public = true
|
||||
`
|
||||
p = true { public_servers[x] }
|
||||
public_servers[server] { server = servers[server_index]; server.ports[port_index] = ports[i].id; ports[i].networks[network_index] = networks[j].id; networks[j].public = true }`
|
||||
|
||||
runTruthTestCase(t, data, module, 12, map[int]*topdown.Event{
|
||||
6: &topdown.Event{
|
||||
|
||||
+508
-553
File diff suppressed because it is too large
Load Diff
+21
-22
@@ -33,7 +33,7 @@ func TestEventEqual(t *testing.T) {
|
||||
{&Event{ParentID: 1}, &Event{ParentID: 2}, false},
|
||||
{&Event{Node: ast.MustParseBody("true")}, &Event{Node: ast.MustParseBody("false")}, false},
|
||||
{&Event{Node: ast.MustParseBody("true")[0]}, &Event{Node: ast.MustParseBody("false")[0]}, false},
|
||||
{&Event{Node: ast.MustParseRule("p :- true")}, &Event{Node: ast.MustParseRule("p :- false")}, false},
|
||||
{&Event{Node: ast.MustParseRule(`p = true { true }`)}, &Event{Node: ast.MustParseRule(`p = true { false }`)}, false},
|
||||
{&Event{Node: "foo"}, &Event{Node: "foo"}, false}, // test some unsupported node type
|
||||
}
|
||||
|
||||
@@ -52,11 +52,10 @@ func TestEventEqual(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPrettyTrace(t *testing.T) {
|
||||
module := `
|
||||
package test
|
||||
p :- q[x], plus(x, 1, n)
|
||||
q[x] :- x = data.a[_]
|
||||
`
|
||||
module := `package test
|
||||
|
||||
p = true { q[x]; n = x + 1 }
|
||||
q[x] { x = data.a[_] }`
|
||||
|
||||
ctx := context.Background()
|
||||
compiler := compileModules([]string{module})
|
||||
@@ -76,34 +75,34 @@ func TestPrettyTrace(t *testing.T) {
|
||||
|
||||
expected := `Enter data.test.p = _
|
||||
| Eval data.test.p = _
|
||||
| Enter p = true :- data.test.q[x], n = x + 1
|
||||
| Enter p = true { data.test.q[x]; n = x + 1 }
|
||||
| | Eval data.test.q[x]
|
||||
| | Enter q[x] :- x = data.a[_]
|
||||
| | Enter q[x] { x = data.a[_] }
|
||||
| | | Eval x = data.a[_]
|
||||
| | | Exit q[x] :- x = data.a[_]
|
||||
| | | Exit q[x] { x = data.a[_] }
|
||||
| | Eval n = x + 1
|
||||
| | Exit p = true :- data.test.q[x], n = x + 1
|
||||
| Redo p = true :- data.test.q[x], n = x + 1
|
||||
| | Exit p = true { data.test.q[x]; n = x + 1 }
|
||||
| Redo p = true { data.test.q[x]; n = x + 1 }
|
||||
| | Redo data.test.q[x]
|
||||
| | Redo q[x] :- x = data.a[_]
|
||||
| | Redo q[x] { x = data.a[_] }
|
||||
| | | Redo x = data.a[_]
|
||||
| | | Exit q[x] :- x = data.a[_]
|
||||
| | | Exit q[x] { x = data.a[_] }
|
||||
| | Eval n = x + 1
|
||||
| | Exit p = true :- data.test.q[x], n = x + 1
|
||||
| Redo p = true :- data.test.q[x], n = x + 1
|
||||
| | Exit p = true { data.test.q[x]; n = x + 1 }
|
||||
| Redo p = true { data.test.q[x]; n = x + 1 }
|
||||
| | Redo data.test.q[x]
|
||||
| | Redo q[x] :- x = data.a[_]
|
||||
| | Redo q[x] { x = data.a[_] }
|
||||
| | | Redo x = data.a[_]
|
||||
| | | Exit q[x] :- x = data.a[_]
|
||||
| | | Exit q[x] { x = data.a[_] }
|
||||
| | Eval n = x + 1
|
||||
| | Exit p = true :- data.test.q[x], n = x + 1
|
||||
| Redo p = true :- data.test.q[x], n = x + 1
|
||||
| | Exit p = true { data.test.q[x]; n = x + 1 }
|
||||
| Redo p = true { data.test.q[x]; n = x + 1 }
|
||||
| | Redo data.test.q[x]
|
||||
| | Redo q[x] :- x = data.a[_]
|
||||
| | Redo q[x] { x = data.a[_] }
|
||||
| | | Redo x = data.a[_]
|
||||
| | | Exit q[x] :- x = data.a[_]
|
||||
| | | Exit q[x] { x = data.a[_] }
|
||||
| | Eval n = x + 1
|
||||
| | Exit p = true :- data.test.q[x], n = x + 1
|
||||
| | Exit p = true { data.test.q[x]; n = x + 1 }
|
||||
| Exit data.test.p = _
|
||||
`
|
||||
|
||||
|
||||
Reference in New Issue
Block a user