Exemple #1
0
func TestCompilerGetRulesExact(t *testing.T) {
	mods := getCompilerTestModules()

	// Add incrementally defined rules.
	mods["mod-incr"] = MustParseModule(`
	package a.b.c
	p[1] :- true
	p[2] :- true
	`)

	c := NewCompiler()
	c.Compile(mods)
	assertNotFailed(t, c)

	tests := []struct {
		note     string
		ref      interface{}
		expected []*Rule
	}{
		{"exact", "data.a.b.c.p", []*Rule{
			c.Modules["mod-incr"].Rules[0],
			c.Modules["mod-incr"].Rules[1],
			c.Modules["mod1"].Rules[0],
		}},
		{"too short", "data.a", []*Rule{}},
		{"too long/not found", "data.a.b.c.p.q", []*Rule{}},
		{"outside data", "req.a.b.c.p", []*Rule{}},
	}

	for _, tc := range tests {
		test.Subtest(t, tc.note, func(t *testing.T) {
			var ref Ref
			switch r := tc.ref.(type) {
			case string:
				ref = MustParseRef(r)
			case Ref:
				ref = r
			}
			rules := c.GetRulesExact(ref)
			if len(rules) != len(tc.expected) {
				t.Fatalf("Expected exactly %v rules but got: %v", len(tc.expected), rules)
			}
			for i := range rules {
				found := false
				for j := range tc.expected {
					if rules[i].Equal(tc.expected[j]) {
						found = true
						break
					}
				}
				if !found {
					t.Fatalf("Expected exactly %v but got: %v", tc.expected, rules)
				}
			}
		})
	}
}
Exemple #2
0
func runQueryCompilerTest(t *testing.T, note, q, pkg string, imports []string, expected interface{}) {
	test.Subtest(t, note, func(t *testing.T) {
		c := NewCompiler()
		c.Compile(getCompilerTestModules())
		assertNotFailed(t, c)
		qc := c.QueryCompiler()
		query := MustParseBody(q)
		var qctx *QueryContext
		if pkg != "" {
			pkg := MustParsePackage(pkg)
			qctx = NewQueryContext(pkg, nil)
			if len(imports) > 0 {
				qctx.Imports = MustParseImports(strings.Join(imports, "\n"))
			}
		}
		if qctx != nil {
			qc.WithContext(qctx)
		}
		switch expected := expected.(type) {
		case string:
			expectedQuery := MustParseBody(expected)
			result, err := qc.Compile(query)
			if err != nil {
				t.Fatalf("Unexpected error from %v: %v", query, err)
			}
			if !expectedQuery.Equal(result) {
				t.Fatalf("Expected:\n%v\n\nGot:\n%v", expectedQuery, result)
			}
		case error:
			result, err := qc.Compile(query)
			if err == nil {
				t.Fatalf("Expected error from %v but got: %v", query, result)
			}
			if err.Error() != expected.Error() {
				t.Fatalf("Expected error %v but got: %v", expected, err)
			}
		}
	})
}
Exemple #3
0
func TestDataV1(t *testing.T) {

	testMod1 := `package testmod
                p[x] :- q[x], not r[x]
                q[x] :- data.x.y[i] = x
                r[x] :- data.x.z[i] = x

				import request.req1
				import request.req2 as reqx
				import request.req3.attr1
				g :- req1.a[0] = 1, reqx.b[i] = 1
				h :- attr1[i] > 1

				gt1 :- req1 > 1
				arr = [1,2,3,4]

				undef :- false
				`

	testMod2 := `package testmod

	p = [1,2,3,4]
	q = {"a": 1, "b": 2}
	`

	tests := []struct {
		note string
		reqs []tr
	}{
		{"add root", []tr{
			tr{"PATCH", "/data/x", `[{"op": "add", "path": "/", "value": {"a": 1}}]`, 204, ""},
			tr{"GET", "/data/x/a", "", 200, "1"},
		}},
		{"append array", []tr{
			tr{"PATCH", "/data/x", `[{"op": "add", "path": "/", "value": []}]`, 204, ""},
			tr{"PATCH", "/data/x", `[{"op": "add", "path": "-", "value": {"a": 1}}]`, 204, ""},
			tr{"PATCH", "/data/x", `[{"op": "add", "path": "-", "value": {"a": 2}}]`, 204, ""},
			tr{"GET", "/data/x/0/a", "", 200, "1"},
			tr{"GET", "/data/x/1/a", "", 200, "2"},
		}},
		{"append array one-shot", []tr{
			tr{"PATCH", "/data/x", `[
                {"op": "add", "path": "/", "value": []},
                {"op": "add", "path": "-", "value": {"a": 1}},
                {"op": "add", "path": "-", "value": {"a": 2}}
            ]`, 204, ""},
			tr{"GET", "/data/x/1/a", "", 200, "2"},
		}},
		{"insert array", []tr{
			tr{"PATCH", "/data/x", `[{"op": "add", "path": "/", "value": {
                "y": [
                    {"z": [1,2,3]},
                    {"z": [4,5,6]}
                ]
            }}]`, 204, ""},
			tr{"GET", "/data/x/y/1/z/2", "", 200, "6"},
			tr{"PATCH", "/data/x/y/1", `[{"op": "add", "path": "/z/1", "value": 100}]`, 204, ""},
			tr{"GET", "/data/x/y/1/z", "", 200, "[4, 100, 5, 6]"},
		}},
		{"patch root", []tr{
			tr{"PATCH", "/data", `[
				{"op": "add",
				 "path": "/",
				 "value": {"a": 1, "b": 2}
				}
			]`, 204, ""},
			tr{"GET", "/data", "", 200, `{"a": 1, "b": 2}`},
		}},
		{"patch invalid", []tr{
			tr{"PATCH", "/data", `[
				{
					"op": "remove",
					"path": "/"
				}
			]`, 400, ""},
		}},
		{"put root", []tr{
			tr{"PUT", "/data", `{"foo": [1,2,3]}`, 204, ""},
			tr{"GET", "/data", "", 200, `{"foo": [1,2,3]}`},
		}},
		{"put deep makedir", []tr{
			tr{"PUT", "/data/a/b/c/d", `1`, 204, ""},
			tr{"GET", "/data/a/b/c", "", 200, `{"d": 1}`},
		}},
		{"put deep makedir partial", []tr{
			tr{"PUT", "/data/a/b", `{}`, 204, ""},
			tr{"PUT", "/data/a/b/c/d", `0`, 204, ""},
			tr{"GET", "/data/a/b/c", "", 200, `{"d": 0}`},
		}},
		{"put exists overwrite", []tr{
			tr{"PUT", "/data/a/b/c", `"hello"`, 204, ""},
			tr{"PUT", "/data/a/b", `"goodbye"`, 204, ""},
			tr{"GET", "/data/a", "", 200, `{"b": "goodbye"}`},
		}},
		{"put base write conflict", []tr{
			tr{"PUT", "/data/a/b", `[1,2,3,4]`, 204, ""},
			tr{"PUT", "/data/a/b/c/d", "0", 404, `{
				"Code": 404,
				"Message": "write conflict: /a/b"
			}`},
		}},
		{"put virtual write conflict", []tr{
			tr{"PUT", "/policies/test", testMod2, 200, ""},
			tr{"PUT", "/data/testmod/q/x", "0", 404, `{
				"Code": 404,
				"Message": "write conflict: /testmod/q"
			}`},
		}},
		{"get virtual", []tr{
			tr{"PUT", "/policies/test", testMod1, 200, ""},
			tr{"PATCH", "/data/x", `[{"op": "add", "path": "/", "value": {"y": [1,2,3,4], "z": [3,4,5,6]}}]`, 204, ""},
			tr{"GET", "/data/testmod/p", "", 200, "[1,2]"},
		}},
		{"patch virtual error", []tr{
			tr{"PUT", "/policies/test", testMod1, 200, ""},
			tr{"PATCH", "/data/testmod/p", `[{"op": "add", "path": "-", "value": 1}]`, 404, `{
                "Code": 404,
                "Message": "write conflict: /testmod/p"
            }`},
		}},
		{"get with request", []tr{
			tr{"PUT", "/policies/test", testMod1, 200, ""},
			tr{"GET", "/data/testmod/g?request=req1%3A%7B%22a%22%3A%5B1%5D%7D&request=req2%3A%7B%22b%22%3A%5B0%2C1%5D%7D", "", 200, "true"},
		}},
		{"get with request (missing request value)", []tr{
			tr{"PUT", "/policies/test", testMod1, 200, ""},
			tr{"GET", "/data/testmod/g?request=req1%3A%7B%22a%22%3A%5B1%5D%7D", "", 404, ""},
		}},
		{"get with request (namespaced)", []tr{
			tr{"PUT", "/policies/test", testMod1, 200, ""},
			tr{"GET", "/data/testmod/h?request=req3.attr1%3A%5B4%2C3%2C2%2C1%5D", "", 200, `true`},
		}},
		{"get with request (non-ground ref)", []tr{
			tr{"PUT", "/policies/test", testMod1, 200, ""},
			tr{"GET", "/data/testmod/gt1?request=req1:data.testmod.arr[i]", "", 200, `[[true, {"i": 1}], [true, {"i": 2}], [true, {"i": 3}]]`},
		}},
		{"get with request (root)", []tr{
			tr{"PUT", "/policies/test", testMod1, 200, ""},
			tr{"GET", `/data/testmod/gt1?request=:{"req1":2}`, "", 200, `true`},
		}},
		{"get with request (root-2)", []tr{
			tr{"PUT", "/policies/test", testMod1, 200, ""},
			tr{"GET", `/data/testmod/gt1?request={"req1":2}`, "", 200, `true`},
		}},
		{"get with request (root+non-ground)", []tr{
			tr{"PUT", "/policies/test", testMod1, 200, ""},
			tr{"GET", `/data/testmod/gt1?request={"req1":data.testmod.arr[i]}`, "", 200, `[[true, {"i": 1}], [true, {"i": 2}], [true, {"i": 3}]]`},
		}},
		{"get with request (bad format)", []tr{
			tr{"GET", `/data/deadbeef?request="foo`, "", 400, `{
				"Code": 400,
				"Message": "request parameter format is [[<path>]:]<value> where <path> is either var or ref"
			}`},
		}},
		{"get with request (path error)", []tr{
			tr{"GET", `/data/deadbeef?request="foo:1`, "", 400, `{
				"Code": 400,
				"Message": "request parameter format is [[<path>]:]<value> where <path> is either var or ref"
			}`},
		}},
		{"get undefined", []tr{
			tr{"PUT", "/policies/test", testMod1, 200, ""},
			tr{"GET", "/data/testmod/undef", "", 404, ""},
			tr{"GET", "/data/does/not/exist", "", 404, ""},
		}},
		{"get root", []tr{
			tr{"PUT", "/policies/test", testMod2, 200, ""},
			tr{"PATCH", "/data/x", `[{"op": "add", "path": "/", "value": [1,2,3,4]}]`, 204, ""},
			tr{"GET", "/data", "", 200, `{"testmod": {"p": [1,2,3,4], "q": {"a":1, "b": 2}}, "x": [1,2,3,4]}`},
		}},
		{"query wildcards omitted", []tr{
			tr{"PATCH", "/data/x", `[{"op": "add", "path": "/", "value": [1,2,3,4]}]`, 204, ""},
			tr{"GET", "/query?q=data.x[_]%20=%20x", "", 200, `[{"x": 1}, {"x": 2}, {"x": 3}, {"x": 4}]`},
		}},
		{"query compiler error", []tr{
			tr{"GET", "/query?q=x", "", 400, ""},
			// Subsequent query should not fail.
			tr{"GET", "/query?q=x=1", "", 200, `[{"x": 1}]`},
		}},
	}

	for _, tc := range tests {
		test.Subtest(t, tc.note, func(t *testing.T) {
			executeRequests(t, tc.reqs)
		})
	}
}