policy: more tests and fixes

This commit is contained in:
Michael Muré
2024-09-04 19:08:21 +02:00
parent d042b5cdfd
commit f731f56dd2
5 changed files with 99 additions and 26 deletions

View File

@@ -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)
}

View File

@@ -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]

View File

@@ -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 {

View File

@@ -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

View File

@@ -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))
})
}