Skip to content

Commit 65493c8

Browse files
author
Przybylski Krzysztof
committed
feat(#455): add protectedBranch project variable
1 parent 195c506 commit 65493c8

File tree

10 files changed

+80
-23
lines changed

10 files changed

+80
-23
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- AlterTable
2+
ALTER TABLE "Project" ADD COLUMN "protectedBranch" TEXT;

prisma/schema.prisma

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
generator client {
2-
provider = "prisma-client-js"
2+
provider = "prisma-client-js"
33
binaryTargets = ["native", "debian-openssl-3.0.x", "linux-arm64-openssl-3.0.x"]
44
}
55

@@ -41,6 +41,7 @@ model Project {
4141
builds Build[]
4242
TestRun TestRun[]
4343
testVariations TestVariation[]
44+
protectedBranch String?
4445
}
4546

4647
model TestRun {

src/_data_/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export const TEST_PROJECT: Project = {
1313
autoApproveFeature: true,
1414
imageComparisonConfig: '{ "threshold": 0.1, "ignoreAntialiasing": true, "allowDiffDimensions": false }',
1515
imageComparison: ImageComparison.pixelmatch,
16+
protectedBranch: 'release-[0-9]+'
1617
};
1718

1819
export const TEST_BUILD: Build = {

src/projects/dto/project.dto.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ApiProperty } from '@nestjs/swagger';
2-
import { IsUUID, IsString, IsNumber, IsBoolean, IsEnum, IsJSON, IsDate } from 'class-validator';
2+
import { IsUUID, IsString, IsNumber, IsBoolean, IsEnum, IsJSON, IsDate, IsOptional } from 'class-validator';
33
import { ImageComparison, Project } from '@prisma/client';
44
import { Type } from 'class-transformer';
55

@@ -49,4 +49,9 @@ export class ProjectDto implements Project {
4949
@ApiProperty()
5050
@IsJSON()
5151
imageComparisonConfig: string;
52+
53+
@ApiProperty()
54+
@IsString()
55+
@IsOptional()
56+
protectedBranch: string;
5257
}

src/projects/projects.service.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export class ProjectsService {
4848
imageComparisonConfig: projectDto.imageComparisonConfig,
4949
maxBuildAllowed: projectDto.maxBuildAllowed,
5050
maxBranchLifetime: projectDto.maxBranchLifetime,
51+
protectedBranch: projectDto.protectedBranch
5152
},
5253
});
5354
}
@@ -63,6 +64,7 @@ export class ProjectsService {
6364
maxBuildAllowed: projectDto.maxBuildAllowed,
6465
maxBranchLifetime: projectDto.maxBranchLifetime,
6566
imageComparisonConfig: projectDto.imageComparisonConfig,
67+
protectedBranch: projectDto.protectedBranch
6668
},
6769
});
6870
}

src/shared/tasks/tasks.service.spec.ts

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { TestVariationsService } from '../../test-variations/test-variations.ser
66

77
const initService = async ({
88
projectFindManyMock = jest.fn(),
9-
testVariationFindManyMock = jest.fn(),
9+
findOldTestVariationsMock = jest.fn(),
1010
testVariationsDeleteMock = jest.fn(),
1111
}) => {
1212
const module: TestingModule = await Test.createTestingModule({
@@ -15,9 +15,6 @@ const initService = async ({
1515
{
1616
provide: PrismaService,
1717
useValue: {
18-
testVariation: {
19-
findMany: testVariationFindManyMock,
20-
},
2118
project: {
2219
findMany: projectFindManyMock,
2320
},
@@ -27,6 +24,7 @@ const initService = async ({
2724
provide: TestVariationsService,
2825
useValue: {
2926
delete: testVariationsDeleteMock,
27+
findOldTestVariations: findOldTestVariationsMock,
3028
},
3129
},
3230
],
@@ -43,11 +41,11 @@ describe('cleanOldTestVariations', () => {
4341
const project = TEST_PROJECT;
4442
const testVariation = generateTestVariation();
4543
const projectFindManyMock = jest.fn().mockResolvedValueOnce([project]);
46-
const testVariationFindManyMock = jest.fn().mockResolvedValueOnce([testVariation]);
44+
const findOldTestVariationsMock = jest.fn().mockResolvedValueOnce([testVariation]);
4745
const testVariationsDeleteMock = jest.fn();
4846
service = await initService({
4947
projectFindManyMock: projectFindManyMock,
50-
testVariationFindManyMock: testVariationFindManyMock,
48+
findOldTestVariationsMock: findOldTestVariationsMock,
5149
testVariationsDeleteMock: testVariationsDeleteMock,
5250
});
5351
const dateNow = new Date('2022-10-23');
@@ -59,13 +57,7 @@ describe('cleanOldTestVariations', () => {
5957
await service.cleanOldTestVariations();
6058

6159
// .Assert
62-
expect(testVariationFindManyMock).toHaveBeenCalledWith({
63-
where: {
64-
updatedAt: { lte: dateRemoveAfter },
65-
branchName: { not: project.mainBranchName },
66-
projectId: project.id,
67-
},
68-
});
60+
expect(findOldTestVariationsMock).toHaveBeenCalledWith(project, dateRemoveAfter);
6961
expect(testVariationsDeleteMock).toBeCalledWith(testVariation.id);
7062
});
7163
});

src/shared/tasks/tasks.service.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,8 @@ export class TasksService {
2121
const dateRemoveAfter: Date = new Date();
2222
dateRemoveAfter.setDate(dateRemoveAfter.getDate() - project.maxBranchLifetime);
2323

24-
const testVariations = await this.prismaService.testVariation.findMany({
25-
where: {
26-
projectId: project.id,
27-
updatedAt: { lte: dateRemoveAfter },
28-
branchName: { not: project.mainBranchName },
29-
},
30-
});
24+
const testVariations = await this.testVariationService.findOldTestVariations(project, dateRemoveAfter);
25+
3126
this.logger.debug(
3227
`Removing ${testVariations.length} TestVariations for ${project.name} later than ${dateRemoveAfter}`
3328
);

src/test-variations/test-variations.service.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Injectable, Inject, forwardRef, Logger } from '@nestjs/common';
22
import { PrismaService } from '../prisma/prisma.service';
3-
import { TestVariation, Baseline, Build, TestRun, User } from '@prisma/client';
3+
import { TestVariation, Baseline, Build, TestRun, User, type Project } from '@prisma/client';
44
import { StaticService } from '../static/static.service';
55
import { BuildsService } from '../builds/builds.service';
66
import { TestRunsService } from '../test-runs/test-runs.service';
@@ -246,4 +246,14 @@ export class TestVariationsService {
246246
where: { id: baseline.id },
247247
});
248248
}
249+
250+
async findOldTestVariations(project: Project, dateRemoveAfter: Date) {
251+
return await this.prismaService.$queryRaw<Project[]>`
252+
SELECT * from public."TestVariation"
253+
WHERE "projectId" = ${project.id}
254+
AND "updatedAt" <= ${dateRemoveAfter}
255+
AND "branchName" NOT LIKE ${project.mainBranchName}
256+
AND "branchName" NOT SIMILAR TO ${project.protectedBranch}
257+
`;
258+
}
249259
}

test/projects.e2e-spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const project: ProjectDto = {
2020
maxBuildAllowed: 0,
2121
maxBranchLifetime: 0,
2222
imageComparisonConfig: '{}',
23+
protectedBranch: null
2324
};
2425

2526
const projectServiceMock = {

test/test-variations.e2e-spec.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,52 @@ describe('TestVariations (e2e)', () => {
6767
expect((await testRunsService.findOne(testRun.id)).testVariationId).toBeNull();
6868
});
6969
});
70+
71+
describe('find old test variations', () => {
72+
it('filters out test runs matching releaseBranch and mainBranchName', async () => {
73+
const baselineBranchName = 'release-1';
74+
const image_v1 = './test/image.png';
75+
const image_v2 = './test/image_edited.png';
76+
77+
// Add variation to main branch
78+
const { testRun: mainBranchTestRun } = await haveTestRunCreated(
79+
buildsService,
80+
testRunsService,
81+
project.id,
82+
project.mainBranchName,
83+
image_v1
84+
);
85+
await testRunsService.approve(mainBranchTestRun.id);
86+
87+
// Add variation to release branch
88+
const { testRun: releaseBranchTestRun } = await haveTestRunCreated(
89+
buildsService,
90+
testRunsService,
91+
project.id,
92+
baselineBranchName,
93+
image_v1,
94+
false,
95+
baselineBranchName
96+
);
97+
await testRunsService.approve(releaseBranchTestRun.id);
98+
99+
// Add variation to feature branch
100+
const { testRun: featureBranchTestRun } = await haveTestRunCreated(
101+
buildsService,
102+
testRunsService,
103+
project.id,
104+
'feature',
105+
image_v2,
106+
false,
107+
project.mainBranchName
108+
);
109+
const featureBranchTestRunApproved = await testRunsService.approve(featureBranchTestRun.id);
110+
111+
const result = await testVariationsService.findOldTestVariations(project, new Date());
112+
expect(result).toHaveLength(1);
113+
expect(result).toEqual(
114+
expect.arrayContaining([expect.objectContaining({ id: featureBranchTestRunApproved.testVariationId })])
115+
);
116+
});
117+
});
70118
});

0 commit comments

Comments
 (0)