cli + tui to publish to leaflet (wip) & manage tasks, notes & watch/read lists 馃崈
charm
leaflet
readability
golang
1package public
2
3import (
4 "encoding/json"
5 "testing"
6
7 "github.com/stormlightlabs/noteleaf/internal/shared"
8)
9
10func TestBlockWrap(t *testing.T) {
11 t.Run("UnmarshalJSON", func(t *testing.T) {
12 t.Run("unmarshals text block", func(t *testing.T) {
13 jsonData := `{
14 "$type": "pub.leaflet.pages.linearDocument#block",
15 "block": {
16 "$type": "pub.leaflet.blocks.text",
17 "plaintext": "Hello world"
18 }
19 }`
20
21 var bw BlockWrap
22 err := json.Unmarshal([]byte(jsonData), &bw)
23 shared.AssertNoError(t, err, "unmarshal should succeed")
24 shared.AssertEqual(t, TypeBlock, bw.Type, "type should match")
25
26 block, ok := bw.Block.(TextBlock)
27 shared.AssertTrue(t, ok, "block should be TextBlock")
28 shared.AssertEqual(t, TypeTextBlock, block.Type, "block type should match")
29 shared.AssertEqual(t, "Hello world", block.Plaintext, "plaintext should match")
30 })
31
32 t.Run("unmarshals header block", func(t *testing.T) {
33 jsonData := `{
34 "$type": "pub.leaflet.pages.linearDocument#block",
35 "block": {
36 "$type": "pub.leaflet.blocks.header",
37 "level": 2,
38 "plaintext": "Section Title"
39 }
40 }`
41
42 var bw BlockWrap
43 err := json.Unmarshal([]byte(jsonData), &bw)
44 shared.AssertNoError(t, err, "unmarshal should succeed")
45
46 block, ok := bw.Block.(HeaderBlock)
47 shared.AssertTrue(t, ok, "block should be HeaderBlock")
48 shared.AssertEqual(t, TypeHeaderBlock, block.Type, "block type should match")
49 shared.AssertEqual(t, 2, block.Level, "level should match")
50 shared.AssertEqual(t, "Section Title", block.Plaintext, "plaintext should match")
51 })
52
53 t.Run("unmarshals code block", func(t *testing.T) {
54 jsonData := `{
55 "$type": "pub.leaflet.pages.linearDocument#block",
56 "block": {
57 "$type": "pub.leaflet.blocks.code",
58 "plaintext": "fmt.Println(\"test\")",
59 "language": "go"
60 }
61 }`
62
63 var bw BlockWrap
64 err := json.Unmarshal([]byte(jsonData), &bw)
65 shared.AssertNoError(t, err, "unmarshal should succeed")
66
67 block, ok := bw.Block.(CodeBlock)
68 shared.AssertTrue(t, ok, "block should be CodeBlock")
69 shared.AssertEqual(t, TypeCodeBlock, block.Type, "block type should match")
70 shared.AssertEqual(t, "go", block.Language, "language should match")
71 shared.AssertEqual(t, "fmt.Println(\"test\")", block.Plaintext, "plaintext should match")
72 })
73
74 t.Run("unmarshals image block", func(t *testing.T) {
75 jsonData := `{
76 "$type": "pub.leaflet.pages.linearDocument#block",
77 "block": {
78 "$type": "pub.leaflet.blocks.image",
79 "image": {
80 "$type": "blob",
81 "ref": {
82 "$link": "bafytest123"
83 },
84 "mimeType": "image/png",
85 "size": 1024
86 },
87 "alt": "Test image",
88 "aspectRatio": {
89 "$type": "pub.leaflet.blocks.image#aspectRatio",
90 "width": 800,
91 "height": 600
92 }
93 }
94 }`
95
96 var bw BlockWrap
97 err := json.Unmarshal([]byte(jsonData), &bw)
98 shared.AssertNoError(t, err, "unmarshal should succeed")
99
100 block, ok := bw.Block.(ImageBlock)
101 shared.AssertTrue(t, ok, "block should be ImageBlock")
102 shared.AssertEqual(t, TypeImageBlock, block.Type, "block type should match")
103 shared.AssertEqual(t, "Test image", block.Alt, "alt text should match")
104 shared.AssertEqual(t, 800, block.AspectRatio.Width, "width should match")
105 shared.AssertEqual(t, 600, block.AspectRatio.Height, "height should match")
106 shared.AssertEqual(t, "image/png", block.Image.MimeType, "mime type should match")
107 shared.AssertEqual(t, 1024, block.Image.Size, "size should match")
108 })
109
110 t.Run("unmarshals blockquote block", func(t *testing.T) {
111 jsonData := `{
112 "$type": "pub.leaflet.pages.linearDocument#block",
113 "block": {
114 "$type": "pub.leaflet.blocks.blockquote",
115 "plaintext": "This is a quote"
116 }
117 }`
118
119 var bw BlockWrap
120 err := json.Unmarshal([]byte(jsonData), &bw)
121 shared.AssertNoError(t, err, "unmarshal should succeed")
122
123 block, ok := bw.Block.(BlockquoteBlock)
124 shared.AssertTrue(t, ok, "block should be BlockquoteBlock")
125 shared.AssertEqual(t, TypeBlockquoteBlock, block.Type, "block type should match")
126 shared.AssertEqual(t, "This is a quote", block.Plaintext, "plaintext should match")
127 })
128
129 t.Run("unmarshals unordered list block", func(t *testing.T) {
130 jsonData := `{
131 "$type": "pub.leaflet.pages.linearDocument#block",
132 "block": {
133 "$type": "pub.leaflet.blocks.unorderedList",
134 "children": [
135 {
136 "$type": "pub.leaflet.blocks.unorderedList#listItem",
137 "content": {
138 "$type": "pub.leaflet.blocks.text",
139 "plaintext": "First item"
140 }
141 }
142 ]
143 }
144 }`
145
146 var bw BlockWrap
147 err := json.Unmarshal([]byte(jsonData), &bw)
148 shared.AssertNoError(t, err, "unmarshal should succeed")
149
150 block, ok := bw.Block.(UnorderedListBlock)
151 shared.AssertTrue(t, ok, "block should be UnorderedListBlock")
152 shared.AssertEqual(t, TypeUnorderedListBlock, block.Type, "block type should match")
153 shared.AssertEqual(t, 1, len(block.Children), "should have 1 child")
154 })
155
156 t.Run("unmarshals horizontal rule block", func(t *testing.T) {
157 jsonData := `{
158 "$type": "pub.leaflet.pages.linearDocument#block",
159 "block": {
160 "$type": "pub.leaflet.blocks.horizontalRule"
161 }
162 }`
163
164 var bw BlockWrap
165 err := json.Unmarshal([]byte(jsonData), &bw)
166 shared.AssertNoError(t, err, "unmarshal should succeed")
167
168 block, ok := bw.Block.(HorizontalRuleBlock)
169 shared.AssertTrue(t, ok, "block should be HorizontalRuleBlock")
170 shared.AssertEqual(t, TypeHorizontalRuleBlock, block.Type, "block type should match")
171 })
172
173 t.Run("unmarshals unknown block type as map", func(t *testing.T) {
174 jsonData := `{
175 "$type": "pub.leaflet.pages.linearDocument#block",
176 "block": {
177 "$type": "pub.leaflet.blocks.unknown",
178 "customField": "value"
179 }
180 }`
181
182 var bw BlockWrap
183 err := json.Unmarshal([]byte(jsonData), &bw)
184 shared.AssertNoError(t, err, "unmarshal should succeed")
185
186 block, ok := bw.Block.(map[string]any)
187 shared.AssertTrue(t, ok, "block should be map for unknown type")
188 shared.AssertEqual(t, "pub.leaflet.blocks.unknown", block["$type"], "type should be preserved")
189 })
190
191 t.Run("handles block with alignment", func(t *testing.T) {
192 jsonData := `{
193 "$type": "pub.leaflet.pages.linearDocument#block",
194 "block": {
195 "$type": "pub.leaflet.blocks.text",
196 "plaintext": "Centered text"
197 },
198 "alignment": "#textAlignCenter"
199 }`
200
201 var bw BlockWrap
202 err := json.Unmarshal([]byte(jsonData), &bw)
203 shared.AssertNoError(t, err, "unmarshal should succeed")
204 shared.AssertEqual(t, "#textAlignCenter", bw.Alignment, "alignment should match")
205 })
206
207 t.Run("returns error for invalid JSON", func(t *testing.T) {
208 jsonData := `{invalid json`
209
210 var bw BlockWrap
211 err := json.Unmarshal([]byte(jsonData), &bw)
212 shared.AssertError(t, err, "invalid JSON should return error")
213 })
214
215 t.Run("returns error for malformed block", func(t *testing.T) {
216 jsonData := `{
217 "$type": "pub.leaflet.pages.linearDocument#block",
218 "block": "not an object"
219 }`
220
221 var bw BlockWrap
222 err := json.Unmarshal([]byte(jsonData), &bw)
223 shared.AssertError(t, err, "malformed block should return error")
224 })
225 })
226}
227
228func TestListItem(t *testing.T) {
229 t.Run("UnmarshalJSON", func(t *testing.T) {
230 t.Run("unmarshals text block content", func(t *testing.T) {
231 jsonData := `{
232 "$type": "pub.leaflet.blocks.unorderedList#listItem",
233 "content": {
234 "$type": "pub.leaflet.blocks.text",
235 "plaintext": "List item text"
236 }
237 }`
238
239 var li ListItem
240 err := json.Unmarshal([]byte(jsonData), &li)
241 shared.AssertNoError(t, err, "unmarshal should succeed")
242 shared.AssertEqual(t, TypeListItem, li.Type, "type should match")
243
244 content, ok := li.Content.(TextBlock)
245 shared.AssertTrue(t, ok, "content should be TextBlock")
246 shared.AssertEqual(t, "List item text", content.Plaintext, "plaintext should match")
247 })
248
249 t.Run("unmarshals header block content", func(t *testing.T) {
250 jsonData := `{
251 "$type": "pub.leaflet.blocks.unorderedList#listItem",
252 "content": {
253 "$type": "pub.leaflet.blocks.header",
254 "level": 3,
255 "plaintext": "List header"
256 }
257 }`
258
259 var li ListItem
260 err := json.Unmarshal([]byte(jsonData), &li)
261 shared.AssertNoError(t, err, "unmarshal should succeed")
262
263 content, ok := li.Content.(HeaderBlock)
264 shared.AssertTrue(t, ok, "content should be HeaderBlock")
265 shared.AssertEqual(t, 3, content.Level, "level should match")
266 })
267
268 t.Run("unmarshals image block content", func(t *testing.T) {
269 jsonData := `{
270 "$type": "pub.leaflet.blocks.unorderedList#listItem",
271 "content": {
272 "$type": "pub.leaflet.blocks.image",
273 "image": {
274 "$type": "blob",
275 "ref": {"$link": "cid123"},
276 "mimeType": "image/jpeg",
277 "size": 2048
278 },
279 "alt": "List image",
280 "aspectRatio": {
281 "$type": "pub.leaflet.blocks.image#aspectRatio",
282 "width": 400,
283 "height": 300
284 }
285 }
286 }`
287
288 var li ListItem
289 err := json.Unmarshal([]byte(jsonData), &li)
290 shared.AssertNoError(t, err, "unmarshal should succeed")
291
292 content, ok := li.Content.(ImageBlock)
293 shared.AssertTrue(t, ok, "content should be ImageBlock")
294 shared.AssertEqual(t, "List image", content.Alt, "alt should match")
295 })
296
297 t.Run("unmarshals nested list items", func(t *testing.T) {
298 jsonData := `{
299 "$type": "pub.leaflet.blocks.unorderedList#listItem",
300 "content": {
301 "$type": "pub.leaflet.blocks.text",
302 "plaintext": "Parent item"
303 },
304 "children": [
305 {
306 "$type": "pub.leaflet.blocks.unorderedList#listItem",
307 "content": {
308 "$type": "pub.leaflet.blocks.text",
309 "plaintext": "Child item"
310 }
311 }
312 ]
313 }`
314
315 var li ListItem
316 err := json.Unmarshal([]byte(jsonData), &li)
317 shared.AssertNoError(t, err, "unmarshal should succeed")
318 shared.AssertEqual(t, 1, len(li.Children), "should have 1 child")
319
320 childContent, ok := li.Children[0].Content.(TextBlock)
321 shared.AssertTrue(t, ok, "child content should be TextBlock")
322 shared.AssertEqual(t, "Child item", childContent.Plaintext, "child plaintext should match")
323 })
324
325 t.Run("unmarshals unknown content type as map", func(t *testing.T) {
326 jsonData := `{
327 "$type": "pub.leaflet.blocks.unorderedList#listItem",
328 "content": {
329 "$type": "pub.leaflet.blocks.custom",
330 "data": "value"
331 }
332 }`
333
334 var li ListItem
335 err := json.Unmarshal([]byte(jsonData), &li)
336 shared.AssertNoError(t, err, "unmarshal should succeed")
337
338 content, ok := li.Content.(map[string]any)
339 shared.AssertTrue(t, ok, "unknown content should be map")
340 shared.AssertEqual(t, "pub.leaflet.blocks.custom", content["$type"], "type should be preserved")
341 })
342
343 t.Run("returns error for invalid JSON", func(t *testing.T) {
344 jsonData := `{invalid`
345
346 var li ListItem
347 err := json.Unmarshal([]byte(jsonData), &li)
348 shared.AssertError(t, err, "invalid JSON should return error")
349 })
350 })
351}
352
353func TestFacet(t *testing.T) {
354 t.Run("UnmarshalJSON", func(t *testing.T) {
355 t.Run("unmarshals bold facet", func(t *testing.T) {
356 jsonData := `{
357 "$type": "pub.leaflet.richtext.facet",
358 "index": {
359 "$type": "pub.leaflet.richtext.facet#byteSlice",
360 "byteStart": 0,
361 "byteEnd": 5
362 },
363 "features": [
364 {
365 "$type": "pub.leaflet.richtext.facet#bold"
366 }
367 ]
368 }`
369
370 var f Facet
371 err := json.Unmarshal([]byte(jsonData), &f)
372 shared.AssertNoError(t, err, "unmarshal should succeed")
373 shared.AssertEqual(t, TypeFacet, f.Type, "type should match")
374 shared.AssertEqual(t, 0, f.Index.ByteStart, "byte start should match")
375 shared.AssertEqual(t, 5, f.Index.ByteEnd, "byte end should match")
376 shared.AssertEqual(t, 1, len(f.Features), "should have 1 feature")
377
378 bold, ok := f.Features[0].(FacetBold)
379 shared.AssertTrue(t, ok, "feature should be FacetBold")
380 shared.AssertEqual(t, TypeFacetBold, bold.GetFacetType(), "facet type should match")
381 })
382
383 t.Run("unmarshals italic facet", func(t *testing.T) {
384 jsonData := `{
385 "$type": "pub.leaflet.richtext.facet",
386 "index": {
387 "$type": "pub.leaflet.richtext.facet#byteSlice",
388 "byteStart": 5,
389 "byteEnd": 10
390 },
391 "features": [
392 {
393 "$type": "pub.leaflet.richtext.facet#italic"
394 }
395 ]
396 }`
397
398 var f Facet
399 err := json.Unmarshal([]byte(jsonData), &f)
400 shared.AssertNoError(t, err, "unmarshal should succeed")
401
402 italic, ok := f.Features[0].(FacetItalic)
403 shared.AssertTrue(t, ok, "feature should be FacetItalic")
404 shared.AssertEqual(t, TypeFacetItalic, italic.GetFacetType(), "facet type should match")
405 })
406
407 t.Run("unmarshals code facet", func(t *testing.T) {
408 jsonData := `{
409 "$type": "pub.leaflet.richtext.facet",
410 "index": {
411 "$type": "pub.leaflet.richtext.facet#byteSlice",
412 "byteStart": 0,
413 "byteEnd": 10
414 },
415 "features": [
416 {
417 "$type": "pub.leaflet.richtext.facet#code"
418 }
419 ]
420 }`
421
422 var f Facet
423 err := json.Unmarshal([]byte(jsonData), &f)
424 shared.AssertNoError(t, err, "unmarshal should succeed")
425
426 code, ok := f.Features[0].(FacetCode)
427 shared.AssertTrue(t, ok, "feature should be FacetCode")
428 shared.AssertEqual(t, TypeFacetCode, code.GetFacetType(), "facet type should match")
429 })
430
431 t.Run("unmarshals link facet", func(t *testing.T) {
432 jsonData := `{
433 "$type": "pub.leaflet.richtext.facet",
434 "index": {
435 "$type": "pub.leaflet.richtext.facet#byteSlice",
436 "byteStart": 0,
437 "byteEnd": 15
438 },
439 "features": [
440 {
441 "$type": "pub.leaflet.richtext.facet#link",
442 "uri": "https://example.com"
443 }
444 ]
445 }`
446
447 var f Facet
448 err := json.Unmarshal([]byte(jsonData), &f)
449 shared.AssertNoError(t, err, "unmarshal should succeed")
450
451 link, ok := f.Features[0].(FacetLink)
452 shared.AssertTrue(t, ok, "feature should be FacetLink")
453 shared.AssertEqual(t, TypeFacetLink, link.GetFacetType(), "facet type should match")
454 shared.AssertEqual(t, "https://example.com", link.URI, "URI should match")
455 })
456
457 t.Run("unmarshals strikethrough facet", func(t *testing.T) {
458 jsonData := `{
459 "$type": "pub.leaflet.richtext.facet",
460 "index": {
461 "$type": "pub.leaflet.richtext.facet#byteSlice",
462 "byteStart": 0,
463 "byteEnd": 8
464 },
465 "features": [
466 {
467 "$type": "pub.leaflet.richtext.facet#strikethrough"
468 }
469 ]
470 }`
471
472 var f Facet
473 err := json.Unmarshal([]byte(jsonData), &f)
474 shared.AssertNoError(t, err, "unmarshal should succeed")
475
476 strike, ok := f.Features[0].(FacetStrikethrough)
477 shared.AssertTrue(t, ok, "feature should be FacetStrikethrough")
478 shared.AssertEqual(t, TypeFacetStrike, strike.GetFacetType(), "facet type should match")
479 })
480
481 t.Run("unmarshals underline facet", func(t *testing.T) {
482 jsonData := `{
483 "$type": "pub.leaflet.richtext.facet",
484 "index": {
485 "$type": "pub.leaflet.richtext.facet#byteSlice",
486 "byteStart": 0,
487 "byteEnd": 12
488 },
489 "features": [
490 {
491 "$type": "pub.leaflet.richtext.facet#underline"
492 }
493 ]
494 }`
495
496 var f Facet
497 err := json.Unmarshal([]byte(jsonData), &f)
498 shared.AssertNoError(t, err, "unmarshal should succeed")
499
500 underline, ok := f.Features[0].(FacetUnderline)
501 shared.AssertTrue(t, ok, "feature should be FacetUnderline")
502 shared.AssertEqual(t, TypeFacetUnderline, underline.GetFacetType(), "facet type should match")
503 })
504
505 t.Run("unmarshals highlight facet", func(t *testing.T) {
506 jsonData := `{
507 "$type": "pub.leaflet.richtext.facet",
508 "index": {
509 "$type": "pub.leaflet.richtext.facet#byteSlice",
510 "byteStart": 5,
511 "byteEnd": 15
512 },
513 "features": [
514 {
515 "$type": "pub.leaflet.richtext.facet#highlight"
516 }
517 ]
518 }`
519
520 var f Facet
521 err := json.Unmarshal([]byte(jsonData), &f)
522 shared.AssertNoError(t, err, "unmarshal should succeed")
523
524 highlight, ok := f.Features[0].(FacetHighlight)
525 shared.AssertTrue(t, ok, "feature should be FacetHighlight")
526 shared.AssertEqual(t, TypeFacetHighlight, highlight.GetFacetType(), "facet type should match")
527 })
528
529 t.Run("unmarshals multiple features", func(t *testing.T) {
530 jsonData := `{
531 "$type": "pub.leaflet.richtext.facet",
532 "index": {
533 "$type": "pub.leaflet.richtext.facet#byteSlice",
534 "byteStart": 0,
535 "byteEnd": 10
536 },
537 "features": [
538 {
539 "$type": "pub.leaflet.richtext.facet#bold"
540 },
541 {
542 "$type": "pub.leaflet.richtext.facet#italic"
543 },
544 {
545 "$type": "pub.leaflet.richtext.facet#link",
546 "uri": "https://test.com"
547 }
548 ]
549 }`
550
551 var f Facet
552 err := json.Unmarshal([]byte(jsonData), &f)
553 shared.AssertNoError(t, err, "unmarshal should succeed")
554 shared.AssertEqual(t, 3, len(f.Features), "should have 3 features")
555
556 _, isBold := f.Features[0].(FacetBold)
557 shared.AssertTrue(t, isBold, "first feature should be bold")
558
559 _, isItalic := f.Features[1].(FacetItalic)
560 shared.AssertTrue(t, isItalic, "second feature should be italic")
561
562 link, isLink := f.Features[2].(FacetLink)
563 shared.AssertTrue(t, isLink, "third feature should be link")
564 shared.AssertEqual(t, "https://test.com", link.URI, "link URI should match")
565 })
566
567 t.Run("skips unknown feature types", func(t *testing.T) {
568 jsonData := `{
569 "$type": "pub.leaflet.richtext.facet",
570 "index": {
571 "$type": "pub.leaflet.richtext.facet#byteSlice",
572 "byteStart": 0,
573 "byteEnd": 10
574 },
575 "features": [
576 {
577 "$type": "pub.leaflet.richtext.facet#bold"
578 },
579 {
580 "$type": "pub.leaflet.richtext.facet#unknown"
581 },
582 {
583 "$type": "pub.leaflet.richtext.facet#italic"
584 }
585 ]
586 }`
587
588 var f Facet
589 err := json.Unmarshal([]byte(jsonData), &f)
590 shared.AssertNoError(t, err, "unmarshal should succeed")
591 shared.AssertEqual(t, 2, len(f.Features), "unknown features should be skipped")
592 })
593
594 t.Run("returns error for invalid JSON", func(t *testing.T) {
595 jsonData := `{invalid`
596
597 var f Facet
598 err := json.Unmarshal([]byte(jsonData), &f)
599 shared.AssertError(t, err, "invalid JSON should return error")
600 })
601
602 t.Run("returns error for malformed feature", func(t *testing.T) {
603 jsonData := `{
604 "$type": "pub.leaflet.richtext.facet",
605 "index": {
606 "$type": "pub.leaflet.richtext.facet#byteSlice",
607 "byteStart": 0,
608 "byteEnd": 10
609 },
610 "features": [
611 "not an object"
612 ]
613 }`
614
615 var f Facet
616 err := json.Unmarshal([]byte(jsonData), &f)
617 shared.AssertError(t, err, "malformed feature should return error")
618 })
619 })
620}
621
622func TestDocument(t *testing.T) {
623 t.Run("marshals and unmarshals correctly", func(t *testing.T) {
624 doc := Document{
625 Type: TypeDocument,
626 Author: "did:plc:test123",
627 Title: "Test Document",
628 Description: "A test document",
629 PublishedAt: "2024-01-01T00:00:00Z",
630 Publication: "at://did:plc:test123/pub.leaflet.publication/rkey",
631 Pages: []LinearDocument{
632 {
633 Type: TypeLinearDocument,
634 ID: "page1",
635 Blocks: []BlockWrap{
636 {
637 Type: TypeBlock,
638 Block: TextBlock{
639 Type: TypeTextBlock,
640 Plaintext: "Test content",
641 },
642 },
643 },
644 },
645 },
646 }
647
648 data, err := json.Marshal(doc)
649 shared.AssertNoError(t, err, "marshal should succeed")
650
651 var decoded Document
652 err = json.Unmarshal(data, &decoded)
653 shared.AssertNoError(t, err, "unmarshal should succeed")
654 shared.AssertEqual(t, doc.Title, decoded.Title, "title should match")
655 shared.AssertEqual(t, doc.Author, decoded.Author, "author should match")
656 shared.AssertEqual(t, 1, len(decoded.Pages), "should have 1 page")
657 })
658}
659
660func TestLinearDocument(t *testing.T) {
661 t.Run("marshals with multiple block types", func(t *testing.T) {
662 ld := LinearDocument{
663 Type: TypeLinearDocument,
664 ID: "page1",
665 Blocks: []BlockWrap{
666 {
667 Type: TypeBlock,
668 Block: TextBlock{
669 Type: TypeTextBlock,
670 Plaintext: "Text",
671 },
672 },
673 {
674 Type: TypeBlock,
675 Block: HeaderBlock{
676 Type: TypeHeaderBlock,
677 Level: 1,
678 Plaintext: "Header",
679 },
680 },
681 },
682 }
683
684 data, err := json.Marshal(ld)
685 shared.AssertNoError(t, err, "marshal should succeed")
686
687 var decoded LinearDocument
688 err = json.Unmarshal(data, &decoded)
689 shared.AssertNoError(t, err, "unmarshal should succeed")
690 shared.AssertEqual(t, 2, len(decoded.Blocks), "should have 2 blocks")
691 })
692}
693
694func TestFacetFeatures(t *testing.T) {
695 t.Run("GetFacetType", func(t *testing.T) {
696 t.Run("returns correct type for bold", func(t *testing.T) {
697 bold := FacetBold{Type: TypeFacetBold}
698 shared.AssertEqual(t, TypeFacetBold, bold.GetFacetType(), "type should match")
699 })
700
701 t.Run("returns correct type for italic", func(t *testing.T) {
702 italic := FacetItalic{Type: TypeFacetItalic}
703 shared.AssertEqual(t, TypeFacetItalic, italic.GetFacetType(), "type should match")
704 })
705
706 t.Run("returns correct type for code", func(t *testing.T) {
707 code := FacetCode{Type: TypeFacetCode}
708 shared.AssertEqual(t, TypeFacetCode, code.GetFacetType(), "type should match")
709 })
710
711 t.Run("returns correct type for link", func(t *testing.T) {
712 link := FacetLink{Type: TypeFacetLink, URI: "https://example.com"}
713 shared.AssertEqual(t, TypeFacetLink, link.GetFacetType(), "type should match")
714 })
715
716 t.Run("returns correct type for strikethrough", func(t *testing.T) {
717 strike := FacetStrikethrough{Type: TypeFacetStrike}
718 shared.AssertEqual(t, TypeFacetStrike, strike.GetFacetType(), "type should match")
719 })
720
721 t.Run("returns correct type for underline", func(t *testing.T) {
722 underline := FacetUnderline{Type: TypeFacetUnderline}
723 shared.AssertEqual(t, TypeFacetUnderline, underline.GetFacetType(), "type should match")
724 })
725
726 t.Run("returns correct type for highlight", func(t *testing.T) {
727 highlight := FacetHighlight{Type: TypeFacetHighlight}
728 shared.AssertEqual(t, TypeFacetHighlight, highlight.GetFacetType(), "type should match")
729 })
730 })
731}
732
733func TestBlob(t *testing.T) {
734 t.Run("marshals and unmarshals correctly", func(t *testing.T) {
735 blob := Blob{
736 Type: TypeBlob,
737 Ref: CID{
738 Link: "bafytest123",
739 },
740 MimeType: "image/png",
741 Size: 4096,
742 }
743
744 data, err := json.Marshal(blob)
745 shared.AssertNoError(t, err, "marshal should succeed")
746
747 var decoded Blob
748 err = json.Unmarshal(data, &decoded)
749 shared.AssertNoError(t, err, "unmarshal should succeed")
750 shared.AssertEqual(t, blob.MimeType, decoded.MimeType, "mime type should match")
751 shared.AssertEqual(t, blob.Size, decoded.Size, "size should match")
752 shared.AssertEqual(t, blob.Ref.Link, decoded.Ref.Link, "CID link should match")
753 })
754}
755
756func TestPublication(t *testing.T) {
757 t.Run("marshals and unmarshals correctly", func(t *testing.T) {
758 pub := Publication{
759 Type: TypePublication,
760 Name: "Test Publication",
761 Description: "A test publication",
762 }
763
764 data, err := json.Marshal(pub)
765 shared.AssertNoError(t, err, "marshal should succeed")
766
767 var decoded Publication
768 err = json.Unmarshal(data, &decoded)
769 shared.AssertNoError(t, err, "unmarshal should succeed")
770 shared.AssertEqual(t, pub.Name, decoded.Name, "name should match")
771 shared.AssertEqual(t, pub.Description, decoded.Description, "description should match")
772 })
773}
774
775func TestComplexDocument(t *testing.T) {
776 t.Run("unmarshals complex nested document", func(t *testing.T) {
777 jsonData := `{
778 "$type": "pub.leaflet.document",
779 "author": "did:plc:abc123",
780 "title": "Complex Document",
781 "description": "Testing complex structures",
782 "publishedAt": "2024-01-15T10:30:00Z",
783 "publication": "at://did:plc:abc123/pub.leaflet.publication/xyz",
784 "pages": [
785 {
786 "$type": "pub.leaflet.pages.linearDocument",
787 "id": "page1",
788 "blocks": [
789 {
790 "$type": "pub.leaflet.pages.linearDocument#block",
791 "block": {
792 "$type": "pub.leaflet.blocks.header",
793 "level": 1,
794 "plaintext": "Introduction",
795 "facets": [
796 {
797 "$type": "pub.leaflet.richtext.facet",
798 "index": {
799 "$type": "pub.leaflet.richtext.facet#byteSlice",
800 "byteStart": 0,
801 "byteEnd": 12
802 },
803 "features": [
804 {
805 "$type": "pub.leaflet.richtext.facet#bold"
806 }
807 ]
808 }
809 ]
810 }
811 },
812 {
813 "$type": "pub.leaflet.pages.linearDocument#block",
814 "block": {
815 "$type": "pub.leaflet.blocks.text",
816 "plaintext": "This is a link to example",
817 "facets": [
818 {
819 "$type": "pub.leaflet.richtext.facet",
820 "index": {
821 "$type": "pub.leaflet.richtext.facet#byteSlice",
822 "byteStart": 10,
823 "byteEnd": 14
824 },
825 "features": [
826 {
827 "$type": "pub.leaflet.richtext.facet#link",
828 "uri": "https://example.com"
829 }
830 ]
831 }
832 ]
833 }
834 },
835 {
836 "$type": "pub.leaflet.pages.linearDocument#block",
837 "block": {
838 "$type": "pub.leaflet.blocks.unorderedList",
839 "children": [
840 {
841 "$type": "pub.leaflet.blocks.unorderedList#listItem",
842 "content": {
843 "$type": "pub.leaflet.blocks.text",
844 "plaintext": "First item"
845 },
846 "children": [
847 {
848 "$type": "pub.leaflet.blocks.unorderedList#listItem",
849 "content": {
850 "$type": "pub.leaflet.blocks.text",
851 "plaintext": "Nested item"
852 }
853 }
854 ]
855 }
856 ]
857 }
858 },
859 {
860 "$type": "pub.leaflet.pages.linearDocument#block",
861 "block": {
862 "$type": "pub.leaflet.blocks.horizontalRule"
863 }
864 }
865 ]
866 }
867 ]
868 }`
869
870 var doc Document
871 err := json.Unmarshal([]byte(jsonData), &doc)
872 shared.AssertNoError(t, err, "unmarshal should succeed")
873 shared.AssertEqual(t, TypeDocument, doc.Type, "type should match")
874 shared.AssertEqual(t, "Complex Document", doc.Title, "title should match")
875 shared.AssertEqual(t, 1, len(doc.Pages), "should have 1 page")
876 shared.AssertEqual(t, 4, len(doc.Pages[0].Blocks), "should have 4 blocks")
877
878 headerBlock, ok := doc.Pages[0].Blocks[0].Block.(HeaderBlock)
879 shared.AssertTrue(t, ok, "first block should be HeaderBlock")
880 shared.AssertEqual(t, 1, headerBlock.Level, "header level should be 1")
881 shared.AssertEqual(t, 1, len(headerBlock.Facets), "header should have 1 facet")
882
883 textBlock, ok := doc.Pages[0].Blocks[1].Block.(TextBlock)
884 shared.AssertTrue(t, ok, "second block should be TextBlock")
885 shared.AssertEqual(t, 1, len(textBlock.Facets), "text should have 1 facet")
886 link, ok := textBlock.Facets[0].Features[0].(FacetLink)
887 shared.AssertTrue(t, ok, "facet feature should be link")
888 shared.AssertEqual(t, "https://example.com", link.URI, "link URI should match")
889
890 listBlock, ok := doc.Pages[0].Blocks[2].Block.(UnorderedListBlock)
891 shared.AssertTrue(t, ok, "third block should be UnorderedListBlock")
892 shared.AssertEqual(t, 1, len(listBlock.Children), "list should have 1 child")
893 shared.AssertEqual(t, 1, len(listBlock.Children[0].Children), "first item should have 1 nested child")
894
895 hrBlock, ok := doc.Pages[0].Blocks[3].Block.(HorizontalRuleBlock)
896 shared.AssertTrue(t, ok, "fourth block should be HorizontalRuleBlock")
897 shared.AssertEqual(t, TypeHorizontalRuleBlock, hrBlock.Type, "HR type should match")
898 })
899}