mirror of
https://github.com/open-policy-agent/opa.git
synced 2026-08-28 03:05:04 -06:00
wasm: Add minimal coverage for complete rule definitions
These changes also extend the test runner to allow disabling partial evaluation (which is needed since complete definitons are inlined normally), error checking, and module fixtures. Signed-off-by: Torin Sandall <torinsandall@gmail.com>
This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
cases:
|
||||
- note: constants
|
||||
disable_partial: true
|
||||
query: data.x.p = 1
|
||||
modules:
|
||||
- |
|
||||
package x
|
||||
p = 1
|
||||
return_code: 1
|
||||
- note: constants (negative)
|
||||
disable_partial: true
|
||||
query: data.x.p = 1
|
||||
modules:
|
||||
- |
|
||||
package x
|
||||
p = 2
|
||||
return_code: 0
|
||||
- note: variable
|
||||
disable_partial: true
|
||||
query: data.x.p = 1
|
||||
modules:
|
||||
- |
|
||||
package x
|
||||
p = y { x = 1; y = x }
|
||||
return_code: 1
|
||||
- note: variable (negative)
|
||||
disable_partial: true
|
||||
query: data.x.p = 1
|
||||
modules:
|
||||
- |
|
||||
package x
|
||||
p = y { x = 2; y = x }
|
||||
return_code: 0
|
||||
- note: conjunction
|
||||
disable_partial: true
|
||||
query: data.x.p = 1
|
||||
modules:
|
||||
- |
|
||||
package x
|
||||
p = 1 { q; r }
|
||||
q { true }
|
||||
r { true }
|
||||
return_code: 1
|
||||
- note: conjunction (negative)
|
||||
disable_partial: true
|
||||
query: data.x.p = 1
|
||||
modules:
|
||||
- |
|
||||
package x
|
||||
p = 1 { q; r }
|
||||
q { true }
|
||||
r { false }
|
||||
return_code: 0
|
||||
- note: disjunction
|
||||
disable_partial: true
|
||||
query: data.x.p = 2
|
||||
modules:
|
||||
- |
|
||||
package x
|
||||
p = 1 { false }
|
||||
p = 2 { true }
|
||||
p = 3 { false }
|
||||
return_code: 1
|
||||
- note: disjunction (negative)
|
||||
disable_partial: true
|
||||
query: data.x.p = 1
|
||||
modules:
|
||||
- |
|
||||
package x
|
||||
p = 1 { false }
|
||||
p = 2 { false }
|
||||
p = 3 { true }
|
||||
return_code: 0
|
||||
- note: negation
|
||||
disable_partial: true
|
||||
query: not data.x.p = 1
|
||||
modules:
|
||||
- |
|
||||
package x
|
||||
p = 1 { false } # undefined
|
||||
return_code: 1
|
||||
- note: negation (negative)
|
||||
disable_partial: true
|
||||
query: not data.x.p = 1
|
||||
modules:
|
||||
- |
|
||||
package x
|
||||
p = 1
|
||||
return_code: 0
|
||||
- note: chain
|
||||
disable_partial: true
|
||||
query: data.x.p = 1
|
||||
modules:
|
||||
- |
|
||||
package x
|
||||
p = q
|
||||
q = r
|
||||
r = 1
|
||||
return_code: 1
|
||||
- note: chain (negative)
|
||||
disable_partial: true
|
||||
query: data.x.p = 1
|
||||
modules:
|
||||
- |
|
||||
package x
|
||||
p = q
|
||||
q = r
|
||||
r = 2
|
||||
return_code: 0
|
||||
- note: chain input
|
||||
disable_partial: true
|
||||
query: data.x.p = true
|
||||
modules:
|
||||
- |
|
||||
package x
|
||||
p = q
|
||||
q = r
|
||||
r { input.x = 1 }
|
||||
input: {"x": 1}
|
||||
return_code: 1
|
||||
- note: chain input (negative)
|
||||
disable_partial: true
|
||||
query: data.x.p = true
|
||||
modules:
|
||||
- |
|
||||
package x
|
||||
p = q
|
||||
q = r
|
||||
r { input.x = 2 }
|
||||
input: {"x": 1}
|
||||
return_code: 0
|
||||
- note: iteration
|
||||
disable_partial: true
|
||||
query: data.x.p = 1
|
||||
modules:
|
||||
- |
|
||||
package x
|
||||
p = 1 { input[x] = 1 }
|
||||
input: [3,2,1]
|
||||
return_code: 1
|
||||
- note: iteration (negative)
|
||||
disable_partial: true
|
||||
query: data.x.p = 1
|
||||
modules:
|
||||
- |
|
||||
package x
|
||||
p = 1 { input[x] = 4 }
|
||||
input: [3,2,1]
|
||||
return_code: 0
|
||||
- note: conflict error
|
||||
disable_partial: true
|
||||
query: data.x.p = 1
|
||||
modules:
|
||||
- |
|
||||
package x
|
||||
p = 1
|
||||
p = 2
|
||||
want_error: "unreachable" # TODO(tsandall): replace with conflict error.
|
||||
- note: packages
|
||||
disable_partial: true
|
||||
query: data.x.p = 1
|
||||
modules:
|
||||
- |
|
||||
package x
|
||||
import data.y.p
|
||||
p = p
|
||||
- |
|
||||
package y
|
||||
p = 1
|
||||
return_code: 1
|
||||
+21
-14
@@ -1,7 +1,7 @@
|
||||
const { readFileSync, readdirSync } = require('fs');
|
||||
|
||||
function stringDecoder(mem) {
|
||||
return function(addr) {
|
||||
return function (addr) {
|
||||
const i8 = new Int8Array(mem.buffer);
|
||||
const start = addr;
|
||||
var s = "";
|
||||
@@ -47,10 +47,10 @@ function now() {
|
||||
function formatMicros(us) {
|
||||
if (us <= 1000) {
|
||||
return us + 'µs'
|
||||
} else if (us <= 1000*1000) {
|
||||
} else if (us <= 1000 * 1000) {
|
||||
return (us / 1000).toFixed(4) + 'ms'
|
||||
} else {
|
||||
return (us / (1000*1000)).toFixed(4) + 's'
|
||||
return (us / (1000 * 1000)).toFixed(4) + 's'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,14 +60,14 @@ function evaluate(mem, policy, input) {
|
||||
const addr = policy.instance.exports.opa_malloc(str.length);
|
||||
const buf = new Uint8Array(mem.buffer);
|
||||
|
||||
for(let i = 0; i < str.length; i++) {
|
||||
buf[addr+i] = str.charCodeAt(i);
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
buf[addr + i] = str.charCodeAt(i);
|
||||
}
|
||||
|
||||
|
||||
const returnCode = policy.instance.exports.eval(addr, str.length);
|
||||
|
||||
return {returnCode: returnCode};
|
||||
return { returnCode: returnCode };
|
||||
}
|
||||
|
||||
function namespace(cache, key) {
|
||||
@@ -82,7 +82,7 @@ function namespace(cache, key) {
|
||||
|
||||
async function test() {
|
||||
|
||||
const mem = new WebAssembly.Memory({initial: 5});
|
||||
const mem = new WebAssembly.Memory({ initial: 5 });
|
||||
const addr2string = stringDecoder(mem);
|
||||
|
||||
const t0 = now();
|
||||
@@ -115,12 +115,12 @@ async function test() {
|
||||
let dirty = false;
|
||||
let cache = {};
|
||||
|
||||
for(let i = 0; i < testCases.length; i++) {
|
||||
for (let i = 0; i < testCases.length; i++) {
|
||||
|
||||
const policy = await WebAssembly.instantiate(testCases[i].wasmBytes, {
|
||||
env: {
|
||||
memory: mem,
|
||||
opa_abort: function(addr) {
|
||||
opa_abort: function (addr) {
|
||||
throw addr2string(addr);
|
||||
},
|
||||
},
|
||||
@@ -132,9 +132,16 @@ async function test() {
|
||||
try {
|
||||
const result = evaluate(mem, policy, testCases[i].input);
|
||||
passed = result.returnCode === testCases[i].return_code;
|
||||
} catch(e) {
|
||||
passed = false;
|
||||
error = e;
|
||||
} catch (e) {
|
||||
if (testCases[i].want_error === undefined) {
|
||||
passed = false;
|
||||
error = e;
|
||||
} else if (e.message.includes(testCases[i].want_error)) {
|
||||
passed = true;
|
||||
} else {
|
||||
passed = false;
|
||||
error = e;
|
||||
}
|
||||
}
|
||||
|
||||
if (passed) {
|
||||
@@ -152,7 +159,7 @@ async function test() {
|
||||
const dt_end = t_end - t_load;
|
||||
|
||||
if (dirty) {
|
||||
console.log();
|
||||
console.log();
|
||||
}
|
||||
|
||||
console.log('SUMMARY:');
|
||||
@@ -170,7 +177,7 @@ async function test() {
|
||||
console.log();
|
||||
console.log('TOOK:', formatMicros(dt_end));
|
||||
|
||||
if ((numFailed+numErrors) > 0) {
|
||||
if ((numFailed + numErrors) > 0) {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
@@ -32,10 +33,13 @@ type testCaseSet struct {
|
||||
}
|
||||
|
||||
type testCase struct {
|
||||
Note string `json:"note"`
|
||||
Query string `json:"query"`
|
||||
Input *interface{} `json:"input"`
|
||||
ReturnCode int `json:"return_code"`
|
||||
Note string `json:"note"`
|
||||
Query string `json:"query"`
|
||||
Modules []string `json:"modules"`
|
||||
DisablePartial bool `json:"disable_partial"`
|
||||
Input *interface{} `json:"input"`
|
||||
ReturnCode int `json:"return_code"`
|
||||
WantError string `json:"want_error"`
|
||||
}
|
||||
|
||||
type compiledTestCaseSet struct {
|
||||
@@ -50,7 +54,13 @@ type compiledTestCase struct {
|
||||
func compileTestCases(ctx context.Context, tests testCaseSet) (*compiledTestCaseSet, error) {
|
||||
var result []compiledTestCase
|
||||
for _, tc := range tests.Cases {
|
||||
cr, err := rego.New(rego.Query(tc.Query)).Compile(ctx)
|
||||
args := []func(*rego.Rego){
|
||||
rego.Query(tc.Query),
|
||||
}
|
||||
for idx, module := range tc.Modules {
|
||||
args = append(args, rego.Module(fmt.Sprintf("module%d.rego", idx), module))
|
||||
}
|
||||
cr, err := rego.New(args...).Compile(ctx, rego.CompilePartial(!tc.DisablePartial))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user