Add semantic paths and safe diagnostics
This commit is contained in:
88
path_test.go
Normal file
88
path_test.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package configkit
|
||||
|
||||
import "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)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user