compat_test.go
1package ygo_test
2
3import (
4 "encoding/json"
5 "os"
6 "testing"
7
8 "git.kilimanjaro.io/ygo"
9)
10
11func TestYjsCompatibility(t *testing.T) {
12 t.Run("shared YText", func(t *testing.T) {
13 data, err := os.ReadFile("testdata/shared-YText.yjs")
14 if err != nil {
15 t.Fatalf("failed to read testdata: %v", err)
16 }
17
18 doc, err := ygo.NewDoc()
19 if err != nil {
20 t.Fatalf("failed to create doc: %v", err)
21 }
22 defer doc.Destroy()
23
24 err = doc.WithWriteTransaction(func(txn *ygo.Transaction) error {
25 update := ygo.UpdateFromBytes(data)
26 return txn.ApplyUpdate(update)
27 })
28 if err != nil {
29 t.Fatalf("failed to apply update: %v", err)
30 }
31
32 txt, err := doc.GetText("test")
33 if err != nil {
34 t.Fatalf("failed to get text: %v", err)
35 }
36 defer txt.Destroy()
37
38 var content string
39 err = doc.WithReadTransaction(func(txn *ygo.Transaction) error {
40 var err error
41 content, err = txt.String(txn)
42 return err
43 })
44 if err != nil {
45 t.Fatalf("failed to read text: %v", err)
46 }
47
48 if content != "test text" {
49 t.Errorf("expected 'test text', got %q", content)
50 }
51 })
52
53 t.Run("map value types", func(t *testing.T) {
54 data, err := os.ReadFile("testdata/map-value-types.yjs")
55 if err != nil {
56 t.Fatalf("failed to read testdata: %v", err)
57 }
58
59 doc, err := ygo.NewDoc()
60 if err != nil {
61 t.Fatalf("failed to create doc: %v", err)
62 }
63 defer doc.Destroy()
64
65 err = doc.WithWriteTransaction(func(txn *ygo.Transaction) error {
66 update := ygo.UpdateFromBytes(data)
67 return txn.ApplyUpdate(update)
68 })
69 if err != nil {
70 t.Fatalf("failed to apply update: %v", err)
71 }
72
73 m, err := doc.GetMap("test")
74 if err != nil {
75 t.Fatalf("failed to get map: %v", err)
76 }
77 defer m.Destroy()
78
79 err = doc.WithReadTransaction(func(txn *ygo.Transaction) error {
80 out, err := m.Get(txn, "string")
81 if err != nil {
82 t.Errorf("failed to get 'string': %v", err)
83 } else if v, ok := out.String(); !ok || v != "value" {
84 t.Errorf("expected string='value', got %v", v)
85 }
86 out.Destroy()
87
88 out, err = m.Get(txn, "bool")
89 if err != nil {
90 t.Errorf("failed to get 'bool': %v", err)
91 } else if v, ok := out.Bool(); !ok || v != true {
92 t.Errorf("expected bool=true, got %v", v)
93 }
94 out.Destroy()
95
96 out, err = m.Get(txn, "number")
97 if err != nil {
98 t.Errorf("failed to get 'number': %v", err)
99 } else if v, ok := out.Float(); !ok || v != 1 {
100 t.Errorf("expected number=1, got %v", v)
101 }
102 out.Destroy()
103
104 out, err = m.Get(txn, "object")
105 if err != nil {
106 t.Errorf("failed to get 'object': %v", err)
107 } else {
108 var obj map[string]interface{}
109 if err := out.JSON(&obj); err != nil {
110 t.Errorf("failed to parse JSON: %v", err)
111 } else {
112 if obj["foo"] != "bar" {
113 t.Errorf("expected object.foo='bar', got %v", obj["foo"])
114 }
115 if obj["num"] != float64(1) {
116 t.Errorf("expected object.num=1, got %v", obj["num"])
117 }
118 if obj["test"] != true {
119 t.Errorf("expected object.test=true, got %v", obj["test"])
120 }
121 }
122 }
123 out.Destroy()
124
125 return nil
126 })
127 if err != nil {
128 t.Fatalf("map verification failed: %v", err)
129 }
130 })
131
132 t.Run("shared YArray", func(t *testing.T) {
133 data, err := os.ReadFile("testdata/shared-YArray.yjs")
134 if err != nil {
135 t.Fatalf("failed to read testdata: %v", err)
136 }
137
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 err = doc.WithWriteTransaction(func(txn *ygo.Transaction) error {
145 update := ygo.UpdateFromBytes(data)
146 return txn.ApplyUpdate(update)
147 })
148 if err != nil {
149 t.Fatalf("failed to apply update: %v", err)
150 }
151
152 arr, err := doc.GetArray("test")
153 if err != nil {
154 t.Fatalf("failed to get array: %v", err)
155 }
156 defer arr.Destroy()
157
158 var length uint32
159 err = doc.WithReadTransaction(func(txn *ygo.Transaction) error {
160 var err error
161 length = arr.Len()
162 return err
163 })
164 if err != nil {
165 t.Fatalf("failed to get array length: %v", err)
166 }
167
168 if length != 4 {
169 t.Errorf("expected array length 4, got %d", length)
170 }
171
172 err = doc.WithReadTransaction(func(txn *ygo.Transaction) error {
173 out, err := arr.Get(txn, 0)
174 if err != nil {
175 t.Errorf("failed to get element 0: %v", err)
176 } else if v, ok := out.String(); !ok || v != "first" {
177 t.Errorf("expected element 0='first', got %v", v)
178 }
179 out.Destroy()
180
181 out, err = arr.Get(txn, 1)
182 if err != nil {
183 t.Errorf("failed to get element 1: %v", err)
184 } else if v, ok := out.String(); !ok || v != "middle" {
185 t.Errorf("expected element 1='middle', got %v", v)
186 }
187 out.Destroy()
188
189 out, err = arr.Get(txn, 2)
190 if err != nil {
191 t.Errorf("failed to get element 2: %v", err)
192 } else if v, ok := out.String(); !ok || v != "second" {
193 t.Errorf("expected element 2='second', got %v", v)
194 }
195 out.Destroy()
196
197 out, err = arr.Get(txn, 3)
198 if err != nil {
199 t.Errorf("failed to get element 3: %v", err)
200 } else if v, ok := out.String(); !ok || v != "third" {
201 t.Errorf("expected element 3='third', got %v", v)
202 }
203 out.Destroy()
204
205 return nil
206 })
207 if err != nil {
208 t.Fatalf("array element verification failed: %v", err)
209 }
210 })
211
212 t.Run("nested XmlElement with attrs", func(t *testing.T) {
213 data, err := os.ReadFile("testdata/nested-XmlElement-with-attrs.yjs")
214 if err != nil {
215 t.Fatalf("failed to read testdata: %v", err)
216 }
217
218 doc, err := ygo.NewDoc()
219 if err != nil {
220 t.Fatalf("failed to create doc: %v", err)
221 }
222 defer doc.Destroy()
223
224 err = doc.WithWriteTransaction(func(txn *ygo.Transaction) error {
225 update := ygo.UpdateFromBytes(data)
226 return txn.ApplyUpdate(update)
227 })
228 if err != nil {
229 t.Fatalf("failed to apply update: %v", err)
230 }
231
232 frag, err := doc.GetXmlFragment("test")
233 if err != nil {
234 t.Fatalf("failed to get xml fragment: %v", err)
235 }
236 defer frag.Destroy()
237
238 err = doc.WithReadTransaction(func(txn *ygo.Transaction) error {
239 jsonBytes, err := frag.MarshalJSONWithTxn(txn)
240 if err != nil {
241 return err
242 }
243
244 var children []interface{}
245 if err := json.Unmarshal(jsonBytes, &children); err != nil {
246 return err
247 }
248
249 if len(children) < 1 {
250 t.Error("expected at least one child")
251 return nil
252 }
253
254 div, ok := children[0].(map[string]interface{})
255 if !ok {
256 t.Error("expected first child to be a map")
257 return nil
258 }
259
260 if div["type"] != "div" {
261 t.Errorf("expected type='div', got %v", div["type"])
262 }
263
264 attrs, ok := div["attrs"].(map[string]interface{})
265 if !ok {
266 t.Error("expected attrs to be a map")
267 return nil
268 }
269 if attrs["class"] != "container" {
270 t.Errorf("expected class='container', got %v", attrs["class"])
271 }
272 if attrs["data-id"] != "main" {
273 t.Errorf("expected data-id='main', got %v", attrs["data-id"])
274 }
275
276 childList, ok := div["children"].([]interface{})
277 if !ok {
278 t.Error("expected children to be a list")
279 return nil
280 }
281 if len(childList) != 2 {
282 t.Errorf("expected 2 children, got %d", len(childList))
283 }
284
285 return nil
286 })
287 if err != nil {
288 t.Fatalf("xml verification failed: %v", err)
289 }
290 })
291
292 t.Run("mixed content array", func(t *testing.T) {
293 data, err := os.ReadFile("testdata/mixed-content-array.yjs")
294 if err != nil {
295 t.Fatalf("failed to read testdata: %v", err)
296 }
297
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 err = doc.WithWriteTransaction(func(txn *ygo.Transaction) error {
305 update := ygo.UpdateFromBytes(data)
306 return txn.ApplyUpdate(update)
307 })
308 if err != nil {
309 t.Fatalf("failed to apply update: %v", err)
310 }
311
312 arr, err := doc.GetArray("test")
313 if err != nil {
314 t.Fatalf("failed to get array: %v", err)
315 }
316 defer arr.Destroy()
317
318 var length uint32
319 err = doc.WithReadTransaction(func(txn *ygo.Transaction) error {
320 var err error
321 length = arr.Len()
322 return err
323 })
324 if err != nil {
325 t.Fatalf("failed to get array length: %v", err)
326 }
327
328 if length != 5 {
329 t.Errorf("expected array length 5, got %d", length)
330 }
331
332 err = doc.WithReadTransaction(func(txn *ygo.Transaction) error {
333 out, err := arr.Get(txn, 0)
334 if err != nil {
335 t.Errorf("failed to get element 0: %v", err)
336 } else if v, ok := out.String(); !ok || v != "string" {
337 t.Errorf("expected element 0='string', got %v", v)
338 }
339 out.Destroy()
340
341 out, err = arr.Get(txn, 1)
342 if err != nil {
343 t.Errorf("failed to get element 1: %v", err)
344 } else if v, ok := out.Float(); !ok || v != 42 {
345 t.Errorf("expected element 1=42, got %v", v)
346 }
347 out.Destroy()
348
349 out, err = arr.Get(txn, 2)
350 if err != nil {
351 t.Errorf("failed to get element 2: %v", err)
352 } else if v, ok := out.Bool(); !ok || v != true {
353 t.Errorf("expected element 2=true, got %v", v)
354 }
355 out.Destroy()
356
357 out, err = arr.Get(txn, 3)
358 if err != nil {
359 t.Errorf("failed to get element 3: %v", err)
360 } else if !out.IsNull() {
361 t.Errorf("expected element 3=null")
362 }
363 out.Destroy()
364
365 out, err = arr.Get(txn, 4)
366 if err != nil {
367 t.Errorf("failed to get element 4: %v", err)
368 } else {
369 var obj map[string]interface{}
370 if err := out.JSON(&obj); err != nil {
371 t.Errorf("failed to parse JSON: %v", err)
372 } else {
373 if obj["nested"] != "object" {
374 t.Errorf("expected element 4.nested='object', got %v", obj["nested"])
375 }
376 }
377 }
378 out.Destroy()
379
380 return nil
381 })
382 if err != nil {
383 t.Fatalf("array element verification failed: %v", err)
384 }
385 })
386
387 t.Run("nested YMap in YMap", func(t *testing.T) {
388 data, err := os.ReadFile("testdata/nested-YMap-in-YMap.yjs")
389 if err != nil {
390 t.Fatalf("failed to read testdata: %v", err)
391 }
392
393 doc, err := ygo.NewDoc()
394 if err != nil {
395 t.Fatalf("failed to create doc: %v", err)
396 }
397 defer doc.Destroy()
398
399 err = doc.WithWriteTransaction(func(txn *ygo.Transaction) error {
400 update := ygo.UpdateFromBytes(data)
401 return txn.ApplyUpdate(update)
402 })
403 if err != nil {
404 t.Fatalf("failed to apply update: %v", err)
405 }
406
407 m, err := doc.GetMap("test")
408 if err != nil {
409 t.Fatalf("failed to get map: %v", err)
410 }
411 defer m.Destroy()
412
413 err = doc.WithReadTransaction(func(txn *ygo.Transaction) error {
414 out, err := m.Get(txn, "child")
415 if err != nil {
416 t.Fatalf("failed to get 'child': %v", err)
417 }
418 defer out.Destroy()
419
420 if out.Tag() != ygo.TagMap {
421 t.Fatalf("expected TagMap, got %v", out.Tag())
422 }
423
424 childMap, err := m.GetNestedMap(txn, "child")
425 if err != nil {
426 t.Fatalf("failed to get nested map: %v", err)
427 }
428 defer childMap.Destroy()
429
430 childOut, err := childMap.Get(txn, "key1")
431 if err != nil {
432 t.Fatalf("failed to get nested key1: %v", err)
433 }
434 defer childOut.Destroy()
435
436 val, ok := childOut.String()
437 if !ok || val != "value1" {
438 t.Errorf("expected 'value1', got %v", val)
439 }
440
441 childOut, err = childMap.Get(txn, "key2")
442 if err != nil {
443 t.Fatalf("failed to get nested key2: %v", err)
444 }
445 defer childOut.Destroy()
446
447 num, ok := childOut.Float()
448 if !ok || num != 42 {
449 t.Errorf("expected 42, got %v", num)
450 }
451
452 return nil
453 })
454 if err != nil {
455 t.Fatalf("nested map test failed: %v", err)
456 }
457 })
458
459 t.Run("nested YArray in YMap", func(t *testing.T) {
460 data, err := os.ReadFile("testdata/nested-YArray-in-YMap.yjs")
461 if err != nil {
462 t.Fatalf("failed to read testdata: %v", err)
463 }
464
465 doc, err := ygo.NewDoc()
466 if err != nil {
467 t.Fatalf("failed to create doc: %v", err)
468 }
469 defer doc.Destroy()
470
471 err = doc.WithWriteTransaction(func(txn *ygo.Transaction) error {
472 update := ygo.UpdateFromBytes(data)
473 return txn.ApplyUpdate(update)
474 })
475 if err != nil {
476 t.Fatalf("failed to apply update: %v", err)
477 }
478
479 m, err := doc.GetMap("test")
480 if err != nil {
481 t.Fatalf("failed to get map: %v", err)
482 }
483 defer m.Destroy()
484
485 err = doc.WithReadTransaction(func(txn *ygo.Transaction) error {
486 arr, err := m.GetNestedArray(txn, "arr")
487 if err != nil {
488 t.Fatalf("failed to get nested array: %v", err)
489 }
490 defer arr.Destroy()
491
492 length := arr.Len()
493 if length != 3 {
494 t.Errorf("expected length 3, got %d", length)
495 }
496
497 out, err := arr.Get(txn, 0)
498 if err != nil {
499 t.Fatalf("failed to get element 0: %v", err)
500 }
501 defer out.Destroy()
502
503 val, ok := out.String()
504 if !ok || val != "a" {
505 t.Errorf("expected 'a', got %v", val)
506 }
507
508 out, err = arr.Get(txn, 2)
509 if err != nil {
510 t.Fatalf("failed to get element 2: %v", err)
511 }
512 defer out.Destroy()
513
514 val, ok = out.String()
515 if !ok || val != "c" {
516 t.Errorf("expected 'c', got %v", val)
517 }
518
519 return nil
520 })
521 if err != nil {
522 t.Fatalf("nested array test failed: %v", err)
523 }
524 })
525
526 t.Run("nested YMap in YArray", func(t *testing.T) {
527 data, err := os.ReadFile("testdata/nested-YMap-in-YArray.yjs")
528 if err != nil {
529 t.Fatalf("failed to read testdata: %v", err)
530 }
531
532 doc, err := ygo.NewDoc()
533 if err != nil {
534 t.Fatalf("failed to create doc: %v", err)
535 }
536 defer doc.Destroy()
537
538 err = doc.WithWriteTransaction(func(txn *ygo.Transaction) error {
539 update := ygo.UpdateFromBytes(data)
540 return txn.ApplyUpdate(update)
541 })
542 if err != nil {
543 t.Fatalf("failed to apply update: %v", err)
544 }
545
546 arr, err := doc.GetArray("test")
547 if err != nil {
548 t.Fatalf("failed to get array: %v", err)
549 }
550 defer arr.Destroy()
551
552 err = doc.WithReadTransaction(func(txn *ygo.Transaction) error {
553 length := arr.Len()
554 if length != 2 {
555 t.Errorf("expected length 2, got %d", length)
556 }
557
558 itemOut, err := arr.Get(txn, 0)
559 if err != nil {
560 t.Fatalf("failed to get element 0: %v", err)
561 }
562 defer itemOut.Destroy()
563
564 childMap, err := itemOut.ToMap()
565 if err != nil {
566 t.Fatalf("failed to convert to map: %v", err)
567 }
568 defer childMap.Destroy()
569
570 nameOut, err := childMap.Get(txn, "name")
571 if err != nil {
572 t.Fatalf("failed to get nested 'name': %v", err)
573 }
574 defer nameOut.Destroy()
575
576 val, ok := nameOut.String()
577 if !ok || val != "first" {
578 t.Errorf("expected 'first', got %v", val)
579 }
580
581 itemOut, err = arr.Get(txn, 1)
582 if err != nil {
583 t.Fatalf("failed to get element 1: %v", err)
584 }
585 defer itemOut.Destroy()
586
587 childMap, err = itemOut.ToMap()
588 if err != nil {
589 t.Fatalf("failed to convert to map: %v", err)
590 }
591
592 nameOut, err = childMap.Get(txn, "name")
593 if err != nil {
594 t.Fatalf("failed to get nested 'name' from element 1: %v", err)
595 }
596 defer nameOut.Destroy()
597
598 val, ok = nameOut.String()
599 if !ok || val != "second" {
600 t.Errorf("expected 'second', got %v", val)
601 }
602
603 return nil
604 })
605 if err != nil {
606 t.Fatalf("nested map in array test failed: %v", err)
607 }
608 })
609}