Files
upstream-ory-keto/internal/persistence/sql/traverser_test.go
upstream-mirror 9456a4e922
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
snapshot of https://github.com/ory/keto.git (master, 2026-09-16; blobs > 131072B omitted)
2026-09-16 01:08:34 +05:00

143 lines
5.0 KiB
Go

// Copyright © 2026 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package sql
import (
"testing"
"github.com/gofrs/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/ory/keto/internal/relationtuple"
)
// existsFor returns the EXISTS subquery text that buildFoundExpr produces for the given subject.
func existsFor(t *testing.T, subject relationtuple.Subject) string {
t.Helper()
subjectSQL, _, err := whereSubject(subject)
require.NoError(t, err)
return `EXISTS(
SELECT 1 FROM keto_relation_tuples
WHERE nid = current.nid AND
namespace = current.subject_set_namespace AND
object = current.subject_set_object AND
relation = current.subject_set_relation AND
` + subjectSQL + `
)`
}
func TestBuildFoundExpr(t *testing.T) {
t.Parallel()
t.Run("case=subject as SubjectID", func(t *testing.T) {
subject := &relationtuple.SubjectID{ID: uuid.Must(uuid.NewV4())}
existsExpr := existsFor(t, subject)
// when SubjectID is used, no `allowedSubjectSets` is expected
t.Run("no allowed types: EXISTS runs for all rows", func(t *testing.T) {
expr, args, err := buildFoundExpr(nil, subject)
require.NoError(t, err)
assert.Equal(t, existsExpr, expr)
assert.Equal(t, []any{subject.ID}, args)
})
})
t.Run("case=subject as SubjectSet", func(t *testing.T) {
subject := &relationtuple.SubjectSet{
Namespace: "User",
Object: uuid.Must(uuid.NewV4()),
}
existsExpr := existsFor(t, subject)
t.Run("no allowed types: EXISTS runs for all rows", func(t *testing.T) {
expr, args, err := buildFoundExpr(nil, subject)
require.NoError(t, err)
assert.Equal(t, existsExpr, expr)
assert.Equal(t, []any{subject.Namespace, subject.Object, subject.Relation}, args)
})
// All types have AllowsDirect=false: OPL forbids direct membership for every type,
// so the found column is always false without touching the inner table.
t.Run("all AllowsDirect=false: found is always false", func(t *testing.T) {
types := []relationtuple.SubjectSetType{
{Namespace: "Group", Relation: "member", AllowsDirect: false},
{Namespace: "Org", Relation: "admin", AllowsDirect: false},
}
expr, args, err := buildFoundExpr(types, subject)
require.NoError(t, err)
assert.Equal(t, "false", expr)
assert.Nil(t, args)
})
// All types allow direct: the WHERE clause already restricts rows to those types,
// so EXISTS runs unconditionally — no extra condition needed.
t.Run("all AllowsDirect=true: plain EXISTS, no extra conditions ", func(t *testing.T) {
types := []relationtuple.SubjectSetType{
{Namespace: "Group", Relation: "member", AllowsDirect: true},
{Namespace: "Org", Relation: "admin", AllowsDirect: true},
}
expr, args, err := buildFoundExpr(types, subject)
require.NoError(t, err)
assert.Equal(t, existsExpr, expr)
assert.Equal(t, []any{subject.Namespace, subject.Object, subject.Relation}, args)
})
// Mixed: EXISTS is gated to fire only for the AllowsDirect=true type;
// the false type is excluded from the gate even though it passes the WHERE filter.
t.Run("mixed AllowsDirect: EXISTS gated only on allowed type", func(t *testing.T) {
types := []relationtuple.SubjectSetType{
{Namespace: "Group", Relation: "member", AllowsDirect: true},
{Namespace: "Org", Relation: "admin", AllowsDirect: false},
}
expr, args, err := buildFoundExpr(types, subject)
require.NoError(t, err)
assert.Equal(t,
`(`+existsExpr+` AND (current.subject_set_namespace, current.subject_set_relation) IN ((?, ?)))`,
expr,
)
assert.Equal(t, []any{subject.Namespace, subject.Object, subject.Relation, "Group", "member"}, args)
})
t.Run("mixed AllowsDirect: multiple allowed types with not-allowed type", func(t *testing.T) {
types := []relationtuple.SubjectSetType{
{Namespace: "Group", Relation: "member", AllowsDirect: true},
{Namespace: "Org", Relation: "admin", AllowsDirect: true},
{Namespace: "Team", Relation: "lead", AllowsDirect: false},
}
expr, args, err := buildFoundExpr(types, subject)
require.NoError(t, err)
assert.Equal(t,
`(`+existsExpr+` AND (current.subject_set_namespace, current.subject_set_relation) IN ((?, ?), (?, ?)))`,
expr,
)
assert.Equal(t, []any{subject.Namespace, subject.Object, subject.Relation, "Group", "member", "Org", "admin"}, args)
})
})
}
func TestBuildSubjectSetTypeSQL(t *testing.T) {
t.Parallel()
t.Run("no types: no filter", func(t *testing.T) {
sqlFragment, args := buildSubjectSetTypeSQL(nil)
assert.Empty(t, sqlFragment)
assert.Nil(t, args)
})
t.Run("multiple types share one IN list", func(t *testing.T) {
sqlFragment, args := buildSubjectSetTypeSQL([]relationtuple.SubjectSetType{
{Namespace: "Group", Relation: "member"},
{Namespace: "Org", Relation: "admin"},
})
assert.Equal(t, "\nAND (current.subject_set_namespace, current.subject_set_relation) IN ((?, ?), (?, ?))", sqlFragment)
assert.Equal(t, []any{"Group", "member", "Org", "admin"}, args)
})
}