fix(decode): allocate embedded pointer maps and settle case collisions

Assisted-by: GLM 5.3
This commit is contained in:
2026-09-22 21:15:00 +02:00
parent e677e34508
commit f7e427ae3f
2 changed files with 103 additions and 4 deletions
+82
View File
@@ -1690,3 +1690,85 @@ func TestErrorMessagesGolden(t *testing.T) {
})
}
}
// TestLocalTimeLocationLeavesOffsetsAlone pins that the option's zone is
// used for local date-times only: an offset date-time keeps the offset the
// document wrote.
func TestLocalTimeLocationLeavesOffsetsAlone(t *testing.T) {
var cfg struct {
Stamp time.Time `toml:"stamp"`
}
err := Unmarshal([]byte("stamp = 1979-05-27T07:32:00-07:00\n"), &cfg,
LocalTimeLocation(time.FixedZone("Prague", 2*60*60)))
if err != nil {
t.Fatalf("unmarshal: %v", err)
}
if _, off := cfg.Stamp.Zone(); off != -7*60*60 {
t.Errorf("offset = %d, want the document's -07:00", off/3600)
}
}
// TestUnmarshalCaseCollisionIsDeterministic pins that two keys differing
// only in case, both matching one field, resolve the same way on every run
// and on both decode paths.
func TestUnmarshalCaseCollisionIsDeterministic(t *testing.T) {
type cfg struct {
Host string `toml:"host"`
}
in := []byte("Host = \"upper\"\nhost = \"lower\"\n")
// The tree path iterates a map, so pin the winner across many runs.
var want string
for range 50 {
var viaTree cfg
if err := treeDecodeInto(in, &viaTree); err != nil {
t.Fatalf("tree decode: %v", err)
}
if want == "" {
want = viaTree.Host
} else if viaTree.Host != want {
t.Fatalf("tree decode is not deterministic: %q then %q", want, viaTree.Host)
}
}
var targeted cfg
if err := Unmarshal(in, &targeted); err != nil {
t.Fatalf("unmarshal: %v", err)
}
if targeted.Host != want {
t.Errorf("targeted Host = %q, tree %q", targeted.Host, want)
}
}
// TestUnmarshalEmbeddedPointerMap pins that leftover keys reach an embedded
// pointer to a map, allocating it, rather than panicking on the pointer.
func TestUnmarshalEmbeddedPointerMap(t *testing.T) {
type Extra map[string]int
type cfg struct {
*Extra
Name string `toml:"name"`
}
var c cfg
err := Unmarshal([]byte("name = \"x\"\nrogue = 7\n"), &c)
if err != nil {
t.Fatalf("unmarshal: %v", err)
}
if c.Extra == nil || (*c.Extra)["rogue"] != 7 {
t.Errorf("embedded map = %v, want rogue allocated and filled", c.Extra)
}
}
// TestUnmarshalIgnoresEncodeTagOptions pins that the emission-only tag
// options change nothing on the decode side.
func TestUnmarshalIgnoresEncodeTagOptions(t *testing.T) {
type cfg struct {
Name string `toml:"name,omitempty"`
Port int `toml:"port,omitzero,comment=The port"`
}
var c cfg
err := Unmarshal([]byte("name = \"x\"\nport = 8080\n"), &c)
if err != nil {
t.Fatalf("unmarshal: %v", err)
}
if c.Name != "x" || c.Port != 8080 {
t.Errorf("cfg = %+v, want both fields filled", c)
}
}