// Copyright (c) 2026 Petr BalvĂ­n (https://petrbalvin.org) // SPDX-License-Identifier: MIT package interpres import ( "context" "slices" "testing" ) func TestOrderedMapBasics(t *testing.T) { m := NewOrderedMap() if m.Len() != 0 { t.Fatalf("fresh map holds %d keys", m.Len()) } m.Set("b", 1) m.Set("a", 2) m.Set("c", 3) if got := m.Keys(); !slices.Equal(got, []string{"b", "a", "c"}) { t.Errorf("keys = %v, want [b a c]", got) } if v, ok := m.Get("a"); !ok || v != 2 { t.Errorf("a = %v, %v", v, ok) } m.Set("a", 9) if got := m.Keys(); !slices.Equal(got, []string{"b", "a", "c"}) { t.Errorf("keys after replace = %v, want the position kept", got) } if v, _ := m.Get("a"); v != 9 { t.Errorf("a = %v, want 9", v) } seen := "" m.Range(func(key string, value any) bool { seen += key return key != "a" }) if seen != "ba" { t.Errorf("range visited %q, want \"ba\"", seen) } m.Delete("b") m.Delete("missing") if got := m.Keys(); !slices.Equal(got, []string{"a", "c"}) { t.Errorf("keys after delete = %v, want [a c]", got) } m.Delete("c") m.Set("c", 3) if got := m.Keys(); !slices.Equal(got, []string{"a", "c"}) { t.Errorf("re-set key = %v, want it appended as [a c]", got) } } func TestMarshalOrderedMap(t *testing.T) { t.Run("top level keeps the order", func(t *testing.T) { m := NewOrderedMap() m.Set("zebra", int64(1)) m.Set("alpha", "x") out, err := Marshal(m) if err != nil { t.Fatal(err) } want := "zebra = 1\nalpha = \"x\"\n" if string(out) != want { t.Errorf("output:\n%q\nwant:\n%q", out, want) } }) t.Run("a pointer top level does the same", func(t *testing.T) { m := &OrderedMap{} m.Set("second", true) m.Set("first", int64(2)) out, err := Marshal(m) if err != nil { t.Fatal(err) } if string(out) != "second = true\nfirst = 2\n" { t.Errorf("output %q", out) } }) t.Run("a struct field keeps the order as a table", func(t *testing.T) { type Cfg struct { Title string `toml:"title"` Extra *OrderedMap `toml:"extra"` } m := &OrderedMap{} m.Set("late", int64(1)) m.Set("early", int64(2)) out, err := Marshal(Cfg{Title: "t", Extra: m}) if err != nil { t.Fatal(err) } want := "title = \"t\"\n\n[extra]\nlate = 1\nearly = 2\n" if string(out) != want { t.Errorf("output:\n%q\nwant:\n%q", out, want) } }) t.Run("inline form keeps the order too", func(t *testing.T) { m := NewOrderedMap() m.Set("zebra", int64(1)) m.Set("alpha", int64(2)) out, err := NewEncoder().InlineTables(60).Marshal(map[string]any{"t": m}) if err != nil { t.Fatal(err) } if string(out) != "t = {zebra = 1, alpha = 2}\n" { t.Errorf("output %q", out) } }) t.Run("an array of tables keeps each element's order", func(t *testing.T) { type Cfg struct { Items []*OrderedMap `toml:"items"` } a, b := NewOrderedMap(), NewOrderedMap() a.Set("y", int64(1)) a.Set("x", int64(2)) b.Set("n", int64(3)) out, err := Marshal(Cfg{Items: []*OrderedMap{a, b}}) if err != nil { t.Fatal(err) } want := "[[items]]\ny = 1\nx = 2\n\n[[items]]\nn = 3\n" if string(out) != want { t.Errorf("output:\n%q\nwant:\n%q", out, want) } }) t.Run("a nil value is skipped", func(t *testing.T) { m := NewOrderedMap() m.Set("gone", nil) m.Set("here", int64(1)) out, err := Marshal(m) if err != nil { t.Fatal(err) } if string(out) != "here = 1\n" { t.Errorf("output %q", out) } }) } func TestDecodeOrderedMap(t *testing.T) { t.Run("keys come back in written order", func(t *testing.T) { doc := []byte("zebra = 1\nmiddle = \"m\"\nalpha = true\n") var m OrderedMap if err := Unmarshal(doc, &m); err != nil { t.Fatal(err) } if got := m.Keys(); !slices.Equal(got, []string{"zebra", "middle", "alpha"}) { t.Fatalf("keys = %v", got) } if v, _ := m.Get("middle"); v != "m" { t.Errorf("middle = %#v", v) } }) t.Run("a nested table keeps the table order", func(t *testing.T) { type Cfg struct { Ports []int `toml:"ports"` DB *OrderedMap `toml:"db"` } doc := []byte("ports = [1, 2]\n\n[db]\nslow = 1\nfast = 2\n") var cfg Cfg if err := Unmarshal(doc, &cfg); err != nil { t.Fatal(err) } if got := cfg.DB.Keys(); !slices.Equal(got, []string{"slow", "fast"}) { t.Errorf("db keys = %v", got) } }) t.Run("an array of tables fills in order", func(t *testing.T) { var m OrderedMap doc := []byte("b = 1\n[[items]]\nname = \"x\"\n[[items]]\nname = \"y\"\na = 2\n") if err := Unmarshal(doc, &m); err != nil { t.Fatal(err) } if got := m.Keys(); !slices.Equal(got, []string{"b", "items"}) { t.Errorf("keys = %v, want [b items]", got) } elems, ok := m.values["items"].([]map[string]any) if !ok || len(elems) != 2 { t.Fatalf("items = %#v", m.values["items"]) } if elems[1]["name"] != "y" { t.Errorf("second element = %#v", elems[1]) } }) t.Run("the sorted fallback needs a tree without nodes", func(t *testing.T) { // Unmarshal and Decode build the node tree whenever the destination can // reach an OrderedMap, so the sorted fallback is only reachable from a // tree that never had one. tree, _, err := parseWithOptions(context.Background(), []byte("b = 1\na = 2\n"), parseOptions{}, false) if err != nil { t.Fatal(err) } var m OrderedMap if err := newDecoder().decode(tree, &m); err != nil { t.Fatal(err) } if got := m.Keys(); !slices.Equal(got, []string{"a", "b"}) { t.Errorf("keys = %v, want the sorted [a b]", got) } }) t.Run("the order survives a round trip", func(t *testing.T) { doc := []byte("z = 1\na = 2\nm = 3\n") var m OrderedMap if err := Unmarshal(doc, &m); err != nil { t.Fatal(err) } out, err := Marshal(m) if err != nil { t.Fatal(err) } if string(out) != "z = 1\na = 2\nm = 3\n" { t.Errorf("output:\n%q", out) } }) }