Files
upstream-ory-keto/internal/expand/testhelper.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

128 lines
3.7 KiB
Go

// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package expand
import (
"fmt"
"slices"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/ory/keto/internal/relationtuple"
"github.com/ory/keto/ketoapi"
)
func AssertExternalTreesAreEqual(t *testing.T, expected, actual *ketoapi.Tree[*ketoapi.RelationTuple]) {
t.Helper()
assert.Truef(t, treesAreEqual(t, expected, actual),
"expected:\n%+v\n\nactual:\n%+v", expected, actual)
}
// TODO(hperl): Refactor to generic tree equality helper.
func treesAreEqual(t *testing.T, expected, actual *ketoapi.Tree[*ketoapi.RelationTuple]) bool {
if expected == nil || actual == nil {
return expected == actual
}
if expected.Type != actual.Type {
t.Logf("expected type %q, actual type %q", expected.Type, actual.Type)
return false
}
if !assert.ObjectsAreEqual(expected.Tuple.SubjectID, actual.Tuple.SubjectID) || !assert.ObjectsAreEqual(expected.Tuple.SubjectSet, actual.Tuple.SubjectSet) {
t.Logf("expected subject: %+v %+v, actual subject: %+v %+v", expected.Tuple.SubjectID, expected.Tuple.SubjectSet, actual.Tuple.SubjectID, actual.Tuple.SubjectSet)
return false
}
if len(expected.Children) != len(actual.Children) {
t.Logf("expected len(children)=%d, actual len(children)=%d", len(expected.Children), len(actual.Children))
return false
}
// For children, we check for equality disregarding the order
outer:
for _, expectedChild := range expected.Children {
for _, actualChild := range actual.Children {
if treesAreEqual(t, expectedChild, actualChild) {
continue outer
}
}
t.Logf("expected child:\n%+v\n\nactual child:\n%+v", expectedChild, actual)
return false
}
return true
}
// formatTree returns a human-readable, indented representation of a tree.
func formatTree(tree *relationtuple.Tree, indent string) string {
if tree == nil {
return indent + "<nil>\n"
}
var b strings.Builder
fmt.Fprintf(&b, "%s(%s) %+v", indent, tree.Type, tree.Subject)
if e := tree.Truncation; e != nil {
fmt.Fprintf(&b, " [truncation: reason=%s cursor=%s]", e.Reason, formatExpandCursor(e.Cursor))
}
b.WriteByte('\n')
for _, child := range tree.Children {
b.WriteString(formatTree(child, indent+" "))
}
return b.String()
}
func formatExpandCursor(c *relationtuple.ExpandCursor) string {
if c == nil {
return "<nil>"
}
var parts []string
parts = append(parts, "kind="+string(c.Kind))
parts = append(parts, "subject_set="+c.SubjectSet.String())
if c.TraverseRelation != nil {
parts = append(parts, "traverseRel="+*c.TraverseRelation)
}
return "{" + strings.Join(parts, " ") + "}"
}
// sortTree recursively sorts the children of a tree by subject string, in-place.
func sortTree(tree *relationtuple.Tree) {
if tree == nil {
return
}
for _, child := range tree.Children {
sortTree(child)
}
if tree.Children == nil {
return
}
slices.SortFunc(tree.Children, func(a, b *relationtuple.Tree) int {
if a.Subject == nil && b.Subject == nil {
return 0
}
if a.Subject == nil {
return 1
}
if b.Subject == nil {
return -1
}
return strings.Compare(a.Subject.String(), b.Subject.String())
})
}
// AssertInternalTreesAreEqual compares two trees for equality, ignoring child order.
func AssertInternalTreesAreEqual(t *testing.T, expected, actual *relationtuple.Tree) bool {
t.Helper()
sortTree(expected)
sortTree(actual)
return assert.Equal(t, formatTree(expected, ""), formatTree(actual, ""))
}
// RequireInternalTreesAreEqual compares two trees for equality, ignoring child order.
func RequireInternalTreesAreEqual(t *testing.T, expected, actual *relationtuple.Tree) {
t.Helper()
sortTree(expected)
sortTree(actual)
require.Equal(t, formatTree(expected, ""), formatTree(actual, ""))
}