Fix test cases to work with new syntax

This commit is contained in:
Torin Sandall
2017-02-10 00:47:46 -08:00
parent b1349c8a17
commit 49a963f16a
26 changed files with 1896 additions and 1987 deletions
+7 -5
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
}
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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)
}
}
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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 {
+1 -1
View File
@@ -170,7 +170,7 @@ func ExampleStorage_Open() {
package opa.example
p :- q.r != 0
p { q.r != 0 }
`
+5 -9
View File
@@ -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 {
+7 -8
View File
@@ -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
}`
+3 -2
View File
@@ -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
View File
@@ -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.
}
+27 -38
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+21 -22
View File
@@ -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 = _
`