func TestStorageIndexingBasicUpdate(t *testing.T) { refA := ast.MustParseRef("data.a[i]") refB := ast.MustParseRef("data.b[x]") store, ds := newStorageWithIndices(refA, refB) ds.Write(context.Background(), nil, AddOp, MustParsePath("/a/-"), nil) if store.IndexExists(refA) { t.Errorf("Expected index to be removed after patch") } }
func runIndexBuildTestCase(t *testing.T, i int, note string, refStr string, expectedStr string, value interface{}) { indices := newIndices() data := loadSmallTestData() store := NewDataStoreFromJSONObject(data) ref := ast.MustParseRef(refStr) if indices.Get(ref) != nil { t.Errorf("Test case %d (%v): Did not expect indices to contain %v yet", i, note, ref) return } // TODO(tsandall): err := indices.Build(context.Background(), store, invalidTXN, ref) if err != nil { t.Errorf("Test case %d (%v): Did not expect error from build: %v", i, note, err) return } index := indices.Get(ref) if index == nil { t.Errorf("Test case %d (%v): Did not expect nil index for %v", i, note, ref) return } assertBindingsEqual(t, fmt.Sprintf("Test case %d (%v)", i, note), index, value, expectedStr) }
func TestIndicesAdd(t *testing.T) { indices := newIndices() data := loadSmallTestData() store := NewDataStoreFromJSONObject(data) ref := ast.MustParseRef("data.d[x][y]") // TODO(tsandall): indices.Build(context.Background(), store, invalidTXN, ref) index := indices.Get(ref) // new value to add var val1 interface{} err := util.UnmarshalJSON([]byte(`{"x":[1,true]}`), &val1) if err != nil { panic(err) } bindings1 := loadExpectedBindings(`[{"x": "e", "y": 2}]`)[0] index.Add(val1, bindings1) assertBindingsEqual(t, "new value", index, val1, `[{"x": "e", "y": 2}]`) // existing value val2 := "baz" bindings2 := loadExpectedBindings(`[{"x": "e", "y": 3}]`)[0] index.Add(val2, bindings2) assertBindingsEqual(t, "existing value", index, val2, `[{"x": "e", "y": 1}, {"x": "e", "y": 3}]`) index.Add(val2, bindings2) assertBindingsEqual(t, "same value (no change)", index, val2, `[{"x": "e", "y": 1}, {"x": "e", "y": 3}]`) }
func setupBenchmark(nodes int, pods int) *topdown.QueryParams { // policy compilation c := ast.NewCompiler() modules := map[string]*ast.Module{ "test": ast.MustParseModule(policy), } if c.Compile(modules); c.Failed() { panic(c.Errors) } // storage setup store := storage.New(storage.InMemoryConfig()) // parameter setup ctx := context.Background() request := ast.ObjectTerm(ast.Item(ast.StringTerm("pod"), ast.MustParseTerm(requestedPod))) path := ast.MustParseRef("data.opa.test.scheduler.fit") txn := storage.NewTransactionOrDie(ctx, store) params := topdown.NewQueryParams(ctx, c, store, txn, request.Value, path) // data setup setupNodes(ctx, store, txn, nodes) setupRCs(ctx, store, txn, 1) setupPods(ctx, store, txn, pods, nodes) return params }
func setup(t *testing.T, filename string) *topdown.QueryParams { // policy compilation c := ast.NewCompiler() modules := map[string]*ast.Module{ "test": ast.MustParseModule(policy), } if c.Compile(modules); c.Failed() { t.Fatal("unexpected error:", c.Errors) } // storage setup store := storage.New(storage.Config{ Builtin: loadDataStore(filename), }) // parameter setup ctx := context.Background() request := ast.ObjectTerm(ast.Item(ast.StringTerm("pod"), ast.MustParseTerm(requestedPod))) path := ast.MustParseRef("data.opa.test.scheduler.fit") txn := storage.NewTransactionOrDie(ctx, store) params := topdown.NewQueryParams(ctx, c, store, txn, request.Value, path) return params }
func ExampleQuery() { // Initialize context for the example. Normally the caller would obtain the // context from an input parameter or instantiate their own. ctx := context.Background() compiler := ast.NewCompiler() // Define a dummy module with rules that produce documents that we will query below. module, err := ast.ParseModule("my_module.rego", ` package opa.example p[x] :- q[x], not r[x] q[y] :- a = [1,2,3], y = a[_] r[z] :- b = [2,4], z = b[_] `) mods := map[string]*ast.Module{ "my_module": module, } if compiler.Compile(mods); compiler.Failed() { fmt.Println(compiler.Errors) } if err != nil { // Handle error. } // Instantiate the policy engine's storage layer. store := storage.New(storage.InMemoryConfig()) // Create a new transaction. Transactions allow the policy engine to // evaluate the query over a consistent snapshot fo the storage layer. txn, err := store.NewTransaction(ctx) if err != nil { // Handle error. } defer store.Close(ctx, txn) // Prepare query parameters. In this case, there are no additional documents // required by the policy so the request is nil. var request ast.Value params := topdown.NewQueryParams(ctx, compiler, store, txn, request, ast.MustParseRef("data.opa.example.p")) // Execute the query against "p". v1, err1 := topdown.Query(params) // Inspect the result. fmt.Println("v1:", v1[0].Result) fmt.Println("err1:", err1) // Output: // v1: [1 3] // err1: <nil> }
// loadRequest returns the request defined in the REPL. The REPL loads the // request from the data.repl.request document. func (r *REPL) loadRequest(ctx context.Context, compiler *ast.Compiler) (ast.Value, error) { params := topdown.NewQueryParams(ctx, compiler, r.store, r.txn, nil, ast.MustParseRef("data.repl.request")) result, err := topdown.Query(params) if err != nil { return nil, err } if result.Undefined() { return nil, nil } return ast.InterfaceToValue(result[0].Result) }
func TestPathRef(t *testing.T) { tests := []struct { path string head string ref string }{ {"/", "data", "data"}, {"/foo/bar", "data", "data.foo.bar"}, {"/foo/bar/3", "data", "data.foo.bar[3]"}, } for _, tc := range tests { path := MustParsePath(tc.path) head := ast.VarTerm(tc.head) ref := ast.MustParseRef(tc.ref) result := path.Ref(head) if !result.Equal(ref) { t.Errorf("Expected %v but got %v", ref, result) } } }
func executeQuery(data string, compiler *ast.Compiler, tracer topdown.Tracer) { topdown.ResetQueryIDs() d := map[string]interface{}{} if len(data) > 0 { if err := util.UnmarshalJSON([]byte(data), &d); err != nil { panic(err) } } ctx := context.Background() store := storage.New(storage.InMemoryWithJSONConfig(d)) txn := storage.NewTransactionOrDie(ctx, store) defer store.Close(ctx, txn) params := topdown.NewQueryParams(ctx, compiler, store, txn, nil, ast.MustParseRef("data.test.p")) params.Tracer = tracer _, err := topdown.Query(params) if err != nil { panic(err) } }
func TestNewPathForRef(t *testing.T) { tests := []struct { input ast.Ref result Path err error }{ {ast.Ref{}, nil, fmt.Errorf("empty reference (indicates error in caller)")}, {ast.MustParseRef("data.foo[x]"), nil, fmt.Errorf("unresolved reference (indicates error in caller): data.foo[x]")}, {ast.MustParseRef("data.foo[true]"), nil, notFoundRefError(ast.MustParseRef("data.foo[true]"), doesNotExistMsg)}, {ast.MustParseRef("data"), Path{}, nil}, {ast.MustParseRef("data.foo"), Path{"foo"}, nil}, {ast.MustParseRef("data.foo[1]"), Path{"foo", "1"}, nil}, {ast.MustParseRef("data.foo.bar"), Path{"foo", "bar"}, nil}, } for _, tc := range tests { result, err := NewPathForRef(tc.input) if tc.err != nil && !reflect.DeepEqual(tc.err, err) { t.Errorf("For %v expected %v but got %v", tc.input, tc.err, err) } else if !result.Equal(tc.result) { t.Errorf("For %v expected %v but got %v", tc.input, tc.result, result) } } }
func TestPrettyTrace(t *testing.T) { module := ` package test p :- q[x], plus(x, 1, n) q[x] :- x = data.a[_] ` ctx := context.Background() compiler := compileModules([]string{module}) data := loadSmallTestData() store := storage.New(storage.InMemoryWithJSONConfig(data)) txn := storage.NewTransactionOrDie(ctx, store) defer store.Close(ctx, txn) params := NewQueryParams(ctx, compiler, store, txn, nil, ast.MustParseRef("data.test.p")) tracer := NewBufferTracer() params.Tracer = tracer _, err := Query(params) if err != nil { panic(err) } expected := `Enter eq(data.test.p, _) | Eval eq(data.test.p, _) | Enter p = true :- data.test.q[x], plus(x, 1, n) | | Eval data.test.q[x] | | Enter q[x] :- eq(x, data.a[_]) | | | Eval eq(x, data.a[_]) | | | Exit q[x] :- eq(x, data.a[_]) | | Eval plus(x, 1, n) | | Exit p = true :- data.test.q[x], plus(x, 1, n) | Redo p = true :- data.test.q[x], plus(x, 1, n) | | Redo data.test.q[x] | | Redo q[x] :- eq(x, data.a[_]) | | | Redo eq(x, data.a[_]) | | | Exit q[x] :- eq(x, data.a[_]) | | Eval plus(x, 1, n) | | Exit p = true :- data.test.q[x], plus(x, 1, n) | Redo p = true :- data.test.q[x], plus(x, 1, n) | | Redo data.test.q[x] | | Redo q[x] :- eq(x, data.a[_]) | | | Redo eq(x, data.a[_]) | | | Exit q[x] :- eq(x, data.a[_]) | | Eval plus(x, 1, n) | | Exit p = true :- data.test.q[x], plus(x, 1, n) | Redo p = true :- data.test.q[x], plus(x, 1, n) | | Redo data.test.q[x] | | Redo q[x] :- eq(x, data.a[_]) | | | Redo eq(x, data.a[_]) | | | Exit q[x] :- eq(x, data.a[_]) | | Eval plus(x, 1, n) | | Exit p = true :- data.test.q[x], plus(x, 1, n) | Exit eq(data.test.p, _) ` a := strings.Split(expected, "\n") var buf bytes.Buffer PrettyTrace(&buf, *tracer) b := strings.Split(buf.String(), "\n") min := len(a) if min > len(b) { min = len(b) } for i := 0; i < min; i++ { if a[i] != b[i] { t.Errorf("Line %v in trace is incorrect. Expected %v but got: %v", i+1, a[i], b[i]) } } if len(a) < len(b) { t.Fatalf("Extra lines in trace:\n%v", strings.Join(b[min:], "\n")) } else if len(b) < len(a) { t.Fatalf("Missing lines in trace:\n%v", strings.Join(a[min:], "\n")) } }