Auto-indexing service and GraphQL API for AT Protocol Records quickslice.slices.network/
atproto gleam graphql
at main 1541 lines 39 kB view raw
1/// Integration tests for joins and DataLoader with actual database 2/// 3/// Tests verify that: 4/// - Forward joins (at-uri and strongRef) resolve correctly 5/// - Reverse joins discover and resolve relationships 6/// - DataLoader batches queries efficiently 7/// - All join types work with actual SQLite database queries 8import database/repositories/lexicons 9import database/repositories/records 10import gleam/json 11import gleam/option 12import gleam/string 13import gleeunit/should 14import graphql/lexicon/schema as lexicon_schema 15import lib/oauth/did_cache 16import test_helpers 17 18// Helper to create a post lexicon JSON 19fn create_post_lexicon() -> String { 20 json.object([ 21 #("lexicon", json.int(1)), 22 #("id", json.string("app.bsky.feed.post")), 23 #( 24 "defs", 25 json.object([ 26 #( 27 "main", 28 json.object([ 29 #("type", json.string("record")), 30 #("key", json.string("tid")), 31 #( 32 "record", 33 json.object([ 34 #("type", json.string("object")), 35 #( 36 "required", 37 json.array([json.string("text")], of: fn(x) { x }), 38 ), 39 #( 40 "properties", 41 json.object([ 42 #( 43 "text", 44 json.object([ 45 #("type", json.string("string")), 46 #("maxLength", json.int(300)), 47 ]), 48 ), 49 #( 50 "replyTo", 51 json.object([ 52 #("type", json.string("string")), 53 #("format", json.string("at-uri")), 54 ]), 55 ), 56 ]), 57 ), 58 ]), 59 ), 60 ]), 61 ), 62 ]), 63 ), 64 ]) 65 |> json.to_string 66} 67 68// Helper to create a like lexicon JSON with subject field (at-uri) 69fn create_like_lexicon() -> String { 70 json.object([ 71 #("lexicon", json.int(1)), 72 #("id", json.string("app.bsky.feed.like")), 73 #( 74 "defs", 75 json.object([ 76 #( 77 "main", 78 json.object([ 79 #("type", json.string("record")), 80 #("key", json.string("tid")), 81 #( 82 "record", 83 json.object([ 84 #("type", json.string("object")), 85 #( 86 "required", 87 json.array([json.string("subject")], of: fn(x) { x }), 88 ), 89 #( 90 "properties", 91 json.object([ 92 #( 93 "subject", 94 json.object([ 95 #("type", json.string("string")), 96 #("format", json.string("at-uri")), 97 ]), 98 ), 99 #( 100 "createdAt", 101 json.object([ 102 #("type", json.string("string")), 103 #("format", json.string("datetime")), 104 ]), 105 ), 106 ]), 107 ), 108 ]), 109 ), 110 ]), 111 ), 112 ]), 113 ), 114 ]) 115 |> json.to_string 116} 117 118// Helper to create a profile lexicon with strongRef 119fn create_profile_lexicon() -> String { 120 json.object([ 121 #("lexicon", json.int(1)), 122 #("id", json.string("app.bsky.actor.profile")), 123 #( 124 "defs", 125 json.object([ 126 #( 127 "main", 128 json.object([ 129 #("type", json.string("record")), 130 #("key", json.string("self")), 131 #( 132 "record", 133 json.object([ 134 #("type", json.string("object")), 135 #( 136 "properties", 137 json.object([ 138 #( 139 "displayName", 140 json.object([#("type", json.string("string"))]), 141 ), 142 #( 143 "pinnedPost", 144 json.object([ 145 #("type", json.string("ref")), 146 #("ref", json.string("com.atproto.repo.strongRef")), 147 ]), 148 ), 149 ]), 150 ), 151 ]), 152 ), 153 ]), 154 ), 155 ]), 156 ), 157 ]) 158 |> json.to_string 159} 160 161// Test: Forward join with at-uri field resolves correctly 162pub fn forward_join_at_uri_resolves_test() { 163 // Setup database 164 let assert Ok(exec) = test_helpers.create_test_db() 165 let assert Ok(_) = test_helpers.create_lexicon_table(exec) 166 let assert Ok(_) = test_helpers.create_record_table(exec) 167 let assert Ok(_) = test_helpers.create_actor_table(exec) 168 169 // Insert lexicons 170 let post_lexicon = create_post_lexicon() 171 let assert Ok(_) = lexicons.insert(exec, "app.bsky.feed.post", post_lexicon) 172 173 // Insert test records 174 // Parent post 175 let parent_uri = "at://did:plc:parent123/app.bsky.feed.post/parent1" 176 let parent_json = 177 json.object([#("text", json.string("This is the parent post"))]) 178 |> json.to_string 179 180 let assert Ok(_) = 181 records.insert( 182 exec, 183 parent_uri, 184 "cid_parent", 185 "did:plc:parent123", 186 "app.bsky.feed.post", 187 parent_json, 188 ) 189 190 // Reply post that references parent 191 let reply_uri = "at://did:plc:user456/app.bsky.feed.post/reply1" 192 let reply_json = 193 json.object([ 194 #("text", json.string("This is a reply")), 195 #("replyTo", json.string(parent_uri)), 196 ]) 197 |> json.to_string 198 199 let assert Ok(_) = 200 records.insert( 201 exec, 202 reply_uri, 203 "cid_reply", 204 "did:plc:user456", 205 "app.bsky.feed.post", 206 reply_json, 207 ) 208 209 // Execute GraphQL query with forward join 210 let query = 211 " 212 { 213 appBskyFeedPost { 214 edges { 215 node { 216 uri 217 replyToResolved { 218 uri 219 } 220 } 221 } 222 } 223 } 224 " 225 226 let assert Ok(cache) = did_cache.start() 227 let assert Ok(response_json) = 228 lexicon_schema.execute_query_with_db( 229 exec, 230 query, 231 "{}", 232 Error(Nil), 233 cache, 234 option.None, 235 "", 236 "https://plc.directory", 237 ) 238 239 // Verify the response contains resolved join with parent URI 240 string.contains(response_json, reply_uri) 241 |> should.be_true 242 243 string.contains(response_json, parent_uri) 244 |> should.be_true 245 246 string.contains(response_json, "replyToResolved") 247 |> should.be_true 248} 249 250// Test: Forward join with strongRef resolves correctly 251pub fn forward_join_strong_ref_resolves_test() { 252 // Setup database 253 let assert Ok(exec) = test_helpers.create_test_db() 254 let assert Ok(_) = test_helpers.create_lexicon_table(exec) 255 let assert Ok(_) = test_helpers.create_record_table(exec) 256 let assert Ok(_) = test_helpers.create_actor_table(exec) 257 258 // Insert lexicons 259 let post_lexicon = create_post_lexicon() 260 let profile_lexicon = create_profile_lexicon() 261 let assert Ok(_) = lexicons.insert(exec, "app.bsky.feed.post", post_lexicon) 262 let assert Ok(_) = 263 lexicons.insert(exec, "app.bsky.actor.profile", profile_lexicon) 264 265 // Insert test records 266 // A post 267 let post_uri = "at://did:plc:user123/app.bsky.feed.post/post1" 268 let post_json = 269 json.object([#("text", json.string("My favorite post"))]) 270 |> json.to_string 271 272 let assert Ok(_) = 273 records.insert( 274 exec, 275 post_uri, 276 "cid_post1", 277 "did:plc:user123", 278 "app.bsky.feed.post", 279 post_json, 280 ) 281 282 // A profile that pins the post (using strongRef) 283 let profile_uri = "at://did:plc:user123/app.bsky.actor.profile/self" 284 let profile_json = 285 json.object([ 286 #("displayName", json.string("Alice")), 287 #( 288 "pinnedPost", 289 json.object([ 290 #("uri", json.string(post_uri)), 291 #("cid", json.string("cid_post1")), 292 ]), 293 ), 294 ]) 295 |> json.to_string 296 297 let assert Ok(_) = 298 records.insert( 299 exec, 300 profile_uri, 301 "cid_profile", 302 "did:plc:user123", 303 "app.bsky.actor.profile", 304 profile_json, 305 ) 306 307 // Execute GraphQL query with forward join on strongRef 308 let query = 309 " 310 { 311 appBskyActorProfile { 312 edges { 313 node { 314 uri 315 pinnedPostResolved { 316 uri 317 } 318 } 319 } 320 } 321 } 322 " 323 324 let assert Ok(cache) = did_cache.start() 325 let assert Ok(response_json) = 326 lexicon_schema.execute_query_with_db( 327 exec, 328 query, 329 "{}", 330 Error(Nil), 331 cache, 332 option.None, 333 "", 334 "https://plc.directory", 335 ) 336 337 // Verify the response contains resolved strongRef join with post URI 338 string.contains(response_json, profile_uri) 339 |> should.be_true 340 341 string.contains(response_json, post_uri) 342 |> should.be_true 343 344 string.contains(response_json, "pinnedPostResolved") 345 |> should.be_true 346} 347 348// Test: Reverse join discovers and resolves relationships 349pub fn reverse_join_resolves_test() { 350 // Setup database 351 let assert Ok(exec) = test_helpers.create_test_db() 352 let assert Ok(_) = test_helpers.create_lexicon_table(exec) 353 let assert Ok(_) = test_helpers.create_record_table(exec) 354 let assert Ok(_) = test_helpers.create_actor_table(exec) 355 356 // Insert lexicons 357 let post_lexicon = create_post_lexicon() 358 let like_lexicon = create_like_lexicon() 359 360 let assert Ok(_) = lexicons.insert(exec, "app.bsky.feed.post", post_lexicon) 361 362 let assert Ok(_) = lexicons.insert(exec, "app.bsky.feed.like", like_lexicon) 363 364 // Insert test records 365 // A post 366 let post_uri = "at://did:plc:author789/app.bsky.feed.post/post1" 367 let post_json = 368 json.object([#("text", json.string("Great content!"))]) 369 |> json.to_string 370 371 let assert Ok(_) = 372 records.insert( 373 exec, 374 post_uri, 375 "cid_post", 376 "did:plc:author789", 377 "app.bsky.feed.post", 378 post_json, 379 ) 380 381 // Multiple likes that reference the post (to test batching) 382 let like1_uri = "at://did:plc:liker1/app.bsky.feed.like/like1" 383 let like1_json = 384 json.object([ 385 #("subject", json.string(post_uri)), 386 #("createdAt", json.string("2024-01-01T12:00:00Z")), 387 ]) 388 |> json.to_string 389 390 let assert Ok(_) = 391 records.insert( 392 exec, 393 like1_uri, 394 "cid_like1", 395 "did:plc:liker1", 396 "app.bsky.feed.like", 397 like1_json, 398 ) 399 400 let like2_uri = "at://did:plc:liker2/app.bsky.feed.like/like2" 401 let like2_json = 402 json.object([ 403 #("subject", json.string(post_uri)), 404 #("createdAt", json.string("2024-01-01T12:05:00Z")), 405 ]) 406 |> json.to_string 407 408 let assert Ok(_) = 409 records.insert( 410 exec, 411 like2_uri, 412 "cid_like2", 413 "did:plc:liker2", 414 "app.bsky.feed.like", 415 like2_json, 416 ) 417 418 // Execute GraphQL query with reverse join (now returns connection) 419 let query = 420 " 421 { 422 appBskyFeedPost { 423 edges { 424 node { 425 uri 426 appBskyFeedLikeViaSubject { 427 edges { 428 node { 429 uri 430 } 431 } 432 } 433 } 434 } 435 } 436 } 437 " 438 439 let assert Ok(cache) = did_cache.start() 440 let assert Ok(response_json) = 441 lexicon_schema.execute_query_with_db( 442 exec, 443 query, 444 "{}", 445 Error(Nil), 446 cache, 447 option.None, 448 "", 449 "https://plc.directory", 450 ) 451 452 // Verify the response contains reverse join results 453 string.contains(response_json, post_uri) 454 |> should.be_true 455 456 string.contains(response_json, "appBskyFeedLikeViaSubject") 457 |> should.be_true 458 459 // Both likes should be in the response 460 string.contains(response_json, like1_uri) 461 |> should.be_true 462 463 string.contains(response_json, like2_uri) 464 |> should.be_true 465} 466 467// Test: DataLoader batches multiple forward joins efficiently 468pub fn dataloader_batches_forward_joins_test() { 469 // Setup database 470 let assert Ok(exec) = test_helpers.create_test_db() 471 let assert Ok(_) = test_helpers.create_lexicon_table(exec) 472 let assert Ok(_) = test_helpers.create_record_table(exec) 473 let assert Ok(_) = test_helpers.create_actor_table(exec) 474 475 // Insert lexicons 476 let post_lexicon = create_post_lexicon() 477 let assert Ok(_) = lexicons.insert(exec, "app.bsky.feed.post", post_lexicon) 478 479 // Insert multiple parent posts 480 let parent1_uri = "at://did:plc:user1/app.bsky.feed.post/parent1" 481 let parent1_json = 482 json.object([#("text", json.string("Parent post 1"))]) 483 |> json.to_string 484 485 let assert Ok(_) = 486 records.insert( 487 exec, 488 parent1_uri, 489 "cid_p1", 490 "did:plc:user1", 491 "app.bsky.feed.post", 492 parent1_json, 493 ) 494 495 let parent2_uri = "at://did:plc:user2/app.bsky.feed.post/parent2" 496 let parent2_json = 497 json.object([#("text", json.string("Parent post 2"))]) 498 |> json.to_string 499 500 let assert Ok(_) = 501 records.insert( 502 exec, 503 parent2_uri, 504 "cid_p2", 505 "did:plc:user2", 506 "app.bsky.feed.post", 507 parent2_json, 508 ) 509 510 // Insert multiple reply posts 511 let reply1_uri = "at://did:plc:user3/app.bsky.feed.post/reply1" 512 let reply1_json = 513 json.object([ 514 #("text", json.string("Reply to post 1")), 515 #("replyTo", json.string(parent1_uri)), 516 ]) 517 |> json.to_string 518 519 let assert Ok(_) = 520 records.insert( 521 exec, 522 reply1_uri, 523 "cid_r1", 524 "did:plc:user3", 525 "app.bsky.feed.post", 526 reply1_json, 527 ) 528 529 let reply2_uri = "at://did:plc:user4/app.bsky.feed.post/reply2" 530 let reply2_json = 531 json.object([ 532 #("text", json.string("Reply to post 2")), 533 #("replyTo", json.string(parent2_uri)), 534 ]) 535 |> json.to_string 536 537 let assert Ok(_) = 538 records.insert( 539 exec, 540 reply2_uri, 541 "cid_r2", 542 "did:plc:user4", 543 "app.bsky.feed.post", 544 reply2_json, 545 ) 546 547 // Execute GraphQL query that fetches multiple posts with joins 548 // DataLoader should batch the replyToResolved lookups 549 let query = 550 " 551 { 552 appBskyFeedPost { 553 edges { 554 node { 555 uri 556 replyToResolved { 557 uri 558 } 559 } 560 } 561 } 562 } 563 " 564 565 let assert Ok(cache) = did_cache.start() 566 let assert Ok(response_json) = 567 lexicon_schema.execute_query_with_db( 568 exec, 569 query, 570 "{}", 571 Error(Nil), 572 cache, 573 option.None, 574 "", 575 "https://plc.directory", 576 ) 577 578 // Verify all posts appear 579 string.contains(response_json, reply1_uri) 580 |> should.be_true 581 582 string.contains(response_json, reply2_uri) 583 |> should.be_true 584 585 string.contains(response_json, parent1_uri) 586 |> should.be_true 587 588 string.contains(response_json, parent2_uri) 589 |> should.be_true 590 // Note: To truly verify batching, we'd need to instrument the database 591 // layer to count queries. For now, this test ensures correctness. 592} 593 594// Test: Reverse joins work with strongRef fields 595pub fn reverse_join_with_strong_ref_test() { 596 // Setup database 597 let assert Ok(exec) = test_helpers.create_test_db() 598 let assert Ok(_) = test_helpers.create_lexicon_table(exec) 599 let assert Ok(_) = test_helpers.create_record_table(exec) 600 let assert Ok(_) = test_helpers.create_actor_table(exec) 601 602 // Insert lexicons 603 let post_lexicon = create_post_lexicon() 604 let profile_lexicon = create_profile_lexicon() 605 let assert Ok(_) = lexicons.insert(exec, "app.bsky.feed.post", post_lexicon) 606 let assert Ok(_) = 607 lexicons.insert(exec, "app.bsky.actor.profile", profile_lexicon) 608 609 // Insert a post 610 let post_uri = "at://did:plc:creator/app.bsky.feed.post/amazing" 611 let post_json = 612 json.object([#("text", json.string("Amazing post"))]) 613 |> json.to_string 614 615 let assert Ok(_) = 616 records.insert( 617 exec, 618 post_uri, 619 "cid_amazing", 620 "did:plc:creator", 621 "app.bsky.feed.post", 622 post_json, 623 ) 624 625 // Multiple profiles pin this post (using strongRef) 626 let profile1_uri = "at://did:plc:user1/app.bsky.actor.profile/self" 627 let profile1_json = 628 json.object([ 629 #("displayName", json.string("User One")), 630 #( 631 "pinnedPost", 632 json.object([ 633 #("uri", json.string(post_uri)), 634 #("cid", json.string("cid_amazing")), 635 ]), 636 ), 637 ]) 638 |> json.to_string 639 640 let assert Ok(_) = 641 records.insert( 642 exec, 643 profile1_uri, 644 "cid_prof1", 645 "did:plc:user1", 646 "app.bsky.actor.profile", 647 profile1_json, 648 ) 649 650 let profile2_uri = "at://did:plc:user2/app.bsky.actor.profile/self" 651 let profile2_json = 652 json.object([ 653 #("displayName", json.string("User Two")), 654 #( 655 "pinnedPost", 656 json.object([ 657 #("uri", json.string(post_uri)), 658 #("cid", json.string("cid_amazing")), 659 ]), 660 ), 661 ]) 662 |> json.to_string 663 664 let assert Ok(_) = 665 records.insert( 666 exec, 667 profile2_uri, 668 "cid_prof2", 669 "did:plc:user2", 670 "app.bsky.actor.profile", 671 profile2_json, 672 ) 673 674 // Query post with reverse join to find all profiles that pinned it (now returns connection) 675 let query = 676 " 677 { 678 appBskyFeedPost { 679 edges { 680 node { 681 uri 682 appBskyActorProfileViaPinnedPost { 683 edges { 684 node { 685 uri 686 } 687 } 688 } 689 } 690 } 691 } 692 } 693 " 694 695 let assert Ok(cache) = did_cache.start() 696 let assert Ok(response_json) = 697 lexicon_schema.execute_query_with_db( 698 exec, 699 query, 700 "{}", 701 Error(Nil), 702 cache, 703 option.None, 704 "", 705 "https://plc.directory", 706 ) 707 708 // Verify the reverse join through strongRef works 709 string.contains(response_json, post_uri) 710 |> should.be_true 711 712 string.contains(response_json, "appBskyActorProfileViaPinnedPost") 713 |> should.be_true 714 715 string.contains(response_json, profile1_uri) 716 |> should.be_true 717 718 string.contains(response_json, profile2_uri) 719 |> should.be_true 720} 721 722// Test: Forward join with union type and inline fragments 723pub fn forward_join_union_inline_fragments_test() { 724 // Setup database 725 let assert Ok(exec) = test_helpers.create_test_db() 726 let assert Ok(_) = test_helpers.create_lexicon_table(exec) 727 let assert Ok(_) = test_helpers.create_record_table(exec) 728 let assert Ok(_) = test_helpers.create_actor_table(exec) 729 730 // Insert lexicons 731 let post_lexicon = create_post_lexicon() 732 let like_lexicon = create_like_lexicon() 733 let assert Ok(_) = lexicons.insert(exec, "app.bsky.feed.post", post_lexicon) 734 let assert Ok(_) = lexicons.insert(exec, "app.bsky.feed.like", like_lexicon) 735 736 // Insert a parent post 737 let parent_post_uri = "at://did:plc:parent/app.bsky.feed.post/parent1" 738 let parent_post_json = 739 json.object([#("text", json.string("This is the parent post"))]) 740 |> json.to_string 741 742 let assert Ok(_) = 743 records.insert( 744 exec, 745 parent_post_uri, 746 "cid_parent_post", 747 "did:plc:parent", 748 "app.bsky.feed.post", 749 parent_post_json, 750 ) 751 752 // Insert a like that will be referenced 753 let target_like_uri = "at://did:plc:liker/app.bsky.feed.like/like1" 754 let target_like_json = 755 json.object([ 756 #("subject", json.string(parent_post_uri)), 757 #("createdAt", json.string("2024-01-01T12:00:00Z")), 758 ]) 759 |> json.to_string 760 761 let assert Ok(_) = 762 records.insert( 763 exec, 764 target_like_uri, 765 "cid_like", 766 "did:plc:liker", 767 "app.bsky.feed.like", 768 target_like_json, 769 ) 770 771 // Insert a reply post that references the parent (post) 772 let reply_to_post_uri = "at://did:plc:user1/app.bsky.feed.post/reply1" 773 let reply_to_post_json = 774 json.object([ 775 #("text", json.string("Reply to a post")), 776 #("replyTo", json.string(parent_post_uri)), 777 ]) 778 |> json.to_string 779 780 let assert Ok(_) = 781 records.insert( 782 exec, 783 reply_to_post_uri, 784 "cid_reply1", 785 "did:plc:user1", 786 "app.bsky.feed.post", 787 reply_to_post_json, 788 ) 789 790 // Insert a reply post that references the like 791 let reply_to_like_uri = "at://did:plc:user2/app.bsky.feed.post/reply2" 792 let reply_to_like_json = 793 json.object([ 794 #("text", json.string("Reply to a like")), 795 #("replyTo", json.string(target_like_uri)), 796 ]) 797 |> json.to_string 798 799 let assert Ok(_) = 800 records.insert( 801 exec, 802 reply_to_like_uri, 803 "cid_reply2", 804 "did:plc:user2", 805 "app.bsky.feed.post", 806 reply_to_like_json, 807 ) 808 809 // Execute GraphQL query with inline fragments to access type-specific fields 810 let query = 811 " 812 { 813 appBskyFeedPost { 814 edges { 815 node { 816 uri 817 text 818 replyToResolved { 819 ... on AppBskyFeedPost { 820 uri 821 text 822 } 823 ... on AppBskyFeedLike { 824 uri 825 subject 826 createdAt 827 } 828 } 829 } 830 } 831 } 832 } 833 " 834 835 let assert Ok(cache) = did_cache.start() 836 let assert Ok(response_json) = 837 lexicon_schema.execute_query_with_db( 838 exec, 839 query, 840 "{}", 841 Error(Nil), 842 cache, 843 option.None, 844 "", 845 "https://plc.directory", 846 ) 847 848 // Verify we can access type-specific fields through inline fragments 849 850 // For the post reply, we should see the parent post's text 851 string.contains(response_json, reply_to_post_uri) 852 |> should.be_true 853 854 string.contains(response_json, "Reply to a post") 855 |> should.be_true 856 857 string.contains(response_json, "This is the parent post") 858 |> should.be_true 859 860 // For the like reply, we should see the like's subject and createdAt 861 string.contains(response_json, reply_to_like_uri) 862 |> should.be_true 863 864 string.contains(response_json, "Reply to a like") 865 |> should.be_true 866 867 string.contains(response_json, "2024-01-01T12:00:00Z") 868 |> should.be_true 869 870 // Verify the resolved records have the correct URIs 871 string.contains(response_json, parent_post_uri) 872 |> should.be_true 873 874 string.contains(response_json, target_like_uri) 875 |> should.be_true 876} 877 878// Helper to create a profile lexicon with literal:self key 879fn create_profile_lexicon_with_literal_self() -> String { 880 json.object([ 881 #("lexicon", json.int(1)), 882 #("id", json.string("app.bsky.actor.profile")), 883 #( 884 "defs", 885 json.object([ 886 #( 887 "main", 888 json.object([ 889 #("type", json.string("record")), 890 #("key", json.string("literal:self")), 891 #( 892 "record", 893 json.object([ 894 #("type", json.string("object")), 895 #( 896 "properties", 897 json.object([ 898 #( 899 "displayName", 900 json.object([#("type", json.string("string"))]), 901 ), 902 #("bio", json.object([#("type", json.string("string"))])), 903 ]), 904 ), 905 ]), 906 ), 907 ]), 908 ), 909 ]), 910 ), 911 ]) 912 |> json.to_string 913} 914 915// Test: DID join to literal:self collection returns single nullable object 916pub fn did_join_to_literal_self_returns_single_test() { 917 // Setup database 918 let assert Ok(exec) = test_helpers.create_test_db() 919 let assert Ok(_) = test_helpers.create_lexicon_table(exec) 920 let assert Ok(_) = test_helpers.create_record_table(exec) 921 let assert Ok(_) = test_helpers.create_actor_table(exec) 922 923 // Insert lexicons 924 let post_lexicon = create_post_lexicon() 925 let profile_lexicon = create_profile_lexicon_with_literal_self() 926 let assert Ok(_) = lexicons.insert(exec, "app.bsky.feed.post", post_lexicon) 927 let assert Ok(_) = 928 lexicons.insert(exec, "app.bsky.actor.profile", profile_lexicon) 929 930 // Insert a profile with literal:self key 931 let profile_uri = "at://did:plc:user123/app.bsky.actor.profile/self" 932 let profile_json = 933 json.object([ 934 #("displayName", json.string("Alice")), 935 #("bio", json.string("Software engineer")), 936 ]) 937 |> json.to_string 938 939 let assert Ok(_) = 940 records.insert( 941 exec, 942 profile_uri, 943 "cid_profile", 944 "did:plc:user123", 945 "app.bsky.actor.profile", 946 profile_json, 947 ) 948 949 // Insert a post by the same DID 950 let post_uri = "at://did:plc:user123/app.bsky.feed.post/post1" 951 let post_json = 952 json.object([#("text", json.string("My first post"))]) 953 |> json.to_string 954 955 let assert Ok(_) = 956 records.insert( 957 exec, 958 post_uri, 959 "cid_post1", 960 "did:plc:user123", 961 "app.bsky.feed.post", 962 post_json, 963 ) 964 965 // Execute GraphQL query with DID join from post to profile 966 let query = 967 " 968 { 969 appBskyFeedPost { 970 edges { 971 node { 972 uri 973 text 974 appBskyActorProfileByDid { 975 uri 976 displayName 977 bio 978 } 979 } 980 } 981 } 982 } 983 " 984 985 let assert Ok(cache) = did_cache.start() 986 let assert Ok(response_json) = 987 lexicon_schema.execute_query_with_db( 988 exec, 989 query, 990 "{}", 991 Error(Nil), 992 cache, 993 option.None, 994 "", 995 "https://plc.directory", 996 ) 997 998 // Verify the response contains the DID-joined profile as a single object (not array) 999 string.contains(response_json, post_uri) 1000 |> should.be_true 1001 1002 string.contains(response_json, "appBskyActorProfileByDid") 1003 |> should.be_true 1004 1005 string.contains(response_json, profile_uri) 1006 |> should.be_true 1007 1008 string.contains(response_json, "Alice") 1009 |> should.be_true 1010 1011 string.contains(response_json, "Software engineer") 1012 |> should.be_true 1013} 1014 1015// Test: DID join to non-literal:self collection returns list 1016pub fn did_join_to_non_literal_self_returns_list_test() { 1017 // Setup database 1018 let assert Ok(exec) = test_helpers.create_test_db() 1019 let assert Ok(_) = test_helpers.create_lexicon_table(exec) 1020 let assert Ok(_) = test_helpers.create_record_table(exec) 1021 let assert Ok(_) = test_helpers.create_actor_table(exec) 1022 1023 // Insert lexicons 1024 let post_lexicon = create_post_lexicon() 1025 let profile_lexicon = create_profile_lexicon_with_literal_self() 1026 let assert Ok(_) = lexicons.insert(exec, "app.bsky.feed.post", post_lexicon) 1027 let assert Ok(_) = 1028 lexicons.insert(exec, "app.bsky.actor.profile", profile_lexicon) 1029 1030 // Insert a profile 1031 let profile_uri = "at://did:plc:author/app.bsky.actor.profile/self" 1032 let profile_json = 1033 json.object([ 1034 #("displayName", json.string("Bob")), 1035 #("bio", json.string("Writes a lot")), 1036 ]) 1037 |> json.to_string 1038 1039 let assert Ok(_) = 1040 records.insert( 1041 exec, 1042 profile_uri, 1043 "cid_profile", 1044 "did:plc:author", 1045 "app.bsky.actor.profile", 1046 profile_json, 1047 ) 1048 1049 // Insert multiple posts by the same DID 1050 let post1_uri = "at://did:plc:author/app.bsky.feed.post/post1" 1051 let post1_json = 1052 json.object([#("text", json.string("First post"))]) 1053 |> json.to_string 1054 1055 let assert Ok(_) = 1056 records.insert( 1057 exec, 1058 post1_uri, 1059 "cid_post1", 1060 "did:plc:author", 1061 "app.bsky.feed.post", 1062 post1_json, 1063 ) 1064 1065 let post2_uri = "at://did:plc:author/app.bsky.feed.post/post2" 1066 let post2_json = 1067 json.object([#("text", json.string("Second post"))]) 1068 |> json.to_string 1069 1070 let assert Ok(_) = 1071 records.insert( 1072 exec, 1073 post2_uri, 1074 "cid_post2", 1075 "did:plc:author", 1076 "app.bsky.feed.post", 1077 post2_json, 1078 ) 1079 1080 // Execute GraphQL query with DID join from profile to posts (now returns connection) 1081 let query = 1082 " 1083 { 1084 appBskyActorProfile { 1085 edges { 1086 node { 1087 uri 1088 displayName 1089 appBskyFeedPostByDid { 1090 edges { 1091 node { 1092 uri 1093 text 1094 } 1095 } 1096 } 1097 } 1098 } 1099 } 1100 } 1101 " 1102 1103 let assert Ok(cache) = did_cache.start() 1104 let assert Ok(response_json) = 1105 lexicon_schema.execute_query_with_db( 1106 exec, 1107 query, 1108 "{}", 1109 Error(Nil), 1110 cache, 1111 option.None, 1112 "", 1113 "https://plc.directory", 1114 ) 1115 1116 // Verify the response contains the DID-joined posts as a list 1117 string.contains(response_json, profile_uri) 1118 |> should.be_true 1119 1120 string.contains(response_json, "appBskyFeedPostByDid") 1121 |> should.be_true 1122 1123 string.contains(response_json, post1_uri) 1124 |> should.be_true 1125 1126 string.contains(response_json, "First post") 1127 |> should.be_true 1128 1129 string.contains(response_json, post2_uri) 1130 |> should.be_true 1131 1132 string.contains(response_json, "Second post") 1133 |> should.be_true 1134} 1135 1136// Test: DID join batches queries efficiently for multiple records 1137pub fn did_join_batches_queries_test() { 1138 // Setup database 1139 let assert Ok(exec) = test_helpers.create_test_db() 1140 let assert Ok(_) = test_helpers.create_lexicon_table(exec) 1141 let assert Ok(_) = test_helpers.create_record_table(exec) 1142 let assert Ok(_) = test_helpers.create_actor_table(exec) 1143 1144 // Insert lexicons 1145 let post_lexicon = create_post_lexicon() 1146 let profile_lexicon = create_profile_lexicon_with_literal_self() 1147 let assert Ok(_) = lexicons.insert(exec, "app.bsky.feed.post", post_lexicon) 1148 let assert Ok(_) = 1149 lexicons.insert(exec, "app.bsky.actor.profile", profile_lexicon) 1150 1151 // Insert multiple profiles 1152 let profile1_uri = "at://did:plc:user1/app.bsky.actor.profile/self" 1153 let profile1_json = 1154 json.object([ 1155 #("displayName", json.string("User One")), 1156 #("bio", json.string("First user")), 1157 ]) 1158 |> json.to_string 1159 1160 let assert Ok(_) = 1161 records.insert( 1162 exec, 1163 profile1_uri, 1164 "cid_profile1", 1165 "did:plc:user1", 1166 "app.bsky.actor.profile", 1167 profile1_json, 1168 ) 1169 1170 let profile2_uri = "at://did:plc:user2/app.bsky.actor.profile/self" 1171 let profile2_json = 1172 json.object([ 1173 #("displayName", json.string("User Two")), 1174 #("bio", json.string("Second user")), 1175 ]) 1176 |> json.to_string 1177 1178 let assert Ok(_) = 1179 records.insert( 1180 exec, 1181 profile2_uri, 1182 "cid_profile2", 1183 "did:plc:user2", 1184 "app.bsky.actor.profile", 1185 profile2_json, 1186 ) 1187 1188 // Insert posts by different DIDs 1189 let post1_uri = "at://did:plc:user1/app.bsky.feed.post/post1" 1190 let post1_json = 1191 json.object([#("text", json.string("Post by user 1"))]) 1192 |> json.to_string 1193 1194 let assert Ok(_) = 1195 records.insert( 1196 exec, 1197 post1_uri, 1198 "cid_post1", 1199 "did:plc:user1", 1200 "app.bsky.feed.post", 1201 post1_json, 1202 ) 1203 1204 let post2_uri = "at://did:plc:user2/app.bsky.feed.post/post2" 1205 let post2_json = 1206 json.object([#("text", json.string("Post by user 2"))]) 1207 |> json.to_string 1208 1209 let assert Ok(_) = 1210 records.insert( 1211 exec, 1212 post2_uri, 1213 "cid_post2", 1214 "did:plc:user2", 1215 "app.bsky.feed.post", 1216 post2_json, 1217 ) 1218 1219 // Execute GraphQL query that fetches multiple posts with DID joins to profiles 1220 // DataLoader should batch the profile lookups by DID 1221 let query = 1222 " 1223 { 1224 appBskyFeedPost { 1225 edges { 1226 node { 1227 uri 1228 text 1229 appBskyActorProfileByDid { 1230 uri 1231 displayName 1232 } 1233 } 1234 } 1235 } 1236 } 1237 " 1238 1239 let assert Ok(cache) = did_cache.start() 1240 let assert Ok(response_json) = 1241 lexicon_schema.execute_query_with_db( 1242 exec, 1243 query, 1244 "{}", 1245 Error(Nil), 1246 cache, 1247 option.None, 1248 "", 1249 "https://plc.directory", 1250 ) 1251 1252 // Verify all posts and their associated profiles appear 1253 string.contains(response_json, post1_uri) 1254 |> should.be_true 1255 1256 string.contains(response_json, "Post by user 1") 1257 |> should.be_true 1258 1259 string.contains(response_json, profile1_uri) 1260 |> should.be_true 1261 1262 string.contains(response_json, "User One") 1263 |> should.be_true 1264 1265 string.contains(response_json, post2_uri) 1266 |> should.be_true 1267 1268 string.contains(response_json, "Post by user 2") 1269 |> should.be_true 1270 1271 string.contains(response_json, profile2_uri) 1272 |> should.be_true 1273 1274 string.contains(response_json, "User Two") 1275 |> should.be_true 1276 // Note: To truly verify batching, we'd need to instrument the database 1277 // layer to count queries. For now, this test ensures correctness. 1278} 1279 1280// Helper to create a post lexicon with nested reply object containing strongRef fields 1281fn create_post_lexicon_with_nested_reply() -> String { 1282 json.object([ 1283 #("lexicon", json.int(1)), 1284 #("id", json.string("app.bsky.feed.post")), 1285 #( 1286 "defs", 1287 json.object([ 1288 #( 1289 "main", 1290 json.object([ 1291 #("type", json.string("record")), 1292 #("key", json.string("tid")), 1293 #( 1294 "record", 1295 json.object([ 1296 #("type", json.string("object")), 1297 #( 1298 "required", 1299 json.array([json.string("text")], of: fn(x) { x }), 1300 ), 1301 #( 1302 "properties", 1303 json.object([ 1304 #( 1305 "text", 1306 json.object([ 1307 #("type", json.string("string")), 1308 #("maxLength", json.int(300)), 1309 ]), 1310 ), 1311 #( 1312 "reply", 1313 json.object([ 1314 #("type", json.string("ref")), 1315 #("ref", json.string("#replyRef")), 1316 ]), 1317 ), 1318 ]), 1319 ), 1320 ]), 1321 ), 1322 ]), 1323 ), 1324 #( 1325 "replyRef", 1326 json.object([ 1327 #("type", json.string("object")), 1328 #( 1329 "required", 1330 json.array( 1331 [json.string("parent"), json.string("root")], 1332 of: fn(x) { x }, 1333 ), 1334 ), 1335 #( 1336 "properties", 1337 json.object([ 1338 #( 1339 "parent", 1340 json.object([ 1341 #("type", json.string("ref")), 1342 #("ref", json.string("com.atproto.repo.strongRef")), 1343 ]), 1344 ), 1345 #( 1346 "root", 1347 json.object([ 1348 #("type", json.string("ref")), 1349 #("ref", json.string("com.atproto.repo.strongRef")), 1350 ]), 1351 ), 1352 ]), 1353 ), 1354 ]), 1355 ), 1356 ]), 1357 ), 1358 ]) 1359 |> json.to_string 1360} 1361 1362// Test: Nested forward join resolution through reply.parentResolved 1363pub fn nested_forward_join_resolves_reply_parent_test() { 1364 // Setup database 1365 let assert Ok(exec) = test_helpers.create_test_db() 1366 let assert Ok(_) = test_helpers.create_lexicon_table(exec) 1367 let assert Ok(_) = test_helpers.create_record_table(exec) 1368 let assert Ok(_) = test_helpers.create_actor_table(exec) 1369 1370 // Insert lexicon with nested reply object 1371 let post_lexicon = create_post_lexicon_with_nested_reply() 1372 let assert Ok(_) = lexicons.insert(exec, "app.bsky.feed.post", post_lexicon) 1373 1374 // Insert root post 1375 let root_uri = "at://did:plc:root123/app.bsky.feed.post/root1" 1376 let root_json = 1377 json.object([#("text", json.string("This is the root post"))]) 1378 |> json.to_string 1379 1380 let assert Ok(_) = 1381 records.insert( 1382 exec, 1383 root_uri, 1384 "cid_root", 1385 "did:plc:root123", 1386 "app.bsky.feed.post", 1387 root_json, 1388 ) 1389 1390 // Insert parent post (reply to root) 1391 let parent_uri = "at://did:plc:parent456/app.bsky.feed.post/parent1" 1392 let parent_json = 1393 json.object([ 1394 #("text", json.string("This is a reply to the root")), 1395 #( 1396 "reply", 1397 json.object([ 1398 #( 1399 "parent", 1400 json.object([ 1401 #("uri", json.string(root_uri)), 1402 #("cid", json.string("cid_root")), 1403 ]), 1404 ), 1405 #( 1406 "root", 1407 json.object([ 1408 #("uri", json.string(root_uri)), 1409 #("cid", json.string("cid_root")), 1410 ]), 1411 ), 1412 ]), 1413 ), 1414 ]) 1415 |> json.to_string 1416 1417 let assert Ok(_) = 1418 records.insert( 1419 exec, 1420 parent_uri, 1421 "cid_parent", 1422 "did:plc:parent456", 1423 "app.bsky.feed.post", 1424 parent_json, 1425 ) 1426 1427 // Insert reply post (reply to parent) 1428 let reply_uri = "at://did:plc:user789/app.bsky.feed.post/reply1" 1429 let reply_json = 1430 json.object([ 1431 #("text", json.string("This is a reply to the parent")), 1432 #( 1433 "reply", 1434 json.object([ 1435 #( 1436 "parent", 1437 json.object([ 1438 #("uri", json.string(parent_uri)), 1439 #("cid", json.string("cid_parent")), 1440 ]), 1441 ), 1442 #( 1443 "root", 1444 json.object([ 1445 #("uri", json.string(root_uri)), 1446 #("cid", json.string("cid_root")), 1447 ]), 1448 ), 1449 ]), 1450 ), 1451 ]) 1452 |> json.to_string 1453 1454 let assert Ok(_) = 1455 records.insert( 1456 exec, 1457 reply_uri, 1458 "cid_reply", 1459 "did:plc:user789", 1460 "app.bsky.feed.post", 1461 reply_json, 1462 ) 1463 1464 // Execute GraphQL query that uses nested forward join: reply.parentResolved 1465 let query = 1466 " 1467 { 1468 appBskyFeedPost { 1469 edges { 1470 node { 1471 uri 1472 text 1473 reply { 1474 parent { 1475 uri 1476 cid 1477 } 1478 parentResolved { 1479 ... on AppBskyFeedPost { 1480 uri 1481 text 1482 } 1483 } 1484 root { 1485 uri 1486 cid 1487 } 1488 rootResolved { 1489 ... on AppBskyFeedPost { 1490 uri 1491 text 1492 } 1493 } 1494 } 1495 } 1496 } 1497 } 1498 } 1499 " 1500 1501 let assert Ok(cache) = did_cache.start() 1502 let assert Ok(response_json) = 1503 lexicon_schema.execute_query_with_db( 1504 exec, 1505 query, 1506 "{}", 1507 Error(Nil), 1508 cache, 1509 option.None, 1510 "", 1511 "https://plc.directory", 1512 ) 1513 1514 // Verify the nested forward joins work correctly 1515 // The reply post should have its parent resolved 1516 string.contains(response_json, reply_uri) 1517 |> should.be_true 1518 1519 string.contains(response_json, "This is a reply to the parent") 1520 |> should.be_true 1521 1522 // The parentResolved field should contain the parent post 1523 string.contains(response_json, "parentResolved") 1524 |> should.be_true 1525 1526 string.contains(response_json, parent_uri) 1527 |> should.be_true 1528 1529 string.contains(response_json, "This is a reply to the root") 1530 |> should.be_true 1531 1532 // The rootResolved field should contain the root post 1533 string.contains(response_json, "rootResolved") 1534 |> should.be_true 1535 1536 string.contains(response_json, root_uri) 1537 |> should.be_true 1538 1539 string.contains(response_json, "This is the root post") 1540 |> should.be_true 1541}