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}