diff --git a/.gitignore b/.gitignore index dbc1d777..e8782206 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,9 @@ node_modules # unit tests coverage +# macOS +.DS_Store + # ignore npm lock package-json.lock .npmrc \ No newline at end of file diff --git a/src/collections/domain/models/CollectionLinks.ts b/src/collections/domain/models/CollectionLinks.ts new file mode 100644 index 00000000..40b7799b --- /dev/null +++ b/src/collections/domain/models/CollectionLinks.ts @@ -0,0 +1,8 @@ +import { CollectionSummary } from './CollectionSummary' +import { DatasetSummary } from '../../../datasets/domain/models/DatasetSummary' + +export interface CollectionLinks { + linkedCollections: CollectionSummary[] + collectionsLinkingToThis: CollectionSummary[] + linkedDatasets: DatasetSummary[] +} diff --git a/src/collections/domain/models/CollectionSummary.ts b/src/collections/domain/models/CollectionSummary.ts new file mode 100644 index 00000000..bb4ee24d --- /dev/null +++ b/src/collections/domain/models/CollectionSummary.ts @@ -0,0 +1,5 @@ +export interface CollectionSummary { + id: number + alias: string + displayName: string +} diff --git a/src/collections/domain/repositories/ICollectionsRepository.ts b/src/collections/domain/repositories/ICollectionsRepository.ts index a9ec708d..820a1356 100644 --- a/src/collections/domain/repositories/ICollectionsRepository.ts +++ b/src/collections/domain/repositories/ICollectionsRepository.ts @@ -9,6 +9,7 @@ import { CollectionSearchCriteria } from '../models/CollectionSearchCriteria' import { CollectionUserPermissions } from '../models/CollectionUserPermissions' import { PublicationStatus } from '../../../core/domain/models/PublicationStatus' import { CollectionItemType } from '../../../collections/domain/models/CollectionItemType' +import { CollectionLinks } from '../models/CollectionLinks' export interface ICollectionsRepository { getCollection(collectionIdOrAlias: number | string): Promise @@ -50,4 +51,13 @@ export interface ICollectionsRepository { ): Promise deleteCollectionFeaturedItems(collectionIdOrAlias: number | string): Promise deleteCollectionFeaturedItem(featuredItemId: number): Promise + linkCollection( + linkedCollectionIdOrAlias: number | string, + linkingCollectionIdOrAlias: number | string + ): Promise + unlinkCollection( + linkedCollectionIdOrAlias: number | string, + linkingCollectionIdOrAlias: number | string + ): Promise + getCollectionLinks(collectionIdOrAlias: number | string): Promise } diff --git a/src/collections/domain/useCases/GetCollectionLinks.ts b/src/collections/domain/useCases/GetCollectionLinks.ts new file mode 100644 index 00000000..fa0b6c92 --- /dev/null +++ b/src/collections/domain/useCases/GetCollectionLinks.ts @@ -0,0 +1,22 @@ +import { UseCase } from '../../../core/domain/useCases/UseCase' +import { ICollectionsRepository } from '../repositories/ICollectionsRepository' +import { CollectionLinks } from '../models/CollectionLinks' + +export class GetCollectionLinks implements UseCase { + private collectionsRepository: ICollectionsRepository + + constructor(collectionsRepository: ICollectionsRepository) { + this.collectionsRepository = collectionsRepository + } + + /** + * Returns a CollectionLinks object containing other collections this collection is linked to, the other collections linking to this collection, and datasets linked to this collection, given the collection identifier or alias. + * + * @param {number | string} [collectionIdOrAlias] - A generic collection identifier, which can be either a string (for queries by CollectionAlias), or a number (for queries by CollectionId) + * If this parameter is not set, the default value is: ':root' + * @returns {Promise} + */ + async execute(collectionId: number | string): Promise { + return await this.collectionsRepository.getCollectionLinks(collectionId) + } +} diff --git a/src/collections/domain/useCases/LinkCollection.ts b/src/collections/domain/useCases/LinkCollection.ts new file mode 100644 index 00000000..4ea0b2b4 --- /dev/null +++ b/src/collections/domain/useCases/LinkCollection.ts @@ -0,0 +1,27 @@ +import { UseCase } from '../../../core/domain/useCases/UseCase' +import { ICollectionsRepository } from '../repositories/ICollectionsRepository' + +export class LinkCollection implements UseCase { + private collectionsRepository: ICollectionsRepository + + constructor(collectionsRepository: ICollectionsRepository) { + this.collectionsRepository = collectionsRepository + } + + /** + * Creates a link between two collections. The linked collection will be linked to the linking collection.: + * + * @param {number| string} [linkedCollectionIdOrAlias] - The collection to be linked. Can be either a string (collection alias), or a number (collection id) + * @param { number | string} [linkingCollectionIdOrAlias] - The collection that will be linking to the linked collection. Can be either a string (collection alias), or a number (collection id) + * @returns {Promise} -This method does not return anything upon successful completion. + */ + async execute( + linkedCollectionIdOrAlias: number | string, + linkingCollectionIdOrAlias: number | string + ): Promise { + return await this.collectionsRepository.linkCollection( + linkedCollectionIdOrAlias, + linkingCollectionIdOrAlias + ) + } +} diff --git a/src/collections/domain/useCases/UnlinkCollection.ts b/src/collections/domain/useCases/UnlinkCollection.ts new file mode 100644 index 00000000..2f4d4e3e --- /dev/null +++ b/src/collections/domain/useCases/UnlinkCollection.ts @@ -0,0 +1,27 @@ +import { UseCase } from '../../../core/domain/useCases/UseCase' +import { ICollectionsRepository } from '../repositories/ICollectionsRepository' + +export class UnlinkCollection implements UseCase { + private collectionsRepository: ICollectionsRepository + + constructor(collectionsRepository: ICollectionsRepository) { + this.collectionsRepository = collectionsRepository + } + + /** + * Unlinks a collection from the collection that links to it + * + * @param {number| string} [linkedCollectionIdOrAlias] - The collection that is linked. Can be either a string (collection alias), or a number (collection id) + * @param { number | string} [linkingCollectionIdOrAlias] - The collection that links to the linked collection. Can be either a string (collection alias), or a number (collection id) + * @returns {Promise} -This method does not return anything upon successful completion. + */ + async execute( + linkedCollectionIdOrAlias: number | string, + linkingCollectionIdOrAlias: number | string + ): Promise { + return await this.collectionsRepository.unlinkCollection( + linkedCollectionIdOrAlias, + linkingCollectionIdOrAlias + ) + } +} diff --git a/src/collections/index.ts b/src/collections/index.ts index c275402e..05e49954 100644 --- a/src/collections/index.ts +++ b/src/collections/index.ts @@ -12,6 +12,9 @@ import { DeleteCollectionFeaturedItems } from './domain/useCases/DeleteCollectio import { DeleteCollection } from './domain/useCases/DeleteCollection' import { GetMyDataCollectionItems } from './domain/useCases/GetMyDataCollectionItems' import { DeleteCollectionFeaturedItem } from './domain/useCases/DeleteCollectionFeaturedItem' +import { LinkCollection } from './domain/useCases/LinkCollection' +import { UnlinkCollection } from './domain/useCases/UnlinkCollection' +import { GetCollectionLinks } from './domain/useCases/GetCollectionLinks' const collectionsRepository = new CollectionsRepository() @@ -28,6 +31,9 @@ const updateCollectionFeaturedItems = new UpdateCollectionFeaturedItems(collecti const deleteCollectionFeaturedItems = new DeleteCollectionFeaturedItems(collectionsRepository) const deleteCollection = new DeleteCollection(collectionsRepository) const deleteCollectionFeaturedItem = new DeleteCollectionFeaturedItem(collectionsRepository) +const linkCollection = new LinkCollection(collectionsRepository) +const unlinkCollection = new UnlinkCollection(collectionsRepository) +const getCollectionLinks = new GetCollectionLinks(collectionsRepository) export { getCollection, @@ -42,7 +48,10 @@ export { updateCollectionFeaturedItems, deleteCollectionFeaturedItems, deleteCollection, - deleteCollectionFeaturedItem + deleteCollectionFeaturedItem, + linkCollection, + unlinkCollection, + getCollectionLinks } export { Collection, CollectionInputLevel } from './domain/models/Collection' export { CollectionFacet } from './domain/models/CollectionFacet' diff --git a/src/collections/infra/repositories/CollectionsRepository.ts b/src/collections/infra/repositories/CollectionsRepository.ts index b200fa37..18e3c474 100644 --- a/src/collections/infra/repositories/CollectionsRepository.ts +++ b/src/collections/infra/repositories/CollectionsRepository.ts @@ -3,6 +3,7 @@ import { ICollectionsRepository } from '../../domain/repositories/ICollectionsRe import { transformCollectionFacetsResponseToCollectionFacets, transformCollectionItemsResponseToCollectionItemSubset, + transformCollectionLinksResponseToCollectionLinks, transformCollectionResponseToCollection, transformMyDataResponseToCollectionItemSubset } from './transformers/collectionTransformers' @@ -36,6 +37,7 @@ import { import { ApiConstants } from '../../../core/infra/repositories/ApiConstants' import { PublicationStatus } from '../../../core/domain/models/PublicationStatus' import { ReadError } from '../../../core/domain/repositories/ReadError' +import { CollectionLinks } from '../../domain/models/CollectionLinks' export interface NewCollectionRequestPayload { alias: string @@ -446,4 +448,38 @@ export class CollectionsRepository extends ApiRepository implements ICollections throw error }) } + public async linkCollection( + linkedCollectionIdOrAlias: number | string, + linkingCollectionIdOrAlias: number | string + ): Promise { + return this.doPut( + `/dataverses/${linkedCollectionIdOrAlias}/link/${linkingCollectionIdOrAlias}`, + {} // No data is needed for this operation + ) + .then(() => undefined) + .catch((error) => { + throw error + }) + } + public async unlinkCollection( + linkedCollectionIdOrAlias: number | string, + linkingCollectionIdOrAlias: number | string + ): Promise { + return this.doDelete( + `/dataverses/${linkedCollectionIdOrAlias}/deleteLink/${linkingCollectionIdOrAlias}` + ) + .then(() => undefined) + .catch((error) => { + throw error + }) + } + public async getCollectionLinks(collectionIdOrAlias: number | string): Promise { + return this.doGet(`/${this.collectionsResourceName}/${collectionIdOrAlias}/links`, true) + .then((response) => { + return transformCollectionLinksResponseToCollectionLinks(response) + }) + .catch((error) => { + throw error + }) + } } diff --git a/src/collections/infra/repositories/transformers/collectionTransformers.ts b/src/collections/infra/repositories/transformers/collectionTransformers.ts index 43802706..a26c4718 100644 --- a/src/collections/infra/repositories/transformers/collectionTransformers.ts +++ b/src/collections/infra/repositories/transformers/collectionTransformers.ts @@ -44,6 +44,7 @@ import { PublicationStatusCount } from '../../../domain/models/MyDataCollectionItemSubset' import { PublicationStatus } from '../../../../core/domain/models/PublicationStatus' +import { CollectionLinks } from '../../../domain/models/CollectionLinks' export const transformCollectionResponseToCollection = (response: AxiosResponse): Collection => { const collectionPayload = response.data.data @@ -152,7 +153,19 @@ export const transformCollectionItemsResponseToCollectionItemSubset = ( ...(countPerObjectType && { countPerObjectType }) } } - +export const transformCollectionLinksResponseToCollectionLinks = ( + response: AxiosResponse +): CollectionLinks => { + const responseDataPayload = response.data.data + const linkedCollections = responseDataPayload.linkedDataverses + const collectionsLinkingToThis = responseDataPayload.dataversesLinkingToThis + const linkedDatasets = responseDataPayload.linkedDatasets + return { + linkedCollections, + collectionsLinkingToThis, + linkedDatasets + } +} export const transformMyDataResponseToCollectionItemSubset = ( response: AxiosResponse ): MyDataCollectionItemSubset => { diff --git a/src/datasets/domain/models/DatasetSummary.ts b/src/datasets/domain/models/DatasetSummary.ts new file mode 100644 index 00000000..a868ad32 --- /dev/null +++ b/src/datasets/domain/models/DatasetSummary.ts @@ -0,0 +1,4 @@ +export interface DatasetSummary { + persistentId: string + title: string +} diff --git a/test/functional/collections/LinkCollection.test.ts b/test/functional/collections/LinkCollection.test.ts new file mode 100644 index 00000000..eff7550d --- /dev/null +++ b/test/functional/collections/LinkCollection.test.ts @@ -0,0 +1,71 @@ +import { + ApiConfig, + WriteError, + createCollection, + getCollection, + linkCollection, + deleteCollection, + getCollectionItems +} from '../../../src' +import { TestConstants } from '../../testHelpers/TestConstants' +import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig' +import { createCollectionDTO } from '../../testHelpers/collections/collectionHelper' + +describe('execute', () => { + const firstCollectionAlias = 'linkCollection-functional-test-first' + const secondCollectionAlias = 'linkCollection-functional-test-second' + let firstCollectionId: number + let secondCollectionId: number + beforeEach(async () => { + ApiConfig.init( + TestConstants.TEST_API_URL, + DataverseApiAuthMechanism.API_KEY, + process.env.TEST_API_KEY + ) + const firstCollection = createCollectionDTO(firstCollectionAlias) + const secondCollection = createCollectionDTO(secondCollectionAlias) + firstCollectionId = await createCollection.execute(firstCollection) + secondCollectionId = await createCollection.execute(secondCollection) + }) + + afterEach(async () => { + await Promise.all([ + deleteCollection.execute(firstCollectionId), + deleteCollection.execute(secondCollectionId) + ]) + }) + + test('should successfully link two collections', async () => { + expect.assertions(1) + try { + await linkCollection.execute(secondCollectionAlias, firstCollectionAlias) + } catch (error) { + throw new Error('Collections should be linked successfully') + } finally { + // Wait for the linking to be processed by Solr + await new Promise((resolve) => setTimeout(resolve, 5000)) + const collectionItemSubset = await getCollectionItems.execute(firstCollectionAlias) + + expect(collectionItemSubset.items.length).toBe(1) + } + }) + + test('should throw an error when linking a non-existent collection', async () => { + const invalidCollectionId = 99999 + const firstCollection = await getCollection.execute(firstCollectionAlias) + + expect.assertions(2) + let writeError: WriteError | undefined = undefined + try { + await linkCollection.execute(invalidCollectionId, firstCollection.id) + throw new Error('Use case should throw an error') + } catch (error) { + writeError = error as WriteError + } finally { + expect(writeError).toBeInstanceOf(WriteError) + expect(writeError?.message).toEqual( + `There was an error when writing the resource. Reason was: [404] Can't find dataverse with identifier='${invalidCollectionId}'` + ) + } + }) +}) diff --git a/test/functional/collections/UnlinkCollection.test.ts b/test/functional/collections/UnlinkCollection.test.ts new file mode 100644 index 00000000..0b20b455 --- /dev/null +++ b/test/functional/collections/UnlinkCollection.test.ts @@ -0,0 +1,71 @@ +import { + ApiConfig, + WriteError, + createCollection, + linkCollection, + deleteCollection, + getCollectionItems, + unlinkCollection +} from '../../../src' +import { TestConstants } from '../../testHelpers/TestConstants' +import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig' +import { createCollectionDTO } from '../../testHelpers/collections/collectionHelper' + +describe('execute', () => { + const firstCollectionAlias = 'unlinkCollection-functional-test-first' + const secondCollectionAlias = 'unlinkCollection-functional-test-second' + + let firstCollectionId: number + let secondCollectionId: number + beforeEach(async () => { + ApiConfig.init( + TestConstants.TEST_API_URL, + DataverseApiAuthMechanism.API_KEY, + process.env.TEST_API_KEY + ) + const firstCollectionDTO = createCollectionDTO(firstCollectionAlias) + const secondCollectionDTO = createCollectionDTO(secondCollectionAlias) + firstCollectionId = await createCollection.execute(firstCollectionDTO) + secondCollectionId = await createCollection.execute(secondCollectionDTO) + await linkCollection.execute(secondCollectionAlias, firstCollectionAlias) + // Give enough time to Solr for indexing + await new Promise((resolve) => setTimeout(resolve, 5000)) + }) + + afterEach(async () => { + await Promise.all([ + deleteCollection.execute(firstCollectionId), + deleteCollection.execute(secondCollectionId) + ]) + }) + + test('should successfully unlink two collections', async () => { + // Verify that the collections are linked + const collectionItemSubset = await getCollectionItems.execute(firstCollectionAlias) + expect(collectionItemSubset.items.length).toBe(1) + + await unlinkCollection.execute(secondCollectionAlias, firstCollectionAlias) + // Wait for the unlinking to be processed by Solr + await new Promise((resolve) => setTimeout(resolve, 5000)) + const collectionItemSubset2 = await getCollectionItems.execute(firstCollectionAlias) + expect(collectionItemSubset2.items.length).toBe(0) + }) + + test('should throw an error when unlinking a non-existent collection', async () => { + const invalidCollectionId = 99999 + + expect.assertions(2) + let writeError: WriteError | undefined = undefined + try { + await unlinkCollection.execute(invalidCollectionId, firstCollectionId) + throw new Error('Use case should throw an error') + } catch (error) { + writeError = error as WriteError + } finally { + expect(writeError).toBeInstanceOf(WriteError) + expect(writeError?.message).toEqual( + `There was an error when writing the resource. Reason was: [404] Can't find dataverse with identifier='${invalidCollectionId}'` + ) + } + }) +}) diff --git a/test/integration/collections/CollectionsRepository.test.ts b/test/integration/collections/CollectionsRepository.test.ts index b215f681..d1afd76d 100644 --- a/test/integration/collections/CollectionsRepository.test.ts +++ b/test/integration/collections/CollectionsRepository.test.ts @@ -15,7 +15,8 @@ import { createCollection, getDatasetFiles, restrictFile, - deleteFile + deleteFile, + linkDataset } from '../../../src' import { ApiConfig } from '../../../src' import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig' @@ -1911,4 +1912,137 @@ describe('CollectionsRepository', () => { ).rejects.toThrow(expectedError) }) }) + describe('linkCollection', () => { + const firstCollectionAlias = 'linkCollectionFirst' + const secondCollectionAlias = 'linkCollectionSecond' + + beforeAll(async () => { + await createCollectionViaApi(firstCollectionAlias) + await createCollectionViaApi(secondCollectionAlias) + }) + + afterAll(async () => { + await deleteCollectionViaApi(firstCollectionAlias) + await deleteCollectionViaApi(secondCollectionAlias) + }) + + test('should link a collection successfully', async () => { + const firstCollection = await sut.getCollection(firstCollectionAlias) + await sut.getCollection(secondCollectionAlias) + + await sut.linkCollection(secondCollectionAlias, firstCollectionAlias) + + await sut.getCollection(secondCollectionAlias) + await new Promise((res) => setTimeout(res, 2000)) + const collectionItemSubset = await sut.getCollectionItems(firstCollection.alias) + expect(collectionItemSubset.items.length).toBe(1) + }) + + test('should throw error when linking a non-existent collection', async () => { + const invalidCollectionId = 99999 + const firstCollection = await sut.getCollection(firstCollectionAlias) + + const expectedError = new WriteError("[404] Can't find dataverse with identifier='99999'") + + await expect(sut.linkCollection(invalidCollectionId, firstCollection.id)).rejects.toThrow( + expectedError + ) + }) + }) + + describe('unlinkCollection', () => { + const firstCollectionAlias = 'unlinkCollectionFirst' + const secondCollectionAlias = 'unlinkCollectionSecond' + + beforeAll(async () => { + await createCollectionViaApi(firstCollectionAlias) + await createCollectionViaApi(secondCollectionAlias) + + const firstCollection = await sut.getCollection(firstCollectionAlias) + const secondCollection = await sut.getCollection(secondCollectionAlias) + + await sut.linkCollection(secondCollection.id, firstCollection.id) + }) + + afterAll(async () => { + await deleteCollectionViaApi(firstCollectionAlias) + await deleteCollectionViaApi(secondCollectionAlias) + }) + + test('should unlink a collection successfully', async () => { + const firstCollection = await sut.getCollection(firstCollectionAlias) + const secondCollection = await sut.getCollection(secondCollectionAlias) + + await sut.unlinkCollection(secondCollection.id, firstCollection.id) + await new Promise((res) => setTimeout(res, 2000)) + + await sut.getCollection(secondCollectionAlias) + const collectionItemSubset = await sut.getCollectionItems(firstCollection.alias) + expect(collectionItemSubset.items).toStrictEqual([]) + }) + + test('should throw error when unlinking a non-existent collection', async () => { + const invalidCollectionId = 99999 + const firstCollection = await sut.getCollection(firstCollectionAlias) + + const expectedError = new WriteError("[404] Can't find dataverse with identifier='99999'") + + await expect(sut.unlinkCollection(invalidCollectionId, firstCollection.id)).rejects.toThrow( + expectedError + ) + }) + }) + describe('getCollectionLinks', () => { + const firstCollectionAlias = 'getCollectionLinksFirst' + const secondCollectionAlias = 'getCollectionLinksSecond' + const thirdCollectionAlias = 'getCollectionLinksThird' + const fourthCollectionAlias = 'getCollectionLinksFourth' + let childDatasetNumericId: number + beforeAll(async () => { + await createCollectionViaApi(firstCollectionAlias) + await createCollectionViaApi(secondCollectionAlias) + await createCollectionViaApi(thirdCollectionAlias) + await createCollectionViaApi(fourthCollectionAlias) + const { numericId: createdId } = await createDataset.execute( + TestConstants.TEST_NEW_DATASET_DTO, + fourthCollectionAlias + ) + childDatasetNumericId = createdId + await sut.linkCollection(secondCollectionAlias, firstCollectionAlias) + await sut.linkCollection(firstCollectionAlias, thirdCollectionAlias) + await sut.linkCollection(firstCollectionAlias, fourthCollectionAlias) + await linkDataset.execute(childDatasetNumericId, firstCollectionAlias) + }) + + afterAll(async () => { + await deleteUnpublishedDatasetViaApi(childDatasetNumericId) + await deleteCollectionViaApi(firstCollectionAlias) + await deleteCollectionViaApi(secondCollectionAlias) + await deleteCollectionViaApi(thirdCollectionAlias) + await deleteCollectionViaApi(fourthCollectionAlias) + }) + + test('should return collection links successfully', async () => { + const firstCollection = await sut.getCollection(firstCollectionAlias) + const collectionLinks = await sut.getCollectionLinks(firstCollection.id) + + expect(collectionLinks.linkedCollections).toHaveLength(1) + + expect(collectionLinks.linkedCollections[0].alias).toBe(secondCollectionAlias) + expect(collectionLinks.collectionsLinkingToThis).toHaveLength(2) + expect(collectionLinks.collectionsLinkingToThis[0].alias).toBe(thirdCollectionAlias) + expect(collectionLinks.collectionsLinkingToThis[1].alias).toBe(fourthCollectionAlias) + expect(collectionLinks.linkedDatasets).toHaveLength(1) + expect(collectionLinks.linkedDatasets[0].title).toBe( + 'Dataset created using the createDataset use case' + ) + }) + + test('should return error when collection does not exist', async () => { + const invalidCollectionId = 99999 + const expectedError = new ReadError("[404] Can't find dataverse with identifier='99999'") + + await expect(sut.getCollectionLinks(invalidCollectionId)).rejects.toThrow(expectedError) + }) + }) }) diff --git a/test/unit/collections/LinkCollection.test.ts b/test/unit/collections/LinkCollection.test.ts new file mode 100644 index 00000000..7555caa2 --- /dev/null +++ b/test/unit/collections/LinkCollection.test.ts @@ -0,0 +1,25 @@ +import { ICollectionsRepository } from '../../../src/collections/domain/repositories/ICollectionsRepository' +import { WriteError } from '../../../src' +import { LinkCollection } from '../../../src/collections/domain/useCases/LinkCollection' + +describe('execute', () => { + test('should link collection successfully on repository success', async () => { + const collectionRepositoryStub: ICollectionsRepository = {} as ICollectionsRepository + collectionRepositoryStub.linkCollection = jest.fn().mockResolvedValue(undefined) + + const testLinkCollection = new LinkCollection(collectionRepositoryStub) + + await expect(testLinkCollection.execute(1, 2)).resolves.toBeUndefined() + expect(collectionRepositoryStub.linkCollection).toHaveBeenCalledWith(1, 2) + }) + + test('should throw error on repository failure', async () => { + const collectionRepositoryStub: ICollectionsRepository = {} as ICollectionsRepository + collectionRepositoryStub.linkCollection = jest.fn().mockRejectedValue(new WriteError()) + + const testLinkCollection = new LinkCollection(collectionRepositoryStub) + + await expect(testLinkCollection.execute(1, 2)).rejects.toThrow(WriteError) + expect(collectionRepositoryStub.linkCollection).toHaveBeenCalledWith(1, 2) + }) +}) diff --git a/test/unit/collections/UnlinkCollection.test.ts b/test/unit/collections/UnlinkCollection.test.ts new file mode 100644 index 00000000..907aa769 --- /dev/null +++ b/test/unit/collections/UnlinkCollection.test.ts @@ -0,0 +1,25 @@ +import { ICollectionsRepository } from '../../../src/collections/domain/repositories/ICollectionsRepository' +import { WriteError } from '../../../src' +import { UnlinkCollection } from '../../../src/collections/domain/useCases/UnlinkCollection' + +describe('execute', () => { + test('should unlink collection successfully on repository success', async () => { + const collectionRepositoryStub: ICollectionsRepository = {} as ICollectionsRepository + collectionRepositoryStub.unlinkCollection = jest.fn().mockResolvedValue(undefined) + + const testUnlinkCollection = new UnlinkCollection(collectionRepositoryStub) + + await expect(testUnlinkCollection.execute(1, 2)).resolves.toBeUndefined() + expect(collectionRepositoryStub.unlinkCollection).toHaveBeenCalledWith(1, 2) + }) + + test('should throw error on repository failure', async () => { + const collectionRepositoryStub: ICollectionsRepository = {} as ICollectionsRepository + collectionRepositoryStub.unlinkCollection = jest.fn().mockRejectedValue(new WriteError()) + + const testUnlinkCollection = new UnlinkCollection(collectionRepositoryStub) + + await expect(testUnlinkCollection.execute(1, 2)).rejects.toThrow(WriteError) + expect(collectionRepositoryStub.unlinkCollection).toHaveBeenCalledWith(1, 2) + }) +})