Code from github.com/anakrish/rego-rs

Authored by anakrish and mingweishih

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
This commit is contained in:
Anand Krishnamoorthi
2023-02-09 10:56:54 -08:00
parent 8f67aeecb0
commit cb0b3a1790
85 changed files with 11144 additions and 0 deletions
+60
View File
@@ -0,0 +1,60 @@
# Copyright (c) Rego-Rs Authors.
# Licensed under the Apache 2.0 license.
cases:
- note: basic
rego: |
package test
import future.keywords
y = 8 {
every x in [2] {
x > 0
every a, b in vals { check(a) }
}
}
policy:
- spec:
head:
compr:
refr:
var: y
assign:
op: "="
value:
number: 8
bodies:
- query:
stmts:
- literal:
every:
key: x
domain:
array:
- number: 2
query:
stmts:
- literal:
expr:
boolexpr:
op: ">"
lhs:
var: x
rhs:
number: 0
- literal:
every:
key: a
value: b
domain:
var: vals
query:
stmts:
- literal:
expr:
call:
fcn:
var: check
params:
- var: a
@@ -0,0 +1,40 @@
# Copyright (c) Rego-Rs Authors.
# Licensed under the Apache 2.0 license.
cases:
- note: basic
rego: |
package test
x = 1 - 2 * 3 / 4 + 2
policy:
- spec:
head:
compr:
refr:
var: x
assign:
op: =
value:
arithexpr:
op: +
lhs:
arithexpr:
op: "-"
lhs:
number: 1
rhs:
arithexpr:
op: "/"
lhs:
arithexpr:
op: "*"
lhs:
number: 2
rhs:
number: 3
rhs:
number: 4
rhs:
number: 2
bodies: []
@@ -0,0 +1,209 @@
# Copyright (c) Rego-Rs Authors.
# Licensed under the Apache 2.0 license.
cases:
- note: case1-compr
rego: |
package test
x = {1}
y = {2}
z = [ x | y ] #, 5] # in {5}, 6]
policy:
- --skip--
- --skip--
- spec:
head:
compr:
refr:
var: z
assign:
op: =
value:
arraycompr:
term:
var: x
query:
stmts:
- span: y
literal:
expr:
var: y
bodies: []
- note: case2-array
rego: |
package test
x = {1}
y = {2}
z = [ x | y, 5] # in {5}, 6]
policy:
- --skip--
- --skip--
- spec:
head:
compr:
refr:
var: z
assign:
op: =
value:
array:
- binexpr:
op: "|"
lhs:
var: x
rhs:
var: y
- number: 5
bodies: []
- note: case3-compr
rego: |
package test
import future.keywords.in
x = {1}
y = {2}
z = [ x | y, 5 in {5}] #, 6]
policy:
- --skip--
- --skip--
- spec:
head:
compr:
refr:
var: z
assign:
op: =
value:
arraycompr:
term:
var: x
query:
stmts:
- span: y, 5 in {5}
literal:
expr:
inexpr:
key:
var: y
value:
number: 5
collection:
set:
- number: 5
bodies: []
- note: case4-array
rego: |
package test
import future.keywords.in
x = {1}
y = {2}
z = [ x | y, 5 in {5}, 6]
policy:
- --skip--
- --skip--
- spec:
head:
compr:
refr:
var: z
assign:
op: =
value:
array:
- binexpr:
op: "|"
lhs:
var: x
rhs:
var: y
- inexpr:
key:
number: 5
collection:
set:
- number: 5
- number: 6
bodies: []
- note: case5-array
rego: |
package test
import future.keywords.in
x = {1}
y = {2}
z = [ x - {10} | y, 5 in {5}] #, 6]
policy:
- --skip--
- --skip--
- spec:
head:
compr:
refr:
var: z
assign:
op: =
value:
array:
- binexpr:
op: "|"
lhs:
arithexpr:
op: "-"
lhs:
var: x
rhs:
set:
- number: 10
rhs:
var: y
- inexpr:
key:
number: 5
collection:
set:
- number: 5
bodies: []
- note: case6-compr
rego: |
package test
import future.keywords.in
x = {1}
y = {2}
z = [ (x - {10}) | y, 5 in {5}] #, 6]
policy:
- --skip--
- --skip--
- spec:
head:
compr:
refr:
var: z
assign:
op: =
value:
arraycompr:
term:
arithexpr:
op: "-"
lhs:
var: x
rhs:
set:
- number: 10
query:
stmts:
- literal:
expr:
inexpr:
key:
var: y
value:
number: 5
collection:
set:
- number: 5
bodies: []
+108
View File
@@ -0,0 +1,108 @@
# Copyright (c) Rego-Rs Authors.
# Licensed under the Apache 2.0 license.
cases:
- note: all
rego: |
package test
# One item
x = {1}
# Multiple items
y = [
2.5, "abc", [ 4, `raw`, # Trailing comma
],
# Empty array
[],
# Nested empty
[[[[[]]]]]
]
policy:
- spec:
head:
compr:
refr:
var: x
assign:
span: = {1}
op: "="
value:
set:
- number: 1
bodies: --skip--
- spec:
head:
compr:
refr:
var: y
assign:
op: "="
value:
array:
- number: 2.5
- string: abc
- array:
- number: 4
- rawstring: raw
- array: []
- array:
- array:
- array:
- array:
- array: []
bodies: []
- note: trailing-comma
rego: |
package test
x = [1,]
y = [1,2
,]
policy:
- spec:
head:
compr:
refr:
var: x
assign:
op: "="
value:
array:
span: "[1,]"
values:
- number: 1
bodies: []
- spec:
head:
compr:
refr:
var: y
assign:
op: "="
value:
array:
span: "[1,2\n,]"
values:
- number: 1
- number: 2
bodies: []
- note: no-comma
rego: |
package test
x = [1 2]
error: expecting `]` while parsing array
- note: two-trailing-commas
rego: |
package test
x = [1,2,,]
error: expecting expression
- note: unclosed
rego: |
package test
x = [ 1
error: expecting `]` while parsing array
+64
View File
@@ -0,0 +1,64 @@
# Copyright (c) Rego-Rs Authors.
# Licensed under the Apache 2.0 license.
cases:
- note: basic
rego: |
package test
# & has higher precedence than |.
# If not the following expressions would evaluate to empty set.
x = {1, 2, 3} | {2} & {4}
y = {2} & {4} | {1, 2, 3}
policy:
- spec:
head:
compr:
refr:
var: x
assign:
op: =
value:
binexpr:
op: "|"
lhs:
set:
- number: 1
- number: 2
- number: 3
rhs:
binexpr:
op: "&"
lhs:
set:
- number: 2
rhs:
set:
- number: 4
bodies: []
- spec:
head:
compr:
refr:
var: y
assign:
op: =
value:
binexpr:
op: "|"
rhs:
set:
- number: 1
- number: 2
- number: 3
lhs:
binexpr:
op: "&"
lhs:
set:
- number: 2
rhs:
set:
- number: 4
bodies: []
+43
View File
@@ -0,0 +1,43 @@
# Copyright (c) Rego-Rs Authors.
# Licensed under the Apache 2.0 license.
cases:
- note: basic
rego: |
package test
# bool ops have lower precedence than arithmetic operators
x = 1 + 2 > 3 - 2
# TODO: lock down
# different types against object
# different types against set
# strings etc
policy:
- spec:
head:
compr:
refr:
var: x
assign:
op: =
value:
boolexpr:
op: ">"
lhs:
arithexpr:
op: "+"
lhs:
number: 1
rhs:
number: 2
rhs:
arithexpr:
op: "-"
lhs:
number: 3
rhs:
number: 2
bodies: []
+48
View File
@@ -0,0 +1,48 @@
# Copyright (c) Rego-Rs Authors.
# Licensed under the Apache 2.0 license.
cases:
- note: basic
rego: |
package test
# A call in rule-ref. This creates an object.
deny[sprintf("Hello %v", ["world"])] = 1
# Trailing comma
x = inc(5,)
policy:
- spec:
head:
compr:
refr:
refbrack:
refr:
var: deny
index:
call:
fcn:
var: sprintf
params:
- string: "Hello %v"
- array:
- string: "world"
assign:
op: =
value:
number: 1
bodies: []
- spec:
head:
compr:
refr:
var: x
assign:
op: "="
value:
call:
fcn:
var: inc
params:
- number: 5
bodies: []
+59
View File
@@ -0,0 +1,59 @@
# Copyright (c) Rego-Rs Authors.
# Licensed under the Apache 2.0 license.
cases:
- note: basic
rego: |
package test
import future.keywords.in
x = 5 in [4, 5]
# in-exprs are left associative and ahve lower-precedence than bin-expr.
# The following will be parsed as
# (5 in [4, 5]) in (set() | {true})
z = 5 in [4, 5] in set() | {true}
policy:
- spec:
head:
compr:
refr:
var: x
assign:
op: "="
value:
inexpr:
key:
number: 5
collection:
array:
- number: 4
- number: 5
bodies: []
- spec:
head:
compr:
refr:
var: z
assign:
op: "="
value:
inexpr:
key:
inexpr:
key:
number: 5
collection:
array:
- number: 4
- number: 5
collection:
binexpr:
op: "|"
lhs:
set: []
rhs:
set:
- true
bodies: []
@@ -0,0 +1,53 @@
# Copyright (c) Rego-Rs Authors.
# Licensed under the Apache 2.0 license.
cases:
- note: basic
rego: |
package test
import future.keywords.in
# key, value in collection
x = 0, 5 in [5]
# Chained in-exprs
y = 0, 5 in c in d
policy:
- spec:
head:
compr:
refr:
var: x
assign:
op: "="
value:
inexpr:
key:
number: 0
value:
number: 5
collection:
array:
- number: 5
bodies: []
- spec:
head:
compr:
refr:
var: y
assign:
op: "="
value:
inexpr:
key:
inexpr:
key:
number: 0
value:
number: 5
collection:
var: c
collection:
var: d
bodies: []
+163
View File
@@ -0,0 +1,163 @@
# Copyright (c) Rego-Rs Authors.
# Licensed under the Apache 2.0 license.
cases:
- note: all
rego: |
package test
import future.keywords.in
# Empty object
x = {}
# Single field
y = {
"a" : 5
}
# Multiple fields
z = {
"a" : 5,
"b" : [ 1, 2, 3 ],
"c" : { 4, 5, 6 },
"d" : {
"a" : 5,
"b" : set()
}
# array as key
, [1,
2,
3] : 4,
# set as key
{ 1 } : 2,
# Object as key
{
"a" : 1,
"b" : 2, # Trailing comma
} : `hello,
world`
# Null, boolean
, null: false,
true : true,
# Only single var in is supported as value
"p" : "q", "r" in "d" : "e"
}
policy:
- spec:
span: x = {}
head:
compr:
span: x = {}
refr:
var: x
assign:
span: = {}
op: =
value:
object:
fields: []
bodies: []
- spec:
head:
compr:
refr:
var: y
assign:
op: =
value:
object:
fields:
- key:
string: a
value:
number: 5
bodies: []
- spec:
head:
compr:
refr:
var: z
assign:
op: =
value:
object:
fields:
- key:
string: a
value:
number: 5
- key:
string: b
value:
array:
- number: 1
- number: 2
- number: 3
- key:
string: c
value:
set:
- number: 4
- number: 5
- number : 6
- key:
string: d
value:
object:
fields:
- key:
string: a
value:
number: 5
- key:
string: b
value:
set: []
- key:
array:
- number: 1
- number: 2
- number: 3
value:
number: 4
- key:
set:
- number: 1
value:
number: 2
- key:
object:
fields:
- key:
string: a
value:
number: 1
- key:
string: b
value:
number: 2
value:
rawstring: "hello,\n world"
- key: null
value: false
- key: true
value: true
- key:
string: p
value:
string: q
- key:
inexpr:
key:
string: r
collection:
string: d
value:
string: e
bodies: []
@@ -0,0 +1,209 @@
# Copyright (c) Rego-Rs Authors.
# Licensed under the Apache 2.0 license.
cases:
- note: case1-compr
rego: |
package test
x = {1}
y = {2}
z = { x | y } #, 5} # in {5}, 6}
policy:
- --skip--
- --skip--
- spec:
head:
compr:
refr:
var: z
assign:
op: =
value:
setcompr:
term:
var: x
query:
stmts:
- span: y
literal:
expr:
var: y
bodies: []
- note: case2-set
rego: |
package test
x = {1}
y = {2}
z = { x | y, 5} # in {5}, 6}
policy:
- --skip--
- --skip--
- spec:
head:
compr:
refr:
var: z
assign:
op: =
value:
set:
- binexpr:
op: "|"
lhs:
var: x
rhs:
var: y
- number: 5
bodies: []
- note: case3-compr
rego: |
package test
import future.keywords.in
x = {1}
y = {2}
z = { x | y, 5 in {5}} #, 6}
policy:
- --skip--
- --skip--
- spec:
head:
compr:
refr:
var: z
assign:
op: =
value:
setcompr:
term:
var: x
query:
stmts:
- span: y, 5 in {5}
literal:
expr:
inexpr:
key:
var: y
value:
number: 5
collection:
set:
- number: 5
bodies: []
- note: case4-set
rego: |
package test
import future.keywords.in
x = {1}
y = {2}
z = { x | y, 5 in {5}, 6}
policy:
- --skip--
- --skip--
- spec:
head:
compr:
refr:
var: z
assign:
op: =
value:
set:
- binexpr:
op: "|"
lhs:
var: x
rhs:
var: y
- inexpr:
key:
number: 5
collection:
set:
- number: 5
- number: 6
bodies: []
- note: case5-set
rego: |
package test
import future.keywords.in
x = {1}
y = {2}
z = { x - {10} | y, 5 in {5}} #, 6}
policy:
- --skip--
- --skip--
- spec:
head:
compr:
refr:
var: z
assign:
op: =
value:
set:
- binexpr:
op: "|"
lhs:
arithexpr:
op: "-"
lhs:
var: x
rhs:
set:
- number: 10
rhs:
var: y
- inexpr:
key:
number: 5
collection:
set:
- number: 5
bodies: []
- note: case6-compr
rego: |
package test
import future.keywords.in
x = {1}
y = {2}
z = { (x - {10}) | y, 5 in {5}} #, 6}
policy:
- --skip--
- --skip--
- spec:
head:
compr:
refr:
var: z
assign:
op: =
value:
setcompr:
term:
arithexpr:
op: "-"
lhs:
var: x
rhs:
set:
- number: 10
query:
stmts:
- literal:
expr:
inexpr:
key:
var: y
value:
number: 5
collection:
set:
- number: 5
bodies: []
+112
View File
@@ -0,0 +1,112 @@
# Copyright (c) Rego-Rs Authors.
# Licensed under the Apache 2.0 license.
cases:
- note: all
rego: |
package test
# One item
x = {1}
# Multiple items
y = {
2.5, "abc", { 4, `raw`
},
# Empty set has a special syntax
set( ),
# Nested empty
{{{{{}}}}}
}
policy:
- spec:
head:
compr:
refr:
var: x
assign:
span: = {1}
op: "="
value:
set:
- number: 1
bodies: --skip--
- spec:
head:
compr:
refr:
var: y
assign:
op: "="
value:
set:
- number: 2.5
- string: abc
- set:
- number: 4
- rawstring: raw
- set: []
- set:
- set:
- set:
- set:
- object:
span: "{}"
fields: []
bodies: []
- note: trailing-comma
rego: |
package test
x = {1,}
y = {1,2
,}
policy:
- spec:
head:
compr:
refr:
var: x
assign:
op: "="
value:
set:
span: "{1,}"
values:
- number: 1
bodies: []
- spec:
head:
compr:
refr:
var: y
assign:
op: "="
value:
set:
span: "{1,2\n,}"
values:
- number: 1
- number: 2
bodies: []
- note: no-comma
rego: |
package test
x = {1 2}
error: expecting `}` while parsing set
- note: two-trailing-commas
rego: |
package test
x = {1,2,,}
error: expecting expression
- note: unclosed
rego: |
package test
x = { 1
error: expecting `}` while parsing set
+154
View File
@@ -0,0 +1,154 @@
# Copyright (c) Rego-Rs Authors.
# Licensed under the Apache 2.0 license.
cases:
- note: namespace
rego: |
package test
import future.keywords
imports:
- span: import future.keywords
refr:
refdot:
span: future.keywords
refr:
var: future
field: keywords
- note: all
rego: |
package test
import future.keywords.contains import future.keywords.every
import future.keywords.if
import
future.keywords.in
imports:
- span: import future.keywords.contains
refr:
refdot:
span: future.keywords.contains
refr:
refdot:
span: future.keywords
refr:
var: future
field: keywords
field: contains
- span: import future.keywords.every
refr:
refdot:
span: future.keywords.every
refr:
refdot:
span: future.keywords
refr:
var: future
field: keywords
field: every
- span: import future.keywords.if
refr:
refdot:
span: future.keywords.if
refr:
refdot:
span: future.keywords
refr:
var: future
field: keywords
field: if
- span: "import\nfuture.keywords.in"
refr:
refdot:
span: future.keywords.in
refr:
refdot:
span: future.keywords
refr:
var: future
field: keywords
field: in
- note: bracket
rego: |
package test
import future["keywords"]["contains"] import future.keywords["every"]
import
future["keywords"].if
imports:
- span: import future["keywords"]["contains"]
refr:
refbrack:
span: future["keywords"]["contains"]
refr:
refbrack:
span: future["keywords"]
refr:
var: future
index:
string: keywords
index:
string: contains
- span: import future.keywords["every"]
refr:
refbrack:
span: future.keywords["every"]
refr:
refdot:
span: future.keywords
refr:
var: future
field: keywords
index:
string: every
- span: "import\nfuture[\"keywords\"].if"
refr:
refdot:
span: future["keywords"].if
refr:
refbrack:
span: future["keywords"]
refr:
var: future
index:
string: keywords
field: if
- note: as
rego: |
package test
import future.keywords.in as on
error: "`future` imports cannot be aliased"
- note: shadow
rego: |
package test
import future.keywords
import future["keywords"]
error: "this import shadows previous import of `contains`"
- note: shadow/1
rego: |
package test
import future.keywords
import future.keywords.if
error: "this import shadows previous import of `if`"
- note: shadow/2
rego: |
package test
import future.keywords.if
import future.keywords
error: "this import shadows previous import of `if`"
- note: in-as-var
rego: |
package test
import future.keywords.if
in = 5
- note: in-as-var-imported
rego: |
package test
import future.keywords.in
in = 5
error: unexpected keyword `in`
+308
View File
@@ -0,0 +1,308 @@
# Copyright (c) Rego-Rs Authors.
# Licensed under the Apache 2.0 license.
cases:
- note: data
rego: |
package test
import data
import input
imports:
- span: import data
refr:
var: data
- span: import input
refr:
var: input
- note: input
rego: |
package test
import input
imports:
- span: import input
refr:
var: input
- note: dot
rego: |
package test
import input.a
import data.b
imports:
- span: import input.a
refr:
refdot:
refr:
var: input
field: a
- span: import data.b
refr:
refdot:
refr:
var: data
field: b
- note: bracket
rego: |
package test
import input["a"]
import data["b"]
imports:
- span: import input["a"]
refr:
refbrack:
refr:
var: input
index:
string: a
- span: import data["b"]
refr:
refbrack:
refr:
var: data
index:
string: b
- note: multi-dot
rego: |
package test
import input.a.b
import data.c.d
imports:
- span: import input.a.b
refr:
refdot:
span: input.a.b
refr:
refdot:
span: input.a
refr:
var: input
field: a
field: b
- span: import data.c.d
refr:
refdot:
span: data.c.d
refr:
refdot:
span: data.c
refr:
var: data
field: c
field: d
policy: []
- note: complex
rego: |
package test
import input["b.c"].d["e.f"].g
import data.a["b.c"].d["e.f"]
package: --skip--
imports:
- span: import input["b.c"].d["e.f"].g
refr:
refdot:
span: input["b.c"].d["e.f"].g
refr:
refbrack:
span: input["b.c"].d["e.f"]
refr:
refdot:
span: input["b.c"].d
refr:
refbrack:
span: input["b.c"]
refr:
var: input
index:
string: b.c
field: d
index:
string: e.f
field: g
- span: import data.a["b.c"].d["e.f"]
refr:
refbrack:
span: data.a["b.c"].d["e.f"]
refr:
refdot:
span: data.a["b.c"].d
refr:
refbrack:
span: data.a["b.c"]
refr:
refdot:
span: data.a
refr:
var: data
field: a
index:
string: b.c
field: d
index:
string: e.f
- note: same-line
rego: package test import input.a["b"] import data["c"].d
package:
span: package test
refr:
var: test
imports:
- span: import input.a["b"]
refr:
refbrack:
span: input.a["b"]
refr:
refdot:
span: input.a
refr:
var: input
field: a
index:
string: b
- span: import data["c"].d
refr:
refdot:
span: data["c"].d
refr:
refbrack:
span: data["c"]
refr:
var: data
index:
string: c
field: d
- note: as
rego: |
package test
import input.x as y
imports:
- span: import input.x as y
refr:
refdot:
span: input.x
refr:
var: input
field: x
as: y
- note: as/newline
rego: |
package test
import
input.x
as
y
imports:
- span: "import\ninput.x\nas\ny"
refr:
refdot:
span: input.x
refr:
var: input
field: x
as: y
- note: missing-ref
rego: |
package test
import ( a)
error: expecting identifier
- note: missing-ref-1
rego: |
package test
import ["a"]
error: expecting identifier
- note: invalid-beginning
rego: |
package test
import foo
error: "import path must begin with one of: {data, future, input}"
- note: invalid-beginning-1
rego: |
package test
import foo.bar
error: "import path must begin with one of: {data, future, input}"
- note: missing-field-1
rego: |
package test
import data.a.
error: expecting identifier
- note: missing-field-2
rego: |
package test
import data.a.b.
error: expecting identifier
- note: space-after-dot
rego: |
package test
import input. a
error: invalid whitespace between . and identifier
- note: space-before-dot
rego: |
package test
import input .a.b
error: invalid whitespace before .
- note: space-after-lbracket
rego: package test import a ["b"]
error: invalid whitespace before [
- note: non-string-index
rego: package test import a[1]
error: expected string
- note: shadow
rego:
package test
import data.x import data.y import data["x"]
error: import shadows following import defined earlier
- note: keyword/else
rego:
package test
import data.x as else
error: "unexpected keyword `else`"
- note: keyword/as
rego:
package test
import data.x as as
error: "unexpected keyword `as`"
- note: as/_
rego:
package test
import data.x as _
error: "`_` cannot be used as alias"
- note: as/_
rego:
package test
import data.x as 1
error: expecting identifier
- note: as/eof
rego:
package test
import data.x as
error: expecting identifier
- note: as/multiple
rego:
package test
import data.x as y
import data.y as y
error: import shadows following import defined earlier
+129
View File
@@ -0,0 +1,129 @@
# Copyright (c) Rego-Rs Authors.
# Licensed under the Apache 2.0 license.
cases:
- note: single-char
rego: package a
package:
span: package a
refr:
var: a
- note: simple
rego: package test
package:
span: package test
refr:
var: test
- note: dot
rego: package a.b
package:
span: package a.b
refr:
refdot:
span: a.b
refr:
var: a
field: b
- note: multi-dot
rego: package a.b.c
package:
span: package a.b.c
refr:
refdot:
span: a.b.c
refr:
refdot:
span: a.b
refr:
var: a
field: b
field: c
- note: bracket
rego: package a["b"]
package:
span: package a["b"]
refr:
refbrack:
span: a["b"]
refr:
var: a
index:
string: b
- note: multi-bracket
rego: package a["b"]["c.d"]
package:
span: package a["b"]["c.d"]
refr:
refbrack:
span: a["b"]["c.d"]
refr:
refbrack:
span: a["b"]
refr:
var: a
index:
string: b
index:
string: c.d
- note: complex
rego: package a["b.c"].d["e.f"].g
package:
span: package a["b.c"].d["e.f"].g
refr:
refdot:
span: a["b.c"].d["e.f"].g
refr:
refbrack:
span: a["b.c"].d["e.f"]
refr:
refdot:
span: a["b.c"].d
refr:
refbrack:
span: a["b.c"]
refr:
var: a
index:
string: "b.c"
field: d
index:
string: e.f
field: g
- note: missing-package-keyword
rego: packge a
error: expecting `package`
- note: missing-var
rego: package 5
error: expecting identifier
- note: missing-var-1
rego: package (
error: expecting identifier
- note: missing-field
rego: package a.b.
error: expecting identifier
- note: space-after-dot
rego: package a. b
error: invalid whitespace between . and identifier
- note: space-before-dot
rego: package a .b
error: invalid whitespace before .
- note: space-after-lbracket
rego: package a ["b"]
error: invalid whitespace before [
- note: non-string-index
rego: package a[1]
error: expected string
+44
View File
@@ -0,0 +1,44 @@
# Copyright (c) Rego-Rs Authors.
# Licensed under the Apache 2.0 license.
cases:
- note: same-line-no-body
rego: |
package test
add(x, y) := 5 sub(x, y) = 5
package: --skip--
imports:
policy:
- spec:
span: add(x, y) := 5
head:
func:
span: add(x, y) := 5
refr:
var: add
args:
- var: x
- var: y
assign:
span: := 5
op: :=
value:
number: 5
bodies: []
- spec:
span: sub(x, y) = 5
head:
func:
span: sub(x, y) = 5
refr:
var: sub
args:
- var: x
- var: y
assign:
span: = 5
op: =
value:
number: 5
bodies: []
+250
View File
@@ -0,0 +1,250 @@
# Copyright (c) Rego-Rs Authors.
# Licensed under the Apache 2.0 license.
cases:
- note: no-query
rego: |
package test
x = 10 else {
true
}
error: unexpected keyword `else`
- note: no-query
rego: |
package test
x = 10 {
false
} {
true
}
policy:
- spec:
head:
compr:
refr:
var: x
assign:
op: "="
value:
number: 10
bodies:
- query:
stmts:
- literal:
expr:
false
- query:
stmts:
- literal:
expr:
true
- note: no-else
rego: |
package test
x = 10 {
false
} {
true
}
policy:
- spec:
head:
compr:
refr:
var: x
assign:
op: "="
value:
number: 10
bodies:
- query:
stmts:
- literal:
expr:
false
- query:
stmts:
- literal:
expr:
true
- note: if-no-else
rego: |
package test
import future.keywords.if
x = 10 if {
false
} {
true
}
policy:
- spec:
head:
compr:
refr:
var: x
assign:
op: "="
value:
number: 10
bodies:
- query:
stmts:
- literal:
expr:
false
- query:
stmts:
- literal:
expr:
true
- note: rule-named-if
rego: |
package test
x = 10 if {
false
} {
true
}
policy:
- spec:
head:
compr:
refr:
var: x
assign:
op: "="
value:
number: 10
bodies: []
- spec:
head:
compr:
refr:
var: if
bodies:
- query:
stmts:
- literal:
expr:
false
- query:
stmts:
- literal:
expr:
true
- note: query-else
rego: |
package test
x = 10 {
false
} else {
true
}
policy:
- spec:
head:
compr:
refr:
var: x
assign:
op: "="
value:
number: 10
bodies:
- query:
stmts:
- literal:
expr:
false
- query:
stmts:
- literal:
expr:
true
- note: if-literal-else
rego: |
package test
import future.keywords.if
x = 10 if 1 < 0 else {
true
}
policy:
- spec:
head:
compr:
refr:
var: x
assign:
op: "="
value:
number: 10
bodies:
- query:
stmts:
- literal:
expr:
boolexpr:
op: "<"
lhs:
number: 1
rhs:
number: 0
- query:
stmts:
- literal:
expr:
true
- note: if-literal-else-assign
rego: |
package test
import future.keywords.if
# This will evaluate to 10
x = 10 if 1 < 0 else := 20 {
true
}
policy:
- spec:
head:
compr:
refr:
var: x
assign:
op: "="
value:
number: 10
bodies:
- query:
stmts:
- literal:
expr:
boolexpr:
op: "<"
lhs:
number: 1
rhs:
number: 0
- assign:
op: ":="
value:
number: 20
query:
stmts:
- literal:
expr:
true
+135
View File
@@ -0,0 +1,135 @@
# Copyright (c) Rego-Rs Authors.
# Licensed under the Apache 2.0 license.
cases:
- note: contains
rego: |
package test
import future.keywords
deny.a contains 0, "bar" in ["bar1"] if true
policy:
- spec:
head:
set:
refr:
refdot:
refr:
var: deny
field: a
key:
inexpr:
key:
number: 0
value:
string: bar
collection:
array:
- string: bar1
bodies:
- query:
stmts:
- literal:
expr:
true
- note: old-syntax
rego: |
package test
import future.keywords.if
# The following are not sets
x1 if true
x2 { true }
# The following are sets
y.a { true }
y["b"] { true }
# The following are not sets
z.a if { true }
z["b"] if { true }
policy:
- spec:
head:
compr:
refr:
var: x1
bodies:
- query:
stmts:
- literal:
expr:
true
- spec:
head:
compr:
refr:
var: x2
bodies:
- query:
stmts:
- literal:
expr:
true
- spec:
head:
set:
refr:
refdot:
refr:
var: y
field: a
bodies:
- query:
stmts:
- literal:
expr:
true
- spec:
head:
set:
refr:
var: y
key:
string: b
bodies:
- query:
stmts:
- literal:
expr:
true
- spec:
head:
compr:
refr:
refdot:
refr:
var: z
field: a
bodies:
- query:
stmts:
- literal:
expr:
true
- spec:
head:
compr:
refr:
refbrack:
refr:
var: z
index:
string: b
bodies:
- query:
stmts:
- literal:
expr:
true
+226
View File
@@ -0,0 +1,226 @@
# Copyright (c) Rego-Rs Authors.
# Licensed under the Apache 2.0 license.
cases:
- note: all
rego: |
package test
import future.keywords.in
x = y {
# Empty set. This evaluates to false.
some a in {1}
# Key, value combo
some a
, b in r
# Key, value combo can be non-vars
some 5, x in array
some "hello", "world" in map
some p, q in { r |
some a, b in d
}
}
policy:
- spec:
head: --skip--
bodies:
- query:
stmts:
- span: some a in {1}
literal:
some-decl:
key:
var: a
collection:
set:
- number: 1
- span: "some a\n , b in r"
literal:
some-decl:
key:
var: a
value:
var: b
collection:
var: r
- span: "some 5, x in array"
literal:
some-decl:
key:
number: 5
value:
var: x
collection:
var: array
- span: "some \"hello\", \"world\" in map"
literal:
some-decl:
key:
string: hello
value:
string: world
collection:
var: map
- literal:
some-decl:
key:
var: p
value:
var: q
collection:
setcompr:
term:
var: r
query:
stmts:
- literal:
some-decl:
key:
var: a
value:
var: b
collection:
var: d
- note: unimported-in
rego: |
package test
x = y {
some a in b
}
error: expecting `}` while parsing query
- note: more-refs
rego: |
package test
import future.keywords.in
x = y {
some a, b, c in d
}
error: encountered `c` while expecting `in`
- note: eof
rego: |
package test
import future.keywords.in
x = y {
some a, b in
error: expecting expression
- note: missing-expr
rego: |
package test
import future.keywords.in
x = y {
some a, b in
}
error: expecting expression
- note: missing-comma
rego: |
package test
import future.keywords.in
x = y {
some a b in c
}
error: expecting `}` while parsing query
- note: same-line
rego: |
package test
x = y{
some a b in {4, 5}
[1, 2, 3][a] == 3
y = a
}
error: expecting `}` while parsing query
- note: multi-line-parsed as membership
rego: |
package test
import future.keywords.in
b := 5
x = y{
some a
b in {4, 5}
# The following [ starting a line ought to get
# parsed as a literal statement and not raise errors
# regarding gap from previous refr.
[1, 2, 3][a] == 3
y = a
}
policy:
- spec:
head:
compr:
span: b := 5
refr:
var: b
assign:
op: :=
value:
number: 5
bodies: []
- spec:
head:
compr:
span: x = y
refr:
var: x
assign:
op: =
value:
var: y
bodies:
- query:
stmts:
- literal:
some-vars:
span: some a
vars:
- a
- literal:
expr:
inexpr:
span: b in {4, 5}
key:
var: b
collection:
set:
- number: 4
- number: 5
- literal:
expr:
boolexpr:
span: "[1, 2, 3][a] == 3"
op: ==
lhs:
refbrack:
span: "[1, 2, 3][a]"
refr:
array:
- number: 1
- number: 2
- number: 3
index:
var: a
rhs:
number: 3
- literal:
expr:
assignexpr:
span: y = a
op: =
lhs:
var: y
rhs:
var: a
+74
View File
@@ -0,0 +1,74 @@
# Copyright (c) Rego-Rs Authors.
# Licensed under the Apache 2.0 license.
cases:
- note: single-multi-vars
rego: |
package test
x = y {
# Single var
some a
# Multiple var. With and without spaces.
some
d,e,
f
}
policy:
- spec:
head: --skip--
bodies:
- query:
stmts:
- span: some a
literal:
some-vars:
span: some a
vars:
- a
- span: "some\n d,e,\n f"
literal:
some-vars:
span: "some\n d,e,\n f"
vars: [d, e, f]
- note: same-line-error
rego: |
package test
x = y {
some a some b
}
error: expecting `}` while parsing query
- note: keyword
rego: |
package test
x = y {
some as
}
error: unexpected keyword `as`
- note: top-level
rego: |
package test
x = some y
error: unexpected keyword `some`
- note: top-level1
rego: |
package test
some y
error: unexpected keyword `some`
- note: some-in-set
rego: |
package test
x = { some y }
error: unexpected keyword `some`
- note: non-var
rego: |
package test
x = y {
some a, 5
}
error: encountered `5` while expecting identifier
+795
View File
@@ -0,0 +1,795 @@
// Copyright (c) Rego-Rs Authors.
// Licensed under the Apache 2.0 license.
#![cfg(test)]
use anyhow::{anyhow, bail, Result};
use rego_rs::*;
use serde::{Deserialize, Serialize};
use std::env;
use test_generator::test_resources;
//use walkdir::WalkDir;
macro_rules! my_assert_eq {
($left:expr, $right:expr, $($arg:tt)+) => {
match (&($left), &($right)) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
return Err(anyhow!("mismatch:\nleft = {}\nright = {}\n{}",
&$left, &$right, format_args!($($arg)+)));
}
}
}
}
}
#[test]
#[ignore = "intended for use by scripts/rego-parse"]
fn one_file() -> Result<()> {
let mut file = String::default();
for a in env::args() {
if a.ends_with(".rego") {
file = a;
break;
}
}
if file.is_empty() {
bail!("missing <policy.rego>");
}
let contents = std::fs::read_to_string(&file)?;
let source = Source {
file: file.as_str(),
contents: contents.as_str(),
lines: contents.split('\n').collect(),
};
let mut parser = Parser::new(&source)?;
let ast = parser.parse()?;
println!("{:#?}", ast);
Ok(())
}
fn skip_value(v: &Value) -> bool {
matches!(v, Value::String(s) if s == "--skip--")
}
fn match_span(s: &Span, v: &Value) -> Result<()> {
match &v {
Value::String(vs) => {
my_assert_eq!(
s.text(),
vs,
"{}",
s.source
.message(s.line, s.col, "match-error", "mismatch happened here.")
);
}
_ => {
my_assert_eq!(
s.text(),
serde_json::to_string_pretty(v)?,
"{}",
s.source
.message(s.line, s.col, "match-error", "mismatch happened here.")
)
}
}
Ok(())
}
fn match_span_opt(s: &Span, v: &Value) -> Result<()> {
if *v != Value::Undefined {
match_span(s, v)
} else {
Ok(())
}
}
fn match_vec(s: &Span, vec: &Vec<Expr>, v: &Value) -> Result<()> {
if v.as_object().is_ok() {
match_span_opt(s, &v["span"])?;
return match_vec(s, vec, &v["values"]);
}
let v = v.as_array()?;
my_assert_eq!(
vec.len(),
v.len(),
"{}",
s.source.message(
s.line,
s.col,
"match-error",
"mismatch in number of elements in sequence following this location"
)
);
for i in 0..vec.len() {
match_expr(&vec[i], &v[i])?;
}
Ok(())
}
fn match_object(s: &Span, fields: &Vec<(Span, Expr, Expr)>, v: &Value) -> Result<()> {
if skip_value(v) {
return Ok(());
}
match_span_opt(s, &v["span"])?;
match &v["fields"].as_array() {
Ok(a) => {
my_assert_eq!(fields.len(), a.len(), "field length mismatch");
for (idx, (_, k, v)) in fields.iter().enumerate() {
match_expr(k, &a[idx]["key"])?;
match_expr(v, &a[idx]["value"])?;
}
Ok(())
}
_ => bail!("incorrect field specification in yaml. Must be array."),
}
}
fn match_expr_impl(e: &Expr, v: &Value) -> Result<()> {
if skip_value(v) {
return Ok(());
}
match e {
Expr::String(s) => match_span(s, &v["string"]),
Expr::RawString(s) => match_span(s, &v["rawstring"]),
Expr::Number(s) => match_span(s, &v["number"]),
Expr::True(s) => match_span(s, v),
Expr::False(s) => match_span(s, v),
Expr::Null(s) => match_span(s, v),
Expr::Var(s) => match_span(s, &v["var"]),
Expr::Array { span, items } => match_vec(span, items, &v["array"]),
Expr::Set { span, items } => match_vec(span, items, &v["set"]),
Expr::Object { span, fields } => match_object(span, fields, &v["object"]),
Expr::ArrayCompr { span, term, query } => {
match_span_opt(span, &v["arraycompr"]["span"])?;
match_expr(term, &v["arraycompr"]["term"])?;
match_query(query, &v["arraycompr"]["query"])
}
Expr::SetCompr { span, term, query } => {
match_span_opt(span, &v["setcompr"]["span"])?;
match_expr(term, &v["setcompr"]["term"])?;
match_query(query, &v["setcompr"]["query"])
}
Expr::ObjectCompr {
span,
key,
value,
query,
} => {
match_span_opt(span, &v["objectcompr"]["span"])?;
match_expr(key, &v["objectcompr"]["key"])?;
match_expr(value, &v["objectcompr"]["value"])?;
match_query(query, &v["objectcompr"]["query"])
}
Expr::Call { span, fcn, params } => {
match_span_opt(span, &v["call"]["span"])?;
match_expr(fcn, &v["call"]["fcn"])?;
match_vec(span /*dummy*/, params, &v["call"]["params"])
}
Expr::RefDot { span, refr, field } => {
match_span_opt(span, &v["refdot"]["span"])?;
match_expr(refr, &v["refdot"]["refr"])?;
match_span(field, &v["refdot"]["field"])
}
Expr::RefBrack { span, refr, index } => {
match_span_opt(span, &v["refbrack"]["span"])?;
match_expr(refr, &v["refbrack"]["refr"])?;
match_expr(index, &v["refbrack"]["index"])
}
Expr::UnaryExpr { span, expr } => {
match_span_opt(span, &v["span"])?;
my_assert_eq!(
&Value::String("-".to_owned()),
&v["op"],
"{}",
span.source.message(
span.line,
span.col,
"mismatch-error",
"could not match `-` operator",
),
);
match_expr(expr, &v["expr"])
}
Expr::BinExpr { span, op, lhs, rhs } => {
match_span_opt(span, &v["binexpr"]["span"])?;
match_bin_op(span, op, &v["binexpr"]["op"])?;
match_expr(lhs, &v["binexpr"]["lhs"])?;
match_expr(rhs, &v["binexpr"]["rhs"])
}
Expr::ArithExpr { span, op, lhs, rhs } => {
match_span_opt(span, &v["arithexpr"]["span"])?;
match_arith_op(span, op, &v["arithexpr"]["op"])?;
match_expr(lhs, &v["arithexpr"]["lhs"])?;
match_expr(rhs, &v["arithexpr"]["rhs"])
}
Expr::BoolExpr { span, op, lhs, rhs } => {
match_span_opt(span, &v["boolexpr"]["span"])?;
match_bool_op(span, op, &v["boolexpr"]["op"])?;
match_expr(lhs, &v["boolexpr"]["lhs"])?;
match_expr(rhs, &v["boolexpr"]["rhs"])
}
Expr::AssignExpr { span, op, lhs, rhs } => {
match_span_opt(span, &v["assignexpr"]["span"])?;
match_assign_op(span, op, &v["assignexpr"]["op"])?;
match_expr(lhs, &v["assignexpr"]["lhs"])?;
match_expr(rhs, &v["assignexpr"]["rhs"])
}
Expr::Membership {
span,
key,
value,
collection,
} => {
match_span_opt(span, &v["inexpr"]["span"])?;
match_expr(key, &v["inexpr"]["key"])?;
match_expr_opt(span, value, &v["inexpr"]["value"])?;
match_expr(collection, &v["inexpr"]["collection"])
}
}
}
fn match_expr(expr: &Expr, v: &Value) -> Result<()> {
match match_expr_impl(expr, v) {
Ok(()) => Ok(()),
Err(e) => bail!(
"{e}\nexpr = {expr:#?}\nv={}\n-----------------------\n",
serde_json::to_string_pretty(v)?
),
}
}
fn match_with_mod(m: &WithModifier, v: &Value) -> Result<()> {
if skip_value(v) {
return Ok(());
}
match_span_opt(&m.span, &v["span"])?;
match_expr(&m.refr, &v["refr"])?;
match_expr(&m.r#as, &v["as"])
}
fn match_literal_stmt(ls: &LiteralStmt, v: &Value) -> Result<()> {
if skip_value(v) {
return Ok(());
}
match_span_opt(&ls.span, &v["span"])?;
match_literal(&ls.literal, &v["literal"])?;
let with_mods = &v["with-mods"];
if skip_value(with_mods) {
return Ok(());
}
match with_mods.as_array() {
Ok(a) => {
my_assert_eq!(
ls.with_mods.len(),
a.len(),
"{}",
ls.span.source.message(
ls.span.line,
ls.span.col,
"mismatch-error",
"with-modifier count mismatch"
)
);
for (idx, with_mod) in a.iter().enumerate() {
match_with_mod(&ls.with_mods[idx], with_mod)?;
}
}
_ if ls.with_mods.is_empty() => (),
_ => {
bail!(
"{}",
ls.span.source.message(
ls.span.line,
ls.span.col,
"mismatch-error",
"failed to match with-modifiers"
)
)
}
}
Ok(())
}
fn match_query(q: &Query, v: &Value) -> Result<()> {
match_span_opt(&q.span, &v["span"])?;
let stmts = &v["stmts"].as_array();
let stmts = match &stmts {
Ok(s) => s,
_ => {
bail!(
"{}",
q.span.source.message(
q.span.line,
q.span.col,
"mismatch-error",
"empty statements list in query specified"
)
)
}
};
my_assert_eq!(
q.stmts.len(),
stmts.len(),
"{}",
q.span.source.message(
q.span.line,
q.span.col,
"mismatch-error",
"mismatch in statement count"
)
);
for (idx, stmt) in stmts.iter().enumerate() {
match_literal_stmt(&q.stmts[idx], stmt)?;
}
Ok(())
}
fn match_expr_opt(s: &Span, e: &Option<Expr>, v: &Value) -> Result<()> {
match (e, v) {
(Some(e), v) => match_expr(e, v),
(None, Value::Undefined) => Ok(()),
_ => {
bail!(
"{}",
s.source.message(
s.line,
s.col,
"mismatch-error",
format!(
"failed to match {:#?} and {}",
e,
serde_json::to_string_pretty(&v)?
)
.as_str()
)
)
}
}
}
fn match_bin_op(s: &Span, op: &BinOp, v: &Value) -> Result<()> {
match (op, v) {
(BinOp::And, Value::String(s)) if s == "&" => Ok(()),
(BinOp::Or, Value::String(s)) if s == "|" => Ok(()),
_ => bail!(
"{}",
s.source.message(
s.line,
s.col,
"mismatch-error",
format!("left = {:?}\nright = {:?}\n", op, v).as_str()
)
),
}
}
fn match_arith_op(s: &Span, op: &ArithOp, v: &Value) -> Result<()> {
match (op, v) {
(ArithOp::Add, Value::String(s)) if s == "+" => Ok(()),
(ArithOp::Sub, Value::String(s)) if s == "-" => Ok(()),
(ArithOp::Mul, Value::String(s)) if s == "*" => Ok(()),
(ArithOp::Div, Value::String(s)) if s == "/" => Ok(()),
_ => bail!(
"{}",
s.source.message(
s.line,
s.col,
"mismatch-error",
format!("left = {:?}\nright = {:?}\n", op, v).as_str()
)
),
}
}
fn match_bool_op(s: &Span, op: &BoolOp, v: &Value) -> Result<()> {
match (op, v) {
(BoolOp::Lt, Value::String(s)) if s == "<" => Ok(()),
(BoolOp::Le, Value::String(s)) if s == "<=" => Ok(()),
(BoolOp::Eq, Value::String(s)) if s == "==" => Ok(()),
(BoolOp::Ge, Value::String(s)) if s == ">=" => Ok(()),
(BoolOp::Gt, Value::String(s)) if s == ">" => Ok(()),
_ => bail!(
"{}",
s.source.message(
s.line,
s.col,
"mismatch-error",
format!("left = {:?}\nright = {:?}\n", op, v).as_str()
)
),
}
}
fn match_assign_op(s: &Span, op: &AssignOp, v: &Value) -> Result<()> {
match (op, v) {
(AssignOp::Eq, Value::String(s)) if s == "=" => Ok(()),
(AssignOp::ColEq, Value::String(s)) if s == ":=" => Ok(()),
_ => bail!(
"{}",
s.source.message(
s.line,
s.col,
"mismatch-error",
format!("left = {:?}\nright = {:?}\n", op, v).as_str()
)
),
}
}
fn match_rule_assign(a: &RuleAssign, v: &Value) -> Result<()> {
match_span_opt(&a.span, &v["span"])?;
match_assign_op(&a.span, &a.op, &v["op"])?;
match_expr(&a.value, &v["value"])
}
fn match_rule_assign_opt(a: &Option<RuleAssign>, v: &Value) -> Result<()> {
match a {
Some(a) => match_rule_assign(a, v),
None => {
my_assert_eq!(*v, Value::Undefined, "mismatch in null assign");
Ok(())
}
}
}
fn match_rule_head(h: &RuleHead, v: &Value) -> Result<()> {
if skip_value(v) {
return Ok(());
}
match h {
RuleHead::Compr { span, refr, assign } => {
match_span_opt(span, &v["compr"]["span"])?;
match_expr(refr, &v["compr"]["refr"])?;
match_rule_assign_opt(assign, &v["compr"]["assign"])
}
RuleHead::Set { span, refr, key } => {
match_span_opt(span, &v["set"]["span"])?;
match_expr(refr, &v["set"]["refr"])?;
match_expr_opt(span, key, &v["set"]["key"])
}
RuleHead::Func {
span,
refr,
args,
assign,
} => {
match_span_opt(span, &v["func"]["span"])?;
match_expr(refr, &v["func"]["refr"])?;
match_vec(span /*dummy*/, args, &v["func"]["args"])?;
match_rule_assign_opt(assign, &v["func"]["assign"])
}
}
}
fn match_literal(l: &Literal, v: &Value) -> Result<()> {
match l {
Literal::SomeVars { span, vars } => {
let v = &v["some-vars"];
match_span_opt(span, &v["span"])?;
let values = &v["vars"].as_array()?;
my_assert_eq!(
vars.len(),
values.len(),
"some-vars mismatch {:#?} {}",
vars,
serde_json::to_string_pretty(&values)?
);
for idx in 0..vars.len() {
match_span(&vars[idx], &values[idx])?
}
Ok(())
}
Literal::SomeIn {
span,
key,
value,
collection,
} => {
let v = &v["some-decl"];
match_span_opt(span, &v["span"])?;
match_expr(key, &v["key"])?;
match_expr_opt(span, value, &v["value"])?;
match_expr(collection, &v["collection"])
}
Literal::Expr { expr, .. } => match_expr(expr, &v["expr"]),
Literal::NotExpr { expr, span } => {
let v = &v["notexpr"];
match &v["op"] {
Value::String(s) if s == "not" => (),
_ => {
bail!(
"{}",
span.source.message(
span.line,
span.col,
"mismatch-error",
"`op: -` not found in value`"
)
)
}
}
match_expr(expr, v)
}
Literal::Every {
span,
key,
value,
domain,
query,
} => {
match_span_opt(span, &v["every"]["span"])?;
match_span(key, &v["every"]["key"])?;
match value {
Some(s) => match_span(s, &v["every"]["value"])?,
None => {
my_assert_eq!(
&Value::Undefined,
&v["value"],
"{}",
span.source.message(
span.line,
span.col,
"mismatch-error",
"could not match `value``"
)
);
}
}
match_expr(domain, &v["every"]["domain"])?;
match_query(query, &v["every"]["query"])
}
}
}
fn match_rule_body(b: &RuleBody, v: &Value) -> Result<()> {
if skip_value(v) {
return Ok(());
}
match_span_opt(&b.span, &v["span"])?;
match_rule_assign_opt(&b.assign, &v["assign"])?;
match_query(&b.query, &v["query"])
}
fn match_rule_bodies(span: &Span, bodies: &Vec<RuleBody>, v: &Value) -> Result<()> {
if skip_value(v) {
return Ok(());
}
let v = &v.as_array();
let v = match &v {
Ok(v) => v,
_ => {
bail!(
"incorrect yaml. bodies is not an array. Corresponding rego: {}",
span.source.message(span.line, span.col, "invalid-yaml", "")
);
}
};
my_assert_eq!(
bodies.len(),
v.len(),
"{}",
span.source.message(
span.line,
span.col,
"mismatch-error",
"mismatch in body count",
),
);
for idx in 0..bodies.len() {
match_rule_body(&bodies[idx], &v[idx])?;
}
Ok(())
}
fn match_rule(r: &Rule, v: &Value) -> Result<()> {
match r {
Rule::Spec { span, head, bodies } => {
let obj = &v["spec"];
match_span_opt(span, &obj["span"])?;
match_rule_head(head, &obj["head"])?;
match_rule_bodies(span, bodies, &obj["bodies"])
}
Rule::Default {
span,
refr,
op,
value,
} => {
let obj = &v["default"];
match_span_opt(span, &obj["span"])?;
match_expr(refr, &obj["refr"])?;
match_assign_op(span, op, &obj["op"])?;
match_expr(value, &obj["value"])
}
}
}
fn match_package(p: &Package, v: &Value) -> Result<()> {
if skip_value(v) {
return Ok(());
}
match_span_opt(&p.span, &v["span"])?;
match_expr(&p.refr, &v["refr"])
}
fn match_import(i: &Import, v: &Value) -> Result<()> {
if skip_value(v) {
return Ok(());
}
match_span_opt(&i.span, &v["span"])?;
match_expr(&i.refr, &v["refr"])?;
match (&i.r#as, &v["as"]) {
(Some(a), v) => match_span(a, v),
(None, Value::Undefined) => Ok(()),
_ => Err(i
.span
.source
.error(i.span.line, i.span.col, "import does not have `as` binding")),
}
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct TestCase {
rego: String,
note: String,
package: Option<Value>,
imports: Option<Vec<Value>>,
policy: Option<Vec<Value>>,
error: Option<String>,
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct YamlTest {
cases: Vec<TestCase>,
}
fn yaml_test_impl(file: &str) -> Result<()> {
println!("\nrunning {}", file);
let yaml_str = std::fs::read_to_string(file)?;
let test: YamlTest = serde_yaml::from_str(&yaml_str)?;
for case in &test.cases {
print!("\ncase {} ", case.note);
let source = Source {
file: "case.rego",
contents: case.rego.as_str(),
lines: case.rego.split('\n').collect(),
};
let mut parser = Parser::new(&source)?;
match parser.parse() {
Ok(module) => {
if let Some(e) = &case.error {
bail!("error `{}` not raised by parser.", e);
}
if let Some(p) = &case.package {
match_package(&module.package, p)?;
}
if let Some(imports) = &case.imports {
my_assert_eq!(
module.imports.len(),
imports.len(),
"mismatch in number of imports"
);
for (idx, import) in imports.iter().enumerate().take(module.imports.len()) {
match_import(&module.imports[idx], import)?;
}
}
if let Some(policy) = &case.policy {
my_assert_eq!(
module.policy.len(),
policy.len(),
"mismatch in policy length"
);
for (idx, policy) in policy.iter().enumerate().take(module.policy.len()) {
if skip_value(policy) {
continue;
}
match_rule(&module.policy[idx], policy)?;
}
}
}
Err(actual) => match &case.error {
Some(expected) => {
let actual = actual.to_string();
if !actual.contains(expected) {
bail!(
"Error message\n`{}\n`\ndoes not contain `{}`",
actual,
expected
);
}
println!("{actual}");
}
_ => return Err(actual),
},
}
println!("passed");
}
println!("{} cases passed.", test.cases.len());
Ok(())
}
fn yaml_test(file: &str) -> Result<()> {
match yaml_test_impl(file) {
Ok(_) => Ok(()),
Err(e) => {
// If Err is returned, it doesn't always get printed by cargo test.
// Therefore, panic with the error.
panic!("{}", e);
}
}
}
#[test]
#[ignore = "intended for use by scripts/yaml-test-parse"]
fn one_yaml() -> Result<()> {
let mut file = String::default();
for a in env::args() {
if a.ends_with(".yaml") {
file = a;
break;
}
}
if file.is_empty() {
bail!("missing <policy.rego>");
}
yaml_test(file.as_str())
}
/*
fn run_yaml_tests_in(folder: &str) -> Result<()> {
let mut total = 0;
for entry in WalkDir::new(folder)
.follow_links(true)
.into_iter()
.filter_map(|e| e.ok())
{
let path = entry
.path()
.to_str()
.ok_or_else(|| anyhow!("failed to convert path to utf8 {:?}", entry.path()))?;
if !path.ends_with(".yaml") {
continue;
}
total += 1;
match yaml_test(path) {
Ok(_) => (),
Err(e) => {
bail!("test failed.");
}
}
}
println!("{} parser yaml tests passed.", total);
Ok(())
}
#[test]
fn parser_yaml_tests() -> Result<()> {
run_yaml_tests_in("tests/parser")
}
*/
#[test_resources("tests/parser/**/*.yaml")]
fn run(path: &str) {
yaml_test(path).unwrap()
}