policy: more tests and fixes
This commit is contained in:
@@ -40,14 +40,14 @@ func statementFromIPLD(path string, node datamodel.Node) (Statement, error) {
|
|||||||
}
|
}
|
||||||
op := must.String(opNode)
|
op := must.String(opNode)
|
||||||
|
|
||||||
arg2AsSelector := func() (selector.Selector, error) {
|
arg2AsSelector := func(op string) (selector.Selector, error) {
|
||||||
nd, _ := node.LookupByIndex(1)
|
nd, _ := node.LookupByIndex(1)
|
||||||
if nd.Kind() != datamodel.Kind_String {
|
if nd.Kind() != datamodel.Kind_String {
|
||||||
return nil, ErrNotAString(path + "1/")
|
return nil, ErrNotAString(combinePath(path, op, 1))
|
||||||
}
|
}
|
||||||
sel, err := selector.Parse(must.String(nd))
|
sel, err := selector.Parse(must.String(nd))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, ErrInvalidSelector(path+"1/", err)
|
return nil, ErrInvalidSelector(combinePath(path, op, 1), err)
|
||||||
}
|
}
|
||||||
return sel, nil
|
return sel, nil
|
||||||
}
|
}
|
||||||
@@ -57,7 +57,7 @@ func statementFromIPLD(path string, node datamodel.Node) (Statement, error) {
|
|||||||
switch op {
|
switch op {
|
||||||
case KindNot:
|
case KindNot:
|
||||||
arg2, _ := node.LookupByIndex(1)
|
arg2, _ := node.LookupByIndex(1)
|
||||||
statement, err := statementFromIPLD(path+"1/", arg2)
|
statement, err := statementFromIPLD(combinePath(path, op, 1), arg2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -65,7 +65,7 @@ func statementFromIPLD(path string, node datamodel.Node) (Statement, error) {
|
|||||||
|
|
||||||
case KindAnd, KindOr:
|
case KindAnd, KindOr:
|
||||||
arg2, _ := node.LookupByIndex(1)
|
arg2, _ := node.LookupByIndex(1)
|
||||||
statement, err := statementsFromIPLD(path+"1/", arg2)
|
statement, err := statementsFromIPLD(combinePath(path, op, 1), arg2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -77,7 +77,7 @@ func statementFromIPLD(path string, node datamodel.Node) (Statement, error) {
|
|||||||
case 3:
|
case 3:
|
||||||
switch op {
|
switch op {
|
||||||
case KindEqual, KindLessThan, KindLessThanOrEqual, KindGreaterThan, KindGreaterThanOrEqual:
|
case KindEqual, KindLessThan, KindLessThanOrEqual, KindGreaterThan, KindGreaterThanOrEqual:
|
||||||
sel, err := arg2AsSelector()
|
sel, err := arg2AsSelector(op)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -85,27 +85,30 @@ func statementFromIPLD(path string, node datamodel.Node) (Statement, error) {
|
|||||||
return equality{kind: op, selector: sel, value: arg3}, nil
|
return equality{kind: op, selector: sel, value: arg3}, nil
|
||||||
|
|
||||||
case KindLike:
|
case KindLike:
|
||||||
sel, err := arg2AsSelector()
|
sel, err := arg2AsSelector(op)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
pattern, _ := node.LookupByIndex(2)
|
pattern, _ := node.LookupByIndex(2)
|
||||||
if pattern.Kind() != datamodel.Kind_String {
|
if pattern.Kind() != datamodel.Kind_String {
|
||||||
return nil, ErrNotAString(path + "2/")
|
return nil, ErrNotAString(combinePath(path, op, 2))
|
||||||
}
|
}
|
||||||
res, err := Like(sel, must.String(pattern))
|
res, err := Like(sel, must.String(pattern))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, ErrInvalidPattern(path+"2/", err)
|
return nil, ErrInvalidPattern(combinePath(path, op, 2), err)
|
||||||
}
|
}
|
||||||
return res, nil
|
return res, nil
|
||||||
|
|
||||||
case KindAll, KindAny:
|
case KindAll, KindAny:
|
||||||
sel, err := arg2AsSelector()
|
sel, err := arg2AsSelector(op)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
statementsNode, _ := node.LookupByIndex(2)
|
statementsNode, _ := node.LookupByIndex(2)
|
||||||
statement, err := statementFromIPLD(path+"1/", statementsNode)
|
statement, err := statementFromIPLD(combinePath(path, op, 1), statementsNode)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
return quantifier{kind: op, selector: sel, statement: statement}, nil
|
return quantifier{kind: op, selector: sel, statement: statement}, nil
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -123,7 +126,7 @@ func statementsFromIPLD(path string, node datamodel.Node) ([]Statement, error) {
|
|||||||
return nil, ErrNotATuple(path)
|
return nil, ErrNotATuple(path)
|
||||||
}
|
}
|
||||||
if node.Length() == 0 {
|
if node.Length() == 0 {
|
||||||
return nil, ErrEmptyList(path)
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
res := make([]Statement, node.Length())
|
res := make([]Statement, node.Length())
|
||||||
@@ -260,3 +263,7 @@ func statementToIPLD(statement Statement) (datamodel.Node, error) {
|
|||||||
|
|
||||||
return list.Build(), nil
|
return list.Build(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func combinePath(prev string, operator string, index int) string {
|
||||||
|
return fmt.Sprintf("%s%d-%s/", prev, index, operator)
|
||||||
|
}
|
||||||
|
|||||||
@@ -35,10 +35,6 @@ func ErrNotATuple(path string) error {
|
|||||||
return errWithPath{path: path, msg: "not a tuple"}
|
return errWithPath{path: path, msg: "not a tuple"}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ErrEmptyList(path string) error {
|
|
||||||
return errWithPath{path: path, msg: "empty list"}
|
|
||||||
}
|
|
||||||
|
|
||||||
func safeStr(str string) string {
|
func safeStr(str string) string {
|
||||||
if len(str) > 10 {
|
if len(str) > 10 {
|
||||||
return str[:10]
|
return str[:10]
|
||||||
|
|||||||
@@ -11,16 +11,12 @@ import (
|
|||||||
func TestIpldRoundTrip(t *testing.T) {
|
func TestIpldRoundTrip(t *testing.T) {
|
||||||
const illustrativeExample = `
|
const illustrativeExample = `
|
||||||
[
|
[
|
||||||
["==", ".status", "draft"],
|
["==", ".status", "draft"],
|
||||||
["all", ".reviewer", [
|
["all", ".reviewer", ["like", ".email", "*@example.com"]],
|
||||||
["like", ".email", "*@example.com"]]
|
["any", ".tags",
|
||||||
],
|
["or", [
|
||||||
["any", ".tags", [
|
["==", ".", "news"],
|
||||||
["or", [
|
["==", ".", "press"]]]
|
||||||
["==", ".", "news"],
|
|
||||||
["==", ".", "press"]]
|
|
||||||
]]
|
|
||||||
]
|
|
||||||
]`
|
]`
|
||||||
|
|
||||||
for _, tc := range []struct {
|
for _, tc := range []struct {
|
||||||
|
|||||||
@@ -119,6 +119,7 @@ func matchStatement(statement Statement, node ipld.Node) bool {
|
|||||||
}
|
}
|
||||||
case KindAny:
|
case KindAny:
|
||||||
if s, ok := statement.(quantifier); ok {
|
if s, ok := statement.(quantifier); ok {
|
||||||
|
// FIXME: line below return a single node, not many
|
||||||
_, many, err := selector.Select(s.selector, node)
|
_, many, err := selector.Select(s.selector, node)
|
||||||
if err != nil || many == nil {
|
if err != nil || many == nil {
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/ipfs/go-cid"
|
"github.com/ipfs/go-cid"
|
||||||
"github.com/ipld/go-ipld-prime"
|
"github.com/ipld/go-ipld-prime"
|
||||||
|
"github.com/ipld/go-ipld-prime/codec/dagjson"
|
||||||
cidlink "github.com/ipld/go-ipld-prime/linking/cid"
|
cidlink "github.com/ipld/go-ipld-prime/linking/cid"
|
||||||
"github.com/ipld/go-ipld-prime/node/basicnode"
|
"github.com/ipld/go-ipld-prime/node/basicnode"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@@ -417,3 +418,75 @@ func TestMatch(t *testing.T) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPolicyExamples(t *testing.T) {
|
||||||
|
makeNode := func(data string) ipld.Node {
|
||||||
|
nd, err := ipld.Decode([]byte(data), dagjson.Decode)
|
||||||
|
require.NoError(t, err)
|
||||||
|
return nd
|
||||||
|
}
|
||||||
|
|
||||||
|
evaluate := func(statement string, data ipld.Node) bool {
|
||||||
|
// we need to wrap statement with [] to make them a policy
|
||||||
|
policy := fmt.Sprintf("[%s]", statement)
|
||||||
|
|
||||||
|
pol, err := FromDagJson(policy)
|
||||||
|
require.NoError(t, err)
|
||||||
|
return Match(pol, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("And", func(t *testing.T) {
|
||||||
|
data := makeNode(`{ "name": "Katie", "age": 35, "nationalities": ["Canadian", "South African"] }`)
|
||||||
|
|
||||||
|
require.True(t, evaluate(`["and", []]`, data))
|
||||||
|
require.True(t, evaluate(`
|
||||||
|
["and", [
|
||||||
|
["==", ".name", "Katie"],
|
||||||
|
[">=", ".age", 21]
|
||||||
|
]]`, data))
|
||||||
|
require.False(t, evaluate(`
|
||||||
|
["and", [
|
||||||
|
["==", ".name", "Katie"],
|
||||||
|
[">=", ".age", 21],
|
||||||
|
["==", ".nationalities", ["American"]]
|
||||||
|
]]`, data))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Or", func(t *testing.T) {
|
||||||
|
data := makeNode(`{ "name": "Katie", "age": 35, "nationalities": ["Canadian", "South African"] }`)
|
||||||
|
|
||||||
|
require.True(t, evaluate(`["or", []]`, data))
|
||||||
|
require.True(t, evaluate(`
|
||||||
|
["or", [
|
||||||
|
["==", ".name", "Katie"],
|
||||||
|
[">", ".age", 45]
|
||||||
|
]]
|
||||||
|
`, data))
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Not", func(t *testing.T) {
|
||||||
|
data := makeNode(`{ "name": "Katie", "nationalities": ["Canadian", "South African"] }`)
|
||||||
|
|
||||||
|
require.True(t, evaluate(`
|
||||||
|
["not",
|
||||||
|
["and", [
|
||||||
|
["==", ".name", "Katie"],
|
||||||
|
["==", ".nationalities", ["American"]]
|
||||||
|
]]
|
||||||
|
]
|
||||||
|
`, data))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("All", func(t *testing.T) {
|
||||||
|
data := makeNode(`{"a": [{"b": 1}, {"b": 2}, {"z": [7, 8, 9]}]}`)
|
||||||
|
|
||||||
|
require.False(t, evaluate(`["all", ".a", [">", ".b", 0]]`, data))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Any", func(t *testing.T) {
|
||||||
|
data := makeNode(`{"a": [{"b": 1}, {"b": 2}, {"z": [7, 8, 9]}]}`)
|
||||||
|
|
||||||
|
require.True(t, evaluate(`["any", ".a", ["==", ".b", 2]]`, data))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user