A social knowledge tool for researchers built on ATProto
1import { 2 PostgreSqlContainer, 3 StartedPostgreSqlContainer, 4} from '@testcontainers/postgresql'; 5import postgres from 'postgres'; 6import { drizzle, PostgresJsDatabase } from 'drizzle-orm/postgres-js'; 7import { DrizzleCollectionQueryRepository } from '../../infrastructure/repositories/DrizzleCollectionQueryRepository'; 8import { DrizzleCardRepository } from '../../infrastructure/repositories/DrizzleCardRepository'; 9import { DrizzleCollectionRepository } from '../../infrastructure/repositories/DrizzleCollectionRepository'; 10import { CuratorId } from '../../domain/value-objects/CuratorId'; 11import { UniqueEntityID } from '../../../../shared/domain/UniqueEntityID'; 12import { 13 collections, 14 collectionCollaborators, 15 collectionCards, 16} from '../../infrastructure/repositories/schema/collection.sql'; 17import { cards } from '../../infrastructure/repositories/schema/card.sql'; 18import { libraryMemberships } from '../../infrastructure/repositories/schema/libraryMembership.sql'; 19import { publishedRecords } from '../../infrastructure/repositories/schema/publishedRecord.sql'; 20import { Collection, CollectionAccessType } from '../../domain/Collection'; 21import { CardFactory } from '../../domain/CardFactory'; 22import { CardTypeEnum } from '../../domain/value-objects/CardType'; 23import { PublishedRecordId } from '../../domain/value-objects/PublishedRecordId'; 24import { UrlMetadata } from '../../domain/value-objects/UrlMetadata'; 25import { createTestSchema } from '../test-utils/createTestSchema'; 26 27describe('DrizzleCollectionQueryRepository - getCollectionsContainingCardForUser', () => { 28 let container: StartedPostgreSqlContainer; 29 let db: PostgresJsDatabase; 30 let queryRepository: DrizzleCollectionQueryRepository; 31 let collectionRepository: DrizzleCollectionRepository; 32 let cardRepository: DrizzleCardRepository; 33 34 // Test data 35 let curatorId: CuratorId; 36 let otherCuratorId: CuratorId; 37 38 // Setup before all tests 39 beforeAll(async () => { 40 // Start PostgreSQL container 41 container = await new PostgreSqlContainer('postgres:14').start(); 42 43 // Create database connection 44 const connectionString = container.getConnectionUri(); 45 process.env.DATABASE_URL = connectionString; 46 const client = postgres(connectionString); 47 db = drizzle(client); 48 49 // Create repositories 50 queryRepository = new DrizzleCollectionQueryRepository(db); 51 collectionRepository = new DrizzleCollectionRepository(db); 52 cardRepository = new DrizzleCardRepository(db); 53 54 // Create schema using helper function 55 await createTestSchema(db); 56 57 // Create test data 58 curatorId = CuratorId.create('did:plc:testcurator').unwrap(); 59 otherCuratorId = CuratorId.create('did:plc:othercurator').unwrap(); 60 }, 60000); // Increase timeout for container startup 61 62 // Cleanup after all tests 63 afterAll(async () => { 64 // Stop container 65 await container.stop(); 66 }); 67 68 // Clear data between tests 69 beforeEach(async () => { 70 await db.delete(collectionCards); 71 await db.delete(collectionCollaborators); 72 await db.delete(collections); 73 await db.delete(libraryMemberships); 74 await db.delete(cards); 75 await db.delete(publishedRecords); 76 }); 77 78 describe('URL card in collections', () => { 79 it('should return collections when user has URL card in multiple collections', async () => { 80 const testUrl = 'https://example.com/test-article'; 81 82 // Create a URL card 83 const urlMetadata = UrlMetadata.create({ 84 url: testUrl, 85 title: 'Test Article', 86 description: 'A test article for testing', 87 author: 'Test Author', 88 siteName: 'Example.com', 89 }).unwrap(); 90 91 const card = CardFactory.create({ 92 curatorId: curatorId.value, 93 cardInput: { 94 type: CardTypeEnum.URL, 95 url: testUrl, 96 metadata: urlMetadata, 97 }, 98 }).unwrap(); 99 100 // Add card to library 101 const addToLibResult = card.addToLibrary(curatorId); 102 expect(addToLibResult.isOk()).toBe(true); 103 104 await cardRepository.save(card); 105 106 // Create first collection 107 const collection1 = Collection.create( 108 { 109 authorId: curatorId, 110 name: 'Tech Articles', 111 description: 'Collection of technology articles', 112 accessType: CollectionAccessType.OPEN, 113 collaboratorIds: [], 114 createdAt: new Date(), 115 updatedAt: new Date(), 116 }, 117 new UniqueEntityID(), 118 ).unwrap(); 119 120 // Create second collection 121 const collection2 = Collection.create( 122 { 123 authorId: curatorId, 124 name: 'Reading List', 125 description: 'My personal reading list', 126 accessType: CollectionAccessType.OPEN, 127 collaboratorIds: [], 128 createdAt: new Date(), 129 updatedAt: new Date(), 130 }, 131 new UniqueEntityID(), 132 ).unwrap(); 133 134 // Add card to both collections 135 const addToCollection1Result = collection1.addCard( 136 card.cardId, 137 curatorId, 138 ); 139 expect(addToCollection1Result.isOk()).toBe(true); 140 141 const addToCollection2Result = collection2.addCard( 142 card.cardId, 143 curatorId, 144 ); 145 expect(addToCollection2Result.isOk()).toBe(true); 146 147 // Mark collections as published 148 const collection1PublishedRecordId = PublishedRecordId.create({ 149 uri: 'at://did:plc:testcurator/network.cosmik.collection/collection1', 150 cid: 'bafyreicollection1cid', 151 }); 152 153 const collection2PublishedRecordId = PublishedRecordId.create({ 154 uri: 'at://did:plc:testcurator/network.cosmik.collection/collection2', 155 cid: 'bafyreicollection2cid', 156 }); 157 158 collection1.markAsPublished(collection1PublishedRecordId); 159 collection2.markAsPublished(collection2PublishedRecordId); 160 161 // Save collections 162 await collectionRepository.save(collection1); 163 await collectionRepository.save(collection2); 164 165 // Execute the query 166 const result = await queryRepository.getCollectionsContainingCardForUser( 167 card.cardId.getStringValue(), 168 curatorId.value, 169 ); 170 171 // Verify the result 172 expect(result).toHaveLength(2); 173 174 // Sort by name for consistent testing 175 result.sort((a, b) => a.name.localeCompare(b.name)); 176 177 // Verify collection details 178 expect(result[0]?.id).toBe(collection2.collectionId.getStringValue()); // Reading List comes first alphabetically 179 expect(result[0]?.uri).toBe( 180 'at://did:plc:testcurator/network.cosmik.collection/collection2', 181 ); 182 expect(result[0]?.name).toBe('Reading List'); 183 expect(result[0]?.description).toBe('My personal reading list'); 184 185 expect(result[1]?.id).toBe(collection1.collectionId.getStringValue()); // Tech Articles comes second 186 expect(result[1]?.uri).toBe( 187 'at://did:plc:testcurator/network.cosmik.collection/collection1', 188 ); 189 expect(result[1]?.name).toBe('Tech Articles'); 190 expect(result[1]?.description).toBe('Collection of technology articles'); 191 }); 192 193 it('should return empty array when user has URL card but not in any collections', async () => { 194 const testUrl = 'https://example.com/standalone-article'; 195 196 // Create a URL card 197 const urlMetadata = UrlMetadata.create({ 198 url: testUrl, 199 title: 'Standalone Article', 200 description: 'A standalone article for testing', 201 }).unwrap(); 202 203 const card = CardFactory.create({ 204 curatorId: curatorId.value, 205 cardInput: { 206 type: CardTypeEnum.URL, 207 url: testUrl, 208 metadata: urlMetadata, 209 }, 210 }).unwrap(); 211 212 // Add card to library 213 const addToLibResult = card.addToLibrary(curatorId); 214 expect(addToLibResult.isOk()).toBe(true); 215 216 await cardRepository.save(card); 217 218 // Execute the query 219 const result = await queryRepository.getCollectionsContainingCardForUser( 220 card.cardId.getStringValue(), 221 curatorId.value, 222 ); 223 224 // Verify the result 225 expect(result).toHaveLength(0); 226 }); 227 228 it('should return empty array when card does not exist', async () => { 229 const nonExistentCardId = new UniqueEntityID().toString(); 230 231 // Execute the query 232 const result = await queryRepository.getCollectionsContainingCardForUser( 233 nonExistentCardId, 234 curatorId.value, 235 ); 236 237 // Verify the result 238 expect(result).toHaveLength(0); 239 }); 240 241 it('should not return collections from other users even if they have the same card', async () => { 242 const testUrl = 'https://example.com/shared-article'; 243 244 // Create URL card for first user 245 const urlMetadata1 = UrlMetadata.create({ 246 url: testUrl, 247 title: 'Shared Article', 248 description: 'An article shared between users', 249 }).unwrap(); 250 251 const card1 = CardFactory.create({ 252 curatorId: curatorId.value, 253 cardInput: { 254 type: CardTypeEnum.URL, 255 url: testUrl, 256 metadata: urlMetadata1, 257 }, 258 }).unwrap(); 259 260 const addToLibResult1 = card1.addToLibrary(curatorId); 261 expect(addToLibResult1.isOk()).toBe(true); 262 263 await cardRepository.save(card1); 264 265 // Create URL card for second user (different card, same URL) 266 const urlMetadata2 = UrlMetadata.create({ 267 url: testUrl, 268 title: 'Shared Article', 269 description: 'An article shared between users', 270 }).unwrap(); 271 272 const card2 = CardFactory.create({ 273 curatorId: otherCuratorId.value, 274 cardInput: { 275 type: CardTypeEnum.URL, 276 url: testUrl, 277 metadata: urlMetadata2, 278 }, 279 }).unwrap(); 280 281 const addToLibResult2 = card2.addToLibrary(otherCuratorId); 282 expect(addToLibResult2.isOk()).toBe(true); 283 284 await cardRepository.save(card2); 285 286 // Create collection for second user and add their card 287 const otherUserCollection = Collection.create( 288 { 289 authorId: otherCuratorId, 290 name: 'Other User Collection', 291 accessType: CollectionAccessType.OPEN, 292 collaboratorIds: [], 293 createdAt: new Date(), 294 updatedAt: new Date(), 295 }, 296 new UniqueEntityID(), 297 ).unwrap(); 298 299 const addToOtherCollectionResult = otherUserCollection.addCard( 300 card2.cardId, 301 otherCuratorId, 302 ); 303 expect(addToOtherCollectionResult.isOk()).toBe(true); 304 305 await collectionRepository.save(otherUserCollection); 306 307 // Execute the query for first user's card 308 const result = await queryRepository.getCollectionsContainingCardForUser( 309 card1.cardId.getStringValue(), 310 curatorId.value, 311 ); 312 313 // Verify the result - should be empty since first user's card is not in any collections 314 expect(result).toHaveLength(0); 315 }); 316 317 it('should only return collections owned by the requesting user', async () => { 318 const testUrl = 'https://example.com/multi-user-article'; 319 320 // Create URL card for the user 321 const urlMetadata = UrlMetadata.create({ 322 url: testUrl, 323 title: 'Multi User Article', 324 description: 'An article for multi-user testing', 325 }).unwrap(); 326 327 const card = CardFactory.create({ 328 curatorId: curatorId.value, 329 cardInput: { 330 type: CardTypeEnum.URL, 331 url: testUrl, 332 metadata: urlMetadata, 333 }, 334 }).unwrap(); 335 336 const addToLibResult = card.addToLibrary(curatorId); 337 expect(addToLibResult.isOk()).toBe(true); 338 339 await cardRepository.save(card); 340 341 // Create user's own collection 342 const userCollection = Collection.create( 343 { 344 authorId: curatorId, 345 name: 'My Collection', 346 accessType: CollectionAccessType.OPEN, 347 collaboratorIds: [], 348 createdAt: new Date(), 349 updatedAt: new Date(), 350 }, 351 new UniqueEntityID(), 352 ).unwrap(); 353 354 const addToUserCollectionResult = userCollection.addCard( 355 card.cardId, 356 curatorId, 357 ); 358 expect(addToUserCollectionResult.isOk()).toBe(true); 359 360 await collectionRepository.save(userCollection); 361 362 // Create another user's collection (this should not appear in results) 363 const otherUserCollection = Collection.create( 364 { 365 authorId: otherCuratorId, 366 name: 'Other User Collection', 367 accessType: CollectionAccessType.OPEN, 368 collaboratorIds: [], 369 createdAt: new Date(), 370 updatedAt: new Date(), 371 }, 372 new UniqueEntityID(), 373 ).unwrap(); 374 375 // Note: We don't add the card to the other user's collection since they can't add 376 // another user's card to their collection in this domain model 377 378 await collectionRepository.save(otherUserCollection); 379 380 // Execute the query 381 const result = await queryRepository.getCollectionsContainingCardForUser( 382 card.cardId.getStringValue(), 383 curatorId.value, 384 ); 385 386 // Verify the result - should only see user's own collection 387 expect(result).toHaveLength(1); 388 expect(result[0]?.name).toBe('My Collection'); 389 expect(result[0]?.id).toBe(userCollection.collectionId.getStringValue()); 390 }); 391 }); 392 393 describe('Note cards in collections', () => { 394 it('should return collections containing note cards', async () => { 395 // Create a note card 396 const card = CardFactory.create({ 397 curatorId: curatorId.value, 398 cardInput: { 399 type: CardTypeEnum.NOTE, 400 text: 'This is a test note', 401 }, 402 }).unwrap(); 403 404 // Add card to library 405 const addToLibResult = card.addToLibrary(curatorId); 406 expect(addToLibResult.isOk()).toBe(true); 407 408 await cardRepository.save(card); 409 410 // Create collection 411 const collection = Collection.create( 412 { 413 authorId: curatorId, 414 name: 'My Notes', 415 description: 'Collection of my personal notes', 416 accessType: CollectionAccessType.OPEN, 417 collaboratorIds: [], 418 createdAt: new Date(), 419 updatedAt: new Date(), 420 }, 421 new UniqueEntityID(), 422 ).unwrap(); 423 424 // Add card to collection 425 const addToCollectionResult = collection.addCard(card.cardId, curatorId); 426 expect(addToCollectionResult.isOk()).toBe(true); 427 428 await collectionRepository.save(collection); 429 430 // Execute the query 431 const result = await queryRepository.getCollectionsContainingCardForUser( 432 card.cardId.getStringValue(), 433 curatorId.value, 434 ); 435 436 // Verify the result 437 expect(result).toHaveLength(1); 438 expect(result[0]?.id).toBe(collection.collectionId.getStringValue()); 439 expect(result[0]?.name).toBe('My Notes'); 440 expect(result[0]?.description).toBe('Collection of my personal notes'); 441 expect(result[0]?.uri).toBeUndefined(); // Not published 442 }); 443 444 it('should handle collections with and without descriptions', async () => { 445 // Create a note card 446 const card = CardFactory.create({ 447 curatorId: curatorId.value, 448 cardInput: { 449 type: CardTypeEnum.NOTE, 450 text: 'Test note for collections', 451 }, 452 }).unwrap(); 453 454 await cardRepository.save(card); 455 456 // Create collection with description 457 const collectionWithDesc = Collection.create( 458 { 459 authorId: curatorId, 460 name: 'Collection With Description', 461 description: 'This collection has a description', 462 accessType: CollectionAccessType.OPEN, 463 collaboratorIds: [], 464 createdAt: new Date(), 465 updatedAt: new Date(), 466 }, 467 new UniqueEntityID(), 468 ).unwrap(); 469 470 // Create collection without description 471 const collectionWithoutDesc = Collection.create( 472 { 473 authorId: curatorId, 474 name: 'Collection Without Description', 475 // No description provided 476 accessType: CollectionAccessType.OPEN, 477 collaboratorIds: [], 478 createdAt: new Date(), 479 updatedAt: new Date(), 480 }, 481 new UniqueEntityID(), 482 ).unwrap(); 483 484 // Add card to both collections 485 collectionWithDesc.addCard(card.cardId, curatorId); 486 collectionWithoutDesc.addCard(card.cardId, curatorId); 487 488 await collectionRepository.save(collectionWithDesc); 489 await collectionRepository.save(collectionWithoutDesc); 490 491 // Execute the query 492 const result = await queryRepository.getCollectionsContainingCardForUser( 493 card.cardId.getStringValue(), 494 curatorId.value, 495 ); 496 497 // Verify the result 498 expect(result).toHaveLength(2); 499 500 // Sort by name for consistent testing 501 result.sort((a, b) => a.name.localeCompare(b.name)); 502 503 expect(result[0]?.name).toBe('Collection With Description'); 504 expect(result[0]?.description).toBe('This collection has a description'); 505 506 expect(result[1]?.name).toBe('Collection Without Description'); 507 expect(result[1]?.description).toBeUndefined(); 508 }); 509 }); 510 511 describe('Published and unpublished collections', () => { 512 it('should return URI for published collections and undefined for unpublished', async () => { 513 // Create a card 514 const card = CardFactory.create({ 515 curatorId: curatorId.value, 516 cardInput: { 517 type: CardTypeEnum.NOTE, 518 text: 'Card for published/unpublished test', 519 }, 520 }).unwrap(); 521 522 await cardRepository.save(card); 523 524 // Create published collection 525 const publishedCollection = Collection.create( 526 { 527 authorId: curatorId, 528 name: 'Published Collection', 529 description: 'This collection is published', 530 accessType: CollectionAccessType.OPEN, 531 collaboratorIds: [], 532 createdAt: new Date(), 533 updatedAt: new Date(), 534 }, 535 new UniqueEntityID(), 536 ).unwrap(); 537 538 // Create unpublished collection 539 const unpublishedCollection = Collection.create( 540 { 541 authorId: curatorId, 542 name: 'Unpublished Collection', 543 description: 'This collection is not published', 544 accessType: CollectionAccessType.OPEN, 545 collaboratorIds: [], 546 createdAt: new Date(), 547 updatedAt: new Date(), 548 }, 549 new UniqueEntityID(), 550 ).unwrap(); 551 552 // Mark published collection as published 553 const publishedRecordId = PublishedRecordId.create({ 554 uri: 'at://did:plc:testcurator/network.cosmik.collection/published123', 555 cid: 'bafyreipublishedcid', 556 }); 557 558 publishedCollection.markAsPublished(publishedRecordId); 559 560 // Add card to both collections 561 publishedCollection.addCard(card.cardId, curatorId); 562 unpublishedCollection.addCard(card.cardId, curatorId); 563 564 await collectionRepository.save(publishedCollection); 565 await collectionRepository.save(unpublishedCollection); 566 567 // Execute the query 568 const result = await queryRepository.getCollectionsContainingCardForUser( 569 card.cardId.getStringValue(), 570 curatorId.value, 571 ); 572 573 // Verify the result 574 expect(result).toHaveLength(2); 575 576 // Find collections by name 577 const publishedResult = result.find( 578 (c) => c.name === 'Published Collection', 579 ); 580 const unpublishedResult = result.find( 581 (c) => c.name === 'Unpublished Collection', 582 ); 583 584 expect(publishedResult?.uri).toBe( 585 'at://did:plc:testcurator/network.cosmik.collection/published123', 586 ); 587 expect(unpublishedResult?.uri).toBeUndefined(); 588 }); 589 }); 590 591 describe('Sorting and ordering', () => { 592 it('should return collections sorted by name in ascending order', async () => { 593 // Create a card 594 const card = CardFactory.create({ 595 curatorId: curatorId.value, 596 cardInput: { 597 type: CardTypeEnum.NOTE, 598 text: 'Card for sorting test', 599 }, 600 }).unwrap(); 601 602 await cardRepository.save(card); 603 604 // Create collections with names that will test alphabetical sorting 605 const collectionNames = [ 606 'Zebra Collection', 607 'Alpha Collection', 608 'Beta Collection', 609 ]; 610 611 for (const name of collectionNames) { 612 const collection = Collection.create( 613 { 614 authorId: curatorId, 615 name, 616 accessType: CollectionAccessType.OPEN, 617 collaboratorIds: [], 618 createdAt: new Date(), 619 updatedAt: new Date(), 620 }, 621 new UniqueEntityID(), 622 ).unwrap(); 623 624 collection.addCard(card.cardId, curatorId); 625 await collectionRepository.save(collection); 626 } 627 628 // Execute the query 629 const result = await queryRepository.getCollectionsContainingCardForUser( 630 card.cardId.getStringValue(), 631 curatorId.value, 632 ); 633 634 // Verify the result is sorted by name 635 expect(result).toHaveLength(3); 636 expect(result[0]?.name).toBe('Alpha Collection'); 637 expect(result[1]?.name).toBe('Beta Collection'); 638 expect(result[2]?.name).toBe('Zebra Collection'); 639 }); 640 }); 641 642 describe('Edge cases', () => { 643 it('should handle non-existent curator gracefully', async () => { 644 const card = CardFactory.create({ 645 curatorId: curatorId.value, 646 cardInput: { 647 type: CardTypeEnum.NOTE, 648 text: 'Test card', 649 }, 650 }).unwrap(); 651 652 await cardRepository.save(card); 653 654 // Execute the query with non-existent curator 655 const result = await queryRepository.getCollectionsContainingCardForUser( 656 card.cardId.getStringValue(), 657 'did:plc:nonexistent', 658 ); 659 660 // Verify the result 661 expect(result).toHaveLength(0); 662 }); 663 664 it('should handle empty curator ID gracefully', async () => { 665 const card = CardFactory.create({ 666 curatorId: curatorId.value, 667 cardInput: { 668 type: CardTypeEnum.NOTE, 669 text: 'Test card', 670 }, 671 }).unwrap(); 672 673 await cardRepository.save(card); 674 675 // Execute the query with empty curator ID 676 const result = await queryRepository.getCollectionsContainingCardForUser( 677 card.cardId.getStringValue(), 678 '', 679 ); 680 681 // Verify the result 682 expect(result).toHaveLength(0); 683 }); 684 685 it('should handle collections with null published records', async () => { 686 // Create a card 687 const card = CardFactory.create({ 688 curatorId: curatorId.value, 689 cardInput: { 690 type: CardTypeEnum.NOTE, 691 text: 'Card for null published record test', 692 }, 693 }).unwrap(); 694 695 await cardRepository.save(card); 696 697 // Create collection without published record 698 const collection = Collection.create( 699 { 700 authorId: curatorId, 701 name: 'Collection Without Published Record', 702 accessType: CollectionAccessType.OPEN, 703 collaboratorIds: [], 704 createdAt: new Date(), 705 updatedAt: new Date(), 706 }, 707 new UniqueEntityID(), 708 ).unwrap(); 709 710 collection.addCard(card.cardId, curatorId); 711 await collectionRepository.save(collection); 712 713 // Execute the query 714 const result = await queryRepository.getCollectionsContainingCardForUser( 715 card.cardId.getStringValue(), 716 curatorId.value, 717 ); 718 719 // Verify the result 720 expect(result).toHaveLength(1); 721 expect(result[0]?.uri).toBeUndefined(); 722 expect(result[0]?.name).toBe('Collection Without Published Record'); 723 }); 724 }); 725 726 describe('Multiple card types', () => { 727 it('should work with different card types in the same collection', async () => { 728 // Create different types of cards 729 const urlMetadata = UrlMetadata.create({ 730 url: 'https://example.com/test', 731 title: 'Test URL', 732 description: 'A test URL for mixed content', 733 }).unwrap(); 734 735 const urlCard = CardFactory.create({ 736 curatorId: curatorId.value, 737 cardInput: { 738 type: CardTypeEnum.URL, 739 url: 'https://example.com/test', 740 metadata: urlMetadata, 741 }, 742 }).unwrap(); 743 744 const noteCard = CardFactory.create({ 745 curatorId: curatorId.value, 746 cardInput: { 747 type: CardTypeEnum.NOTE, 748 text: 'Test note', 749 }, 750 }).unwrap(); 751 752 await cardRepository.save(urlCard); 753 await cardRepository.save(noteCard); 754 755 // Create collection and add all cards 756 const collection = Collection.create( 757 { 758 authorId: curatorId, 759 name: 'Mixed Content Collection', 760 description: 'Collection with different card types', 761 accessType: CollectionAccessType.OPEN, 762 collaboratorIds: [], 763 createdAt: new Date(), 764 updatedAt: new Date(), 765 }, 766 new UniqueEntityID(), 767 ).unwrap(); 768 769 collection.addCard(urlCard.cardId, curatorId); 770 collection.addCard(noteCard.cardId, curatorId); 771 772 await collectionRepository.save(collection); 773 774 // Test each card type 775 const urlResult = 776 await queryRepository.getCollectionsContainingCardForUser( 777 urlCard.cardId.getStringValue(), 778 curatorId.value, 779 ); 780 781 const noteResult = 782 await queryRepository.getCollectionsContainingCardForUser( 783 noteCard.cardId.getStringValue(), 784 curatorId.value, 785 ); 786 787 // Verify all return the same collection 788 expect(urlResult).toHaveLength(1); 789 expect(noteResult).toHaveLength(1); 790 791 expect(urlResult[0]?.name).toBe('Mixed Content Collection'); 792 expect(noteResult[0]?.name).toBe('Mixed Content Collection'); 793 794 expect(urlResult[0]?.id).toBe(collection.collectionId.getStringValue()); 795 expect(noteResult[0]?.id).toBe(collection.collectionId.getStringValue()); 796 }); 797 }); 798});