2026-10-11 21:48:49 +00:00
|
|
|
package ckconfig
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"reflect"
|
|
|
|
|
"strings"
|
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
|
|
"go.yaml.in/yaml/v4"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func TestDecodeSafeSnapshotPreservesYAMLTypes(t *testing.T) {
|
|
|
|
|
tests := []struct {
|
|
|
|
|
name string
|
|
|
|
|
text string
|
|
|
|
|
kind yaml.Kind
|
|
|
|
|
tag string
|
|
|
|
|
value string
|
|
|
|
|
}{
|
|
|
|
|
{name: "null", text: "null\n", kind: yaml.ScalarNode, tag: "!!null", value: "null"},
|
|
|
|
|
{name: "boolean", text: "true\n", kind: yaml.ScalarNode, tag: "!!bool", value: "true"},
|
|
|
|
|
{name: "large integer", text: "9007199254740993\n", kind: yaml.ScalarNode, tag: "!!int", value: "9007199254740993"},
|
|
|
|
|
{name: "numeric string", text: "'123'\n", kind: yaml.ScalarNode, tag: "!!str", value: "123"},
|
|
|
|
|
{name: "boolean-like string", text: "'false'\n", kind: yaml.ScalarNode, tag: "!!str", value: "false"},
|
|
|
|
|
{name: "empty mapping", text: "{}\n", kind: yaml.MappingNode, tag: "!!map"},
|
|
|
|
|
{name: "empty sequence", text: "[]\n", kind: yaml.SequenceNode, tag: "!!seq"},
|
|
|
|
|
}
|
|
|
|
|
for _, test := range tests {
|
|
|
|
|
t.Run(test.name, func(t *testing.T) {
|
|
|
|
|
node, err := decodeSafeSnapshot(test.text)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if node.Kind != test.kind || node.Tag != test.tag || node.Value != test.value {
|
|
|
|
|
t.Fatalf("node kind/tag/value = %v/%q/%q, want %v/%q/%q", node.Kind, node.Tag, node.Value, test.kind, test.tag, test.value)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nested, err := decodeSafeSnapshot("items:\n - name: example\n enabled: false\n")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if nested.Kind != yaml.MappingNode || nested.Content[1].Kind != yaml.SequenceNode || nested.Content[1].Content[0].Content[3].Tag != "!!bool" {
|
|
|
|
|
t.Fatalf("nested collection/scalar tags were not preserved: %#v", nested)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestDecodeJSONPointerPreservesSemanticTokens(t *testing.T) {
|
|
|
|
|
tests := []struct {
|
|
|
|
|
pointer string
|
|
|
|
|
want []string
|
|
|
|
|
}{
|
|
|
|
|
{pointer: "", want: nil},
|
|
|
|
|
{pointer: "/", want: []string{""}},
|
|
|
|
|
{pointer: "/parent/child", want: []string{"parent", "child"}},
|
|
|
|
|
{pointer: "/0/../a.b/50%", want: []string{"0", "..", "a.b", "50%"}},
|
|
|
|
|
{pointer: "/a~1b/c~0d", want: []string{"a/b", "c~d"}},
|
|
|
|
|
{pointer: "/~01", want: []string{"~1"}},
|
|
|
|
|
}
|
|
|
|
|
for _, test := range tests {
|
|
|
|
|
got, err := decodeJSONPointer(test.pointer)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("decode %q: %v", test.pointer, err)
|
|
|
|
|
}
|
|
|
|
|
if !reflect.DeepEqual(got, test.want) {
|
|
|
|
|
t.Errorf("decode %q = %#v, want %#v", test.pointer, got, test.want)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for _, pointer := range []string{"missing-leading-slash", "/bad~", "/bad~2escape"} {
|
|
|
|
|
if _, err := decodeJSONPointer(pointer); err == nil {
|
|
|
|
|
t.Errorf("decode %q unexpectedly succeeded", pointer)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestPlaceYAMLValueBuildsOrderedSemanticMappings(t *testing.T) {
|
2026-10-12 00:14:20 +00:00
|
|
|
var placer yamlValuePlacer
|
2026-10-11 21:48:49 +00:00
|
|
|
category := &yaml.Node{Kind: yaml.MappingNode, Tag: "!!map"}
|
|
|
|
|
paths := [][]string{
|
|
|
|
|
{"parent", "0", "dot.name", "50%"},
|
|
|
|
|
{"parent", "second"},
|
|
|
|
|
{"a/b", "~1"},
|
|
|
|
|
{""},
|
|
|
|
|
}
|
|
|
|
|
for index, path := range paths {
|
|
|
|
|
value := &yaml.Node{Kind: yaml.ScalarNode, Tag: "!!str", Value: string(rune('a' + index))}
|
2026-10-12 00:14:20 +00:00
|
|
|
updated, err := placer.placeValue(category, path, value)
|
2026-10-11 21:48:49 +00:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("place %q: %v", pointerFromTokens(path), err)
|
|
|
|
|
}
|
|
|
|
|
category = updated
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(category.Content) != 6 || category.Content[0].Value != "parent" || category.Content[2].Value != "a/b" || category.Content[4].Value != "" {
|
|
|
|
|
t.Fatalf("mapping insertion order or empty key = %#v", category.Content)
|
|
|
|
|
}
|
|
|
|
|
parent := category.Content[1]
|
|
|
|
|
if parent.Kind != yaml.MappingNode || len(parent.Content) != 4 || parent.Content[0].Value != "0" || parent.Content[2].Value != "second" {
|
|
|
|
|
t.Fatalf("nested shared ancestor/order = %#v", parent)
|
|
|
|
|
}
|
|
|
|
|
numeric := parent.Content[1]
|
|
|
|
|
dotted := numeric.Content[1]
|
|
|
|
|
if numeric.Kind != yaml.MappingNode || numeric.Content[0].Value != "dot.name" || dotted.Kind != yaml.MappingNode || dotted.Content[0].Value != "50%" {
|
|
|
|
|
t.Fatalf("semantic tokens were changed: numeric=%#v dotted=%#v", numeric, dotted)
|
|
|
|
|
}
|
|
|
|
|
if category.Content[1].Content[0].Tag != "!!str" || category.Content[2].Tag != "!!str" || category.Content[4].Tag != "!!str" {
|
|
|
|
|
t.Fatal("mapping keys are not explicitly tagged as YAML strings")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestPlaceYAMLValueHandlesRootAndEmptyKey(t *testing.T) {
|
2026-10-12 00:14:20 +00:00
|
|
|
var placer yamlValuePlacer
|
2026-10-11 21:48:49 +00:00
|
|
|
category := &yaml.Node{Kind: yaml.MappingNode, Tag: "!!map"}
|
|
|
|
|
root := &yaml.Node{Kind: yaml.ScalarNode, Tag: "!!null", Value: "null"}
|
2026-10-12 00:14:20 +00:00
|
|
|
updated, err := placer.placeValue(category, nil, root)
|
2026-10-11 21:48:49 +00:00
|
|
|
if err != nil || updated != root {
|
|
|
|
|
t.Fatalf("root placement = %#v, %v", updated, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
category = &yaml.Node{Kind: yaml.MappingNode, Tag: "!!map"}
|
|
|
|
|
value := &yaml.Node{Kind: yaml.ScalarNode, Tag: "!!str", Value: "empty-key"}
|
2026-10-12 00:14:20 +00:00
|
|
|
updated, err = placer.placeValue(category, []string{""}, value)
|
2026-10-11 21:48:49 +00:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if updated != category || len(category.Content) != 2 || category.Content[0].Value != "" || category.Content[0].Tag != "!!str" || category.Content[1] != value {
|
|
|
|
|
t.Fatalf("slash path placement = %#v", category)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestPlaceYAMLValueRejectsIncompatibleAncestors(t *testing.T) {
|
2026-10-12 00:14:20 +00:00
|
|
|
var placer yamlValuePlacer
|
2026-10-11 21:48:49 +00:00
|
|
|
category := &yaml.Node{Kind: yaml.MappingNode, Tag: "!!map", Content: []*yaml.Node{
|
|
|
|
|
stringKey("parent"), {Kind: yaml.ScalarNode, Tag: "!!str", Value: "leaf"},
|
|
|
|
|
}}
|
2026-10-12 00:14:20 +00:00
|
|
|
_, err := placer.placeValue(category, []string{"parent", "child"}, &yaml.Node{Kind: yaml.ScalarNode, Tag: "!!null", Value: "null"})
|
2026-10-11 21:48:49 +00:00
|
|
|
if err == nil || !strings.Contains(err.Error(), "/parent/child") || !strings.Contains(err.Error(), "/parent") {
|
|
|
|
|
t.Fatalf("incompatible ancestor error = %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestDecodeSafeSnapshotRejectsMalformedYAML(t *testing.T) {
|
|
|
|
|
if _, err := decodeSafeSnapshot("value: [unterminated\n"); err == nil {
|
|
|
|
|
t.Fatal("malformed snapshot unexpectedly decoded")
|
|
|
|
|
}
|
|
|
|
|
}
|