diff --git a/apps/backend/src/app/modules/maps/maps.service.spec.ts b/apps/backend/src/app/modules/maps/maps.service.spec.ts index 31e2de383..e80a9ae11 100644 --- a/apps/backend/src/app/modules/maps/maps.service.spec.ts +++ b/apps/backend/src/app/modules/maps/maps.service.spec.ts @@ -1,6 +1,7 @@ import { Test, TestingModule } from '@nestjs/testing'; import { mockDeep } from 'jest-mock-extended'; import { + ConflictException, ForbiddenException, InternalServerErrorException, NotFoundException @@ -14,6 +15,8 @@ import { } from '../../../../test/prisma-mock.const'; import { MapsService } from './maps.service'; import * as Bitflags from '@momentum/bitflags'; +import { CreateMapDto } from '../../dto'; +import { FileStoreService } from '../filestore/file-store.service'; describe('MapsService', () => { let service: MapsService, db: PrismaMock; @@ -45,7 +48,10 @@ describe('MapsService', () => { it('should 500 for invalid map data', async () => { db.mMap.findUnique.mockResolvedValueOnce('sausage' as any); - await expect(service.getMapAndCheckReadAccess({ mapID: 1, userID: 1 })).rejects.toThrow(InternalServerErrorException); + await expect(service.getMapAndCheckReadAccess({ + mapID: 1, + userID: 1 + })).rejects.toThrow(InternalServerErrorException); }); it('should only allow mod and admins to access APPROVED maps when submissionOnly is true', async () => { @@ -74,19 +80,19 @@ describe('MapsService', () => { ).rejects.toThrow(ForbiddenException); } } - }) + }); - it('should only non-logged in requests to access APPROVED and PUBLIC_TESTING maps', async () => { - for (const status of Enum.values(MapStatus)) { - db.mMap.findUnique.mockResolvedValueOnce({id: 1, status } as any); + it('should only non-logged in requests to access APPROVED and PUBLIC_TESTING maps', async () => { + for (const status of Enum.values(MapStatus)) { + db.mMap.findUnique.mockResolvedValueOnce({ id: 1, status } as any); - if ([MapStatus.APPROVED, MapStatus.PUBLIC_TESTING, MapStatus.FINAL_APPROVAL].includes(status)) { - expect(await service.getMapAndCheckReadAccess({ mapID: 1, })).toBeTruthy(); - } else { - await expect(service.getMapAndCheckReadAccess({ mapID: 1 })).rejects.toThrow(ForbiddenException); - } - } - }); + if ([MapStatus.APPROVED, MapStatus.PUBLIC_TESTING, MapStatus.FINAL_APPROVAL].includes(status)) { + expect(await service.getMapAndCheckReadAccess({ mapID: 1 })).toBeTruthy(); + } else { + await expect(service.getMapAndCheckReadAccess({ mapID: 1 })).rejects.toThrow(ForbiddenException); + } + } + }); // Map of what states should PASS access checks for each MapStatus, // otherwise should fail. Below code performs each check against each state. @@ -94,7 +100,7 @@ describe('MapsService', () => { [MapStatus.APPROVED]: 'any', [MapStatus.PUBLIC_TESTING]: 'any', [MapStatus.PRIVATE_TESTING]: ['admin', 'moderator', 'submitter', 'acceptedRequest', 'inCredits'], - [MapStatus.CONTENT_APPROVAL]: ['admin', 'moderator', 'submitter' ,'reviewer', 'acceptedRequest', 'inCredits'], + [MapStatus.CONTENT_APPROVAL]: ['admin', 'moderator', 'submitter', 'reviewer', 'acceptedRequest', 'inCredits'], [MapStatus.FINAL_APPROVAL]: 'any', [MapStatus.DISABLED]: ['admin', 'moderator'] }; @@ -206,4 +212,41 @@ describe('MapsService', () => { }); } }); + + describe('submitMap', () => { + it('should throw ConflictException if map with the same bspHash already exists', async () => { + const dto = {} as CreateMapDto; + const userID = 1; + const bspHash = 'existingHash'; + const bspFile = { buffer: Buffer.from('dummy data') } as any; + + jest.spyOn(service, 'checkCreateDto').mockResolvedValueOnce(undefined); + jest.spyOn(service, 'getBspFromTemp').mockResolvedValueOnce(bspFile); + jest + .spyOn(service, 'checkMapCompression') + .mockResolvedValueOnce(undefined); + jest.spyOn(service, 'checkMapFiles').mockImplementationOnce(() => {}); + jest + .spyOn(service, 'checkSuggestionsAndZones') + .mockImplementationOnce(() => {}); + jest + .spyOn(FileStoreService, 'getHashForBuffer') + .mockReturnValueOnce(bspHash); + + db.mMap.findFirst.mockResolvedValueOnce({ + id: 123, + name: '123', + status: 123, + images: [], + submitterID: 123, + currentVersionID: '123', + createdAt: new Date(), + updatedAt: new Date() + }); + + await expect(service.submitMap(dto, userID, [])).rejects.toThrow( + ConflictException + ); + }); + }); }); diff --git a/apps/backend/src/app/modules/maps/maps.service.ts b/apps/backend/src/app/modules/maps/maps.service.ts index 8465c644e..738eb7856 100644 --- a/apps/backend/src/app/modules/maps/maps.service.ts +++ b/apps/backend/src/app/modules/maps/maps.service.ts @@ -760,7 +760,7 @@ export class MapsService { ); } - private async checkCreateDto(userID: number, dto: CreateMapDto) { + async checkCreateDto(userID: number, dto: CreateMapDto) { const user = await this.db.user.findUnique({ where: { id: userID }, include: { submittedMaps: true } @@ -970,7 +970,7 @@ export class MapsService { }); } - private checkMapFiles(bspFile: File, vmfFiles: File[]) { + checkMapFiles(bspFile: File, vmfFiles: File[]) { if (!bspFile.originalname.endsWith('.bsp')) throw new BadRequestException('Bad BSP name');