mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
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:
@@ -0,0 +1,45 @@
|
||||
// Copyright (c) Rego-Rs Authors.
|
||||
// Licensed under the Apache 2.0 license.
|
||||
#![cfg(test)]
|
||||
|
||||
use crate::interpreter::*;
|
||||
use anyhow::Result;
|
||||
|
||||
#[test]
|
||||
fn basic() -> Result<()> {
|
||||
let rego = r#"
|
||||
package test
|
||||
|
||||
add {
|
||||
1 + 2 == 3
|
||||
}
|
||||
|
||||
sub {
|
||||
5 - 1 == 4
|
||||
}
|
||||
|
||||
mul {
|
||||
3 * 4 == 12
|
||||
}
|
||||
|
||||
# Lock down float operation.
|
||||
div {
|
||||
21 / 5 == 4.2
|
||||
}
|
||||
"#;
|
||||
|
||||
let expected = Value::from_json_str(
|
||||
r#" {
|
||||
"add" : true,
|
||||
"sub" : true,
|
||||
"mul" : true,
|
||||
"div" : true
|
||||
}"#,
|
||||
)?;
|
||||
|
||||
assert_eq!(
|
||||
eval_file(&[rego.to_owned()], None, None, "data.test")?,
|
||||
expected
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
# Copyright (c) Rego-Rs Authors.
|
||||
# Licensed under the Apache 2.0 license.
|
||||
cases:
|
||||
- data: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
add {
|
||||
1 + 2 == 3
|
||||
}
|
||||
|
||||
sub {
|
||||
5 - 1 == 4
|
||||
}
|
||||
|
||||
mul {
|
||||
3 * 4 == 12
|
||||
}
|
||||
|
||||
# Lock down float operation.
|
||||
div {
|
||||
21 / 5 == 4.2
|
||||
}
|
||||
note: arithmetic/basic
|
||||
query: data.test
|
||||
sort_bindings: true
|
||||
want_result:
|
||||
add: true
|
||||
sub: true
|
||||
mul: true
|
||||
div: true
|
||||
@@ -0,0 +1,234 @@
|
||||
# Copyright (c) Rego-Rs Authors.
|
||||
# Licensed under the Apache 2.0 license.
|
||||
|
||||
cases:
|
||||
- note: number-equals
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
v = 1
|
||||
r = [
|
||||
# Comparison with number
|
||||
v == 1,
|
||||
v == 2,
|
||||
|
||||
# Comparison with primitives
|
||||
v == null,
|
||||
v == "hello",
|
||||
v == true,
|
||||
v == false,
|
||||
|
||||
# Comparison with arrays
|
||||
v == [],
|
||||
v == [v],
|
||||
|
||||
# Comparison with sets
|
||||
v == set(),
|
||||
v == { v },
|
||||
|
||||
# Comparison with objects
|
||||
v == {},
|
||||
v == { "a": v},
|
||||
]
|
||||
sort_bindings: false
|
||||
query: data.test
|
||||
want_result:
|
||||
v: 1
|
||||
r: [ true, false, false, false, false, false,
|
||||
false, false, false, false, false, false]
|
||||
|
||||
- note: null-equals
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
v = null
|
||||
r = [
|
||||
# Comparison with number
|
||||
v == 1,
|
||||
v == 2,
|
||||
|
||||
# Comparison with primitives
|
||||
v == null,
|
||||
v == "hello",
|
||||
v == true,
|
||||
v == false,
|
||||
|
||||
# Comparison with arrays
|
||||
v == [],
|
||||
v == [v],
|
||||
|
||||
# Comparison with sets
|
||||
v == set(),
|
||||
v == { v },
|
||||
|
||||
# Comparison with objects
|
||||
v == {},
|
||||
v == { "a": v},
|
||||
]
|
||||
sort_bindings: false
|
||||
query: data.test
|
||||
want_result:
|
||||
v: null
|
||||
r: [ false, false, true, false, false, false,
|
||||
false, false, false, false, false, false]
|
||||
|
||||
- note: string-equals
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
v = "hello"
|
||||
r = [
|
||||
# Comparison with number
|
||||
v == 1,
|
||||
v == 2,
|
||||
|
||||
# Comparison with primitives
|
||||
v == null,
|
||||
v == "hello",
|
||||
v == true,
|
||||
v == false,
|
||||
|
||||
# Comparison with arrays
|
||||
v == [],
|
||||
v == [v],
|
||||
|
||||
# Comparison with sets
|
||||
v == set(),
|
||||
v == { v },
|
||||
|
||||
# Comparison with objects
|
||||
v == {},
|
||||
v == { "a": v},
|
||||
]
|
||||
sort_bindings: false
|
||||
query: data.test
|
||||
want_result:
|
||||
v: "hello"
|
||||
r: [ false, false, false, true, false, false,
|
||||
false, false, false, false, false, false]
|
||||
|
||||
- note: true-equals
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
v = true
|
||||
r = [
|
||||
# Comparison with number
|
||||
v == 1,
|
||||
v == 2,
|
||||
|
||||
# Comparison with primitives
|
||||
v == null,
|
||||
v == "hello",
|
||||
v == true,
|
||||
v == false,
|
||||
|
||||
# Comparison with arrays
|
||||
v == [],
|
||||
v == [v],
|
||||
|
||||
# Comparison with sets
|
||||
v == set(),
|
||||
v == { v },
|
||||
|
||||
# Comparison with objects
|
||||
v == {},
|
||||
v == { "a": v},
|
||||
]
|
||||
sort_bindings: false
|
||||
query: data.test
|
||||
want_result:
|
||||
v: true
|
||||
r: [ false, false, false, false, true, false,
|
||||
false, false, false, false, false, false]
|
||||
|
||||
|
||||
- note: false-equals
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
v = false
|
||||
r = [
|
||||
# Comparison with number
|
||||
v == 1, # true
|
||||
v == 1, # false
|
||||
|
||||
# Comparison with primitives
|
||||
v == null,
|
||||
v == "hello",
|
||||
v == true,
|
||||
v == false,
|
||||
|
||||
# Comparison with arrays
|
||||
v == [],
|
||||
v == [v],
|
||||
|
||||
# Comparison with sets
|
||||
v == set(),
|
||||
v == { v },
|
||||
|
||||
# Comparison with objects
|
||||
v == {},
|
||||
v == { "a": v},
|
||||
]
|
||||
sort_bindings: false
|
||||
query: data.test
|
||||
want_result:
|
||||
v: false
|
||||
r: [ false, false, false, false, false, true,
|
||||
false, false, false, false, false, false]
|
||||
|
||||
- note: undefined-equals
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
import future.keywords
|
||||
|
||||
v = 1 if false
|
||||
r = [
|
||||
# Comparison with number
|
||||
v == 1,
|
||||
v == 2,
|
||||
|
||||
# Comparison with primitives
|
||||
v == null,
|
||||
v == "hello",
|
||||
v == true,
|
||||
v == false,
|
||||
|
||||
# Comparison with arrays
|
||||
v == [],
|
||||
v == [v],
|
||||
|
||||
# Comparison with sets
|
||||
v == set(),
|
||||
v == { v },
|
||||
|
||||
# Comparison with objects
|
||||
v == {},
|
||||
v == { "a": v},
|
||||
]
|
||||
|
||||
r2 = 1 if v
|
||||
|
||||
# This variable should appear in output since it is not undefined.
|
||||
ok = true
|
||||
|
||||
sort_bindings: false
|
||||
query: data.test
|
||||
want_result:
|
||||
ok: true
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
cases:
|
||||
- note: basic
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
inc(x) = x + 1
|
||||
|
||||
a1 = inc(5)
|
||||
query: data.test
|
||||
want_result:
|
||||
a1: 6
|
||||
|
||||
- note: call-in-arg
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
inc(x) = x + 1
|
||||
|
||||
a1 = inc(inc(5))
|
||||
query: data.test
|
||||
want_result:
|
||||
a1: 7
|
||||
|
||||
- note: call-nested
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
sub(a, b) := a - b
|
||||
|
||||
foo(a, b) := r {
|
||||
r =(a + b) * sub(a, b)
|
||||
}
|
||||
|
||||
a = foo(5, 6)
|
||||
query: data.test
|
||||
want_result:
|
||||
a: -11
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
# Copyright (c) Rego-Rs Authors.
|
||||
# Licensed under the Apache 2.0 license.
|
||||
|
||||
# Captures tricky cases where similar looking expressions will be
|
||||
# treated as arrays or array-comprs.
|
||||
cases:
|
||||
- note: case1-compr
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
x = {1}
|
||||
y = {2}
|
||||
# If there is one term and the term has an | operator, it will be parsed
|
||||
# as a compr.
|
||||
z = [ x | y ] #, 5] # in {5}, 6]
|
||||
query: data.test
|
||||
sort_bindings: true
|
||||
want_result:
|
||||
x:
|
||||
set!: [1]
|
||||
y:
|
||||
set!: [2]
|
||||
z:
|
||||
- set!: [1]
|
||||
|
||||
- note: case2-array
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
x = {1}
|
||||
y = {2}
|
||||
# If there is a comma following the expression to the right of |
|
||||
# ie "y ," then enclosing expression will be parsed as array.
|
||||
z = [ x | y, 5] # in {5}, 6]
|
||||
query: data.test
|
||||
sort_bindings: true
|
||||
want_result:
|
||||
x:
|
||||
set!: [1]
|
||||
y:
|
||||
set!: [2]
|
||||
z:
|
||||
- set!: [2, 1]
|
||||
- 5
|
||||
|
||||
- note: case3-compr
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
import future.keywords.in
|
||||
x = {1}
|
||||
y = {2}
|
||||
# In the following case, the "y, 5 in {5}" which is to the right of |
|
||||
# will be parsed as a membership expression. Since this expression is not
|
||||
# followed by a ",", the enclosing expression will be parsed as a compr.
|
||||
z = [ x | y, 5 in {5}] #, 6]
|
||||
query: data.test
|
||||
sort_bindings: true
|
||||
want_result:
|
||||
x:
|
||||
set!: [1]
|
||||
y:
|
||||
set!: [2]
|
||||
z: []
|
||||
|
||||
- note: case4-array
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
import future.keywords.in
|
||||
x = {1}
|
||||
y = {2}
|
||||
# Unlike the previous case, the "y, 5 in {5}" is followed by a ",".
|
||||
# Therefore, the enclosing expression will be parsed as an array.
|
||||
z = [ x | y, 5 in {5}, 6]
|
||||
query: data.test
|
||||
sort_bindings: true
|
||||
want_result:
|
||||
x:
|
||||
set!: [1]
|
||||
y:
|
||||
set!: [2]
|
||||
z:
|
||||
- set!: [2, 1]
|
||||
- true
|
||||
- 6
|
||||
|
||||
- note: case5-array
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
import future.keywords.in
|
||||
x = {1}
|
||||
y = {2}
|
||||
# If the first expression "x - {10}"is something higher than an in-expr
|
||||
# (e.g arithexpr) then the enclosing expression will be parsed as an array.
|
||||
z = [ x - {10} | y, 5 in {5}] #, 6]
|
||||
query: data.test
|
||||
sort_bindings: true
|
||||
want_result:
|
||||
x:
|
||||
set!: [1]
|
||||
y:
|
||||
set!: [2]
|
||||
z:
|
||||
- set!: [2, 1]
|
||||
- true
|
||||
|
||||
- note: case6-compr
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
import future.keywords.in
|
||||
x = {1}
|
||||
y = {2}
|
||||
# The way to get the previous case parsed as a compr is to enclose the first
|
||||
# expression in parentheses.
|
||||
z = [ (x - {10}) | y, 5 in {5}] #, 6]
|
||||
query: data.test
|
||||
sort_bindings: true
|
||||
want_result:
|
||||
x:
|
||||
set!: [1]
|
||||
y:
|
||||
set!: [2]
|
||||
z: []
|
||||
@@ -0,0 +1,123 @@
|
||||
// Copyright (c) Rego-Rs Authors.
|
||||
// Licensed under the Apache 2.0 license.
|
||||
#![cfg(test)]
|
||||
|
||||
use crate::interpreter::*;
|
||||
|
||||
#[test]
|
||||
fn basic_array() -> Result<()> {
|
||||
let rego = r#"
|
||||
package test
|
||||
|
||||
array = [1, 2, 3]
|
||||
|
||||
array_compr_0 = [ x | x = 1 ]
|
||||
|
||||
array_compr_1 = [ array | true ]
|
||||
|
||||
array_compr_2 = [ x | x = array[_] ]
|
||||
|
||||
array_compr_3 = [ x | x = array[_]; x != 2 ]
|
||||
|
||||
# This produces 3 values
|
||||
array_compr_4 = [ 1 | [1, 2, 3][_] ]
|
||||
|
||||
# This produces 6 values.
|
||||
array_compr_5 = [ 1 | [1, 2, 3][_]; {1, 2}[_] ]
|
||||
|
||||
# This also produces 6 values.
|
||||
array_compr_6 = [ 1 | [1, 2, 3][_]; {"a":1, "b":2}[_] ]
|
||||
|
||||
# This produces 3 values.
|
||||
array_compr_7 = [ 1 | [1, 2, 3][_]; [1, 2][_] >= 2 ]
|
||||
"#;
|
||||
|
||||
let expected = Value::from_json_str(
|
||||
r#" {
|
||||
"array": [1, 2, 3],
|
||||
"array_compr_0": [1],
|
||||
"array_compr_1": [[1, 2, 3]],
|
||||
"array_compr_2": [1, 2, 3],
|
||||
"array_compr_3": [1, 3],
|
||||
"array_compr_4": [1, 1, 1],
|
||||
"array_compr_5": [1, 1, 1, 1, 1, 1],
|
||||
"array_compr_6": [1, 1, 1, 1, 1, 1],
|
||||
"array_compr_7": [1, 1, 1]
|
||||
}"#,
|
||||
)?;
|
||||
|
||||
assert_match(
|
||||
eval_file(&[rego.to_owned()], None, None, "data.test")?,
|
||||
expected,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic_set() -> Result<()> {
|
||||
let rego = r#"
|
||||
package test
|
||||
|
||||
set = { 1, "string", 1, [2, 3, 4], 567, false, 1 }
|
||||
|
||||
set_compr_0 = { x | x = 1 }
|
||||
|
||||
set_compr_1 = { set | true }
|
||||
|
||||
set_compr_2 = { x | x = set[_] }
|
||||
|
||||
set_compr_3 = { x | x = set[_]; x != [2, 3, 4] }
|
||||
|
||||
# This produces 1 value
|
||||
set_compr_4 = { 1 | [1, 2, 3][_] }
|
||||
|
||||
# This produces 4 values.
|
||||
set_compr_5 = { (a+b) | a=[1, 2, 3][_]; b={1, 2}[_] }
|
||||
|
||||
# This also produces 2 values.
|
||||
set_compr_6 = { a | [1, 2, 3][_]; a={"a":1, "b":2}[_] }
|
||||
|
||||
# This produces 3 values.
|
||||
set_compr_7 = { a | a = [1, 2, 3][_]; [1, 2][_] >= 2 }
|
||||
"#;
|
||||
|
||||
let expected = Value::from_json_str(
|
||||
r#" {
|
||||
"set": {
|
||||
"set!": [1, "string", [2, 3, 4], 567, false]
|
||||
},
|
||||
"set_compr_0": {
|
||||
"set!": [1]
|
||||
},
|
||||
"set_compr_1": {
|
||||
"set!" : [{
|
||||
"set!": [1, "string", [2, 3, 4], 567, false]
|
||||
}]
|
||||
},
|
||||
"set_compr_2": {
|
||||
"set!": [1, "string", [2, 3, 4], 567, false]
|
||||
},
|
||||
"set_compr_3": {
|
||||
"set!": [1, "string", 567, false]
|
||||
},
|
||||
"set_compr_4": {
|
||||
"set!": [1]
|
||||
},
|
||||
"set_compr_5": {
|
||||
"set!": [2, 3, 4, 5]
|
||||
},
|
||||
"set_compr_6": {
|
||||
"set!": [1, 2]
|
||||
},
|
||||
"set_compr_7": {
|
||||
"set!": [1, 2, 3]
|
||||
}
|
||||
}"#,
|
||||
)?;
|
||||
|
||||
assert_match(
|
||||
eval_file(&[rego.to_owned()], None, None, "data.test")?,
|
||||
expected,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
# Copyright (c) Rego-Rs Authors.
|
||||
# Licensed under the Apache 2.0 license.
|
||||
|
||||
cases:
|
||||
- note: simple
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
x = { k:1 | k = ["Hello", "world", 1][_] }
|
||||
query: data.test
|
||||
want_result:
|
||||
x:
|
||||
object!:
|
||||
- key: "Hello"
|
||||
value: 1
|
||||
- key: "world"
|
||||
value: 1
|
||||
- key: 1
|
||||
value: 1
|
||||
|
||||
|
||||
- note: key-loop
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
x = { ["Hello", "world", 1][_]:1 | true }
|
||||
query: data.test
|
||||
want_result:
|
||||
x:
|
||||
object!:
|
||||
- key: "Hello"
|
||||
value: 1
|
||||
- key: "world"
|
||||
value: 1
|
||||
- key: 1
|
||||
value: 1
|
||||
|
||||
- note: multiple-occurance-of-same-key-value-pair
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
x = { k:v | k = ["Hello", "world", 1][_]; v = [1, 1][_] }
|
||||
query: data.test
|
||||
want_result:
|
||||
x:
|
||||
object!:
|
||||
- key: "Hello"
|
||||
value: 1
|
||||
- key: "world"
|
||||
value: 1
|
||||
- key: 1
|
||||
value: 1
|
||||
|
||||
- note: different-values-for-same-key
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
x = { k:v | k = ["Hello", "world", 1][_]; v = [1, 2][_] }
|
||||
query: data.test
|
||||
error: "value for key `\"Hello\"` generated multiple times: `1` and `2`"
|
||||
want_result:
|
||||
@@ -0,0 +1,56 @@
|
||||
# Copyright (c) Rego-Rs Authors.
|
||||
# Licensed under the Apache 2.0 license.
|
||||
|
||||
cases:
|
||||
- note: basic
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
default x = 5
|
||||
|
||||
a = b
|
||||
|
||||
default b = 6
|
||||
|
||||
c = d
|
||||
|
||||
d {
|
||||
x == 3
|
||||
}
|
||||
|
||||
d {
|
||||
x == 4
|
||||
}
|
||||
|
||||
default d = "has_default"
|
||||
|
||||
default object["key"] = "string"
|
||||
|
||||
default complex[true] = "bool_true"
|
||||
|
||||
default complex[false] = "bool_false"
|
||||
|
||||
complex["hello"] = "world"
|
||||
|
||||
query: data.test
|
||||
want_result:
|
||||
x: 5
|
||||
a: 6
|
||||
b: 6
|
||||
c: "has_default"
|
||||
d: "has_default"
|
||||
object:
|
||||
object!:
|
||||
- key: "key"
|
||||
value: "string"
|
||||
complex:
|
||||
object!:
|
||||
- key: true
|
||||
value: "bool_true"
|
||||
- key: false
|
||||
value: "bool_false"
|
||||
- key: "hello"
|
||||
value: "world"
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
// Copyright (c) Rego-Rs Authors.
|
||||
// Licensed under the Apache 2.0 license.
|
||||
#![cfg(test)]
|
||||
|
||||
use crate::interpreter::*;
|
||||
|
||||
#[test]
|
||||
fn basic() -> Result<()> {
|
||||
let rego = r#"
|
||||
package test
|
||||
|
||||
import future.keywords.in
|
||||
|
||||
array = [1, 2, 3]
|
||||
|
||||
in_array_key_value {
|
||||
0, 1 in array
|
||||
}
|
||||
|
||||
in_array_key_value_negative {
|
||||
0, 2 in array
|
||||
}
|
||||
|
||||
some_decl_array_key_value {
|
||||
some 0, 1 in array
|
||||
}
|
||||
|
||||
some_decl_array_key_value_negative {
|
||||
some 0, 2 in array
|
||||
}
|
||||
|
||||
some_decl_array_value {
|
||||
some 1 in array
|
||||
}
|
||||
|
||||
some_decl_array_value_negative {
|
||||
some 4 in array
|
||||
}
|
||||
|
||||
in_array_value {
|
||||
1 in array
|
||||
}
|
||||
|
||||
in_array_value_negative {
|
||||
4 in array
|
||||
}
|
||||
|
||||
object = { "number": 1, "array": [2, 3], "string": "test", "bool": true }
|
||||
|
||||
in_object_key_value {
|
||||
"number", 1 in object
|
||||
}
|
||||
|
||||
in_object_key_value_negative {
|
||||
"non-exist", 1 in object
|
||||
}
|
||||
|
||||
some_decl_object_key_value {
|
||||
some "number", 1 in object
|
||||
}
|
||||
|
||||
some_decl_object_key_value_negative {
|
||||
some "non-exist", 1 in object
|
||||
}
|
||||
|
||||
in_object_value {
|
||||
[2, 3] in object
|
||||
}
|
||||
|
||||
in_object_value_negative {
|
||||
false in object
|
||||
}
|
||||
|
||||
some_decl_object_value {
|
||||
some [2, 3] in object
|
||||
}
|
||||
|
||||
some_decl_object_value_negative {
|
||||
some false in object
|
||||
}
|
||||
|
||||
set = { "string", [2, 3, 4], 567, false }
|
||||
|
||||
in_set_value {
|
||||
"string" in set
|
||||
}
|
||||
|
||||
some_decl_set_value {
|
||||
some "string" in set
|
||||
}
|
||||
|
||||
in_set_value_negative {
|
||||
"non-exist" in set
|
||||
}
|
||||
|
||||
some_decl_set_value_negative {
|
||||
some "non-exist" in set
|
||||
}
|
||||
"#;
|
||||
|
||||
let expected = Value::from_json_str(
|
||||
r#" {
|
||||
"array": [1, 2, 3],
|
||||
"in_array_key_value": true,
|
||||
"some_decl_array_key_value": true,
|
||||
"in_array_value": true,
|
||||
"some_decl_array_value": true,
|
||||
"object": { "number": 1, "array": [2, 3], "string": "test", "bool": true},
|
||||
"in_object_key_value": true,
|
||||
"some_decl_object_key_value": true,
|
||||
"in_object_value": true,
|
||||
"some_decl_object_value": true,
|
||||
"set": {
|
||||
"set!": ["string", [2, 3, 4], 567, false]
|
||||
},
|
||||
"in_set_value": true,
|
||||
"some_decl_set_value": true
|
||||
}"#,
|
||||
)?;
|
||||
|
||||
assert_match(
|
||||
eval_file(&[rego.to_owned()], None, None, "data.test")?,
|
||||
expected,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// Copyright (c) Rego-Rs Authors.
|
||||
// Licensed under the Apache 2.0 license.
|
||||
|
||||
mod arithmetic;
|
||||
mod compr;
|
||||
mod r#in;
|
||||
mod variables;
|
||||
@@ -0,0 +1,32 @@
|
||||
cases:
|
||||
- note: simple-no-cross-ref
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package a
|
||||
b = 1
|
||||
- |
|
||||
package b
|
||||
c = 1
|
||||
query: data
|
||||
want_result:
|
||||
a:
|
||||
b: 1
|
||||
b:
|
||||
c: 1
|
||||
|
||||
- note: simple-cross-ref-in-order
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package a
|
||||
b = 1
|
||||
- |
|
||||
package b
|
||||
c = data.a.b
|
||||
query: data
|
||||
want_result:
|
||||
a:
|
||||
b: 1
|
||||
b:
|
||||
c: 1
|
||||
@@ -0,0 +1,32 @@
|
||||
# Copyright (c) Rego-Rs Authors.
|
||||
# Licensed under the Apache 2.0 license.
|
||||
|
||||
cases:
|
||||
- note: simple
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
import future.keywords
|
||||
tbl contains name if {
|
||||
name = ["a", "b", "c"][_]
|
||||
}
|
||||
query: data.test
|
||||
want_result:
|
||||
tbl:
|
||||
set!: ["c", "b", "a"]
|
||||
|
||||
- note: arithmetic
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
import future.keywords
|
||||
tbl contains x + 10 if {
|
||||
x = [1, 2, 3][_]
|
||||
}
|
||||
query: data.test
|
||||
want_result:
|
||||
tbl:
|
||||
set!: [11, 12, 13]
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
cases:
|
||||
- note: forward-ref
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
a = b
|
||||
b = 5
|
||||
query: data.test
|
||||
want_result:
|
||||
a: 5
|
||||
b: 5
|
||||
|
||||
- note: recursive
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
a = b
|
||||
b = c
|
||||
c = a
|
||||
query: data.test
|
||||
error: recursion detected
|
||||
|
||||
- note: cross-module
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package a
|
||||
x = data.b.y * 2
|
||||
- |
|
||||
package b
|
||||
y = 10
|
||||
z = data.a.x + 5
|
||||
query: data
|
||||
want_result:
|
||||
a:
|
||||
x: 20
|
||||
b:
|
||||
y: 10
|
||||
z: 25
|
||||
@@ -0,0 +1,35 @@
|
||||
# Copyright (c) Rego-Rs Authors.
|
||||
# Licensed under the Apache 2.0 license.
|
||||
|
||||
cases:
|
||||
- note: simple
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
x[a] = b {
|
||||
a = "hello"
|
||||
b = "world"
|
||||
}
|
||||
query: data.test
|
||||
want_result:
|
||||
x:
|
||||
"hello": "world"
|
||||
|
||||
- note: loop
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
x[a] = b {
|
||||
a = ["hello", "world"][_]
|
||||
b = 1
|
||||
}
|
||||
query: data.test
|
||||
want_result:
|
||||
x:
|
||||
"hello": 1
|
||||
"world": 1
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
# Copyright (c) Rego-Rs Authors.
|
||||
# Licensed under the Apache 2.0 license.
|
||||
|
||||
cases:
|
||||
- note: index-syntax
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
x[a] {
|
||||
a = ["hello", "world"][_]
|
||||
}
|
||||
query: data.test
|
||||
want_result:
|
||||
x:
|
||||
set!: ["hello", "world"]
|
||||
|
||||
- note: special-case-field-syntax
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
x.a {
|
||||
a = ["hello", "world"][_]
|
||||
true
|
||||
}
|
||||
query: data.test
|
||||
want_result:
|
||||
x:
|
||||
set!: ["a"]
|
||||
|
||||
@@ -0,0 +1,249 @@
|
||||
cases:
|
||||
- note: snippet1
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
a := [ 1 | {3} ]
|
||||
|
||||
q:= {4}
|
||||
|
||||
e1 := q & {1} | {true}
|
||||
e2 := (q & {1}) | {true}
|
||||
|
||||
e3 := [ q & {1} | {true} ]
|
||||
e4 := [ (q & {1}) | {true} ]
|
||||
query: data.test
|
||||
want_result:
|
||||
a: [1]
|
||||
e1:
|
||||
set!: [true]
|
||||
e2:
|
||||
set!: [true]
|
||||
e3:
|
||||
- set!: [true]
|
||||
e4:
|
||||
- set!: []
|
||||
q:
|
||||
set!: [4]
|
||||
|
||||
- note: snippet2.1
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
import future.keywords
|
||||
|
||||
b = 15
|
||||
|
||||
get_b(a) := v { v := b }
|
||||
|
||||
x = a {
|
||||
a = b with data.test.b as 10
|
||||
}
|
||||
query: data.test
|
||||
want_result:
|
||||
b: 15
|
||||
x: 10
|
||||
|
||||
- note: snippet2
|
||||
data: {}
|
||||
skip: true
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
a := {4}
|
||||
|
||||
mydoc(x) := path {
|
||||
path := "data.test.a"
|
||||
}
|
||||
|
||||
x := [ y |
|
||||
y := data.test.a | data.test.b with data.test.a as {5} with data.test.b as {6}
|
||||
]
|
||||
|
||||
r := [ m | m := data.test.p with data.test.p as 5 + 6; true ]
|
||||
|
||||
|
||||
allow {
|
||||
input.x
|
||||
== 5
|
||||
|
||||
input.y == 5
|
||||
input.y
|
||||
== 5
|
||||
}
|
||||
query: data.test
|
||||
want_result:
|
||||
a:
|
||||
set!: [4]
|
||||
r: [11]
|
||||
x:
|
||||
- set!: [5, 6]
|
||||
|
||||
|
||||
- note: snippet3
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
a(b) { true }
|
||||
b(a) { false }
|
||||
|
||||
p := 5
|
||||
|
||||
allow {
|
||||
# TODO: ude data.test
|
||||
q := 5 #data.test["p"]
|
||||
y := data.test.a(
|
||||
5)
|
||||
q == 5
|
||||
y
|
||||
}
|
||||
|
||||
r := allow
|
||||
x = a(5)
|
||||
y = b(5)
|
||||
|
||||
query: data.test
|
||||
want_result:
|
||||
allow: true
|
||||
p: 5
|
||||
r: true
|
||||
x: true
|
||||
|
||||
- note: snippet4
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
sum (a,b) := c {
|
||||
c := a + b
|
||||
}
|
||||
|
||||
obj(v) := o {
|
||||
o := { "a" : 5 + v }
|
||||
}
|
||||
p := sum(5, 6) # There must not be space between sum and (
|
||||
#q := { "a" : "sum"}[a](5, 6)
|
||||
q := obj(1).a
|
||||
query: data.test
|
||||
want_result:
|
||||
p: 11
|
||||
q: 6
|
||||
|
||||
- note: snippet5
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
import future.keywords.if
|
||||
import future.keywords.in
|
||||
|
||||
b := {2, 3}
|
||||
|
||||
double(x) := y { y := [x, x] }
|
||||
|
||||
# Membership needs to be enclosed in parens
|
||||
x := [ (y in {2,3}) |
|
||||
y := 5 in { "z" : 5}
|
||||
]
|
||||
query: data.test
|
||||
want_result:
|
||||
b:
|
||||
set!: [2, 3]
|
||||
x: [false]
|
||||
|
||||
- note: snippet6
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
import future.keywords.in
|
||||
import future.keywords.every
|
||||
import future.keywords.if
|
||||
import future.keywords.contains
|
||||
|
||||
# expr top; membership
|
||||
x := false in {a, b} {
|
||||
a := 1
|
||||
b := 0, 2 in [5, 6]
|
||||
0, 2 in [5, 6]
|
||||
}
|
||||
|
||||
a.y(a) := b { b := a }
|
||||
|
||||
p["q"](a1) := b { b:= a1 }
|
||||
|
||||
c := a.y(5)
|
||||
d := p.q(5)
|
||||
|
||||
r["s"] p1 {
|
||||
s1 := "1"
|
||||
p1 := s1
|
||||
}
|
||||
query: data.test
|
||||
want_result:
|
||||
a: {}
|
||||
c: 5
|
||||
d: 5
|
||||
p: {}
|
||||
p1: true
|
||||
r:
|
||||
set!: ["s"]
|
||||
|
||||
- note: snippet8
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
import future.keywords.if
|
||||
import future.keywords.in
|
||||
import future.keywords.contains
|
||||
|
||||
# Try removing parenthesis below
|
||||
ref["p"] contains [(x | {5})] # [a]
|
||||
# Uncomment [a]. Why doesn't rego complain?
|
||||
# Then add a := 5 to the body.
|
||||
{
|
||||
x:= {6}
|
||||
}
|
||||
|
||||
arg := 5
|
||||
ref1[arg] {
|
||||
arg := {5, 6}
|
||||
}
|
||||
|
||||
ref2.arg := {5, 6}
|
||||
|
||||
a[b] {
|
||||
b := {5,6}[_]
|
||||
}
|
||||
|
||||
#x["b"] := b {
|
||||
# b := {5,6}[_]
|
||||
#}
|
||||
query: data.test
|
||||
want_result:
|
||||
a:
|
||||
set!: [5, 6]
|
||||
arg: 5
|
||||
ref:
|
||||
p:
|
||||
set!:
|
||||
-
|
||||
- set!: [5, 6]
|
||||
ref1:
|
||||
set!:
|
||||
- set!: [5, 6]
|
||||
ref2:
|
||||
arg:
|
||||
set!: [5, 6]
|
||||
@@ -0,0 +1,75 @@
|
||||
# Copyright (c) Rego-Rs Authors.
|
||||
# Licensed under the Apache 2.0 license.
|
||||
|
||||
cases:
|
||||
- note: basic
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
array = [1, 2, 3]
|
||||
|
||||
nested_array = [1, [2, 3, 4], 5, 6]
|
||||
|
||||
object = { "key0": "value0" }
|
||||
|
||||
key = "key"
|
||||
|
||||
object_var = { key: array }
|
||||
|
||||
local_0 = x {
|
||||
x = 10
|
||||
}
|
||||
|
||||
local_1 = x {
|
||||
x = "test_local"
|
||||
}
|
||||
|
||||
# Set with nested object, array and set.
|
||||
set = { 1, 2,
|
||||
{"a": 3, "b" : 4}, [5, 6], {7, 8}}
|
||||
|
||||
# Object with non-string as keys
|
||||
complex_object = {
|
||||
{1, 2, 3} : [4, 5, 6],
|
||||
true: false,
|
||||
[1, 3] : {"hello", "world"}
|
||||
}
|
||||
|
||||
query: data.test
|
||||
want_result:
|
||||
array: [1, 2, 3]
|
||||
nested_array: [1, [2, 3, 4], 5, 6]
|
||||
object:
|
||||
key0: value0
|
||||
key: key
|
||||
object_var:
|
||||
key: [1, 2, 3]
|
||||
local_0: 10
|
||||
local_1: test_local
|
||||
set:
|
||||
# Specify set using special encoding.
|
||||
# Order of elements shouldn't matter for set.
|
||||
set!:
|
||||
- 2
|
||||
- 1
|
||||
- a : 3
|
||||
b : 4
|
||||
- [5, 6]
|
||||
- set!: [8, 7]
|
||||
complex_object:
|
||||
# Specify object using special encoding.
|
||||
object!:
|
||||
- key:
|
||||
set!: [3, 2, 1]
|
||||
value: [4, 5, 6]
|
||||
- key: true
|
||||
value: false
|
||||
- key: [1, 3]
|
||||
value:
|
||||
set!:
|
||||
- "hello"
|
||||
- "world"
|
||||
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
// Copyright (c) Rego-Rs Authors.
|
||||
// Licensed under the Apache 2.0 license.
|
||||
#![cfg(test)]
|
||||
|
||||
use crate::interpreter::*;
|
||||
|
||||
#[test]
|
||||
fn basic() -> Result<()> {
|
||||
let rego = r#"
|
||||
package test
|
||||
|
||||
array = [1, 2, 3]
|
||||
|
||||
nested_array = [1, [2, 3, 4], 5, 6]
|
||||
|
||||
object = { "key0": "value0" }
|
||||
|
||||
key = "key"
|
||||
|
||||
object_var = { key: array }
|
||||
|
||||
local_0 = x {
|
||||
x = 10
|
||||
}
|
||||
|
||||
local_1 = x {
|
||||
some x
|
||||
x = "test_local"
|
||||
}
|
||||
|
||||
set = {1, 2, 3}
|
||||
"#;
|
||||
|
||||
let expected = Value::from_json_str(
|
||||
r#" {
|
||||
"array": [1, 2, 3],
|
||||
"nested_array": [1, [2, 3, 4], 5, 6],
|
||||
"object": { "key0": "value0" },
|
||||
"key": "key",
|
||||
"object_var": { "key": [1, 2, 3] },
|
||||
"set" : {
|
||||
"set!" : [3, 2, 1]
|
||||
},
|
||||
"local_0": 10,
|
||||
"local_1": "test_local"
|
||||
}"#,
|
||||
)?;
|
||||
|
||||
assert_match(
|
||||
eval_file(&[rego.to_owned()], None, None, "data.test")?,
|
||||
expected,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,386 @@
|
||||
// Copyright (c) Rego-Rs Authors.
|
||||
// Licensed under the Apache 2.0 license.
|
||||
#![cfg(test)]
|
||||
|
||||
use std::env;
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use rego_rs::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use test_generator::test_resources;
|
||||
//use walkdir::WalkDir;
|
||||
|
||||
mod cases;
|
||||
|
||||
// Process test value specified in json/yaml to interpret special encodings.
|
||||
pub fn process_value(v: &Value) -> Result<Value> {
|
||||
match v {
|
||||
// Handle Undefined encoded as a string "#undefined"
|
||||
Value::String(s) if s == "#undefined" => Ok(Value::Undefined),
|
||||
|
||||
// Handle set encoded as an object
|
||||
// set! :
|
||||
// - item1
|
||||
// - item2
|
||||
// ...
|
||||
Value::Object(ref fields) if fields.len() == 1 && matches!(&v["set!"], Value::Array(_)) => {
|
||||
let mut set_value = Value::new_set();
|
||||
let set = set_value.as_set_mut()?;
|
||||
for item in v["set!"].as_array()? {
|
||||
set.insert(process_value(item)?);
|
||||
}
|
||||
Ok(set_value)
|
||||
}
|
||||
|
||||
// Handle complex object specified explicitly:
|
||||
// object! :
|
||||
// - key: ...
|
||||
// value: ...
|
||||
Value::Object(fields) if fields.len() == 1 && matches!(&v["object!"], Value::Array(_)) => {
|
||||
let mut object_value = Value::new_object();
|
||||
let object = object_value.as_object_mut()?;
|
||||
for item in v["object!"].as_array()? {
|
||||
object.insert(process_value(&item["key"])?, process_value(&item["value"])?);
|
||||
}
|
||||
Ok(object_value)
|
||||
}
|
||||
|
||||
// Recursively process arrays
|
||||
Value::Array(items) => {
|
||||
let mut array_value = Value::new_array();
|
||||
let array = array_value.as_array_mut()?;
|
||||
for item in items.iter() {
|
||||
array.push(process_value(item)?);
|
||||
}
|
||||
Ok(array_value)
|
||||
}
|
||||
|
||||
// Recursively process objects
|
||||
Value::Object(fields) => {
|
||||
let mut object_value = Value::new_object();
|
||||
let object = object_value.as_object_mut()?;
|
||||
for (key, value) in fields.iter() {
|
||||
object.insert(process_value(key)?, process_value(value)?);
|
||||
}
|
||||
Ok(object_value)
|
||||
}
|
||||
|
||||
Value::Set(_) => bail!("unexpected set in value read from json/yaml"),
|
||||
|
||||
// Simple variants
|
||||
_ => Ok(v.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
fn display_values(c: &Value, e: &Value) -> Result<String> {
|
||||
Ok(format!(
|
||||
"\nleft = {}\nright = {}\n",
|
||||
serde_json::to_string_pretty(c)?,
|
||||
serde_json::to_string_pretty(e)?
|
||||
))
|
||||
}
|
||||
|
||||
// Helper function to match computed and expecte values.
|
||||
// On mismatch, prints the failing sub-value instead of the whole value.
|
||||
fn match_values_impl(computed: &Value, expected: &Value) -> Result<()> {
|
||||
match (&computed, &expected) {
|
||||
(Value::Array(a1), Value::Array(a2)) => {
|
||||
if a1.len() != a2.len() {
|
||||
bail!(
|
||||
"array length mismatch: {} != {}{}",
|
||||
a1.len(),
|
||||
a2.len(),
|
||||
display_values(computed, expected)?
|
||||
);
|
||||
}
|
||||
|
||||
for (idx, v1) in a1.iter().enumerate() {
|
||||
match_values_impl(v1, &a2[idx])?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
(Value::Set(s1), Value::Set(s2)) => {
|
||||
if s1.len() != s2.len() {
|
||||
bail!(
|
||||
"set length mismatch: {} != {}{}",
|
||||
s1.len(),
|
||||
s2.len(),
|
||||
display_values(computed, expected)?
|
||||
);
|
||||
}
|
||||
|
||||
let mut itr2 = s2.iter();
|
||||
for v1 in s1.iter() {
|
||||
match_values_impl(v1, itr2.next().unwrap())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
(Value::Object(o1), Value::Object(o2)) => {
|
||||
if o1.len() != o2.len() {
|
||||
bail!(
|
||||
"object length mismatch: {} != {}{}",
|
||||
o1.len(),
|
||||
o2.len(),
|
||||
display_values(computed, expected)?
|
||||
);
|
||||
}
|
||||
|
||||
let mut itr2 = o2.iter();
|
||||
for (k1, v1) in o1.iter() {
|
||||
let (k2, v2) = itr2.next().unwrap();
|
||||
match_values_impl(k1, k2)?;
|
||||
match_values_impl(v1, v2)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
(Value::Number(n1), Value::Number(n2)) if n1 == n2 => Ok(()),
|
||||
(Value::String(s1), Value::String(s2)) if s1 == s2 => Ok(()),
|
||||
(Value::Bool(b1), Value::Bool(b2)) if b1 == b2 => Ok(()),
|
||||
(Value::Null, Value::Null) => Ok(()),
|
||||
(Value::Undefined, Value::Undefined) => Ok(()),
|
||||
|
||||
_ => bail!("value mismatch: {}", display_values(computed, expected)?),
|
||||
}
|
||||
}
|
||||
|
||||
fn match_values(computed: &Value, expected: &Value) -> Result<()> {
|
||||
match match_values_impl(computed, expected) {
|
||||
Ok(()) => Ok(()),
|
||||
Err(e) => bail!("\nmismatch in {}{}", display_values(computed, expected)?, e),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assert_match(computed: Value, expected: Value) {
|
||||
let expected = match process_value(&expected) {
|
||||
Ok(e) => e,
|
||||
_ => panic!("unable to process value :\n {expected:?}"),
|
||||
};
|
||||
match match_values(&computed, &expected) {
|
||||
Ok(()) => (),
|
||||
Err(e) => panic!("{}", e),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eval_file(
|
||||
regos: &[String],
|
||||
data: Option<Value>,
|
||||
input: Option<Value>,
|
||||
query: &str,
|
||||
) -> Result<Value> {
|
||||
let mut files = vec![];
|
||||
let mut sources = vec![];
|
||||
let mut modules = vec![];
|
||||
let mut modules_ref = vec![];
|
||||
for (idx, _) in regos.iter().enumerate() {
|
||||
files.push(format!("rego_{idx}"));
|
||||
}
|
||||
|
||||
for (idx, file) in files.iter().enumerate() {
|
||||
let contents = regos[idx].as_str();
|
||||
sources.push(Source {
|
||||
file,
|
||||
contents,
|
||||
lines: contents.split('\n').collect(),
|
||||
});
|
||||
}
|
||||
|
||||
for source in &sources {
|
||||
let mut parser = Parser::new(source)?;
|
||||
modules.push(parser.parse()?);
|
||||
}
|
||||
|
||||
for m in &modules {
|
||||
modules_ref.push(m);
|
||||
}
|
||||
|
||||
// First eval the modules.
|
||||
let mut interpreter = interpreter::Interpreter::new(modules_ref)?;
|
||||
interpreter.eval(&data, &input)?;
|
||||
|
||||
// Now eval the query.
|
||||
let source = Source {
|
||||
file: "<query.rego>",
|
||||
contents: query,
|
||||
lines: query.split('\n').collect(),
|
||||
};
|
||||
let mut parser = Parser::new(&source)?;
|
||||
let expr = parser.parse_membership_expr()?;
|
||||
interpreter.eval_query_snippet(&expr)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore = "intended for use by scripts/rego-eval"]
|
||||
fn one_file() -> Result<()> {
|
||||
env_logger::init();
|
||||
|
||||
let mut file = String::default();
|
||||
let mut input = None;
|
||||
for a in env::args() {
|
||||
if a.ends_with(".rego") {
|
||||
file = a;
|
||||
} else if a.ends_with(".json") {
|
||||
let input_json = std::fs::read_to_string(&a)?;
|
||||
let value = Value::from_json_str(input_json.as_str())?;
|
||||
input = Some(value);
|
||||
}
|
||||
}
|
||||
|
||||
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 tree = parser.parse()?;
|
||||
let mut interpreter = interpreter::Interpreter::new(vec![&tree])?;
|
||||
let results = interpreter.eval(&None, &input)?;
|
||||
println!("eval results:\n{}", serde_json::to_string_pretty(&results)?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||
struct TestCase {
|
||||
data: Value,
|
||||
input: Option<Value>,
|
||||
modules: Vec<String>,
|
||||
note: String,
|
||||
query: String,
|
||||
sort_bindings: Option<bool>,
|
||||
want_result: Option<Value>,
|
||||
skip: Option<bool>,
|
||||
error: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||
struct YamlTest {
|
||||
cases: Vec<TestCase>,
|
||||
}
|
||||
|
||||
fn yaml_test_impl(file: &str) -> Result<()> {
|
||||
let yaml_str = std::fs::read_to_string(file)?;
|
||||
let test: YamlTest = serde_yaml::from_str(&yaml_str)?;
|
||||
|
||||
println!("running {}", file);
|
||||
for case in test.cases {
|
||||
print!("case {} ", case.note);
|
||||
if case.skip == Some(true) {
|
||||
println!("skipped");
|
||||
continue;
|
||||
}
|
||||
|
||||
match (&case.want_result, &case.error) {
|
||||
(Some(_), None) | (None, Some(_)) => (),
|
||||
_ => panic!("either want_result or error must be specified in test case."),
|
||||
}
|
||||
|
||||
// First eval the modules.
|
||||
match eval_file(
|
||||
&case.modules,
|
||||
Some(case.data),
|
||||
case.input,
|
||||
case.query.as_str(),
|
||||
) {
|
||||
Ok(results) => match case.want_result {
|
||||
Some(want_result) => assert_match(results, want_result),
|
||||
_ => panic!("eval succeeded and did not produce any errors"),
|
||||
},
|
||||
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");
|
||||
}
|
||||
|
||||
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]
|
||||
fn yaml_test_basic() -> Result<()> {
|
||||
yaml_test("tests/interpreter/cases/basic_001.yaml")
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore = "intended for use by scripts/yaml-test-eval"]
|
||||
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;
|
||||
yaml_test(path)?;
|
||||
}
|
||||
|
||||
println!("{} yaml tests passed.", total);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_yaml_tests() -> Result<()> {
|
||||
run_yaml_tests_in("tests/interpreter")
|
||||
}
|
||||
*/
|
||||
|
||||
#[test_resources("tests/interpreter/**/*.yaml")]
|
||||
fn run(path: &str) {
|
||||
yaml_test(path).unwrap()
|
||||
}
|
||||
Reference in New Issue
Block a user