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}