snapshot of https://github.com/ory/keto.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
CI / Run lints and checks (push) Failing after 47s
CI / Run tests (push) Failing after 1s
CI / Run race tests (push) Failing after 5s
CI / Build CLI docs (push) Has been skipped
CI / Run buf actions (push) Failing after 5s
CodeQL / Analyze (go) (push) Failing after 3s
CodeQL / Analyze (javascript) (push) Failing after 2s
Docker Image Scanners / scanners (push) Failing after 3s
Format / format (push) Failing after 3s
Synchronize Issue Labels / Synchronize Issue Labels (push) Has been skipped
CI / Generate release (push) Has been skipped
CI / Draft newsletter (push) Has been skipped
CI / Pending approval Slack notification (push) Has been skipped
CI / Send newsletter (push) Has been skipped
Some checks failed
CI / Run lints and checks (push) Failing after 47s
CI / Run tests (push) Failing after 1s
CI / Run race tests (push) Failing after 5s
CI / Build CLI docs (push) Has been skipped
CI / Run buf actions (push) Failing after 5s
CodeQL / Analyze (go) (push) Failing after 3s
CodeQL / Analyze (javascript) (push) Failing after 2s
Docker Image Scanners / scanners (push) Failing after 3s
Format / format (push) Failing after 3s
Synchronize Issue Labels / Synchronize Issue Labels (push) Has been skipped
CI / Generate release (push) Has been skipped
CI / Draft newsletter (push) Has been skipped
CI / Pending approval Slack notification (push) Has been skipped
CI / Send newsletter (push) Has been skipped
This commit is contained in:
413
ketoapi/enc_test.go
Normal file
413
ketoapi/enc_test.go
Normal file
@@ -0,0 +1,413 @@
|
||||
// Copyright © 2023 Ory Corp
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package ketoapi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
rts "github.com/ory/keto/gen/go/ory/keto/relation_tuples/v1alpha2"
|
||||
)
|
||||
|
||||
func TestRelationTuple(t *testing.T) {
|
||||
t.Run("method=string encoding", func(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
tuple *RelationTuple
|
||||
expected string
|
||||
}{
|
||||
{ // full tuple
|
||||
tuple: &RelationTuple{
|
||||
Namespace: "n",
|
||||
Object: "o",
|
||||
Relation: "r",
|
||||
SubjectID: new("s"),
|
||||
},
|
||||
expected: "n:o#r@s",
|
||||
},
|
||||
{ // skip '#' in subject set when relation is empty
|
||||
tuple: &RelationTuple{
|
||||
Namespace: "groups",
|
||||
Object: "dev",
|
||||
Relation: "member",
|
||||
SubjectSet: &SubjectSet{
|
||||
Namespace: "users",
|
||||
Object: "user",
|
||||
},
|
||||
},
|
||||
expected: "groups:dev#member@users:user",
|
||||
},
|
||||
} {
|
||||
t.Run("case="+tc.expected, func(t *testing.T) {
|
||||
assert.Equal(t, tc.expected, tc.tuple.String())
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("method=string decoding", func(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
enc string
|
||||
err error
|
||||
expected *RelationTuple
|
||||
}{
|
||||
{
|
||||
enc: "n:o#r@s",
|
||||
expected: &RelationTuple{
|
||||
Namespace: "n",
|
||||
Object: "o",
|
||||
Relation: "r",
|
||||
SubjectID: new("s"),
|
||||
},
|
||||
},
|
||||
{
|
||||
enc: "n:o#r@n:o#r",
|
||||
expected: &RelationTuple{
|
||||
Namespace: "n",
|
||||
Object: "o",
|
||||
Relation: "r",
|
||||
SubjectSet: &SubjectSet{
|
||||
Namespace: "n",
|
||||
Object: "o",
|
||||
Relation: "r",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
enc: "n:o#r@n:o#...",
|
||||
expected: &RelationTuple{
|
||||
Namespace: "n",
|
||||
Object: "o",
|
||||
Relation: "r",
|
||||
SubjectSet: &SubjectSet{
|
||||
Namespace: "n",
|
||||
Object: "o",
|
||||
Relation: "...",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
enc: "n:o#r@n:o#",
|
||||
expected: &RelationTuple{
|
||||
Namespace: "n",
|
||||
Object: "o",
|
||||
Relation: "r",
|
||||
SubjectSet: &SubjectSet{
|
||||
Namespace: "n",
|
||||
Object: "o",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
enc: "n:o#r@n:o",
|
||||
expected: &RelationTuple{
|
||||
Namespace: "n",
|
||||
Object: "o",
|
||||
Relation: "r",
|
||||
SubjectSet: &SubjectSet{
|
||||
Namespace: "n",
|
||||
Object: "o",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
enc: "n:o#r@(n:o#r)",
|
||||
expected: &RelationTuple{
|
||||
Namespace: "n",
|
||||
Object: "o",
|
||||
Relation: "r",
|
||||
SubjectSet: &SubjectSet{
|
||||
Namespace: "n",
|
||||
Object: "o",
|
||||
Relation: "r",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
enc: "#dev:@ory#:working:@projects:keto#awesome",
|
||||
expected: &RelationTuple{
|
||||
Namespace: "#dev",
|
||||
Object: "@ory",
|
||||
Relation: ":working:",
|
||||
SubjectSet: &SubjectSet{
|
||||
Namespace: "projects",
|
||||
Object: "keto",
|
||||
Relation: "awesome",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
enc: "no-colon#in@this",
|
||||
err: ErrMalformedInput(),
|
||||
},
|
||||
{
|
||||
enc: "no:hash-in@this",
|
||||
err: ErrMalformedInput(),
|
||||
},
|
||||
{
|
||||
enc: "no:at#in-this",
|
||||
err: ErrMalformedInput(),
|
||||
},
|
||||
} {
|
||||
t.Run(fmt.Sprintf("string=%s", tc.enc), func(t *testing.T) {
|
||||
actual, err := (&RelationTuple{}).FromString(tc.enc)
|
||||
assert.True(t, errors.Is(err, tc.err), "%+v", err)
|
||||
assert.Equal(t, tc.expected, actual)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("case=url encoding-decoding", func(t *testing.T) {
|
||||
for i, r := range []*RelationTuple{
|
||||
{
|
||||
Namespace: "n",
|
||||
Object: "o",
|
||||
Relation: "r",
|
||||
SubjectID: new("s"),
|
||||
},
|
||||
{
|
||||
Namespace: "n",
|
||||
Object: "o",
|
||||
Relation: "r",
|
||||
SubjectSet: &SubjectSet{
|
||||
Namespace: "sn",
|
||||
Object: "so",
|
||||
Relation: "sr",
|
||||
},
|
||||
},
|
||||
{
|
||||
SubjectID: new(""),
|
||||
},
|
||||
} {
|
||||
t.Run(fmt.Sprintf("case=%d", i), func(t *testing.T) {
|
||||
vals := r.ToURLQuery()
|
||||
res, err := (&RelationTuple{}).FromURLQuery(vals)
|
||||
require.NoError(t, err, "raw: %+v, enc: %+v", r, vals)
|
||||
assert.Equal(t, r, res)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("case=url decoding-encoding", func(t *testing.T) {
|
||||
for i, v := range []url.Values{
|
||||
{
|
||||
"namespace": []string{"n"},
|
||||
"object": []string{"o"},
|
||||
"relation": []string{"r"},
|
||||
"subject_id": []string{"foo"},
|
||||
},
|
||||
{
|
||||
"namespace": []string{"n"},
|
||||
"object": []string{"o"},
|
||||
"relation": []string{"r"},
|
||||
"subject_set.namespace": []string{"sn"},
|
||||
"subject_set.object": []string{"so"},
|
||||
"subject_set.relation": []string{"sr"},
|
||||
},
|
||||
} {
|
||||
t.Run(fmt.Sprintf("case=%d", i), func(t *testing.T) {
|
||||
rt, err := (&RelationTuple{}).FromURLQuery(v)
|
||||
require.NoError(t, err)
|
||||
q := rt.ToURLQuery()
|
||||
assert.Equal(t, v, q)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("case=proto decoding", func(t *testing.T) {
|
||||
for i, tc := range []struct {
|
||||
proto TupleData
|
||||
expected *RelationTuple
|
||||
err error
|
||||
}{
|
||||
{
|
||||
proto: &rts.RelationTuple{
|
||||
Namespace: "n",
|
||||
Object: "o",
|
||||
Relation: "r",
|
||||
Subject: nil,
|
||||
},
|
||||
err: ErrNilSubject(),
|
||||
},
|
||||
{
|
||||
proto: &rts.RelationTuple{
|
||||
Namespace: "n",
|
||||
Object: "o",
|
||||
Relation: "r",
|
||||
Subject: &rts.Subject{
|
||||
Ref: &rts.Subject_Set{
|
||||
Set: &rts.SubjectSet{
|
||||
Namespace: "n",
|
||||
Object: "o",
|
||||
Relation: "r",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &RelationTuple{
|
||||
Namespace: "n",
|
||||
Object: "o",
|
||||
Relation: "r",
|
||||
SubjectSet: &SubjectSet{
|
||||
Namespace: "n",
|
||||
Object: "o",
|
||||
Relation: "r",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
proto: &rts.RelationTuple{
|
||||
Namespace: "n",
|
||||
Object: "o",
|
||||
Relation: "r",
|
||||
Subject: &rts.Subject{
|
||||
Ref: &rts.Subject_Id{
|
||||
Id: "user",
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &RelationTuple{
|
||||
Namespace: "n",
|
||||
Object: "o",
|
||||
Relation: "r",
|
||||
SubjectID: new("user"),
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(fmt.Sprintf("case=%d", i), func(t *testing.T) {
|
||||
actual, err := (&RelationTuple{}).FromDataProvider(tc.proto)
|
||||
require.ErrorIs(t, err, tc.err)
|
||||
assert.Equal(t, tc.expected, actual)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("format=JSON", func(t *testing.T) {
|
||||
t.Run("direction=encoding-decoding", func(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
rt *RelationTuple
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "with subject ID",
|
||||
rt: &RelationTuple{
|
||||
Namespace: "n",
|
||||
Object: "o",
|
||||
Relation: "r",
|
||||
SubjectID: new("s"),
|
||||
},
|
||||
expected: `
|
||||
{
|
||||
"namespace": "n",
|
||||
"object": "o",
|
||||
"relation": "r",
|
||||
"subject_id": "s"
|
||||
}`,
|
||||
},
|
||||
{
|
||||
name: "with subject set",
|
||||
rt: &RelationTuple{
|
||||
Namespace: "n",
|
||||
Object: "o",
|
||||
Relation: "r",
|
||||
SubjectSet: &SubjectSet{
|
||||
Namespace: "sn",
|
||||
Object: "so",
|
||||
Relation: "sr",
|
||||
},
|
||||
},
|
||||
expected: `
|
||||
{
|
||||
"namespace": "n",
|
||||
"object": "o",
|
||||
"relation": "r",
|
||||
"subject_set": {
|
||||
"namespace": "sn",
|
||||
"object": "so",
|
||||
"relation": "sr"
|
||||
}
|
||||
}`,
|
||||
},
|
||||
} {
|
||||
t.Run("case="+tc.name, func(t *testing.T) {
|
||||
raw, err := json.Marshal(tc.rt)
|
||||
require.NoError(t, err)
|
||||
assert.JSONEq(t, tc.expected, string(raw))
|
||||
|
||||
var dec RelationTuple
|
||||
require.NoError(t, json.Unmarshal(raw, &dec))
|
||||
assert.Equal(t, tc.rt, &dec)
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestRelationQuery(t *testing.T) {
|
||||
t.Run("case=url encoding-decoding-encoding", func(t *testing.T) {
|
||||
for i, tc := range []struct {
|
||||
v url.Values
|
||||
r *RelationQuery
|
||||
}{
|
||||
{
|
||||
v: url.Values{
|
||||
"namespace": []string{"n"},
|
||||
"object": []string{"o"},
|
||||
"relation": []string{"r"},
|
||||
"subject_id": []string{"foo"},
|
||||
},
|
||||
r: &RelationQuery{
|
||||
Namespace: new("n"),
|
||||
Object: new("o"),
|
||||
Relation: new("r"),
|
||||
SubjectID: new("foo"),
|
||||
},
|
||||
},
|
||||
{
|
||||
v: url.Values{
|
||||
"namespace": []string{"n"},
|
||||
"object": []string{"o"},
|
||||
"relation": []string{"r"},
|
||||
"subject_set.namespace": []string{"sn"},
|
||||
"subject_set.object": []string{"so"},
|
||||
"subject_set.relation": []string{"sr"},
|
||||
},
|
||||
r: &RelationQuery{
|
||||
Namespace: new("n"),
|
||||
Object: new("o"),
|
||||
Relation: new("r"),
|
||||
SubjectSet: &SubjectSet{
|
||||
Namespace: "sn",
|
||||
Object: "so",
|
||||
Relation: "sr",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
v: url.Values{
|
||||
"namespace": []string{"n"},
|
||||
"relation": []string{"r"},
|
||||
},
|
||||
r: &RelationQuery{
|
||||
Namespace: new("n"),
|
||||
Relation: new("r"),
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(fmt.Sprintf("case=%d", i), func(t *testing.T) {
|
||||
enc := tc.r.ToURLQuery()
|
||||
assert.Equal(t, tc.v, enc)
|
||||
|
||||
dec, err := (&RelationQuery{}).FromURLQuery(tc.v)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tc.r, dec)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user