xml_test.go
1package ygo_test
2
3import (
4 "encoding/json"
5 "git.kilimanjaro.io/ygo"
6 "strings"
7 "testing"
8)
9
10func TestXmlElementBasic(t *testing.T) {
11 doc, err := ygo.NewDoc()
12 if err != nil {
13 t.Fatalf("failed to create doc: %v", err)
14 }
15 defer doc.Destroy()
16
17 frag, err := doc.GetXmlFragment("test")
18 if err != nil {
19 t.Fatalf("failed to get fragment: %v", err)
20 }
21 defer frag.Destroy()
22
23 var childLen uint32
24 var xmlStr string
25
26 err = doc.WithWriteTransaction(func(txn *ygo.Transaction) error {
27 // Create element
28 elem, err := frag.InsertElement(txn, 0, "div")
29 if err != nil {
30 return err
31 }
32 defer elem.Destroy()
33
34 tag, err := elem.Tag()
35 if err != nil {
36 return err
37 }
38 if tag != "div" {
39 t.Errorf("expected tag 'div', got '%s'", tag)
40 }
41
42 // Set attributes
43 value := ygo.String("value1")
44 elem.SetAttribute(txn, "key1", value)
45 value2 := ygo.String("value2")
46 elem.SetAttribute(txn, "key2", value2)
47
48 // Insert children
49 inner, err := elem.InsertElement(txn, 0, "p")
50 if err != nil {
51 return err
52 }
53 defer inner.Destroy()
54
55 txt, err := inner.InsertText(txn, 0)
56 if err != nil {
57 return err
58 }
59 defer txt.Destroy()
60
61 txt.Insert(txn, 0, "hello")
62
63 childLen, err = elem.ChildLen(txn)
64 if err != nil {
65 return err
66 }
67
68 // Get XML string
69 xmlStr, err = inner.String(txn)
70 return err
71 })
72 if err != nil {
73 t.Fatalf("transaction failed: %v", err)
74 }
75
76 if childLen != 1 {
77 t.Errorf("expected 1 child, got %d", childLen)
78 }
79
80 if !strings.Contains(xmlStr, "<p>") {
81 t.Errorf("expected <p> tag in XML string: %s", xmlStr)
82 }
83}
84
85func TestXmlText(t *testing.T) {
86 doc, err := ygo.NewDoc()
87 if err != nil {
88 t.Fatalf("failed to create doc: %v", err)
89 }
90 defer doc.Destroy()
91
92 frag, err := doc.GetXmlFragment("test")
93 if err != nil {
94 t.Fatalf("failed to get fragment: %v", err)
95 }
96 defer frag.Destroy()
97
98 var length uint32
99 var str string
100
101 err = doc.WithWriteTransaction(func(txn *ygo.Transaction) error {
102 txt, err := frag.InsertText(txn, 0)
103 if err != nil {
104 return err
105 }
106 defer txt.Destroy()
107
108 txt.Insert(txn, 0, "hello world")
109
110 length = txt.Len(txn)
111 if length != 11 {
112 t.Errorf("expected length 11, got %d", length)
113 }
114
115 str, err = txt.String(txn)
116 if err != nil {
117 return err
118 }
119 if str != "hello world" {
120 t.Errorf("expected 'hello world', got '%s'", str)
121 }
122
123 txt.RemoveRange(txn, 5, 6)
124
125 str, err = txt.String(txn)
126 return err
127 })
128 if err != nil {
129 t.Fatalf("transaction failed: %v", err)
130 }
131
132 if str != "hello" {
133 t.Errorf("expected 'hello' after remove, got '%s'", str)
134 }
135}
136
137func TestXmlNestedElements(t *testing.T) {
138 doc, err := ygo.NewDoc()
139 if err != nil {
140 t.Fatalf("failed to create doc: %v", err)
141 }
142 defer doc.Destroy()
143
144 frag, err := doc.GetXmlFragment("test")
145 if err != nil {
146 t.Fatalf("failed to get fragment: %v", err)
147 }
148 defer frag.Destroy()
149
150 var rootChildLen uint32
151 var sectionChildLen uint32
152 var content string
153
154 err = doc.WithWriteTransaction(func(txn *ygo.Transaction) error {
155 // Create nested structure
156 root, err := frag.InsertElement(txn, 0, "div")
157 if err != nil {
158 return err
159 }
160 defer root.Destroy()
161
162 section, err := root.InsertElement(txn, 0, "section")
163 if err != nil {
164 return err
165 }
166 defer section.Destroy()
167
168 paragraph, err := section.InsertText(txn, 0)
169 if err != nil {
170 return err
171 }
172 defer paragraph.Destroy()
173
174 paragraph.Insert(txn, 0, "Nested content")
175
176 // Verify structure
177 rootChildLen, err = root.ChildLen(txn)
178 if err != nil {
179 return err
180 }
181 if rootChildLen != 1 {
182 t.Errorf("expected 1 child in root, got %d", rootChildLen)
183 }
184
185 sectionChildLen, err = section.ChildLen(txn)
186 if err != nil {
187 return err
188 }
189 if sectionChildLen != 1 {
190 t.Errorf("expected 1 child in section, got %d", sectionChildLen)
191 }
192
193 content, err = paragraph.String(txn)
194 return err
195 })
196 if err != nil {
197 t.Fatalf("transaction failed: %v", err)
198 }
199
200 if content != "Nested content" {
201 t.Errorf("expected 'Nested content', got '%s'", content)
202 }
203}
204
205func TestXmlElementAttrIter(t *testing.T) {
206 doc, err := ygo.NewDoc()
207 if err != nil {
208 t.Fatalf("failed to create doc: %v", err)
209 }
210 defer doc.Destroy()
211
212 frag, err := doc.GetXmlFragment("test")
213 if err != nil {
214 t.Fatalf("failed to get fragment: %v", err)
215 }
216 defer frag.Destroy()
217
218 var elem *ygo.XmlElement
219 err = doc.WithWriteTransaction(func(txn *ygo.Transaction) error {
220 var err error
221 elem, err = frag.InsertElement(txn, 0, "div")
222 if err != nil {
223 return err
224 }
225
226 // Set attributes
227 elem.SetAttribute(txn, "class", ygo.String("container"))
228 elem.SetAttribute(txn, "id", ygo.String("main"))
229 elem.SetAttribute(txn, "data-count", ygo.Int(42))
230 return nil
231 })
232 if err != nil {
233 t.Fatalf("transaction failed: %v", err)
234 }
235 defer elem.Destroy()
236
237 // Test attribute iteration
238 var attrs map[string]interface{}
239 err = doc.WithReadTransaction(func(txn *ygo.Transaction) error {
240 var err error
241 attrs, err = collectXmlAttrs(elem, txn)
242 return err
243 })
244 if err != nil {
245 t.Fatalf("attribute iteration failed: %v", err)
246 }
247
248 if len(attrs) != 3 {
249 t.Errorf("expected 3 attributes, got %d", len(attrs))
250 }
251 if attrs["class"] != "container" {
252 t.Errorf("expected class=container, got %v", attrs["class"])
253 }
254 if attrs["id"] != "main" {
255 t.Errorf("expected id=main, got %v", attrs["id"])
256 }
257 // Int values are returned as int64
258 if attrs["data-count"] != int64(42) {
259 t.Errorf("expected data-count=42, got %v", attrs["data-count"])
260 }
261}
262
263func collectXmlAttrs(elem *ygo.XmlElement, txn *ygo.Transaction) (map[string]interface{}, error) {
264 iter, err := elem.AttrIter(txn)
265 if err != nil {
266 return nil, err
267 }
268 defer iter.Destroy()
269
270 attrs := make(map[string]interface{})
271 for {
272 attr, err := iter.Next()
273 if err != nil {
274 return nil, err
275 }
276 if attr == nil {
277 break
278 }
279
280 // Convert value to appropriate Go type
281 var value interface{}
282 switch attr.Value.Tag() {
283 case ygo.TagJSONBool:
284 if v, ok := attr.Value.Bool(); ok {
285 value = v
286 }
287 case ygo.TagJSONNum:
288 if v, ok := attr.Value.Float(); ok {
289 value = v
290 }
291 case ygo.TagJSONInt:
292 if v, ok := attr.Value.Int(); ok {
293 value = v
294 }
295 case ygo.TagJSONStr:
296 if v, ok := attr.Value.String(); ok {
297 value = v
298 }
299 case ygo.TagJSONNull:
300 value = nil
301 default:
302 value = nil
303 }
304
305 attrs[attr.Name] = value
306 attr.Value.Destroy()
307 }
308 return attrs, nil
309}
310
311// Helper to collect children from iterator
312func collectChildren(elem *ygo.XmlElement, txn *ygo.Transaction) ([]string, error) {
313 iter, err := elem.ChildIter(txn)
314 if err != nil {
315 return nil, err
316 }
317
318 var children []string
319 for {
320 child, err := iter.Next()
321 if err != nil {
322 return nil, err
323 }
324 if child == nil {
325 break
326 }
327
328 // Get tag type
329 tag := child.Tag()
330 switch tag {
331 case ygo.TagXmlElem:
332 children = append(children, "elem")
333 case ygo.TagXmlText:
334 children = append(children, "text")
335 default:
336 children = append(children, "unknown")
337 }
338 child.Destroy()
339 }
340 return children, nil
341}
342
343func TestXmlChildIterEmpty(t *testing.T) {
344 doc, err := ygo.NewDoc()
345 if err != nil {
346 t.Fatalf("failed to create doc: %v", err)
347 }
348 defer doc.Destroy()
349
350 frag, err := doc.GetXmlFragment("test")
351 if err != nil {
352 t.Fatalf("failed to get fragment: %v", err)
353 }
354 defer frag.Destroy()
355
356 var elem *ygo.XmlElement
357 err = doc.WithWriteTransaction(func(txn *ygo.Transaction) error {
358 var err error
359 elem, err = frag.InsertElement(txn, 0, "div")
360 return err
361 })
362 if err != nil {
363 t.Fatalf("transaction failed: %v", err)
364 }
365 defer elem.Destroy()
366
367 var children []string
368 err = doc.WithReadTransaction(func(txn *ygo.Transaction) error {
369 var err error
370 children, err = collectChildren(elem, txn)
371 return err
372 })
373 if err != nil {
374 t.Fatalf("child iteration failed: %v", err)
375 }
376
377 if len(children) != 0 {
378 t.Errorf("expected 0 children for empty element, got %d: %v", len(children), children)
379 }
380}
381
382func TestXmlChildIterSingle(t *testing.T) {
383 doc, err := ygo.NewDoc()
384 if err != nil {
385 t.Fatalf("failed to create doc: %v", err)
386 }
387 defer doc.Destroy()
388
389 frag, err := doc.GetXmlFragment("test")
390 if err != nil {
391 t.Fatalf("failed to get fragment: %v", err)
392 }
393 defer frag.Destroy()
394
395 var elem *ygo.XmlElement
396 err = doc.WithWriteTransaction(func(txn *ygo.Transaction) error {
397 var err error
398 elem, err = frag.InsertElement(txn, 0, "div")
399 if err != nil {
400 return err
401 }
402
403 // Insert single child
404 child, err := elem.InsertElement(txn, 0, "span")
405 if err != nil {
406 return err
407 }
408 child.Destroy()
409
410 return nil
411 })
412 if err != nil {
413 t.Fatalf("transaction failed: %v", err)
414 }
415 defer elem.Destroy()
416
417 var children []string
418 err = doc.WithReadTransaction(func(txn *ygo.Transaction) error {
419 var err error
420 children, err = collectChildren(elem, txn)
421 return err
422 })
423 if err != nil {
424 t.Fatalf("child iteration failed: %v", err)
425 }
426
427 if len(children) != 1 {
428 t.Errorf("expected 1 child, got %d: %v", len(children), children)
429 }
430 if children[0] != "elem" {
431 t.Errorf("expected first child to be 'elem', got '%s'", children[0])
432 }
433}
434
435func TestXmlChildIterMultiple(t *testing.T) {
436 doc, err := ygo.NewDoc()
437 if err != nil {
438 t.Fatalf("failed to create doc: %v", err)
439 }
440 defer doc.Destroy()
441
442 frag, err := doc.GetXmlFragment("test")
443 if err != nil {
444 t.Fatalf("failed to get fragment: %v", err)
445 }
446 defer frag.Destroy()
447
448 var elem *ygo.XmlElement
449 err = doc.WithWriteTransaction(func(txn *ygo.Transaction) error {
450 var err error
451 elem, err = frag.InsertElement(txn, 0, "div")
452 if err != nil {
453 return err
454 }
455
456 // Insert multiple children
457 for _, tag := range []string{"p", "span", "div"} {
458 child, err := elem.InsertElement(txn, 0, tag)
459 if err != nil {
460 return err
461 }
462 child.Destroy()
463 }
464
465 return nil
466 })
467 if err != nil {
468 t.Fatalf("transaction failed: %v", err)
469 }
470 defer elem.Destroy()
471
472 var children []string
473 err = doc.WithReadTransaction(func(txn *ygo.Transaction) error {
474 var err error
475 children, err = collectChildren(elem, txn)
476 return err
477 })
478 if err != nil {
479 t.Fatalf("child iteration failed: %v", err)
480 }
481
482 if len(children) != 3 {
483 t.Errorf("expected 3 children, got %d: %v", len(children), children)
484 }
485
486 // Children should be in order: div, span, p (inserted at index 0)
487 expected := []string{"elem", "elem", "elem"}
488 for i, exp := range expected {
489 if children[i] != exp {
490 t.Errorf("expected child[%d] to be '%s', got '%s'", i, exp, children[i])
491 }
492 }
493}
494
495func TestXmlChildIterMixed(t *testing.T) {
496 doc, err := ygo.NewDoc()
497 if err != nil {
498 t.Fatalf("failed to create doc: %v", err)
499 }
500 defer doc.Destroy()
501
502 frag, err := doc.GetXmlFragment("test")
503 if err != nil {
504 t.Fatalf("failed to get fragment: %v", err)
505 }
506 defer frag.Destroy()
507
508 var elem *ygo.XmlElement
509 err = doc.WithWriteTransaction(func(txn *ygo.Transaction) error {
510 var err error
511 elem, err = frag.InsertElement(txn, 0, "div")
512 if err != nil {
513 return err
514 }
515
516 // Insert mixed content: text, element, text, element
517 txt1, err := elem.InsertText(txn, 0)
518 if err != nil {
519 return err
520 }
521 txt1.Insert(txn, 0, "Hello ")
522 txt1.Destroy()
523
524 span, err := elem.InsertElement(txn, 1, "span")
525 if err != nil {
526 return err
527 }
528 span.Destroy()
529
530 txt2, err := elem.InsertText(txn, 2)
531 if err != nil {
532 return err
533 }
534 txt2.Insert(txn, 0, " World")
535 txt2.Destroy()
536
537 p, err := elem.InsertElement(txn, 3, "p")
538 if err != nil {
539 return err
540 }
541 p.Destroy()
542
543 return nil
544 })
545 if err != nil {
546 t.Fatalf("transaction failed: %v", err)
547 }
548 defer elem.Destroy()
549
550 var children []string
551 err = doc.WithReadTransaction(func(txn *ygo.Transaction) error {
552 var err error
553 children, err = collectChildren(elem, txn)
554 return err
555 })
556 if err != nil {
557 t.Fatalf("child iteration failed: %v", err)
558 }
559
560 if len(children) != 4 {
561 t.Errorf("expected 4 children, got %d: %v", len(children), children)
562 }
563
564 // Check all children are present in order
565 expected := []string{"text", "elem", "text", "elem"}
566 for i, exp := range expected {
567 if children[i] != exp {
568 t.Errorf("expected child[%d] to be '%s', got '%s'", i, exp, children[i])
569 }
570 }
571}
572
573func TestXmlElementMarshalJSON(t *testing.T) {
574 doc, err := ygo.NewDoc()
575 if err != nil {
576 t.Fatalf("failed to create doc: %v", err)
577 }
578 defer doc.Destroy()
579
580 frag, err := doc.GetXmlFragment("test")
581 if err != nil {
582 t.Fatalf("failed to get fragment: %v", err)
583 }
584 defer frag.Destroy()
585
586 var elem *ygo.XmlElement
587 err = doc.WithWriteTransaction(func(txn *ygo.Transaction) error {
588 var err error
589 elem, err = frag.InsertElement(txn, 0, "div")
590 if err != nil {
591 return err
592 }
593
594 elem.SetAttribute(txn, "class", ygo.String("container"))
595
596 // Add text child
597 text, err := elem.InsertText(txn, 0)
598 if err != nil {
599 return err
600 }
601 text.Insert(txn, 0, "Hello World")
602
603 return nil
604 })
605 if err != nil {
606 t.Fatalf("transaction failed: %v", err)
607 }
608 defer elem.Destroy()
609
610 // Marshal to JSON
611 var jsonBytes []byte
612 err = doc.WithReadTransaction(func(txn *ygo.Transaction) error {
613 var err error
614 jsonBytes, err = elem.MarshalJSONWithTxn(txn)
615 return err
616 })
617 if err != nil {
618 t.Fatalf("marshal failed: %v", err)
619 }
620
621 // Verify JSON structure
622 var result map[string]interface{}
623 if err := json.Unmarshal(jsonBytes, &result); err != nil {
624 t.Fatalf("failed to unmarshal: %v", err)
625 }
626
627 if result["type"] != "div" {
628 t.Errorf("expected type=div, got %v", result["type"])
629 }
630
631 attrs, ok := result["attrs"].(map[string]interface{})
632 if !ok {
633 t.Fatalf("expected attrs to be a map, got %T", result["attrs"])
634 }
635 if attrs["class"] != "container" {
636 t.Errorf("expected class=container, got %v", attrs["class"])
637 }
638
639 children, ok := result["children"].([]interface{})
640 if !ok {
641 t.Fatalf("expected children to be an array, got %T", result["children"])
642 }
643 if len(children) != 1 {
644 t.Errorf("expected 1 child, got %d", len(children))
645 }
646}