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
233 lines
5.7 KiB
Go
233 lines
5.7 KiB
Go
// Copyright © 2023 Ory Corp
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package e2e
|
|
|
|
import (
|
|
"cmp"
|
|
"slices"
|
|
"strconv"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/ory/x/uuidx"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/ory/keto/internal/namespace"
|
|
"github.com/ory/keto/ketoapi"
|
|
)
|
|
|
|
func runTransactionCases(c transactClient, m *namespaceTestManager) func(*testing.T) {
|
|
return func(t *testing.T) {
|
|
t.Parallel()
|
|
t.Run("case=create and delete", func(t *testing.T) {
|
|
n := &namespace.Namespace{Name: t.Name()}
|
|
m.add(t, n)
|
|
|
|
tuples := []*ketoapi.RelationTuple{
|
|
{
|
|
Namespace: n.Name,
|
|
Object: "o",
|
|
Relation: "rel",
|
|
SubjectSet: &ketoapi.SubjectSet{
|
|
Namespace: n.Name,
|
|
Object: "o",
|
|
Relation: "rel",
|
|
},
|
|
},
|
|
{
|
|
Namespace: n.Name,
|
|
Object: "o",
|
|
Relation: "rel",
|
|
SubjectID: new("sid"),
|
|
},
|
|
}
|
|
|
|
c.transactTuples(t, tuples, nil)
|
|
|
|
resp := c.queryTuple(t, &ketoapi.RelationQuery{
|
|
Namespace: &n.Name,
|
|
})
|
|
for i := range tuples {
|
|
assert.Contains(t, resp.RelationTuples, tuples[i])
|
|
}
|
|
|
|
c.transactTuples(t, nil, tuples)
|
|
|
|
resp = c.queryTuple(t, &ketoapi.RelationQuery{
|
|
Namespace: &n.Name,
|
|
})
|
|
assert.Len(t, resp.RelationTuples, 0)
|
|
})
|
|
|
|
t.Run("case=duplicate string representations", func(t *testing.T) {
|
|
n := &namespace.Namespace{Name: t.Name()}
|
|
m.add(t, n)
|
|
c.transactTuples(t, []*ketoapi.RelationTuple{
|
|
{
|
|
Namespace: n.Name,
|
|
Object: "o",
|
|
Relation: "rel",
|
|
SubjectID: new("sid"),
|
|
},
|
|
{
|
|
Namespace: n.Name,
|
|
Object: "o",
|
|
Relation: "rel",
|
|
SubjectID: new("sid"),
|
|
},
|
|
}, nil)
|
|
})
|
|
|
|
t.Run("case=large inserts and deletes", func(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("This test is fairly expensive, especially the deletion.")
|
|
}
|
|
|
|
ns := []*namespace.Namespace{
|
|
{Name: uuidx.NewV4().String()},
|
|
{Name: uuidx.NewV4().String()},
|
|
}
|
|
m.add(t, ns...)
|
|
|
|
var tuples []*ketoapi.RelationTuple
|
|
for i := range 1200 {
|
|
tuples = append(tuples,
|
|
&ketoapi.RelationTuple{
|
|
Namespace: ns[0].Name,
|
|
Object: "o" + strconv.Itoa(i),
|
|
Relation: "rela",
|
|
SubjectSet: &ketoapi.SubjectSet{
|
|
Namespace: ns[1].Name,
|
|
Object: "o" + strconv.Itoa(i),
|
|
Relation: "relx",
|
|
},
|
|
},
|
|
&ketoapi.RelationTuple{
|
|
Namespace: ns[0].Name,
|
|
Object: "o" + strconv.Itoa(i),
|
|
Relation: "relb",
|
|
SubjectID: new("sid"),
|
|
},
|
|
)
|
|
}
|
|
|
|
t0 := time.Now()
|
|
c.transactTuples(t, tuples, nil)
|
|
t.Logf("insert took %s", time.Since(t0))
|
|
|
|
t0 = time.Now()
|
|
var resp []*ketoapi.RelationTuple
|
|
var pt string
|
|
for {
|
|
r := c.queryTuple(t, &ketoapi.RelationQuery{
|
|
Namespace: &ns[0].Name,
|
|
}, withSize(1000), withToken(pt))
|
|
resp = append(resp, r.RelationTuples...)
|
|
pt = r.NextPageToken
|
|
if pt == "" {
|
|
break
|
|
}
|
|
}
|
|
t.Logf("query took %s", time.Since(t0))
|
|
|
|
sort := func(a, b *ketoapi.RelationTuple) int {
|
|
return cmp.Or(
|
|
cmp.Compare(a.Namespace, b.Namespace),
|
|
cmp.Compare(a.Object, b.Object),
|
|
cmp.Compare(a.Relation, b.Relation),
|
|
)
|
|
}
|
|
slices.SortFunc(resp, sort)
|
|
slices.SortFunc(tuples, sort)
|
|
|
|
require.Equal(t, tuples, resp)
|
|
|
|
t0 = time.Now()
|
|
c.transactTuples(t, nil, tuples)
|
|
t.Logf("delete took %s", time.Since(t0))
|
|
|
|
resp = c.queryTuple(t, &ketoapi.RelationQuery{
|
|
Namespace: &ns[0].Name,
|
|
}).RelationTuples
|
|
assert.Len(t, resp, 0)
|
|
})
|
|
|
|
t.Run("case=expand-api-display-access docs code sample", func(t *testing.T) {
|
|
files := &namespace.Namespace{Name: t.Name() + "files"}
|
|
directories := &namespace.Namespace{Name: t.Name() + "directories"}
|
|
m.add(t, files, directories)
|
|
|
|
tuples := []*ketoapi.RelationTuple{
|
|
{
|
|
Namespace: directories.Name,
|
|
Object: "/photos",
|
|
Relation: "owner",
|
|
SubjectID: new("maureen"),
|
|
},
|
|
{
|
|
Namespace: files.Name,
|
|
Object: "/photos/beach.jpg",
|
|
Relation: "owner",
|
|
SubjectID: new("maureen"),
|
|
},
|
|
{
|
|
Namespace: files.Name,
|
|
Object: "/photos/mountains.jpg",
|
|
Relation: "owner",
|
|
SubjectID: new("laura"),
|
|
},
|
|
{
|
|
Namespace: directories.Name,
|
|
Object: "/photos",
|
|
Relation: "access",
|
|
SubjectID: new("laura"),
|
|
},
|
|
}
|
|
for _, o := range []struct{ n, o string }{
|
|
{files.Name, "/photos/beach.jpg"},
|
|
{files.Name, "/photos/mountains.jpg"},
|
|
{directories.Name, "/photos"},
|
|
} {
|
|
tuples = append(tuples, &ketoapi.RelationTuple{
|
|
Namespace: o.n,
|
|
Object: o.o,
|
|
Relation: "access",
|
|
SubjectSet: &ketoapi.SubjectSet{
|
|
Namespace: o.n,
|
|
Object: o.o,
|
|
Relation: "owner",
|
|
},
|
|
})
|
|
}
|
|
for _, obj := range []string{"/photos/beach.jpg", "/photos/mountains.jpg"} {
|
|
tuples = append(tuples, &ketoapi.RelationTuple{
|
|
Namespace: files.Name,
|
|
Object: obj,
|
|
Relation: "access",
|
|
SubjectSet: &ketoapi.SubjectSet{
|
|
Namespace: directories.Name,
|
|
Object: "/photos",
|
|
Relation: "access",
|
|
},
|
|
})
|
|
}
|
|
|
|
c.transactTuples(t, tuples, nil)
|
|
|
|
fileTuples := c.queryTuple(t, &ketoapi.RelationQuery{Namespace: new(files.Name)})
|
|
directoryTuples := c.queryTuple(t, &ketoapi.RelationQuery{Namespace: new(directories.Name)})
|
|
allTuples := append(fileTuples.RelationTuples, directoryTuples.RelationTuples...)
|
|
assert.ElementsMatch(t, allTuples, tuples)
|
|
|
|
c.transactTuples(t, nil, tuples)
|
|
|
|
fileTuples = c.queryTuple(t, &ketoapi.RelationQuery{Namespace: new(files.Name)})
|
|
directoryTuples = c.queryTuple(t, &ketoapi.RelationQuery{Namespace: new(directories.Name)})
|
|
allTuples = append(fileTuples.RelationTuples, directoryTuples.RelationTuples...)
|
|
assert.Len(t, allTuples, 0)
|
|
})
|
|
}
|
|
}
|