package engine import ( "strconv" "testing" ) func TestPathRoundTripsSemanticKeys(t *testing.T) { t.Parallel() tests := []struct { name string pointer string }{ {name: "root", pointer: ""}, {name: "slash and tilde", pointer: "/a~1b/c~0d"}, {name: "empty key", pointer: "/"}, {name: "dotted key", pointer: "/outputs/a.b"}, {name: "numeric key", pointer: "/outputs/0"}, {name: "dot keys", pointer: "/./.."}, {name: "percent text", pointer: "/%2F/%7E"}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { parsed, err := ParsePath(test.pointer) if err != nil { t.Fatalf("ParsePath() error = %v", err) } if got := parsed.String(); got != test.pointer { t.Fatalf("String() = %q, want %q", got, test.pointer) } }) } if got := Keys("a/b", "c~d", "", "..").String(); got != "/a~1b/c~0d//.." { t.Fatalf("Keys().String() = %q", got) } if got := Keys("items").Index(12).Key("name").String(); got != "/items/12/name" { t.Fatalf("indexed path = %q", got) } maximum := int(^uint(0) >> 1) for _, index := range []int{100, maximum, -1, -maximum - 1} { want := "/items/" + strconv.Itoa(index) if got := Keys("items").Index(index).String(); got != want { t.Fatalf("indexed path = %q, want %q", got, want) } } } func TestPathContainmentMatchesPointerSpelling(t *testing.T) { parse := func(pointer string) Path { path, err := ParsePath(pointer) if err != nil { t.Fatalf("ParsePath(%q) error = %v", pointer, err) } return path } maximum := int(^uint(0) >> 1) minimum := -maximum - 1 paths := []Path{ {}, Keys(""), Keys("a"), Keys("ab"), Keys("a", "b"), Keys("a/b"), Keys("a~1b"), Keys("~"), Keys("0"), Keys("01"), Keys("+1"), Keys("items").Index(0), Keys("items").Index(maximum), Keys("items").Index(minimum), parse("/"), parse("/a~1b"), parse("/a~01b"), parse("/~0"), parse("/0"), parse("/01"), parse("/+1"), parse("/items/0"), parse("/items/" + strconv.Itoa(maximum)), parse("/items/" + strconv.Itoa(minimum)), Path{}.Index(-1), } for _, container := range paths { for _, descendant := range paths { got := pathContains(container, descendant) want := pathContainsByPointer(container, descendant) if got != want { t.Errorf("pathContains(%q, %q) = %v, want %v", container.String(), descendant.String(), got, want) } } } } func TestAppendUniquePathMatchesPointerSpelling(t *testing.T) { parse := func(pointer string) Path { path, err := ParsePath(pointer) if err != nil { t.Fatalf("ParsePath(%q) error = %v", pointer, err) } return path } paths := []Path{ Keys("a/b"), parse("/a~1b"), Keys("a~1b"), parse("/a~01b"), Keys("items").Index(0), parse("/items/0"), Keys("items", "0"), Keys("items", "01"), parse("/items/01"), Path{}.Index(-1), Keys("-1"), Keys("a", "b"), Keys("a/b"), } var got, want []Path for _, path := range paths { got = appendUniquePath(got, path) want = appendUniquePathByPointer(want, path) } if len(got) != len(want) { t.Fatalf("unique path counts = %d, want %d", len(got), len(want)) } for i := range got { if got[i].String() != want[i].String() { t.Fatalf("unique path %d = %q, want %q", i, got[i].String(), want[i].String()) } } } func pathContainsByPointer(container, descendant Path) bool { if !container.valid() || !descendant.valid() || len(container.segments) > len(descendant.segments) { return false } prefix := container.String() if prefix == "" { return true } value := descendant.String() return value == prefix || len(value) > len(prefix) && value[:len(prefix)] == prefix && value[len(prefix)] == '/' } func appendUniquePathByPointer(paths []Path, path Path) []Path { for _, existing := range paths { if existing.String() == path.String() { return paths } } return append(paths, path.clone()) } func TestPathPreservesSegmentIntentAndImmutability(t *testing.T) { t.Parallel() key := Keys("items", "0") index := Keys("items").Index(0) parsed, err := ParsePath("/items/0") if err != nil { t.Fatal(err) } if key.String() != index.String() || index.String() != parsed.String() { t.Fatal("equivalent textual paths should share a JSON Pointer spelling") } if key.segments[1].kind != pathSegmentKey { t.Fatal("Keys numeric segment lost mapping-key intent") } if index.segments[1].kind != pathSegmentIndex { t.Fatal("Index segment lost sequence intent") } if parsed.segments[1].kind != pathSegmentParsed { t.Fatal("parsed numeric segment was resolved before traversal") } base := Keys("parent") child := base.Key("child") child.segments[0].key = "changed" if got := base.String(); got != "/parent" { t.Fatalf("builder mutated parent: %q", got) } negative := Path{}.Index(-1) if negative.valid() { t.Fatal("negative index must produce an invalid path") } } func TestParsePathRejectsMalformedPointers(t *testing.T) { t.Parallel() for _, pointer := range []string{"relative", "~0", "/bad~", "/bad~2", "/bad~01~"} { if _, err := ParsePath(pointer); err == nil { t.Errorf("ParsePath(%q) succeeded", pointer) } } if parsed, err := ParsePath("/missing"); err != nil || !parsed.valid() { t.Fatalf("valid missing path rejected: path=%v error=%v", parsed, err) } }