import { PrismaService } from '../prisma/prisma.service'; import { TeamCollectionService } from '../team-collection/team-collection.service'; import { TeamService } from '../team/team.service'; import { TeamRequestService } from './team-request.service'; import { TEAM_REQ_INVALID_TARGET_COLL_ID, TEAM_INVALID_COLL_ID, TEAM_INVALID_ID, TEAM_REQ_NOT_FOUND, TEAM_REQ_REORDERING_FAILED, TEAM_COLL_NOT_FOUND, } from 'src/errors'; import * as E from 'fp-ts/Either'; import { mockDeep, mockReset } from 'jest-mock-extended'; import { TeamRequest } from './team-request.model'; import { MoveTeamRequestArgs } from './input-type.args'; import { TeamRequest as DbTeamRequest, Team as DbTeam, TeamCollection as DbTeamCollection, } from '@prisma/client'; const mockPrisma = mockDeep(); const mockTeamService = mockDeep(); const mockTeamCollectionService = mockDeep(); const mockPubSub = { publish: jest.fn().mockResolvedValue(null) }; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore const teamRequestService = new TeamRequestService( mockPrisma, mockTeamService as any, mockTeamCollectionService as any, mockPubSub as any, ); const team: DbTeam = { id: 'team-a', name: 'Team A', }; const teamCollection: DbTeamCollection = { id: 'team-coll-1', parentID: null, teamID: team.id, title: 'Team Collection 1', orderIndex: 1, createdOn: new Date(), updatedOn: new Date(), }; const dbTeamRequests: DbTeamRequest[] = []; for (let i = 1; i <= 10; i++) { dbTeamRequests.push({ id: `test-request-${i}`, collectionID: teamCollection.id, teamID: team.id, request: {}, title: `Test Request ${i}`, orderIndex: i, createdOn: new Date(), updatedOn: new Date(), }); } const teamRequests: TeamRequest[] = dbTeamRequests.map((tr) => ({ id: tr.id, collectionID: tr.collectionID, teamID: tr.teamID, title: tr.title, request: JSON.stringify(tr.request), })); beforeEach(async () => { mockReset(mockPrisma); }); describe('updateTeamRequest', () => { test('resolves correctly if title not given in parameter', async () => { const dbRequest = dbTeamRequests[0]; mockPrisma.teamRequest.update.mockResolvedValue(dbRequest); await expect( teamRequestService.updateTeamRequest( dbRequest.id, undefined, // title JSON.stringify(dbRequest.request), // request ), ).resolves.toBeDefined(); }); test('resolves correctly if request not given in parameter', async () => { const dbRequest = dbTeamRequests[0]; mockPrisma.teamRequest.update.mockResolvedValue(dbRequest); await expect( teamRequestService.updateTeamRequest( dbRequest.id, dbRequest.title, undefined, ), ).resolves.toBeDefined(); }); test('resolves correctly if both request and title are null', async () => { const dbRequest = dbTeamRequests[0]; mockPrisma.teamRequest.update.mockResolvedValue(dbRequest); await expect( teamRequestService.updateTeamRequest(dbRequest.id, undefined, undefined), ).resolves.toBeDefined(); }); test('resolves correctly for non-null request and title', async () => { const dbRequest = dbTeamRequests[0]; mockPrisma.teamRequest.update.mockResolvedValue(dbRequest); await expect( teamRequestService.updateTeamRequest( dbRequest.id, dbRequest.title, JSON.stringify(dbRequest.request), ), ).resolves.toBeDefined(); }); test('rejects for invalid request id', async () => { mockPrisma.teamRequest.update.mockRejectedValue('RecordNotFound'); await expect( teamRequestService.updateTeamRequest( 'invalidtestreq', undefined, undefined, ), ).resolves.toEqualLeft(TEAM_REQ_NOT_FOUND); }); test('resolves for valid request id', async () => { const dbRequest = dbTeamRequests[0]; mockPrisma.teamRequest.update.mockResolvedValue(dbRequest); await expect( teamRequestService.updateTeamRequest(dbRequest.id, undefined, undefined), ).resolves.toBeDefined(); }); test('publishes update to pubsub topic "team_req//req_updated"', async () => { const dbRequest = dbTeamRequests[0]; const request = teamRequests[0]; mockPrisma.teamRequest.update.mockResolvedValue(dbRequest); await teamRequestService.updateTeamRequest( dbRequest.id, undefined, undefined, ); expect(mockPubSub.publish).toHaveBeenCalledWith( `team_req/${dbRequest.teamID}/req_updated`, request, ); }); }); describe('searchRequest', () => { test('resolves with the correct info with a null cursor', async () => { const dbRequest = dbTeamRequests[0]; mockPrisma.teamRequest.findMany.mockResolvedValue(dbTeamRequests); await expect( teamRequestService.searchRequest( dbRequest.teamID, dbRequest.title, null, 10, ), ).resolves.toBeDefined(); }); test('resolves with an empty array when a match with the search term is not found', async () => { const dbRequest = dbTeamRequests[0]; mockPrisma.teamRequest.findMany.mockResolvedValue([]); await expect( teamRequestService.searchRequest( dbRequest.teamID, 'unknown_title', null, 10, ), ).resolves.toBeDefined(); }); test('resolves with the correct info with a set cursor', async () => { const dbRequest = dbTeamRequests[0]; mockPrisma.teamRequest.findMany.mockResolvedValue(dbTeamRequests); await expect( teamRequestService.searchRequest( dbRequest.teamID, dbRequest.title, dbRequest.id, 10, ), ).resolves.toBeDefined(); }); }); describe('deleteTeamRequest', () => { test('rejects if the request id is not found', async () => { mockPrisma.teamRequest.findFirst.mockResolvedValue(null as any); const response = teamRequestService.deleteTeamRequest('invalidrequest'); expect(response).resolves.toEqualLeft(TEAM_REQ_NOT_FOUND); expect(mockPrisma.teamRequest.delete).not.toHaveBeenCalled(); }); test('resolves for a valid request id', async () => { const dbRequest = dbTeamRequests[0]; mockPrisma.teamRequest.findFirst.mockResolvedValue(dbRequest); mockPrisma.teamRequest.delete.mockResolvedValue(dbRequest); await expect( teamRequestService.deleteTeamRequest(dbRequest.id), ).resolves.toEqualRight(true); }); test('publishes deletion to pubsub topic "team_req//req_deleted"', async () => { const dbRequest = dbTeamRequests[0]; mockPrisma.teamRequest.findFirst.mockResolvedValue(dbRequest); mockPrisma.teamRequest.delete.mockResolvedValue(dbRequest); await teamRequestService.deleteTeamRequest(dbRequest.id); expect(mockPubSub.publish).toHaveBeenCalledWith( `team_req/${dbRequest.teamID}/req_deleted`, dbRequest.id, ); }); }); describe('createTeamRequest', () => { test('rejects for invalid collection id', async () => { mockTeamCollectionService.getTeamOfCollection.mockResolvedValue( E.left(TEAM_INVALID_COLL_ID), ); const response = await teamRequestService.createTeamRequest( 'invalidcollid', team.id, 'Test Request', '{}', ); expect(response).toEqualLeft(TEAM_INVALID_COLL_ID); expect(mockPrisma.teamRequest.create).not.toHaveBeenCalled(); }); test('resolves for valid collection id', async () => { const dbRequest = dbTeamRequests[0]; const teamRequest = teamRequests[0]; mockTeamCollectionService.getTeamOfCollection.mockResolvedValue( E.right(team), ); mockPrisma.teamRequest.create.mockResolvedValue(dbRequest); const response = teamRequestService.createTeamRequest( 'testcoll', team.id, teamRequest.title, teamRequest.request, ); expect(response).resolves.toEqualRight(teamRequest); }); test('publishes creation to pubsub topic "team_req//req_created"', async () => { const dbRequest = dbTeamRequests[0]; const teamRequest = teamRequests[0]; mockTeamCollectionService.getTeamOfCollection.mockResolvedValue( E.right(team), ); mockPrisma.teamRequest.create.mockResolvedValue(dbRequest); await teamRequestService.createTeamRequest( 'testcoll', team.id, 'Test Request', '{}', ); expect(mockPubSub.publish).toHaveBeenCalledWith( `team_req/${dbRequest.teamID}/req_created`, teamRequest, ); }); }); describe('getRequestsInCollection', () => { test('resolves with an empty array if the collection id does not exist', async () => { mockPrisma.teamRequest.findMany.mockResolvedValue([]); await expect( teamRequestService.getRequestsInCollection('invalidCollID', null, 10), ).resolves.toEqual([]); }); test('resolves with the correct info for the collection id and null cursor', async () => { mockPrisma.teamRequest.findMany.mockResolvedValue(dbTeamRequests); const response = await teamRequestService.getRequestsInCollection( 'testcoll', null, 10, ); expect(response).toEqual(teamRequests); }); test('resolves with the correct info for the collection id and a valid cursor', async () => { mockPrisma.teamRequest.findMany.mockResolvedValue([dbTeamRequests[1]]); const response = teamRequestService.getRequestsInCollection( dbTeamRequests[1].collectionID, dbTeamRequests[0].id, 1, ); expect(response).resolves.toEqual([teamRequests[1]]); }); }); describe('getRequest', () => { test('resolves with the correct request info for valid request id', async () => { mockPrisma.teamRequest.findUnique.mockResolvedValue(dbTeamRequests[0]); expect(teamRequestService.getRequest('testrequest')).resolves.toEqualSome( expect.objectContaining(teamRequests[0]), ); }); test('resolves with null if the request id does not exist', async () => { mockPrisma.teamRequest.findUnique.mockResolvedValue(null as any); await expect( teamRequestService.getRequest('testrequest'), ).resolves.toBeNone(); }); }); describe('getTeamOfRequest', () => { test('rejects for invalid team id', async () => { mockTeamService.getTeamWithID.mockResolvedValue(null as any); expect( teamRequestService.getTeamOfRequest(teamRequests[0]), ).resolves.toEqualLeft(TEAM_INVALID_ID); }); test('resolves for valid team id', async () => { mockTeamService.getTeamWithID.mockResolvedValue(team); expect( teamRequestService.getTeamOfRequest(teamRequests[0]), ).resolves.toEqualRight(expect.objectContaining(team)); }); }); describe('getCollectionOfRequest', () => { test('rejects for invalid collection id', async () => { mockTeamCollectionService.getCollection.mockResolvedValue( E.left(TEAM_COLL_NOT_FOUND), ); expect( teamRequestService.getCollectionOfRequest(teamRequests[0]), ).resolves.toEqualLeft(TEAM_INVALID_COLL_ID); }); test('resolves for valid collection id', async () => { mockTeamCollectionService.getCollection.mockResolvedValue( E.right(teamCollection), ); expect( teamRequestService.getCollectionOfRequest(teamRequests[0]), ).resolves.toEqualRight(expect.objectContaining(teamCollection)); }); }); describe('getTeamOfRequestFromID', () => { test('rejects for invalid request id', async () => { mockPrisma.teamRequest.findUnique.mockResolvedValue(null as any); expect( teamRequestService.getTeamOfRequestFromID('invalidrequest'), ).resolves.toBeNone(); }); test('resolves for valid request id', async () => { mockPrisma.teamRequest.findUnique.mockResolvedValue(dbTeamRequests[0]); expect( teamRequestService.getTeamOfRequestFromID('testrequest'), ).resolves.toBeDefined(); }); }); describe('reorderRequests', () => { test('Should resolve left if transaction throws an error', async () => { const srcCollID = dbTeamRequests[0].collectionID; const request = dbTeamRequests[0]; const destCollID = dbTeamRequests[4].collectionID; const nextRequest = dbTeamRequests[4]; mockPrisma.$transaction.mockRejectedValueOnce(new Error()); const result = await teamRequestService.reorderRequests( request, srcCollID, nextRequest, destCollID, ); expect(result).toEqual(E.left(TEAM_REQ_REORDERING_FAILED)); }); test('Should resolve right and call transaction with the correct data', async () => { const srcCollID = dbTeamRequests[0].collectionID; const request = dbTeamRequests[0]; const destCollID = dbTeamRequests[4].collectionID; const nextRequest = dbTeamRequests[4]; const updatedReq: DbTeamRequest = { ...request, collectionID: destCollID, orderIndex: nextRequest.orderIndex, }; mockPrisma.$transaction.mockResolvedValueOnce(E.right(updatedReq)); const result = await teamRequestService.reorderRequests( request, srcCollID, nextRequest, destCollID, ); expect(mockPrisma.$transaction).toHaveBeenCalledWith(expect.any(Function)); expect(result).toEqual(E.right(updatedReq)); }); }); describe('findRequestAndNextRequest', () => { test('Should resolve right if the request and the next request are found', async () => { const args: MoveTeamRequestArgs = { srcCollID: teamRequests[0].collectionID, destCollID: teamRequests[4].collectionID, requestID: teamRequests[0].id, nextRequestID: teamRequests[4].id, }; mockPrisma.teamRequest.findFirst .mockResolvedValueOnce(dbTeamRequests[0]) .mockResolvedValueOnce(dbTeamRequests[4]); const result = await teamRequestService.findRequestAndNextRequest( args.srcCollID, args.requestID, args.destCollID, args.nextRequestID, ); expect(result).toEqualRight({ request: dbTeamRequests[0], nextRequest: dbTeamRequests[4], }); }); test('Should resolve right if the request and next request null', () => { const args: MoveTeamRequestArgs = { srcCollID: teamRequests[0].collectionID, destCollID: teamRequests[4].collectionID, requestID: teamRequests[0].id, nextRequestID: null, }; mockPrisma.teamRequest.findFirst .mockResolvedValueOnce(dbTeamRequests[0]) .mockResolvedValueOnce(null); const result = teamRequestService.findRequestAndNextRequest( args.srcCollID, args.requestID, args.destCollID, args.nextRequestID, ); expect(result).resolves.toEqualRight({ request: dbTeamRequests[0], nextRequest: null, }); }); test('Should resolve left if the request is not found', () => { const args: MoveTeamRequestArgs = { srcCollID: teamRequests[0].collectionID, destCollID: teamRequests[4].collectionID, requestID: 'invalid', nextRequestID: null, }; mockPrisma.teamRequest.findFirst.mockResolvedValueOnce(null); const result = teamRequestService.findRequestAndNextRequest( args.srcCollID, args.requestID, args.destCollID, args.nextRequestID, ); expect(result).resolves.toEqualLeft(TEAM_REQ_NOT_FOUND); }); test('Should resolve left if the nextRequest is not found', () => { const args: MoveTeamRequestArgs = { srcCollID: teamRequests[0].collectionID, destCollID: teamRequests[1].collectionID, requestID: teamRequests[0].id, nextRequestID: 'invalid', }; mockPrisma.teamRequest.findFirst .mockResolvedValueOnce(dbTeamRequests[0]) .mockResolvedValueOnce(null); const result = teamRequestService.findRequestAndNextRequest( args.srcCollID, args.requestID, args.destCollID, args.nextRequestID, ); expect(result).resolves.toEqualLeft(TEAM_REQ_NOT_FOUND); }); }); describe('moveRequest', () => { test('Should resolve right and the request', () => { const args: MoveTeamRequestArgs = { srcCollID: teamRequests[0].collectionID, destCollID: teamRequests[0].collectionID, requestID: teamRequests[0].id, nextRequestID: null, }; jest .spyOn(teamRequestService, 'findRequestAndNextRequest') .mockResolvedValue( E.right({ request: dbTeamRequests[0], nextRequest: null }), ); jest .spyOn(teamRequestService, 'reorderRequests') .mockResolvedValue(E.right(dbTeamRequests[0])); const result = teamRequestService.moveRequest( args.srcCollID, args.requestID, args.destCollID, args.nextRequestID, 'moveRequest', ); expect(result).resolves.toEqualRight(teamRequests[0]); }); test('Should resolve right and publish message to pubnub if callerFunction is moveRequest', async () => { const args: MoveTeamRequestArgs = { srcCollID: teamRequests[0].collectionID, destCollID: teamRequests[0].collectionID, requestID: teamRequests[0].id, nextRequestID: null, }; jest .spyOn(teamRequestService, 'findRequestAndNextRequest') .mockResolvedValue( E.right({ request: dbTeamRequests[0], nextRequest: null }), ); jest .spyOn(teamRequestService, 'reorderRequests') .mockResolvedValue(E.right(dbTeamRequests[0])); await teamRequestService.moveRequest( args.srcCollID, args.requestID, args.destCollID, args.nextRequestID, 'moveRequest', ); expect(mockPubSub.publish).toHaveBeenCalledWith( `team_req/${teamRequests[0].teamID}/req_moved`, teamRequests[0], ); }); test('Should resolve right and publish message to pubnub if callerFunction is updateLookUpRequestOrder', async () => { const args: MoveTeamRequestArgs = { srcCollID: teamRequests[0].collectionID, destCollID: teamRequests[0].collectionID, requestID: teamRequests[0].id, nextRequestID: null, }; jest .spyOn(teamRequestService, 'findRequestAndNextRequest') .mockResolvedValue( E.right({ request: dbTeamRequests[0], nextRequest: null }), ); jest .spyOn(teamRequestService, 'reorderRequests') .mockResolvedValue(E.right(dbTeamRequests[0])); await teamRequestService.moveRequest( args.srcCollID, args.requestID, args.destCollID, args.nextRequestID, 'updateLookUpRequestOrder', ); expect(mockPubSub.publish).toHaveBeenCalledWith( `team_req/${teamRequests[0].teamID}/req_order_updated`, { request: teamRequests[0], nextRequest: null }, ); }); test('Should resolve left if finding the requests fails', () => { const args: MoveTeamRequestArgs = { srcCollID: teamRequests[0].collectionID, destCollID: teamRequests[0].collectionID, requestID: teamRequests[0].id, nextRequestID: null, }; jest .spyOn(teamRequestService, 'findRequestAndNextRequest') .mockResolvedValue(E.left(TEAM_REQ_NOT_FOUND)); expect( teamRequestService.moveRequest( args.srcCollID, args.requestID, args.destCollID, args.nextRequestID, 'moveRequest', ), ).resolves.toEqualLeft(TEAM_REQ_NOT_FOUND); }); test('Should resolve left if mismatch team/collection of requests fails', () => { const args: MoveTeamRequestArgs = { srcCollID: teamRequests[0].collectionID, destCollID: teamRequests[0].collectionID, requestID: teamRequests[0].id, nextRequestID: null, }; jest .spyOn(teamRequestService, 'findRequestAndNextRequest') .mockResolvedValue(E.left(TEAM_REQ_INVALID_TARGET_COLL_ID)); expect( teamRequestService.moveRequest( args.srcCollID, args.requestID, args.destCollID, args.nextRequestID, 'moveRequest', ), ).resolves.toEqualLeft(TEAM_REQ_INVALID_TARGET_COLL_ID); }); test('Should resolve left if reorder fails', () => { const args: MoveTeamRequestArgs = { srcCollID: teamRequests[0].collectionID, destCollID: teamRequests[0].collectionID, requestID: teamRequests[0].id, nextRequestID: null, }; jest .spyOn(teamRequestService, 'findRequestAndNextRequest') .mockResolvedValue( E.right({ request: dbTeamRequests[0], nextRequest: null }), ); jest .spyOn(teamRequestService, 'reorderRequests') .mockResolvedValue(E.left(TEAM_REQ_REORDERING_FAILED)); expect( teamRequestService.moveRequest( args.srcCollID, args.requestID, args.destCollID, args.nextRequestID, 'moveRequest', ), ).resolves.toEqualLeft(TEAM_REQ_REORDERING_FAILED); }); }); describe('totalRequestsInATeam', () => { test('should resolve right and return a total team reqs count ', async () => { mockPrisma.teamRequest.count.mockResolvedValueOnce(2); const result = await teamRequestService.totalRequestsInATeam('id1'); expect(mockPrisma.teamRequest.count).toHaveBeenCalledWith({ where: { teamID: 'id1', }, }); expect(result).toEqual(2); }); test('should resolve left and return an error when no team reqs found', async () => { mockPrisma.teamRequest.count.mockResolvedValueOnce(0); const result = await teamRequestService.totalRequestsInATeam('id1'); expect(mockPrisma.teamRequest.count).toHaveBeenCalledWith({ where: { teamID: 'id1', }, }); expect(result).toEqual(0); }); describe('getTeamRequestsCount', () => { test('should return count of all Team Collections in the organization', async () => { mockPrisma.teamRequest.count.mockResolvedValueOnce(10); const result = await teamRequestService.getTeamRequestsCount(); expect(result).toEqual(10); }); }); });