test(selector): add tests for "Supported Forms"
This commit is contained in:
163
selector/supported.json
Normal file
163
selector/supported.json
Normal file
@@ -0,0 +1,163 @@
|
||||
{
|
||||
"pass": [
|
||||
{
|
||||
"name": "Identity",
|
||||
"selector": ".",
|
||||
"input": "{\"x\":1}",
|
||||
"output": "{\"x\":1}"
|
||||
},
|
||||
{
|
||||
"name": "Iterator",
|
||||
"selector": ".[]",
|
||||
"input": "[1, 2]",
|
||||
"output": "[1, 2]"
|
||||
},
|
||||
{
|
||||
"name": "Optional Null Iterator",
|
||||
"selector": ".[]?",
|
||||
"input": "null",
|
||||
"output": "()"
|
||||
},
|
||||
{
|
||||
"name": "Optional Iterator",
|
||||
"selector": ".[][]?",
|
||||
"input": "[[1], 2, [3]]",
|
||||
"output": "[1, 3]"
|
||||
},
|
||||
{
|
||||
"name": "Object Key",
|
||||
"selector": ".x",
|
||||
"input": "{\"x\": 1 }",
|
||||
"output": "1"
|
||||
},
|
||||
{
|
||||
"name": "Quoted Key",
|
||||
"selector": ".[\"x\"]",
|
||||
"input": "{\"x\": 1}",
|
||||
"output": "1"
|
||||
},
|
||||
{
|
||||
"name": "Index",
|
||||
"selector": ".[0]",
|
||||
"input": "[1, 2]",
|
||||
"output": "1"
|
||||
},
|
||||
{
|
||||
"name": "Negative Index",
|
||||
"selector": ".[-1]",
|
||||
"input": "[1, 2]",
|
||||
"output": "2"
|
||||
},
|
||||
{
|
||||
"name": "String Index",
|
||||
"selector": ".[0]",
|
||||
"input": "\"Hi\"",
|
||||
"output": "\"H\""
|
||||
},
|
||||
{
|
||||
"name": "Bytes Index",
|
||||
"selector": ".[0]",
|
||||
"input": "{\"/\":{\"bytes\":\"AAE\"}",
|
||||
"output": "0"
|
||||
},
|
||||
{
|
||||
"name": "Array Slice",
|
||||
"selector": ".[0:2]",
|
||||
"input": "[0, 1, 2]",
|
||||
"output": "[0, 1]"
|
||||
},
|
||||
{
|
||||
"name": "Array Slice",
|
||||
"selector": ".[1:]",
|
||||
"input": "[0, 1, 2]",
|
||||
"output": "[1, 2]"
|
||||
},
|
||||
{
|
||||
"name": "Array Slice",
|
||||
"selector": ".[:2]",
|
||||
"input": "[0, 1, 2]",
|
||||
"output": "[0, 1]"
|
||||
},
|
||||
{
|
||||
"name": "String Slice",
|
||||
"selector": ".[0:2]",
|
||||
"input": "\"hello\"",
|
||||
"output": "\"he\""
|
||||
},
|
||||
{
|
||||
"name": "Bytes Index",
|
||||
"selector": ".[1:]",
|
||||
"input": "{\"/\":{\"bytes\":\"AAEC\"}}",
|
||||
"output": "{\"/\":{\"bytes\":\"AQI\"}}"
|
||||
}
|
||||
],
|
||||
"null": [
|
||||
{
|
||||
"name": "Optional Missing Key",
|
||||
"selector": ".x?",
|
||||
"input": "{}"
|
||||
},
|
||||
{
|
||||
"name": "Optional Null Key",
|
||||
"selector": ".x?",
|
||||
"input": "null"
|
||||
},
|
||||
{
|
||||
"name": "Optional Array Key",
|
||||
"selector": ".x?",
|
||||
"input": "[]"
|
||||
},
|
||||
{
|
||||
"name": "Optional Quoted Key",
|
||||
"selector": ".[\"x\"]?",
|
||||
"input": "{}"
|
||||
},
|
||||
{
|
||||
"name": ".length?",
|
||||
"selector": ".length?",
|
||||
"input": "[1, 2]"
|
||||
},
|
||||
{
|
||||
"name": "Optional Index",
|
||||
"selector": ".[4]?",
|
||||
"input": "[0, 1]"
|
||||
}
|
||||
],
|
||||
"fail": [
|
||||
{
|
||||
"name": "Null Iterator",
|
||||
"selector": ".[]",
|
||||
"input": "null"
|
||||
},
|
||||
{
|
||||
"name": "Nested Iterator",
|
||||
"selector": ".[][]",
|
||||
"input": "[[1], 2, [3]]"
|
||||
},
|
||||
{
|
||||
"name": "Missing Key",
|
||||
"selector": ".x",
|
||||
"input": "{}"
|
||||
},
|
||||
{
|
||||
"name": "Null Key",
|
||||
"selector": ".x",
|
||||
"input": "null"
|
||||
},
|
||||
{
|
||||
"name": "Array Key",
|
||||
"selector": ".x",
|
||||
"input": "[]"
|
||||
},
|
||||
{
|
||||
"name": ".length",
|
||||
"selector": ".length",
|
||||
"input": "[1, 2]"
|
||||
},
|
||||
{
|
||||
"name": "Out of bound Index",
|
||||
"selector": ".[4]",
|
||||
"input": "[0, 1]"
|
||||
}
|
||||
]
|
||||
}
|
||||
187
selector/supported_test.go
Normal file
187
selector/supported_test.go
Normal file
@@ -0,0 +1,187 @@
|
||||
package selector_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/ipld/go-ipld-prime"
|
||||
"github.com/ipld/go-ipld-prime/codec/dagjson"
|
||||
"github.com/ipld/go-ipld-prime/datamodel"
|
||||
basicnode "github.com/ipld/go-ipld-prime/node/basic"
|
||||
"github.com/storacha-network/go-ucanto/core/policy/selector"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/wI2L/jsondiff"
|
||||
)
|
||||
|
||||
//go:embed supported.json
|
||||
var supported []byte
|
||||
|
||||
type Testcase struct {
|
||||
Name string `json:"name"`
|
||||
Selector string `json:"selector"`
|
||||
Input string `json:"input"`
|
||||
}
|
||||
|
||||
func (tc Testcase) Select(t *testing.T) (datamodel.Node, []datamodel.Node, error) {
|
||||
t.Helper()
|
||||
|
||||
sel, err := selector.Parse(tc.Selector)
|
||||
require.NoError(t, err)
|
||||
|
||||
return selector.Select(sel, node(t, tc.Input))
|
||||
}
|
||||
|
||||
type SuccessTestcase struct {
|
||||
Testcase
|
||||
Output *string `json:"output"`
|
||||
}
|
||||
|
||||
func (tc SuccessTestcase) SelectAndCompare(t *testing.T) {
|
||||
t.Helper()
|
||||
|
||||
exp := node(t, *tc.Output)
|
||||
|
||||
node, nodes, err := tc.Select(t)
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, node != nil, len(nodes) > 0) // XOR (only one of node or nodes should be set)
|
||||
|
||||
if node == nil {
|
||||
nb := basicnode.Prototype.List.NewBuilder()
|
||||
la, err := nb.BeginList(int64(len(nodes)))
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, n := range nodes {
|
||||
// TODO: This code is probably not needed if the Select operation properly prunes nil values - e.g.: Optional Iterator
|
||||
if n == nil {
|
||||
n = datamodel.Null
|
||||
}
|
||||
|
||||
require.NoError(t, la.AssembleValue().AssignNode(n))
|
||||
}
|
||||
|
||||
require.NoError(t, la.Finish())
|
||||
|
||||
node = nb.Build()
|
||||
}
|
||||
|
||||
equalIPLD(t, exp, node)
|
||||
}
|
||||
|
||||
type Testcases struct {
|
||||
SuccessTestcases []SuccessTestcase `json:"pass"`
|
||||
NullTestcases []Testcase `json:"null"`
|
||||
ErrorTestcases []Testcase `json:"fail"`
|
||||
}
|
||||
|
||||
// TestSupported Forms runs tests against the Selector according to the
|
||||
// proposed "Supported Forms" presented in this GitHub issue:
|
||||
// https://github.com/ucan-wg/delegation/issues/5#issue-2154766496
|
||||
func TestSupportedForms(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var testcases Testcases
|
||||
|
||||
require.NoError(t, json.Unmarshal(supported, &testcases))
|
||||
|
||||
t.Run("node(s)", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, testcase := range testcases.SuccessTestcases {
|
||||
testcase := testcase
|
||||
|
||||
t.Run(testcase.Name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// TODO: This test case panics during Select, though Parse works - reports
|
||||
// "index out of range [-1]" so a bit of subtraction and some bounds checking
|
||||
// should fix this testcase.
|
||||
if testcase.Name == "Negative Index" {
|
||||
t.Skip()
|
||||
}
|
||||
|
||||
testcase.SelectAndCompare(t)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("null", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, testcase := range testcases.NullTestcases {
|
||||
testcase := testcase
|
||||
|
||||
t.Run(testcase.Name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
node, nodes, err := testcase.Select(t)
|
||||
require.NoError(t, err)
|
||||
// TODO: should Select return a single node which is sometimes a list or null?
|
||||
// require.Equal(t, datamodel.Null, node)
|
||||
assert.Nil(t, node)
|
||||
assert.Empty(t, nodes)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("error", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, testcase := range testcases.ErrorTestcases {
|
||||
testcase := testcase
|
||||
|
||||
t.Run(testcase.Name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
node, nodes, err := testcase.Select(t)
|
||||
require.Error(t, err)
|
||||
assert.Nil(t, node)
|
||||
assert.Empty(t, nodes)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func equalIPLD(t *testing.T, expected datamodel.Node, actual datamodel.Node, msgAndArgs ...interface{}) bool {
|
||||
t.Helper()
|
||||
|
||||
if !assert.ObjectsAreEqual(expected, actual) {
|
||||
exp, act := &bytes.Buffer{}, &bytes.Buffer{}
|
||||
if err := dagjson.Encode(expected, exp); err != nil {
|
||||
return assert.Fail(t, "Failed to encode json for expected IPLD node")
|
||||
}
|
||||
|
||||
if err := dagjson.Encode(actual, act); err != nil {
|
||||
return assert.Fail(t, "Failed to encode JSON for actual IPLD node")
|
||||
}
|
||||
|
||||
diff, err := jsondiff.CompareJSON(act.Bytes(), exp.Bytes())
|
||||
if err != nil {
|
||||
return assert.Fail(t, "Failed to create diff of expected and actual IPLD nodes")
|
||||
}
|
||||
|
||||
return assert.Fail(t, fmt.Sprintf("Not equal: \n"+
|
||||
"expected: %s\n"+
|
||||
"actual: %s\n"+
|
||||
"diff: %s", exp, act, diff), msgAndArgs)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func node(t *testing.T, json string) ipld.Node {
|
||||
t.Helper()
|
||||
|
||||
np := basicnode.Prototype.Any
|
||||
nb := np.NewBuilder()
|
||||
require.NoError(t, dagjson.Decode(nb, strings.NewReader(json)))
|
||||
|
||||
node := nb.Build()
|
||||
require.NotNil(t, node)
|
||||
|
||||
return node
|
||||
}
|
||||
Reference in New Issue
Block a user