diff --git a/prisma/migrations/20210228121726-test-run--nullable-test-variation-id/README.md b/prisma/migrations/20210228121726-test-run--nullable-test-variation-id/README.md new file mode 100644 index 00000000..7e965fda --- /dev/null +++ b/prisma/migrations/20210228121726-test-run--nullable-test-variation-id/README.md @@ -0,0 +1,76 @@ +# Migration `20210228121726-test-run--nullable-test-variation-id` + +This migration has been generated by Pavel Strunkin at 2/28/2021, 2:17:26 PM. +You can check out the [state of the schema](./schema.prisma) after the migration. + +## Database Steps + +```sql +ALTER TYPE "TestStatus" ADD VALUE 'autoApproved' +``` + +## Changes + +```diff +diff --git schema.prisma schema.prisma +migration 20210130115922-test-run-auto-approve-status-added..20210228121726-test-run--nullable-test-variation-id +--- datamodel.dml ++++ datamodel.dml +@@ -3,9 +3,9 @@ + } + datasource db { + provider = "postgresql" +- url = "***" ++ url = "***" + } + model Build { + id String @id @default(uuid()) +@@ -38,35 +38,35 @@ + @@unique([name]) + } + model TestRun { +- id String @id @default(uuid()) ++ id String @id @default(uuid()) + imageName String + diffName String? + diffPercent Float? +- diffTollerancePercent Float @default(0) ++ diffTollerancePercent Float @default(0) + pixelMisMatchCount Int? + status TestStatus + buildId String +- build Build @relation(fields: [buildId], references: [id]) +- testVariationId String +- testVariation TestVariation @relation(fields: [testVariationId], references: [id]) +- merge Boolean @default(false) +- updatedAt DateTime @updatedAt +- createdAt DateTime @default(now()) ++ build Build @relation(fields: [buildId], references: [id]) ++ testVariationId String? ++ testVariation TestVariation? @relation(fields: [testVariationId], references: [id]) ++ merge Boolean @default(false) ++ updatedAt DateTime @updatedAt ++ createdAt DateTime @default(now()) + // Test variation data +- name String @default("") ++ name String @default("") + browser String? + device String? + os String? + viewport String? + baselineName String? + comment String? + baseline Baseline? +- branchName String @default("master") ++ branchName String @default("master") + baselineBranchName String? +- ignoreAreas String @default("[]") +- tempIgnoreAreas String @default("[]") ++ ignoreAreas String @default("[]") ++ tempIgnoreAreas String @default("[]") + } + model TestVariation { + id String @id @default(uuid()) +``` + + diff --git a/prisma/migrations/20210228121726-test-run--nullable-test-variation-id/schema.prisma b/prisma/migrations/20210228121726-test-run--nullable-test-variation-id/schema.prisma new file mode 100644 index 00000000..a8ac40c3 --- /dev/null +++ b/prisma/migrations/20210228121726-test-run--nullable-test-variation-id/schema.prisma @@ -0,0 +1,123 @@ +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "postgresql" + url = "***" +} + +model Build { + id String @id @default(uuid()) + ciBuildId String? + number Int? + branchName String? + status String? + testRuns TestRun[] + projectId String + project Project @relation(fields: [projectId], references: [id]) + updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) + user User? @relation(fields: [userId], references: [id]) + userId String? + isRunning Boolean? + + @@unique([projectId, ciBuildId]) +} + +model Project { + id String @id @default(uuid()) + name String + mainBranchName String @default("master") + builds Build[] + buildsCounter Int @default(0) + testVariations TestVariation[] + updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) + + @@unique([name]) +} + +model TestRun { + id String @id @default(uuid()) + imageName String + diffName String? + diffPercent Float? + diffTollerancePercent Float @default(0) + pixelMisMatchCount Int? + status TestStatus + buildId String + build Build @relation(fields: [buildId], references: [id]) + testVariationId String? + testVariation TestVariation? @relation(fields: [testVariationId], references: [id]) + merge Boolean @default(false) + updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) + // Test variation data + name String @default("") + browser String? + device String? + os String? + viewport String? + baselineName String? + comment String? + baseline Baseline? + branchName String @default("master") + baselineBranchName String? + ignoreAreas String @default("[]") + tempIgnoreAreas String @default("[]") +} + +model TestVariation { + id String @id @default(uuid()) + name String + branchName String @default("master") + browser String? + device String? + os String? + viewport String? + baselineName String? + ignoreAreas String @default("[]") + projectId String + project Project @relation(fields: [projectId], references: [id]) + testRuns TestRun[] + baselines Baseline[] + comment String? + updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) + + @@unique([projectId, name, browser, device, os, viewport, branchName]) +} + +model Baseline { + id String @id @default(uuid()) + baselineName String + testVariationId String + testVariation TestVariation @relation(fields: [testVariationId], references: [id]) + testRunId String? + testRun TestRun? @relation(fields: [testRunId], references: [id]) + updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) +} + +model User { + id String @id @default(uuid()) + email String @unique + password String + firstName String? + lastName String? + apiKey String @unique + isActive Boolean @default(true) + builds Build[] + updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) +} + +enum TestStatus { + failed + new + ok + unresolved + approved + autoApproved +} diff --git a/prisma/migrations/20210228121726-test-run--nullable-test-variation-id/steps.json b/prisma/migrations/20210228121726-test-run--nullable-test-variation-id/steps.json new file mode 100644 index 00000000..50baaef1 --- /dev/null +++ b/prisma/migrations/20210228121726-test-run--nullable-test-variation-id/steps.json @@ -0,0 +1,17 @@ +{ + "version": "0.3.14-fixed", + "steps": [ + { + "tag": "UpdateField", + "model": "TestRun", + "field": "testVariationId", + "arity": "Optional" + }, + { + "tag": "UpdateField", + "model": "TestRun", + "field": "testVariation", + "arity": "Optional" + } + ] +} \ No newline at end of file diff --git a/prisma/migrations/migrate.lock b/prisma/migrations/migrate.lock index df95abc5..3d5761f1 100644 --- a/prisma/migrations/migrate.lock +++ b/prisma/migrations/migrate.lock @@ -13,4 +13,5 @@ 20201115155739-ci-build-id-added 20201201211711-test-run--temp-ignore-areas-added 20210118201534-build--project-id---ci-build-id-constraint -20210130115922-test-run-auto-approve-status-added \ No newline at end of file +20210130115922-test-run-auto-approve-status-added +20210228121726-test-run--nullable-test-variation-id \ No newline at end of file diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 48a21c89..f2571305 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -39,22 +39,22 @@ model Project { } model TestRun { - id String @id @default(uuid()) + id String @id @default(uuid()) imageName String diffName String? diffPercent Float? - diffTollerancePercent Float @default(0) + diffTollerancePercent Float @default(0) pixelMisMatchCount Int? status TestStatus buildId String - build Build @relation(fields: [buildId], references: [id]) - testVariationId String - testVariation TestVariation @relation(fields: [testVariationId], references: [id]) - merge Boolean @default(false) - updatedAt DateTime @updatedAt - createdAt DateTime @default(now()) + build Build @relation(fields: [buildId], references: [id]) + testVariationId String? + testVariation TestVariation? @relation(fields: [testVariationId], references: [id]) + merge Boolean @default(false) + updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) // Test variation data - name String @default("") + name String @default("") browser String? device String? os String? @@ -62,10 +62,10 @@ model TestRun { baselineName String? comment String? baseline Baseline? - branchName String @default("master") + branchName String @default("master") baselineBranchName String? - ignoreAreas String @default("[]") - tempIgnoreAreas String @default("[]") + ignoreAreas String @default("[]") + tempIgnoreAreas String @default("[]") } model TestVariation { diff --git a/src/projects/projects.service.ts b/src/projects/projects.service.ts index 0c5603e2..8ed208e6 100644 --- a/src/projects/projects.service.ts +++ b/src/projects/projects.service.ts @@ -64,14 +64,10 @@ export class ProjectsService { }, }); - try { - await Promise.all(project.builds.map((build) => this.buildsService.remove(build.id))); - await Promise.all( - project.testVariations.map((testVariation) => this.testVariationsService.delete(testVariation.id)) - ); - } catch (err) { - console.log(err); - } + await Promise.all(project.builds.map((build) => this.buildsService.remove(build.id))); + await Promise.all( + project.testVariations.map((testVariation) => this.testVariationsService.delete(testVariation.id)) + ); return this.prismaService.project.delete({ where: { id }, diff --git a/src/test-variations/test-variations.service.spec.ts b/src/test-variations/test-variations.service.spec.ts index b268fc28..1b4f2b9a 100644 --- a/src/test-variations/test-variations.service.spec.ts +++ b/src/test-variations/test-variations.service.spec.ts @@ -26,6 +26,7 @@ const initModule = async ({ testRunCreateMock = jest.fn(), testRunFindMany = jest.fn(), testRunDeleteMock = jest.fn(), + $executeRawMock = jest.fn(), }) => { const module: TestingModule = await Test.createTestingModule({ providers: [ @@ -54,6 +55,7 @@ const initModule = async ({ { provide: PrismaService, useValue: { + $executeRaw: $executeRawMock, testVariation: { findUnique: variationfindUniqueMock, findMany: variationFindManyMock, @@ -500,11 +502,13 @@ describe('TestVariationsService', () => { ], }; + const $executeRawMock = jest.fn(); const variationDeleteMock = jest.fn(); const getDetailsMock = jest.fn().mockResolvedValueOnce(variation); const deleteBaselineMock = jest.fn().mockResolvedValueOnce(variation.baselines[0]); const service = await initModule({ variationDeleteMock, + $executeRawMock, }); service.getDetails = getDetailsMock; service.deleteBaseline = deleteBaselineMock; @@ -513,6 +517,7 @@ describe('TestVariationsService', () => { expect(service.getDetails).toHaveBeenCalledWith(testVariationId); expect(service.deleteBaseline).toHaveBeenCalledWith(variation.baselines[0]); + expect($executeRawMock).toHaveBeenCalled(); expect(variationDeleteMock).toHaveBeenCalledWith({ where: { id: testVariationId }, }); diff --git a/src/test-variations/test-variations.service.ts b/src/test-variations/test-variations.service.ts index bf13e8aa..6ac60110 100644 --- a/src/test-variations/test-variations.service.ts +++ b/src/test-variations/test-variations.service.ts @@ -144,10 +144,15 @@ export class TestVariationsService { async delete(id: string): Promise { const testVariation = await this.getDetails(id); - // delete baseline + // delete Baselines await Promise.all(testVariation.baselines.map((baseline) => this.deleteBaseline(baseline))); - // delete testVariation + // disconnect TestRuns + // workaround due to https://github.com/prisma/prisma/issues/2810 + await this.prismaService + .$executeRaw`UPDATE "public"."TestRun" SET "testVariationId" = NULL::text WHERE "testVariationId" = ${id}`; + + // delete TestVariation return this.prismaService.testVariation.delete({ where: { id }, }); diff --git a/test/jest-e2e.json b/test/jest-e2e.json index 93400cd3..ee496a86 100644 --- a/test/jest-e2e.json +++ b/test/jest-e2e.json @@ -3,6 +3,7 @@ "rootDir": "../", "testEnvironment": "node", "testRegex": ".e2e-spec.ts$", + "testTimeout": 30000, "transform": { "^.+\\.(t|j)s$": "ts-jest" } diff --git a/test/test-runs.e2e-spec.ts b/test/test-runs.e2e-spec.ts index 0ce5d36e..9ceae255 100644 --- a/test/test-runs.e2e-spec.ts +++ b/test/test-runs.e2e-spec.ts @@ -9,7 +9,6 @@ import { ProjectsService } from '../src/projects/projects.service'; import { Project, TestStatus } from '@prisma/client'; import { BuildsService } from '../src/builds/builds.service'; -jest.setTimeout(20000); jest.useFakeTimers(); describe('TestRuns (e2e)', () => { diff --git a/test/test-variations.e2e-spec.ts b/test/test-variations.e2e-spec.ts new file mode 100644 index 00000000..89d73596 --- /dev/null +++ b/test/test-variations.e2e-spec.ts @@ -0,0 +1,67 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { INestApplication } from '@nestjs/common'; +import { AppModule } from '../src/app.module'; +import { UsersService } from '../src/users/users.service'; +import { haveTestRunCreated, haveUserLogged } from './preconditions'; +import { UserLoginResponseDto } from '../src/users/dto/user-login-response.dto'; +import { TestRunsService } from '../src/test-runs/test-runs.service'; +import { ProjectsService } from '../src/projects/projects.service'; +import { Project } from '@prisma/client'; +import { BuildsService } from '../src/builds/builds.service'; +import { TestVariationsService } from '../src/test-variations/test-variations.service'; + +jest.useFakeTimers(); + +describe('TestVariations (e2e)', () => { + let app: INestApplication; + let testRunsService: TestRunsService; + let usersService: UsersService; + let projecstService: ProjectsService; + let buildsService: BuildsService; + let user: UserLoginResponseDto; + let project: Project; + let testVariationsService: TestVariationsService; + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication(); + testRunsService = moduleFixture.get(TestRunsService); + usersService = moduleFixture.get(UsersService); + projecstService = moduleFixture.get(ProjectsService); + buildsService = moduleFixture.get(BuildsService); + testVariationsService = moduleFixture.get(TestVariationsService); + + await app.init(); + }); + + beforeEach(async () => { + user = await haveUserLogged(usersService); + project = await projecstService.create({ name: 'TestVariations E2E test', mainBranchName: 'master' }); + }); + + afterEach(async () => { + await projecstService.remove(project.id); + await usersService.delete(user.id); + }); + + afterAll(async () => { + jest.runOnlyPendingTimers(); + await app.close(); + }); + + describe('DELETE /', () => { + const image_v1 = './test/image.png'; + + it('can delete', async () => { + const { testRun } = await haveTestRunCreated(buildsService, testRunsService, project.id, 'develop', image_v1); + await testRunsService.approve(testRun.id, false); + + await testVariationsService.delete(testRun.testVariationId); + + expect((await testRunsService.findOne(testRun.id)).testVariationId).toBeNull(); + }); + }); +});