This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

semble / src / modules / cards / tests / application / GetCollectionsForUrlUseCase.test.ts
25 kB 810 lines
1import { GetCollectionsForUrlUseCase } from '../../application/useCases/queries/GetCollectionsForUrlUseCase'; 2import { InMemoryCardRepository } from '../utils/InMemoryCardRepository'; 3import { InMemoryCollectionRepository } from '../utils/InMemoryCollectionRepository'; 4import { InMemoryCollectionQueryRepository } from '../utils/InMemoryCollectionQueryRepository'; 5import { CuratorId } from '../../domain/value-objects/CuratorId'; 6import { CardBuilder } from '../utils/builders/CardBuilder'; 7import { CollectionBuilder } from '../utils/builders/CollectionBuilder'; 8import { CardTypeEnum } from '../../domain/value-objects/CardType'; 9import { URL } from '../../domain/value-objects/URL'; 10import { PublishedRecordId } from '../../domain/value-objects/PublishedRecordId'; 11import { 12 CollectionSortField, 13 SortOrder, 14} from '../../domain/ICollectionQueryRepository'; 15import { FakeProfileService } from '../utils/FakeProfileService'; 16import { InMemoryFollowsRepository } from '../../../user/tests/infrastructure/InMemoryFollowsRepository'; 17 18describe('GetCollectionsForUrlUseCase', () => { 19 let useCase: GetCollectionsForUrlUseCase; 20 let cardRepository: InMemoryCardRepository; 21 let collectionRepository: InMemoryCollectionRepository; 22 let collectionQueryRepository: InMemoryCollectionQueryRepository; 23 let profileService: FakeProfileService; 24 let followsRepository: InMemoryFollowsRepository; 25 let curator1: CuratorId; 26 let curator2: CuratorId; 27 let curator3: CuratorId; 28 29 beforeEach(() => { 30 cardRepository = InMemoryCardRepository.getInstance(); 31 collectionRepository = InMemoryCollectionRepository.getInstance(); 32 collectionQueryRepository = new InMemoryCollectionQueryRepository( 33 collectionRepository, 34 cardRepository, 35 ); 36 profileService = new FakeProfileService(); 37 followsRepository = InMemoryFollowsRepository.getInstance(); 38 39 useCase = new GetCollectionsForUrlUseCase( 40 collectionQueryRepository, 41 profileService, 42 collectionRepository, 43 followsRepository, 44 ); 45 46 curator1 = CuratorId.create('did:plc:curator1').unwrap(); 47 curator2 = CuratorId.create('did:plc:curator2').unwrap(); 48 curator3 = CuratorId.create('did:plc:curator3').unwrap(); 49 50 // Add profiles for test curators 51 profileService.addProfile({ 52 id: curator1.value, 53 name: 'Curator One', 54 handle: 'curator1', 55 avatarUrl: 'https://example.com/avatar1.jpg', 56 }); 57 58 profileService.addProfile({ 59 id: curator2.value, 60 name: 'Curator Two', 61 handle: 'curator2', 62 avatarUrl: 'https://example.com/avatar2.jpg', 63 }); 64 65 profileService.addProfile({ 66 id: curator3.value, 67 name: 'Curator Three', 68 handle: 'curator3', 69 avatarUrl: 'https://example.com/avatar3.jpg', 70 }); 71 }); 72 73 afterEach(() => { 74 cardRepository.clear(); 75 collectionRepository.clear(); 76 collectionQueryRepository.clear(); 77 profileService.clear(); 78 }); 79 80 describe('Collections with URL cards', () => { 81 it('should return all collections containing cards with the specified URL', async () => { 82 const testUrl = 'https://example.com/shared-article'; 83 const url = URL.create(testUrl).unwrap(); 84 85 // Create URL cards for different users with the same URL 86 const card1 = new CardBuilder() 87 .withCuratorId(curator1.value) 88 .withType(CardTypeEnum.URL) 89 .withUrl(url) 90 .build(); 91 92 const card2 = new CardBuilder() 93 .withCuratorId(curator2.value) 94 .withType(CardTypeEnum.URL) 95 .withUrl(url) 96 .build(); 97 98 const card3 = new CardBuilder() 99 .withCuratorId(curator3.value) 100 .withType(CardTypeEnum.URL) 101 .withUrl(url) 102 .build(); 103 104 if ( 105 card1 instanceof Error || 106 card2 instanceof Error || 107 card3 instanceof Error 108 ) { 109 throw new Error('Failed to create cards'); 110 } 111 112 // Add cards to their respective libraries 113 card1.addToLibrary(curator1); 114 card2.addToLibrary(curator2); 115 card3.addToLibrary(curator3); 116 117 await cardRepository.save(card1); 118 await cardRepository.save(card2); 119 await cardRepository.save(card3); 120 121 // Create collections for each user and add their cards 122 const collection1 = new CollectionBuilder() 123 .withAuthorId(curator1.value) 124 .withName('Tech Articles') 125 .withDescription('My tech articles') 126 .build(); 127 128 const collection2 = new CollectionBuilder() 129 .withAuthorId(curator2.value) 130 .withName('Reading List') 131 .withDescription('Articles to read') 132 .build(); 133 134 const collection3 = new CollectionBuilder() 135 .withAuthorId(curator3.value) 136 .withName('Favorites') 137 .build(); 138 139 if ( 140 collection1 instanceof Error || 141 collection2 instanceof Error || 142 collection3 instanceof Error 143 ) { 144 throw new Error('Failed to create collections'); 145 } 146 147 // Add cards to collections 148 collection1.addCard(card1.cardId, curator1); 149 collection2.addCard(card2.cardId, curator2); 150 collection3.addCard(card3.cardId, curator3); 151 152 // Mark collections as published 153 const publishedRecordId1 = PublishedRecordId.create({ 154 uri: 'at://did:plc:curator1/network.cosmik.collection/collection1', 155 cid: 'bafyreicollection1', 156 }); 157 const publishedRecordId2 = PublishedRecordId.create({ 158 uri: 'at://did:plc:curator2/network.cosmik.collection/collection2', 159 cid: 'bafyreicollection2', 160 }); 161 const publishedRecordId3 = PublishedRecordId.create({ 162 uri: 'at://did:plc:curator3/network.cosmik.collection/collection3', 163 cid: 'bafyreicollection3', 164 }); 165 166 collection1.markAsPublished(publishedRecordId1); 167 collection2.markAsPublished(publishedRecordId2); 168 collection3.markAsPublished(publishedRecordId3); 169 170 await collectionRepository.save(collection1); 171 await collectionRepository.save(collection2); 172 await collectionRepository.save(collection3); 173 174 // Execute the use case 175 const query = { 176 url: testUrl, 177 }; 178 179 const result = await useCase.execute(query); 180 181 // Verify the result 182 expect(result.isOk()).toBe(true); 183 const response = result.unwrap(); 184 185 expect(response.collections).toHaveLength(3); 186 expect(response.pagination.totalCount).toBe(3); 187 188 // Check that all three collections are included 189 const collectionIds = response.collections.map((c) => c.id); 190 expect(collectionIds).toContain( 191 collection1.collectionId.getStringValue(), 192 ); 193 expect(collectionIds).toContain( 194 collection2.collectionId.getStringValue(), 195 ); 196 expect(collectionIds).toContain( 197 collection3.collectionId.getStringValue(), 198 ); 199 200 // Verify collection details 201 const techArticles = response.collections.find( 202 (c) => c.name === 'Tech Articles', 203 ); 204 expect(techArticles).toBeDefined(); 205 expect(techArticles?.description).toBe('My tech articles'); 206 expect(techArticles?.author.id).toBe(curator1.value); 207 expect(techArticles?.uri).toBe( 208 'at://did:plc:curator1/network.cosmik.collection/collection1', 209 ); 210 211 const readingList = response.collections.find( 212 (c) => c.name === 'Reading List', 213 ); 214 expect(readingList).toBeDefined(); 215 expect(readingList?.description).toBe('Articles to read'); 216 expect(readingList?.author.id).toBe(curator2.value); 217 218 const favorites = response.collections.find( 219 (c) => c.name === 'Favorites', 220 ); 221 expect(favorites).toBeDefined(); 222 expect(favorites?.description).toBeUndefined(); 223 expect(favorites?.author.id).toBe(curator3.value); 224 }); 225 226 it('should return empty array when no collections contain cards with the specified URL', async () => { 227 const testUrl = 'https://example.com/nonexistent-article'; 228 229 const query = { 230 url: testUrl, 231 }; 232 233 const result = await useCase.execute(query); 234 235 expect(result.isOk()).toBe(true); 236 const response = result.unwrap(); 237 238 expect(response.collections).toHaveLength(0); 239 expect(response.pagination.totalCount).toBe(0); 240 }); 241 242 it('should not return collections that contain cards with different URLs', async () => { 243 const testUrl1 = 'https://example.com/article1'; 244 const testUrl2 = 'https://example.com/article2'; 245 const url1 = URL.create(testUrl1).unwrap(); 246 const url2 = URL.create(testUrl2).unwrap(); 247 248 // Create cards with different URLs 249 const card1 = new CardBuilder() 250 .withCuratorId(curator1.value) 251 .withType(CardTypeEnum.URL) 252 .withUrl(url1) 253 .build(); 254 255 const card2 = new CardBuilder() 256 .withCuratorId(curator2.value) 257 .withType(CardTypeEnum.URL) 258 .withUrl(url2) 259 .build(); 260 261 if (card1 instanceof Error || card2 instanceof Error) { 262 throw new Error('Failed to create cards'); 263 } 264 265 card1.addToLibrary(curator1); 266 card2.addToLibrary(curator2); 267 268 await cardRepository.save(card1); 269 await cardRepository.save(card2); 270 271 // Create collections 272 const collection1 = new CollectionBuilder() 273 .withAuthorId(curator1.value) 274 .withName('Collection 1') 275 .build(); 276 277 const collection2 = new CollectionBuilder() 278 .withAuthorId(curator2.value) 279 .withName('Collection 2') 280 .build(); 281 282 if (collection1 instanceof Error || collection2 instanceof Error) { 283 throw new Error('Failed to create collections'); 284 } 285 286 collection1.addCard(card1.cardId, curator1); 287 collection2.addCard(card2.cardId, curator2); 288 289 await collectionRepository.save(collection1); 290 await collectionRepository.save(collection2); 291 292 // Query for testUrl1 293 const query = { 294 url: testUrl1, 295 }; 296 297 const result = await useCase.execute(query); 298 299 expect(result.isOk()).toBe(true); 300 const response = result.unwrap(); 301 302 expect(response.collections).toHaveLength(1); 303 expect(response.collections[0]!.name).toBe('Collection 1'); 304 expect(response.collections[0]!.author.id).toBe(curator1.value); 305 }); 306 307 it('should return multiple collections from the same user if they contain the URL', async () => { 308 const testUrl = 'https://example.com/popular-article'; 309 const url = URL.create(testUrl).unwrap(); 310 311 // Create URL card 312 const card = new CardBuilder() 313 .withCuratorId(curator1.value) 314 .withType(CardTypeEnum.URL) 315 .withUrl(url) 316 .build(); 317 318 if (card instanceof Error) { 319 throw new Error('Failed to create card'); 320 } 321 322 card.addToLibrary(curator1); 323 await cardRepository.save(card); 324 325 // Create multiple collections for the same user 326 const collection1 = new CollectionBuilder() 327 .withAuthorId(curator1.value) 328 .withName('Tech') 329 .build(); 330 331 const collection2 = new CollectionBuilder() 332 .withAuthorId(curator1.value) 333 .withName('Favorites') 334 .build(); 335 336 const collection3 = new CollectionBuilder() 337 .withAuthorId(curator1.value) 338 .withName('To Read') 339 .build(); 340 341 if ( 342 collection1 instanceof Error || 343 collection2 instanceof Error || 344 collection3 instanceof Error 345 ) { 346 throw new Error('Failed to create collections'); 347 } 348 349 // Add the same card to all collections 350 collection1.addCard(card.cardId, curator1); 351 collection2.addCard(card.cardId, curator1); 352 collection3.addCard(card.cardId, curator1); 353 354 await collectionRepository.save(collection1); 355 await collectionRepository.save(collection2); 356 await collectionRepository.save(collection3); 357 358 // Execute the use case 359 const query = { 360 url: testUrl, 361 }; 362 363 const result = await useCase.execute(query); 364 365 expect(result.isOk()).toBe(true); 366 const response = result.unwrap(); 367 368 expect(response.collections).toHaveLength(3); 369 370 const collectionNames = response.collections.map((c) => c.name); 371 expect(collectionNames).toContain('Tech'); 372 expect(collectionNames).toContain('Favorites'); 373 expect(collectionNames).toContain('To Read'); 374 375 // All should have the same author 376 response.collections.forEach((collection) => { 377 expect(collection.author.id).toBe(curator1.value); 378 }); 379 }); 380 381 it('should handle collections without published record IDs', async () => { 382 const testUrl = 'https://example.com/unpublished-article'; 383 const url = URL.create(testUrl).unwrap(); 384 385 // Create URL card 386 const card = new CardBuilder() 387 .withCuratorId(curator1.value) 388 .withType(CardTypeEnum.URL) 389 .withUrl(url) 390 .build(); 391 392 if (card instanceof Error) { 393 throw new Error('Failed to create card'); 394 } 395 396 card.addToLibrary(curator1); 397 await cardRepository.save(card); 398 399 // Create collection without publishing it 400 const collection = new CollectionBuilder() 401 .withAuthorId(curator1.value) 402 .withName('Unpublished Collection') 403 .build(); 404 405 if (collection instanceof Error) { 406 throw new Error('Failed to create collection'); 407 } 408 409 collection.addCard(card.cardId, curator1); 410 await collectionRepository.save(collection); 411 412 // Execute the use case 413 const query = { 414 url: testUrl, 415 }; 416 417 const result = await useCase.execute(query); 418 419 expect(result.isOk()).toBe(true); 420 const response = result.unwrap(); 421 422 expect(response.collections).toHaveLength(1); 423 expect(response.collections[0]!.name).toBe('Unpublished Collection'); 424 expect(response.collections[0]!.uri).toBeUndefined(); 425 }); 426 }); 427 428 describe('Pagination', () => { 429 it('should paginate results correctly', async () => { 430 const testUrl = 'https://example.com/popular-article'; 431 const url = URL.create(testUrl).unwrap(); 432 433 // Create 5 cards with the same URL from different users 434 const cards = []; 435 const curators = []; 436 const collections = []; 437 438 for (let i = 1; i <= 5; i++) { 439 const curator = CuratorId.create(`did:plc:curator${i}`).unwrap(); 440 curators.push(curator); 441 442 // Add profile for this curator 443 profileService.addProfile({ 444 id: curator.value, 445 name: `Curator ${i}`, 446 handle: `curator${i}`, 447 avatarUrl: `https://example.com/avatar${i}.jpg`, 448 }); 449 450 const card = new CardBuilder() 451 .withCuratorId(curator.value) 452 .withType(CardTypeEnum.URL) 453 .withUrl(url) 454 .build(); 455 456 if (card instanceof Error) { 457 throw new Error(`Failed to create card ${i}`); 458 } 459 460 card.addToLibrary(curator); 461 cards.push(card); 462 await cardRepository.save(card); 463 464 // Create collection for each user 465 const collection = new CollectionBuilder() 466 .withAuthorId(curator.value) 467 .withName(`Collection ${i}`) 468 .build(); 469 470 if (collection instanceof Error) { 471 throw new Error(`Failed to create collection ${i}`); 472 } 473 474 collection.addCard(card.cardId, curator); 475 collections.push(collection); 476 await collectionRepository.save(collection); 477 } 478 479 // Test first page with limit 2 480 const query1 = { 481 url: testUrl, 482 page: 1, 483 limit: 2, 484 }; 485 486 const result1 = await useCase.execute(query1); 487 expect(result1.isOk()).toBe(true); 488 const response1 = result1.unwrap(); 489 490 expect(response1.collections).toHaveLength(2); 491 expect(response1.pagination.currentPage).toBe(1); 492 expect(response1.pagination.totalCount).toBe(5); 493 expect(response1.pagination.totalPages).toBe(3); 494 expect(response1.pagination.hasMore).toBe(true); 495 496 // Test second page 497 const query2 = { 498 url: testUrl, 499 page: 2, 500 limit: 2, 501 }; 502 503 const result2 = await useCase.execute(query2); 504 expect(result2.isOk()).toBe(true); 505 const response2 = result2.unwrap(); 506 507 expect(response2.collections).toHaveLength(2); 508 expect(response2.pagination.currentPage).toBe(2); 509 expect(response2.pagination.hasMore).toBe(true); 510 511 // Test last page 512 const query3 = { 513 url: testUrl, 514 page: 3, 515 limit: 2, 516 }; 517 518 const result3 = await useCase.execute(query3); 519 expect(result3.isOk()).toBe(true); 520 const response3 = result3.unwrap(); 521 522 expect(response3.collections).toHaveLength(1); 523 expect(response3.pagination.currentPage).toBe(3); 524 expect(response3.pagination.hasMore).toBe(false); 525 }); 526 527 it('should respect limit cap of 100', async () => { 528 const query = { 529 url: 'https://example.com/test', 530 limit: 200, // Should be capped at 100 531 }; 532 533 const result = await useCase.execute(query); 534 expect(result.isOk()).toBe(true); 535 const response = result.unwrap(); 536 537 expect(response.pagination.limit).toBe(100); 538 }); 539 540 it('should use default pagination values', async () => { 541 const testUrl = 'https://example.com/test-article'; 542 543 const query = { 544 url: testUrl, 545 }; 546 547 const result = await useCase.execute(query); 548 expect(result.isOk()).toBe(true); 549 const response = result.unwrap(); 550 551 expect(response.pagination.currentPage).toBe(1); 552 expect(response.pagination.limit).toBe(20); 553 }); 554 }); 555 556 describe('Sorting', () => { 557 it('should use default sorting parameters (NAME, ASC)', async () => { 558 const testUrl = 'https://example.com/test-article'; 559 560 const query = { 561 url: testUrl, 562 }; 563 564 const result = await useCase.execute(query); 565 expect(result.isOk()).toBe(true); 566 const response = result.unwrap(); 567 568 expect(response.sorting.sortBy).toBe(CollectionSortField.NAME); 569 expect(response.sorting.sortOrder).toBe(SortOrder.ASC); 570 }); 571 572 it('should use provided sorting parameters', async () => { 573 const testUrl = 'https://example.com/test-article'; 574 575 const query = { 576 url: testUrl, 577 sortBy: CollectionSortField.CREATED_AT, 578 sortOrder: SortOrder.DESC, 579 }; 580 581 const result = await useCase.execute(query); 582 expect(result.isOk()).toBe(true); 583 const response = result.unwrap(); 584 585 expect(response.sorting.sortBy).toBe(CollectionSortField.CREATED_AT); 586 expect(response.sorting.sortOrder).toBe(SortOrder.DESC); 587 }); 588 589 it('should sort collections by name in ascending order by default', async () => { 590 const testUrl = 'https://example.com/article'; 591 const url = URL.create(testUrl).unwrap(); 592 593 // Create URL cards 594 const card1 = new CardBuilder() 595 .withCuratorId(curator1.value) 596 .withType(CardTypeEnum.URL) 597 .withUrl(url) 598 .build(); 599 600 const card2 = new CardBuilder() 601 .withCuratorId(curator2.value) 602 .withType(CardTypeEnum.URL) 603 .withUrl(url) 604 .build(); 605 606 const card3 = new CardBuilder() 607 .withCuratorId(curator3.value) 608 .withType(CardTypeEnum.URL) 609 .withUrl(url) 610 .build(); 611 612 if ( 613 card1 instanceof Error || 614 card2 instanceof Error || 615 card3 instanceof Error 616 ) { 617 throw new Error('Failed to create cards'); 618 } 619 620 card1.addToLibrary(curator1); 621 card2.addToLibrary(curator2); 622 card3.addToLibrary(curator3); 623 624 await cardRepository.save(card1); 625 await cardRepository.save(card2); 626 await cardRepository.save(card3); 627 628 // Create collections with names that should be sorted 629 const collectionZ = new CollectionBuilder() 630 .withAuthorId(curator1.value) 631 .withName('Zebra Collection') 632 .build(); 633 634 const collectionA = new CollectionBuilder() 635 .withAuthorId(curator2.value) 636 .withName('Apple Collection') 637 .build(); 638 639 const collectionM = new CollectionBuilder() 640 .withAuthorId(curator3.value) 641 .withName('Mango Collection') 642 .build(); 643 644 if ( 645 collectionZ instanceof Error || 646 collectionA instanceof Error || 647 collectionM instanceof Error 648 ) { 649 throw new Error('Failed to create collections'); 650 } 651 652 collectionZ.addCard(card1.cardId, curator1); 653 collectionA.addCard(card2.cardId, curator2); 654 collectionM.addCard(card3.cardId, curator3); 655 656 await collectionRepository.save(collectionZ); 657 await collectionRepository.save(collectionA); 658 await collectionRepository.save(collectionM); 659 660 const query = { 661 url: testUrl, 662 }; 663 664 const result = await useCase.execute(query); 665 expect(result.isOk()).toBe(true); 666 const response = result.unwrap(); 667 668 expect(response.collections).toHaveLength(3); 669 expect(response.collections[0]!.name).toBe('Apple Collection'); 670 expect(response.collections[1]!.name).toBe('Mango Collection'); 671 expect(response.collections[2]!.name).toBe('Zebra Collection'); 672 }); 673 674 it('should sort collections by name in descending order', async () => { 675 const testUrl = 'https://example.com/article'; 676 const url = URL.create(testUrl).unwrap(); 677 678 // Create URL cards 679 const card1 = new CardBuilder() 680 .withCuratorId(curator1.value) 681 .withType(CardTypeEnum.URL) 682 .withUrl(url) 683 .build(); 684 685 const card2 = new CardBuilder() 686 .withCuratorId(curator2.value) 687 .withType(CardTypeEnum.URL) 688 .withUrl(url) 689 .build(); 690 691 const card3 = new CardBuilder() 692 .withCuratorId(curator3.value) 693 .withType(CardTypeEnum.URL) 694 .withUrl(url) 695 .build(); 696 697 if ( 698 card1 instanceof Error || 699 card2 instanceof Error || 700 card3 instanceof Error 701 ) { 702 throw new Error('Failed to create cards'); 703 } 704 705 card1.addToLibrary(curator1); 706 card2.addToLibrary(curator2); 707 card3.addToLibrary(curator3); 708 709 await cardRepository.save(card1); 710 await cardRepository.save(card2); 711 await cardRepository.save(card3); 712 713 // Create collections with names that should be sorted 714 const collectionZ = new CollectionBuilder() 715 .withAuthorId(curator1.value) 716 .withName('Zebra Collection') 717 .build(); 718 719 const collectionA = new CollectionBuilder() 720 .withAuthorId(curator2.value) 721 .withName('Apple Collection') 722 .build(); 723 724 const collectionM = new CollectionBuilder() 725 .withAuthorId(curator3.value) 726 .withName('Mango Collection') 727 .build(); 728 729 if ( 730 collectionZ instanceof Error || 731 collectionA instanceof Error || 732 collectionM instanceof Error 733 ) { 734 throw new Error('Failed to create collections'); 735 } 736 737 collectionZ.addCard(card1.cardId, curator1); 738 collectionA.addCard(card2.cardId, curator2); 739 collectionM.addCard(card3.cardId, curator3); 740 741 await collectionRepository.save(collectionZ); 742 await collectionRepository.save(collectionA); 743 await collectionRepository.save(collectionM); 744 745 const query = { 746 url: testUrl, 747 sortBy: CollectionSortField.NAME, 748 sortOrder: SortOrder.DESC, 749 }; 750 751 const result = await useCase.execute(query); 752 expect(result.isOk()).toBe(true); 753 const response = result.unwrap(); 754 755 expect(response.collections).toHaveLength(3); 756 expect(response.collections[0]!.name).toBe('Zebra Collection'); 757 expect(response.collections[1]!.name).toBe('Mango Collection'); 758 expect(response.collections[2]!.name).toBe('Apple Collection'); 759 }); 760 }); 761 762 describe('Validation', () => { 763 it('should fail with invalid URL', async () => { 764 const query = { 765 url: 'not-a-valid-url', 766 }; 767 768 const result = await useCase.execute(query); 769 770 expect(result.isErr()).toBe(true); 771 if (result.isErr()) { 772 expect(result.error.message).toContain('Invalid URL'); 773 } 774 }); 775 }); 776 777 describe('Error handling', () => { 778 it('should handle repository errors gracefully', async () => { 779 // Create a mock repository that throws an error 780 const errorCollectionQueryRepository = { 781 findByCreator: jest.fn(), 782 getCollectionsContainingCardForUser: jest.fn(), 783 getCollectionsWithUrl: jest 784 .fn() 785 .mockRejectedValue(new Error('Database error')), 786 searchCollections: jest.fn(), 787 getOpenCollectionsWithContributor: jest.fn(), 788 getCollectionContributors: jest.fn(), 789 }; 790 791 const errorUseCase = new GetCollectionsForUrlUseCase( 792 errorCollectionQueryRepository, 793 profileService, 794 collectionRepository, 795 followsRepository, 796 ); 797 798 const query = { 799 url: 'https://example.com/test-url', 800 }; 801 802 const result = await errorUseCase.execute(query); 803 804 expect(result.isErr()).toBe(true); 805 if (result.isErr()) { 806 expect(result.error.message).toContain('Database error'); 807 } 808 }); 809 }); 810});