map_test.go

  1package ygo_test
  2
  3import (
  4	"encoding/json"
  5	"git.kilimanjaro.io/ygo"
  6	"testing"
  7)
  8
  9func TestMapBasic(t *testing.T) {
 10	doc, err := ygo.NewDoc()
 11	if err != nil {
 12		t.Fatalf("failed to create doc: %v", err)
 13	}
 14	defer doc.Destroy()
 15
 16	m, err := doc.GetMap("test")
 17	if err != nil {
 18		t.Fatalf("failed to get map: %v", err)
 19	}
 20	defer m.Destroy()
 21
 22	var length uint32
 23	var removed1, removed2 bool
 24	var out *ygo.Output
 25
 26	err = doc.WithWriteTransaction(func(txn *ygo.Transaction) error {
 27		// Insert values
 28		m.Insert(txn, "a", ygo.String("value"))
 29
 30		// Insert JSON array
 31		arrayItems := []ygo.Input{ygo.Int(11), ygo.Int(22)}
 32		m.Insert(txn, "b", ygo.JSONArray(arrayItems))
 33
 34		length = m.Len(txn)
 35
 36		// Remove key twice
 37		removed1, err = m.Remove(txn, "a")
 38		if err != nil {
 39			return err
 40		}
 41		removed2, err = m.Remove(txn, "a")
 42		if err != nil {
 43			return err
 44		}
 45
 46		// Get remaining value
 47		out, err = m.Get(txn, "b")
 48		if err != nil {
 49			return err
 50		}
 51
 52		// Clear map
 53		m.Clear(txn)
 54		length = m.Len(txn)
 55		return nil
 56	})
 57	if err != nil {
 58		t.Fatalf("transaction failed: %v", err)
 59	}
 60	defer out.Destroy()
 61
 62	// Check results after transaction
 63	if length != 0 {
 64		t.Errorf("expected length 0 after clear, got %d", length)
 65	}
 66
 67	if !removed1 {
 68		t.Error("expected first remove to return true")
 69	}
 70	if removed2 {
 71		t.Error("expected second remove to return false")
 72	}
 73
 74	// Verify we got something back
 75	if out.IsUndefined() {
 76		t.Error("expected non-undefined output")
 77	}
 78}
 79
 80func TestMapNested(t *testing.T) {
 81	doc, err := ygo.NewDoc()
 82	if err != nil {
 83		t.Fatalf("failed to create doc: %v", err)
 84	}
 85	defer doc.Destroy()
 86
 87	m, err := doc.GetMap("test")
 88	if err != nil {
 89		t.Fatalf("failed to get map: %v", err)
 90	}
 91	defer m.Destroy()
 92
 93	var length uint32
 94	var out *ygo.Output
 95
 96	err = doc.WithWriteTransaction(func(txn *ygo.Transaction) error {
 97		// Insert nested maps
 98		innerValue := ygo.String("Nested data")
 99		innerMap, mapErr := ygo.YMap([]string{"text"}, []ygo.Input{innerValue})
100		if mapErr != nil {
101			return mapErr
102		}
103		outerMap, mapErr := ygo.YMap([]string{"innerMap"}, []ygo.Input{innerMap})
104		if mapErr != nil {
105			return mapErr
106		}
107
108		m.Insert(txn, "outerMap", outerMap)
109
110		length = m.Len(txn)
111
112		// Retrieve and verify nested structure
113		out, err = m.Get(txn, "outerMap")
114		return err
115	})
116	if err != nil {
117		t.Fatalf("transaction failed: %v", err)
118	}
119	defer out.Destroy()
120
121	// Check results after transaction
122	if length != 1 {
123		t.Errorf("expected length 1, got %d", length)
124	}
125
126	// Should be a map type
127	if out.IsUndefined() {
128		t.Fatal("expected map output, got undefined")
129	}
130}
131
132func collectMapEntries(m *ygo.Map, txn *ygo.Transaction) (map[string]interface{}, error) {
133	iter, err := m.Iter(txn)
134	if err != nil {
135		return nil, err
136	}
137	defer iter.Destroy()
138
139	entries := make(map[string]interface{})
140	for {
141		entry, err := iter.Next()
142		if err != nil {
143			return nil, err
144		}
145		if entry == nil {
146			break
147		}
148
149		// Convert value based on type
150		var value interface{}
151		switch entry.Value.Tag() {
152		case ygo.TagJSONBool:
153			if v, ok := entry.Value.Bool(); ok {
154				value = v
155			}
156		case ygo.TagJSONNum:
157			if v, ok := entry.Value.Float(); ok {
158				value = v
159			}
160		case ygo.TagJSONInt:
161			if v, ok := entry.Value.Int(); ok {
162				value = v
163			}
164		case ygo.TagJSONStr:
165			if v, ok := entry.Value.String(); ok {
166				value = v
167			}
168		case ygo.TagJSONNull:
169			value = nil
170		default:
171			value = nil
172		}
173
174		entries[entry.Key] = value
175		// Note: Don't destroy entry.Value - it's managed by the entry
176		// The iterator will clean up entries when destroyed
177	}
178	return entries, nil
179}
180
181func TestMapIter(t *testing.T) {
182	doc, err := ygo.NewDoc()
183	if err != nil {
184		t.Fatalf("failed to create doc: %v", err)
185	}
186	defer doc.Destroy()
187
188	m, err := doc.GetMap("test")
189	if err != nil {
190		t.Fatalf("failed to get map: %v", err)
191	}
192	defer m.Destroy()
193
194	// Insert test data
195	err = doc.WithWriteTransaction(func(txn *ygo.Transaction) error {
196		m.Insert(txn, "key1", ygo.String("value1"))
197		m.Insert(txn, "key2", ygo.Int(42))
198		m.Insert(txn, "key3", ygo.Bool(true))
199		return nil
200	})
201	if err != nil {
202		t.Fatalf("transaction failed: %v", err)
203	}
204
205	// Test iteration
206	var entries map[string]interface{}
207	err = doc.WithReadTransaction(func(txn *ygo.Transaction) error {
208		var err error
209		entries, err = collectMapEntries(m, txn)
210		return err
211	})
212	if err != nil {
213		t.Fatalf("iteration failed: %v", err)
214	}
215
216	if len(entries) != 3 {
217		t.Errorf("expected 3 entries, got %d", len(entries))
218	}
219	if entries["key1"] != "value1" {
220		t.Errorf("expected key1=value1, got %v", entries["key1"])
221	}
222	if entries["key2"] != int64(42) {
223		t.Errorf("expected key2=42, got %v", entries["key2"])
224	}
225	if entries["key3"] != true {
226		t.Errorf("expected key3=true, got %v", entries["key3"])
227	}
228}
229
230func TestMapMarshalJSON(t *testing.T) {
231	doc, err := ygo.NewDoc()
232	if err != nil {
233		t.Fatalf("failed to create doc: %v", err)
234	}
235	defer doc.Destroy()
236
237	m, err := doc.GetMap("test")
238	if err != nil {
239		t.Fatalf("failed to get map: %v", err)
240	}
241	defer m.Destroy()
242
243	err = doc.WithWriteTransaction(func(txn *ygo.Transaction) error {
244		m.Insert(txn, "string", ygo.String("hello"))
245		m.Insert(txn, "number", ygo.Int(42))
246		m.Insert(txn, "float", ygo.Float(3.14))
247		m.Insert(txn, "bool", ygo.Bool(true))
248		m.Insert(txn, "null", ygo.Null())
249		return nil
250	})
251	if err != nil {
252		t.Fatalf("transaction failed: %v", err)
253	}
254
255	// Marshal within read transaction
256	var jsonBytes []byte
257	err = doc.WithReadTransaction(func(txn *ygo.Transaction) error {
258		var err error
259		jsonBytes, err = m.MarshalJSONWithTxn(txn)
260		return err
261	})
262	if err != nil {
263		t.Fatalf("marshal failed: %v", err)
264	}
265
266	// Verify JSON is valid
267	var result map[string]interface{}
268	if err := json.Unmarshal(jsonBytes, &result); err != nil {
269		t.Fatalf("failed to unmarshal result: %v", err)
270	}
271
272	// Check values (order doesn't matter for maps)
273	if result["string"] != "hello" {
274		t.Errorf("expected string=hello, got %v", result["string"])
275	}
276	if result["number"] != float64(42) {
277		t.Errorf("expected number=42, got %v", result["number"])
278	}
279	if result["float"] != 3.14 {
280		t.Errorf("expected float=3.14, got %v", result["float"])
281	}
282	if result["bool"] != true {
283		t.Errorf("expected bool=true, got %v", result["bool"])
284	}
285	if result["null"] != nil {
286		t.Errorf("expected null=nil, got %v", result["null"])
287	}
288}