Skip to content

Commit ae7b11d

Browse files
fix: #1939 (#1992)
* fix #1939 Co-authored-by: Saikrishna321 <saikrishna321@yahoo.com> * fix: #1939 Co-authored-by: Saikrishna321 <saikrishna321@yahoo.com> --------- Co-authored-by: Saikrishna321 <saikrishna321@yahoo.com>
1 parent d4e3215 commit ae7b11d

File tree

4 files changed

+762
-2
lines changed

4 files changed

+762
-2
lines changed

src/modules

src/scripts/clear-assets.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ async function selectiveReset(options: ResetOptions) {
6666
try {
6767
// Clear cache directory (this is always done)
6868
if (fs.existsSync(config.cacheDir)) {
69-
fs.rmdirSync(config.cacheDir, { recursive: true });
69+
fs.rmSync(config.cacheDir, { recursive: true, force: true });
7070
}
7171
fs.mkdirSync(config.cacheDir, { recursive: true });
7272

Lines changed: 327 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,327 @@
1+
import { expect } from 'chai';
2+
import * as fs from 'fs';
3+
import * as path from 'path';
4+
import { v4 as uuidv4 } from 'uuid';
5+
import { prisma } from '../../src/prisma';
6+
import { config } from '../../src/config';
7+
8+
describe('Cleanup Builds Integration Test', () => {
9+
let testBuildIds: string[] = [];
10+
let testSessionIds: string[] = [];
11+
let testSessionAssetDirs: string[] = [];
12+
13+
afterEach(async () => {
14+
// Clean up any remaining test data
15+
if (testSessionIds.length > 0) {
16+
await prisma.sessionLog.deleteMany({
17+
where: { sessionId: { in: testSessionIds } },
18+
});
19+
await prisma.testEventJournal.deleteMany({
20+
where: { session_id: { in: testSessionIds } },
21+
});
22+
}
23+
if (testBuildIds.length > 0) {
24+
await prisma.session.deleteMany({
25+
where: { buildId: { in: testBuildIds } },
26+
});
27+
await prisma.build.deleteMany({
28+
where: { id: { in: testBuildIds } },
29+
});
30+
}
31+
32+
// Clean up session asset directories
33+
for (const dir of testSessionAssetDirs) {
34+
if (fs.existsSync(dir)) {
35+
fs.rmSync(dir, { recursive: true, force: true });
36+
}
37+
}
38+
39+
testBuildIds = [];
40+
testSessionIds = [];
41+
testSessionAssetDirs = [];
42+
});
43+
44+
async function createTestBuild(daysOld: number, numSessions: number = 2) {
45+
const build = await prisma.build.create({
46+
data: {
47+
id: uuidv4(),
48+
name: `Test Build ${Date.now()}`,
49+
createdAt: new Date(Date.now() - daysOld * 24 * 60 * 60 * 1000),
50+
},
51+
});
52+
testBuildIds.push(build.id);
53+
54+
const sessions = [];
55+
for (let i = 0; i < numSessions; i++) {
56+
const session = await prisma.session.create({
57+
data: {
58+
id: uuidv4(),
59+
buildId: build.id,
60+
deviceUdid: `test-udid-${build.id}-${i}`,
61+
devicePlatform: 'android',
62+
deviceVersion: '10',
63+
desiredCapabilities: JSON.stringify({ platformName: 'Android' }),
64+
sessionCapabilities: JSON.stringify({ platformName: 'Android' }),
65+
nodeId: 'test-node',
66+
status: 'completed',
67+
hasLiveVideo: false,
68+
},
69+
});
70+
sessions.push(session);
71+
testSessionIds.push(session.id);
72+
73+
// Create session logs
74+
await prisma.sessionLog.createMany({
75+
data: Array.from({ length: 3 }, () => ({
76+
id: uuidv4(),
77+
sessionId: session.id,
78+
commandName: 'click',
79+
url: '/session/click',
80+
method: 'POST',
81+
title: 'Click',
82+
response: '{}',
83+
isSuccess: true,
84+
})),
85+
});
86+
87+
// Create test event journals
88+
await prisma.testEventJournal.create({
89+
data: {
90+
id: uuidv4(),
91+
session_id: session.id,
92+
event_uuid: uuidv4(),
93+
event_type: 'test',
94+
event_sub_type: 'start',
95+
name: 'Test Event',
96+
scopes: '{}',
97+
file: 'test.json',
98+
},
99+
});
100+
101+
// Create session asset directory
102+
const sessionDir = path.join(config.sessionAssetsPath, session.id);
103+
fs.mkdirSync(sessionDir, { recursive: true });
104+
fs.writeFileSync(path.join(sessionDir, 'test.txt'), 'test content');
105+
testSessionAssetDirs.push(sessionDir);
106+
}
107+
108+
return { build, sessions };
109+
}
110+
111+
async function cleanupBuilds(retentionDays: number) {
112+
const retentionDate = new Date();
113+
retentionDate.setDate(retentionDate.getDate() - retentionDays);
114+
retentionDate.setHours(0, 0, 0, 0);
115+
116+
const buildsToDelete = await prisma.build.findMany({
117+
where: {
118+
createdAt: {
119+
lt: retentionDate,
120+
},
121+
},
122+
select: { id: true },
123+
});
124+
125+
const buildIdsToDelete = buildsToDelete.map((b) => b.id);
126+
127+
if (buildIdsToDelete.length === 0) {
128+
return {
129+
deletedBuilds: 0,
130+
deletedSessions: 0,
131+
deletedSessionLogs: 0,
132+
deletedTestEventJournals: 0,
133+
};
134+
}
135+
136+
const sessionsToDelete = await prisma.session.findMany({
137+
where: { buildId: { in: buildIdsToDelete } },
138+
select: { id: true },
139+
});
140+
const sessionIdsToDelete = sessionsToDelete.map((s) => s.id);
141+
142+
let deletedSessionLogs = { count: 0 };
143+
let deletedTestEventJournals = { count: 0 };
144+
let deletedSessions = { count: 0 };
145+
let deletedBuilds = { count: 0 };
146+
147+
if (sessionIdsToDelete.length > 0) {
148+
deletedSessionLogs = await prisma.sessionLog.deleteMany({
149+
where: { sessionId: { in: sessionIdsToDelete } },
150+
});
151+
152+
deletedTestEventJournals = await prisma.testEventJournal.deleteMany({
153+
where: { session_id: { in: sessionIdsToDelete } },
154+
});
155+
}
156+
157+
deletedSessions = await prisma.session.deleteMany({
158+
where: { buildId: { in: buildIdsToDelete } },
159+
});
160+
161+
deletedBuilds = await prisma.build.deleteMany({
162+
where: { id: { in: buildIdsToDelete } },
163+
});
164+
165+
// Clean up session asset directories
166+
for (const sessionId of sessionIdsToDelete) {
167+
const sessionDir = path.join(config.sessionAssetsPath, sessionId);
168+
if (fs.existsSync(sessionDir)) {
169+
fs.rmSync(sessionDir, { recursive: true, force: true });
170+
}
171+
}
172+
173+
return {
174+
deletedBuilds: deletedBuilds.count,
175+
deletedSessions: deletedSessions.count,
176+
deletedSessionLogs: deletedSessionLogs.count,
177+
deletedTestEventJournals: deletedTestEventJournals.count,
178+
};
179+
}
180+
181+
it('should delete old builds and all related data', async () => {
182+
const oldBuild = await createTestBuild(10, 3);
183+
const newBuild = await createTestBuild(1, 2);
184+
185+
const result = await cleanupBuilds(5);
186+
187+
expect(result.deletedBuilds).to.equal(1);
188+
expect(result.deletedSessions).to.equal(3);
189+
expect(result.deletedSessionLogs).to.equal(9);
190+
expect(result.deletedTestEventJournals).to.equal(3);
191+
192+
const remainingOldBuild = await prisma.build.findUnique({
193+
where: { id: oldBuild.build.id },
194+
});
195+
expect(remainingOldBuild).to.be.null;
196+
197+
const remainingNewBuild = await prisma.build.findUnique({
198+
where: { id: newBuild.build.id },
199+
});
200+
expect(remainingNewBuild).to.not.be.null;
201+
202+
const remainingSessions = await prisma.session.findMany({
203+
where: { buildId: oldBuild.build.id },
204+
});
205+
expect(remainingSessions.length).to.equal(0);
206+
207+
const remainingLogs = await prisma.sessionLog.findMany({
208+
where: { sessionId: { in: oldBuild.sessions.map((s) => s.id) } },
209+
});
210+
expect(remainingLogs.length).to.equal(0);
211+
212+
for (const session of oldBuild.sessions) {
213+
const sessionDir = path.join(config.sessionAssetsPath, session.id);
214+
expect(fs.existsSync(sessionDir)).to.be.false;
215+
}
216+
217+
// Clean up new build manually since it wasn't deleted
218+
testBuildIds = testBuildIds.filter((id) => id !== newBuild.build.id);
219+
await prisma.sessionLog.deleteMany({
220+
where: { sessionId: { in: newBuild.sessions.map((s) => s.id) } },
221+
});
222+
await prisma.testEventJournal.deleteMany({
223+
where: { session_id: { in: newBuild.sessions.map((s) => s.id) } },
224+
});
225+
await prisma.session.deleteMany({
226+
where: { buildId: newBuild.build.id },
227+
});
228+
await prisma.build.delete({ where: { id: newBuild.build.id } });
229+
});
230+
231+
it('should handle builds with no sessions', async () => {
232+
const build = await prisma.build.create({
233+
data: {
234+
id: uuidv4(),
235+
name: 'Build without sessions',
236+
createdAt: new Date(Date.now() - 10 * 24 * 60 * 60 * 1000),
237+
},
238+
});
239+
testBuildIds.push(build.id);
240+
241+
const result = await cleanupBuilds(5);
242+
243+
expect(result.deletedBuilds).to.equal(1);
244+
expect(result.deletedSessions).to.equal(0);
245+
expect(result.deletedSessionLogs).to.equal(0);
246+
expect(result.deletedTestEventJournals).to.equal(0);
247+
248+
const remainingBuild = await prisma.build.findUnique({
249+
where: { id: build.id },
250+
});
251+
expect(remainingBuild).to.be.null;
252+
});
253+
254+
it('should not delete builds within retention period', async () => {
255+
const build1 = await createTestBuild(2, 1);
256+
const build2 = await createTestBuild(4, 1);
257+
258+
const result = await cleanupBuilds(5);
259+
260+
expect(result.deletedBuilds).to.equal(0);
261+
expect(result.deletedSessions).to.equal(0);
262+
263+
const remainingBuild1 = await prisma.build.findUnique({
264+
where: { id: build1.build.id },
265+
});
266+
expect(remainingBuild1).to.not.be.null;
267+
268+
const remainingBuild2 = await prisma.build.findUnique({
269+
where: { id: build2.build.id },
270+
});
271+
expect(remainingBuild2).to.not.be.null;
272+
273+
// Clean up manually
274+
testBuildIds = [];
275+
for (const build of [build1, build2]) {
276+
await prisma.sessionLog.deleteMany({
277+
where: { sessionId: { in: build.sessions.map((s) => s.id) } },
278+
});
279+
await prisma.testEventJournal.deleteMany({
280+
where: { session_id: { in: build.sessions.map((s) => s.id) } },
281+
});
282+
await prisma.session.deleteMany({
283+
where: { buildId: build.build.id },
284+
});
285+
await prisma.build.delete({ where: { id: build.build.id } });
286+
}
287+
});
288+
289+
it('should delete multiple old builds correctly', async () => {
290+
const build1 = await createTestBuild(10, 2);
291+
const build2 = await createTestBuild(15, 1);
292+
const build3 = await createTestBuild(20, 3);
293+
const newBuild = await createTestBuild(2, 1);
294+
295+
const result = await cleanupBuilds(5);
296+
297+
expect(result.deletedBuilds).to.equal(3);
298+
expect(result.deletedSessions).to.equal(6);
299+
expect(result.deletedSessionLogs).to.equal(18);
300+
expect(result.deletedTestEventJournals).to.equal(6);
301+
302+
for (const build of [build1, build2, build3]) {
303+
const remainingBuild = await prisma.build.findUnique({
304+
where: { id: build.build.id },
305+
});
306+
expect(remainingBuild).to.be.null;
307+
}
308+
309+
const remainingNewBuild = await prisma.build.findUnique({
310+
where: { id: newBuild.build.id },
311+
});
312+
expect(remainingNewBuild).to.not.be.null;
313+
314+
// Clean up new build manually
315+
testBuildIds = testBuildIds.filter((id) => id !== newBuild.build.id);
316+
await prisma.sessionLog.deleteMany({
317+
where: { sessionId: { in: newBuild.sessions.map((s) => s.id) } },
318+
});
319+
await prisma.testEventJournal.deleteMany({
320+
where: { session_id: { in: newBuild.sessions.map((s) => s.id) } },
321+
});
322+
await prisma.session.deleteMany({
323+
where: { buildId: newBuild.build.id },
324+
});
325+
await prisma.build.delete({ where: { id: newBuild.build.id } });
326+
});
327+
});

0 commit comments

Comments
 (0)