scooter  ·  2026-05-04

output_test.go

  1package ygo_test
  2
  3import (
  4	"fmt"
  5	"git.kilimanjaro.io/ygo"
  6	"testing"
  7)
  8
  9func TestOutputToInterface(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	arr, err := doc.GetArray("test")
 17	if err != nil {
 18		t.Fatalf("failed to get array: %v", err)
 19	}
 20	defer arr.Destroy()
 21
 22	err = doc.WithWriteTransaction(func(txn *ygo.Transaction) error {
 23		items := []ygo.Input{
 24			ygo.String("hello"),
 25			ygo.Int(42),
 26			ygo.Float(3.14),
 27			ygo.Bool(true),
 28			ygo.Null(),
 29		}
 30		return arr.InsertRange(txn, 0, items)
 31	})
 32	if err != nil {
 33		t.Fatalf("transaction failed: %v", err)
 34	}
 35
 36	err = doc.WithReadTransaction(func(txn *ygo.Transaction) error {
 37		expectedValues := []interface{}{"hello", int64(42), float64(3.14), true, nil}
 38		for i := 0; i < len(expectedValues); i++ {
 39			out, err := arr.Get(txn, uint32(i))
 40			if err != nil {
 41				return err
 42			}
 43
 44			val, err := out.ToInterface()
 45			if err != nil {
 46				t.Fatalf("index %d: ToInterface failed: %v", i, err)
 47			}
 48
 49			if val != expectedValues[i] {
 50				t.Errorf("expected %v, got %v", expectedValues[i], val)
 51			}
 52
 53			out.Destroy()
 54		}
 55		return nil
 56	})
 57	if err != nil {
 58		t.Fatalf("read transaction failed: %v", err)
 59	}
 60}
 61
 62func TestOutputToXmlElement(t *testing.T) {
 63	doc, err := ygo.NewDoc()
 64	if err != nil {
 65		t.Fatalf("failed to create doc: %v", err)
 66	}
 67	defer doc.Destroy()
 68
 69	frag, err := doc.GetXmlFragment("test")
 70	if err != nil {
 71		t.Fatalf("failed to get fragment: %v", err)
 72	}
 73	defer frag.Destroy()
 74
 75	// Create parent with child
 76	err = doc.WithWriteTransaction(func(txn *ygo.Transaction) error {
 77		parent, err := frag.InsertElement(txn, 0, "parent")
 78		if err != nil {
 79			return err
 80		}
 81		defer parent.Destroy()
 82
 83		child, err := parent.InsertElement(txn, 0, "child")
 84		if err != nil {
 85			return err
 86		}
 87		child.Destroy()
 88
 89		return nil
 90	})
 91	if err != nil {
 92		t.Fatalf("transaction failed: %v", err)
 93	}
 94
 95	// Read back and test ToXmlElement
 96	err = doc.WithReadTransaction(func(txn *ygo.Transaction) error {
 97		// Get the parent element
 98		first, err := frag.FirstChild(txn)
 99		if err != nil {
100			return err
101		}
102		if first == nil {
103			return fmt.Errorf("expected first child")
104		}
105
106		// Convert to XmlElement using ToXmlElement
107		parent, err := first.ToXmlElement()
108		first.Destroy()
109		if err != nil {
110			return fmt.Errorf("ToXmlElement failed: %w", err)
111		}
112		defer parent.Destroy()
113
114		// Verify we can use the element
115		tag, err := parent.Tag()
116		if err != nil {
117			return err
118		}
119		if tag != "parent" {
120			t.Errorf("expected tag 'parent', got '%s'", tag)
121		}
122
123		// Get child and convert
124		childIter, err := parent.ChildIter(txn)
125		if err != nil {
126			return err
127		}
128
129		childOutput, err := childIter.Next()
130		if err != nil {
131			return err
132		}
133		if childOutput == nil {
134			return fmt.Errorf("expected child")
135		}
136
137		child, err := childOutput.ToXmlElement()
138		childOutput.Destroy()
139		if err != nil {
140			return fmt.Errorf("child ToXmlElement failed: %w", err)
141		}
142		defer child.Destroy()
143
144		childTag, err := child.Tag()
145		if err != nil {
146			return err
147		}
148		if childTag != "child" {
149			t.Errorf("expected child tag 'child', got '%s'", childTag)
150		}
151
152		return nil
153	})
154	if err != nil {
155		t.Fatalf("read transaction failed: %v", err)
156	}
157}
158
159func TestOutputEdgeCases(t *testing.T) {
160	t.Run("nil output pointer", func(t *testing.T) {
161		var out *ygo.Output
162		val, err := out.ToInterface()
163		if err == nil {
164			t.Errorf("expected error for nil output, got value: %v", val)
165		}
166	})
167
168	t.Run("shared type error", func(t *testing.T) {
169		doc, err := ygo.NewDoc()
170		if err != nil {
171			t.Fatalf("failed to create doc: %v", err)
172		}
173		defer doc.Destroy()
174
175		arr, err := doc.GetArray("test")
176		if err != nil {
177			t.Fatalf("failed to get array: %v", err)
178		}
179		defer arr.Destroy()
180
181		err = doc.WithWriteTransaction(func(txn *ygo.Transaction) error {
182			return arr.InsertRange(txn, 0, []ygo.Input{ygo.YArray(nil)})
183		})
184		if err != nil {
185			t.Fatalf("transaction failed: %v", err)
186		}
187
188		err = doc.WithReadTransaction(func(txn *ygo.Transaction) error {
189			out, err := arr.Get(txn, 0)
190			if err != nil {
191				return err
192			}
193			defer out.Destroy()
194
195			val, err := out.ToInterface()
196			if err == nil {
197				t.Errorf("expected error for shared type, got value: %v", val)
198			}
199			return nil
200		})
201		if err != nil {
202			t.Fatalf("read transaction failed: %v", err)
203		}
204	})
205
206	t.Run("binary data", func(t *testing.T) {
207		doc, err := ygo.NewDoc()
208		if err != nil {
209			t.Fatalf("failed to create doc: %v", err)
210		}
211		defer doc.Destroy()
212
213		arr, err := doc.GetArray("test")
214		if err != nil {
215			t.Fatalf("failed to get array: %v", err)
216		}
217		defer arr.Destroy()
218
219		testData := []byte("test binary data")
220		err = doc.WithWriteTransaction(func(txn *ygo.Transaction) error {
221			return arr.InsertRange(txn, 0, []ygo.Input{ygo.Binary(testData)})
222		})
223		if err != nil {
224			t.Fatalf("transaction failed: %v", err)
225		}
226
227		err = doc.WithReadTransaction(func(txn *ygo.Transaction) error {
228			out, err := arr.Get(txn, 0)
229			if err != nil {
230				return err
231			}
232			defer out.Destroy()
233
234			val, err := out.ToInterface()
235			if err != nil {
236				t.Fatalf("ToInterface failed: %v", err)
237			}
238
239			got, ok := val.([]byte)
240			if !ok {
241				t.Fatalf("expected []byte, got %T", val)
242			}
243
244			if string(got) != string(testData) {
245				t.Errorf("expected %q, got %q", string(testData), string(got))
246			}
247			return nil
248		})
249		if err != nil {
250			t.Fatalf("read transaction failed: %v", err)
251		}
252	})
253
254	t.Run("undefined value", func(t *testing.T) {
255		doc, err := ygo.NewDoc()
256		if err != nil {
257			t.Fatalf("failed to create doc: %v", err)
258		}
259		defer doc.Destroy()
260
261		arr, err := doc.GetArray("test")
262		if err != nil {
263			t.Fatalf("failed to get array: %v", err)
264		}
265		defer arr.Destroy()
266
267		err = doc.WithWriteTransaction(func(txn *ygo.Transaction) error {
268			return arr.InsertRange(txn, 0, []ygo.Input{ygo.Undefined()})
269		})
270		if err != nil {
271			t.Fatalf("transaction failed: %v", err)
272		}
273
274		err = doc.WithReadTransaction(func(txn *ygo.Transaction) error {
275			out, err := arr.Get(txn, 0)
276			if err != nil {
277				return err
278			}
279			defer out.Destroy()
280
281			val, err := out.ToInterface()
282			if err != nil {
283				t.Fatalf("ToInterface failed: %v", err)
284			}
285			if val != nil {
286				t.Errorf("expected nil for undefined, got %v", val)
287			}
288			return nil
289		})
290		if err != nil {
291			t.Fatalf("read transaction failed: %v", err)
292		}
293	})
294}
295
296func TestOutputJSON(t *testing.T) {
297	t.Run("unmarshal string struct", func(t *testing.T) {
298		doc, err := ygo.NewDoc()
299		if err != nil {
300			t.Fatalf("failed to create doc: %v", err)
301		}
302		defer doc.Destroy()
303
304		m, err := doc.GetMap("test")
305		if err != nil {
306			t.Fatalf("failed to get map: %v", err)
307		}
308		defer m.Destroy()
309
310		type Person struct {
311			Name string `json:"name"`
312			Age  int    `json:"age"`
313		}
314
315		p, err := ygo.JSON(Person{Name: "Alice", Age: 30})
316		if err != nil {
317			t.Fatalf("JSON failed: %v", err)
318		}
319
320		err = doc.WithWriteTransaction(func(txn *ygo.Transaction) error {
321			return m.Insert(txn, "person", p)
322		})
323		if err != nil {
324			t.Fatalf("transaction failed: %v", err)
325		}
326
327		err = doc.WithReadTransaction(func(txn *ygo.Transaction) error {
328			out, err := m.Get(txn, "person")
329			if err != nil {
330				return err
331			}
332			defer out.Destroy()
333
334			var person Person
335			if err := out.JSON(&person); err != nil {
336				t.Fatalf("JSON unmarshal failed: %v", err)
337			}
338
339			if person.Name != "Alice" {
340				t.Errorf("expected name 'Alice', got %q", person.Name)
341			}
342			if person.Age != 30 {
343				t.Errorf("expected age 30, got %d", person.Age)
344			}
345			return nil
346		})
347		if err != nil {
348			t.Fatalf("read transaction failed: %v", err)
349		}
350	})
351
352	t.Run("unmarshal bool", func(t *testing.T) {
353		doc, err := ygo.NewDoc()
354		if err != nil {
355			t.Fatalf("failed to create doc: %v", err)
356		}
357		defer doc.Destroy()
358
359		m, err := doc.GetMap("test")
360		if err != nil {
361			t.Fatalf("failed to get map: %v", err)
362		}
363		defer m.Destroy()
364
365		err = doc.WithWriteTransaction(func(txn *ygo.Transaction) error {
366			return m.Insert(txn, "flag", ygo.Bool(true))
367		})
368		if err != nil {
369			t.Fatalf("transaction failed: %v", err)
370		}
371
372		err = doc.WithReadTransaction(func(txn *ygo.Transaction) error {
373			out, err := m.Get(txn, "flag")
374			if err != nil {
375				return err
376			}
377			defer out.Destroy()
378
379			var val bool
380			if err := out.JSON(&val); err != nil {
381				t.Fatalf("JSON unmarshal failed: %v", err)
382			}
383
384			if val != true {
385				t.Errorf("expected true, got %t", val)
386			}
387			return nil
388		})
389		if err != nil {
390			t.Fatalf("read transaction failed: %v", err)
391		}
392	})
393
394	t.Run("unmarshal int", func(t *testing.T) {
395		doc, err := ygo.NewDoc()
396		if err != nil {
397			t.Fatalf("failed to create doc: %v", err)
398		}
399		defer doc.Destroy()
400
401		m, err := doc.GetMap("test")
402		if err != nil {
403			t.Fatalf("failed to get map: %v", err)
404		}
405		defer m.Destroy()
406
407		err = doc.WithWriteTransaction(func(txn *ygo.Transaction) error {
408			return m.Insert(txn, "count", ygo.Int(42))
409		})
410		if err != nil {
411			t.Fatalf("transaction failed: %v", err)
412		}
413
414		err = doc.WithReadTransaction(func(txn *ygo.Transaction) error {
415			out, err := m.Get(txn, "count")
416			if err != nil {
417				return err
418			}
419			defer out.Destroy()
420
421			var val int
422			if err := out.JSON(&val); err != nil {
423				t.Fatalf("JSON unmarshal failed: %v", err)
424			}
425
426			if val != 42 {
427				t.Errorf("expected 42, got %d", val)
428			}
429			return nil
430		})
431		if err != nil {
432			t.Fatalf("read transaction failed: %v", err)
433		}
434	})
435
436	t.Run("unmarshal float", func(t *testing.T) {
437		doc, err := ygo.NewDoc()
438		if err != nil {
439			t.Fatalf("failed to create doc: %v", err)
440		}
441		defer doc.Destroy()
442
443		m, err := doc.GetMap("test")
444		if err != nil {
445			t.Fatalf("failed to get map: %v", err)
446		}
447		defer m.Destroy()
448
449		err = doc.WithWriteTransaction(func(txn *ygo.Transaction) error {
450			return m.Insert(txn, "pi", ygo.Float(3.14))
451		})
452		if err != nil {
453			t.Fatalf("transaction failed: %v", err)
454		}
455
456		err = doc.WithReadTransaction(func(txn *ygo.Transaction) error {
457			out, err := m.Get(txn, "pi")
458			if err != nil {
459				return err
460			}
461			defer out.Destroy()
462
463			var val float64
464			if err := out.JSON(&val); err != nil {
465				t.Fatalf("JSON unmarshal failed: %v", err)
466			}
467
468			if val != 3.14 {
469				t.Errorf("expected 3.14, got %f", val)
470			}
471			return nil
472		})
473		if err != nil {
474			t.Fatalf("read transaction failed: %v", err)
475		}
476	})
477
478	t.Run("null value returns error", func(t *testing.T) {
479		doc, err := ygo.NewDoc()
480		if err != nil {
481			t.Fatalf("failed to create doc: %v", err)
482		}
483		defer doc.Destroy()
484
485		m, err := doc.GetMap("test")
486		if err != nil {
487			t.Fatalf("failed to get map: %v", err)
488		}
489		defer m.Destroy()
490
491		err = doc.WithWriteTransaction(func(txn *ygo.Transaction) error {
492			return m.Insert(txn, "nil", ygo.Null())
493		})
494		if err != nil {
495			t.Fatalf("transaction failed: %v", err)
496		}
497
498		err = doc.WithReadTransaction(func(txn *ygo.Transaction) error {
499			out, err := m.Get(txn, "nil")
500			if err != nil {
501				return err
502			}
503			defer out.Destroy()
504
505			var val interface{}
506			err = out.JSON(&val)
507			if err == nil {
508				t.Errorf("expected error for null value")
509			}
510			return nil
511		})
512		if err != nil {
513			t.Fatalf("read transaction failed: %v", err)
514		}
515	})
516}