cli + tui to publish to leaflet (wip) & manage tasks, notes & watch/read lists 馃崈
charm leaflet readability golang
at main 899 lines 27 kB view raw
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}