package jsonvalue_test import ( "encoding/json" "math" "reflect" "testing" "gitea.maximumdirect.net/eric/promptkit/internal/jsonvalue" ) func TestCopyMapPreservesTypesAndIsolatesMutations(t *testing.T) { nested := map[string]int{"limit": 2} sequence := []string{"one", "two"} input := map[string]any{ "count": int64(7), "number": json.Number("-1.25e+2"), "nested": nested, "sequence": sequence, } copied, err := jsonvalue.CopyMap(input) if err != nil { t.Fatalf("copy map: %v", err) } nested["limit"] = 99 sequence[0] = "changed" input["added"] = true if got, ok := copied["count"].(int64); !ok || got != 7 { t.Fatalf("integer type or value changed: %#v", copied["count"]) } if got, ok := copied["number"].(json.Number); !ok || got != "-1.25e+2" { t.Fatalf("JSON number type or value changed: %#v", copied["number"]) } if got := copied["nested"].(map[string]int)["limit"]; got != 2 { t.Fatalf("nested map was not isolated: %d", got) } if got := copied["sequence"].([]string)[0]; got != "one" { t.Fatalf("sequence was not isolated: %q", got) } if _, ok := copied["added"]; ok { t.Fatalf("top-level map was not isolated: %#v", copied) } } func TestCopyMapRejectsInvalidValues(t *testing.T) { cyclicMap := map[string]any{} cyclicMap["self"] = cyclicMap cyclicSlice := []any{nil} cyclicSlice[0] = cyclicSlice tests := []struct { name string value any }{ {name: "empty nested key", value: map[string]int{"": 1}}, {name: "non-string map key", value: map[int]string{1: "one"}}, {name: "unsupported value", value: make(chan int)}, {name: "cyclic map", value: cyclicMap}, {name: "cyclic slice", value: cyclicSlice}, {name: "NaN", value: math.NaN()}, {name: "positive infinity", value: math.Inf(1)}, {name: "unsafe signed integer", value: int64(1 << 53)}, {name: "unsafe unsigned integer", value: uint64(1 << 53)}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { if _, err := jsonvalue.CopyMap(map[string]any{"value": tc.value}); err == nil { t.Fatal("expected validation error") } }) } } func TestCopyMapValidatesJSONNumberSyntaxAndRange(t *testing.T) { for _, number := range []json.Number{"0", "-1", "1.25", "-1.25e+2"} { t.Run("valid "+number.String(), func(t *testing.T) { got, err := jsonvalue.CopyMap(map[string]any{"value": number}) if err != nil { t.Fatalf("copy valid JSON number: %v", err) } if !reflect.DeepEqual(got["value"], number) { t.Fatalf("JSON number changed: got %#v want %#v", got["value"], number) } }) } for _, number := range []json.Number{"", "01", "+1", "1.", ".1", "1e9999", "not-a-number"} { t.Run("invalid "+number.String(), func(t *testing.T) { if _, err := jsonvalue.CopyMap(map[string]any{"value": number}); err == nil { t.Fatal("expected invalid JSON number error") } }) } }