mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-18 02:21:13 +00:00
refactor(core): Create pruning service (no-changelog) (#7564)
https://linear.app/n8n/issue/PAY-954/ensure-only-main-instance-leader-handles-pruning
This commit is contained in:
222
packages/cli/test/integration/pruning.service.test.ts
Normal file
222
packages/cli/test/integration/pruning.service.test.ts
Normal file
@@ -0,0 +1,222 @@
|
||||
import config from '@/config';
|
||||
import * as Db from '@/Db';
|
||||
|
||||
import * as testDb from './shared/testDb';
|
||||
import type { ExecutionStatus } from 'n8n-workflow';
|
||||
import type { ExecutionEntity } from '@/databases/entities/ExecutionEntity';
|
||||
import { TIME } from '@/constants';
|
||||
import { PruningService } from '@/services/pruning.service';
|
||||
import { BinaryDataService } from 'n8n-core';
|
||||
import { Logger } from '@/Logger';
|
||||
import { mockInstance } from './shared/utils';
|
||||
|
||||
describe('softDeleteOnPruningCycle()', () => {
|
||||
let pruningService: PruningService;
|
||||
|
||||
const now = new Date();
|
||||
const yesterday = new Date(Date.now() - TIME.DAY);
|
||||
let workflow: Awaited<ReturnType<typeof testDb.createWorkflow>>;
|
||||
|
||||
beforeAll(async () => {
|
||||
await testDb.init();
|
||||
|
||||
pruningService = new PruningService(
|
||||
mockInstance(Logger),
|
||||
Db.collections.Execution,
|
||||
mockInstance(BinaryDataService),
|
||||
);
|
||||
|
||||
workflow = await testDb.createWorkflow();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await testDb.truncate(['Execution']);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await testDb.terminate();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
config.load(config.default);
|
||||
});
|
||||
|
||||
async function findAllExecutions() {
|
||||
return Db.collections.Execution.find({
|
||||
order: { id: 'asc' },
|
||||
withDeleted: true,
|
||||
});
|
||||
}
|
||||
|
||||
describe('when EXECUTIONS_DATA_PRUNE_MAX_COUNT is set', () => {
|
||||
beforeEach(() => {
|
||||
config.set('executions.pruneDataMaxCount', 1);
|
||||
config.set('executions.pruneDataMaxAge', 336);
|
||||
});
|
||||
|
||||
test('should mark as deleted based on EXECUTIONS_DATA_PRUNE_MAX_COUNT', async () => {
|
||||
const executions = [
|
||||
await testDb.createSuccessfulExecution(workflow),
|
||||
await testDb.createSuccessfulExecution(workflow),
|
||||
await testDb.createSuccessfulExecution(workflow),
|
||||
];
|
||||
|
||||
await pruningService.softDeleteOnPruningCycle();
|
||||
|
||||
const result = await findAllExecutions();
|
||||
expect(result).toEqual([
|
||||
expect.objectContaining({ id: executions[0].id, deletedAt: expect.any(Date) }),
|
||||
expect.objectContaining({ id: executions[1].id, deletedAt: expect.any(Date) }),
|
||||
expect.objectContaining({ id: executions[2].id, deletedAt: null }),
|
||||
]);
|
||||
});
|
||||
|
||||
test('should not re-mark already marked executions', async () => {
|
||||
const executions = [
|
||||
await testDb.createExecution(
|
||||
{ status: 'success', finished: true, startedAt: now, stoppedAt: now, deletedAt: now },
|
||||
workflow,
|
||||
),
|
||||
await testDb.createSuccessfulExecution(workflow),
|
||||
];
|
||||
|
||||
await pruningService.softDeleteOnPruningCycle();
|
||||
|
||||
const result = await findAllExecutions();
|
||||
expect(result).toEqual([
|
||||
expect.objectContaining({ id: executions[0].id, deletedAt: now }),
|
||||
expect.objectContaining({ id: executions[1].id, deletedAt: null }),
|
||||
]);
|
||||
});
|
||||
|
||||
test.each<[ExecutionStatus, Partial<ExecutionEntity>]>([
|
||||
['warning', { startedAt: now, stoppedAt: now }],
|
||||
['unknown', { startedAt: now, stoppedAt: now }],
|
||||
['canceled', { startedAt: now, stoppedAt: now }],
|
||||
['crashed', { startedAt: now, stoppedAt: now }],
|
||||
['failed', { startedAt: now, stoppedAt: now }],
|
||||
['success', { finished: true, startedAt: now, stoppedAt: now }],
|
||||
])('should prune %s executions', async (status, attributes) => {
|
||||
const executions = [
|
||||
await testDb.createExecution({ status, ...attributes }, workflow),
|
||||
await testDb.createSuccessfulExecution(workflow),
|
||||
];
|
||||
|
||||
await pruningService.softDeleteOnPruningCycle();
|
||||
|
||||
const result = await findAllExecutions();
|
||||
expect(result).toEqual([
|
||||
expect.objectContaining({ id: executions[0].id, deletedAt: expect.any(Date) }),
|
||||
expect.objectContaining({ id: executions[1].id, deletedAt: null }),
|
||||
]);
|
||||
});
|
||||
|
||||
test.each<[ExecutionStatus, Partial<ExecutionEntity>]>([
|
||||
['new', {}],
|
||||
['running', { startedAt: now }],
|
||||
['waiting', { startedAt: now, stoppedAt: now, waitTill: now }],
|
||||
])('should not prune %s executions', async (status, attributes) => {
|
||||
const executions = [
|
||||
await testDb.createExecution({ status, ...attributes }, workflow),
|
||||
await testDb.createSuccessfulExecution(workflow),
|
||||
];
|
||||
|
||||
await pruningService.softDeleteOnPruningCycle();
|
||||
|
||||
const result = await findAllExecutions();
|
||||
expect(result).toEqual([
|
||||
expect.objectContaining({ id: executions[0].id, deletedAt: null }),
|
||||
expect.objectContaining({ id: executions[1].id, deletedAt: null }),
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when EXECUTIONS_DATA_MAX_AGE is set', () => {
|
||||
beforeEach(() => {
|
||||
config.set('executions.pruneDataMaxAge', 1); // 1h
|
||||
config.set('executions.pruneDataMaxCount', 0);
|
||||
});
|
||||
|
||||
test('should mark as deleted based on EXECUTIONS_DATA_MAX_AGE', async () => {
|
||||
const executions = [
|
||||
await testDb.createExecution(
|
||||
{ finished: true, startedAt: yesterday, stoppedAt: yesterday, status: 'success' },
|
||||
workflow,
|
||||
),
|
||||
await testDb.createExecution(
|
||||
{ finished: true, startedAt: now, stoppedAt: now, status: 'success' },
|
||||
workflow,
|
||||
),
|
||||
];
|
||||
|
||||
await pruningService.softDeleteOnPruningCycle();
|
||||
|
||||
const result = await findAllExecutions();
|
||||
expect(result).toEqual([
|
||||
expect.objectContaining({ id: executions[0].id, deletedAt: expect.any(Date) }),
|
||||
expect.objectContaining({ id: executions[1].id, deletedAt: null }),
|
||||
]);
|
||||
});
|
||||
|
||||
test('should not re-mark already marked executions', async () => {
|
||||
const executions = [
|
||||
await testDb.createExecution(
|
||||
{
|
||||
status: 'success',
|
||||
finished: true,
|
||||
startedAt: yesterday,
|
||||
stoppedAt: yesterday,
|
||||
deletedAt: yesterday,
|
||||
},
|
||||
workflow,
|
||||
),
|
||||
await testDb.createSuccessfulExecution(workflow),
|
||||
];
|
||||
|
||||
await pruningService.softDeleteOnPruningCycle();
|
||||
|
||||
const result = await findAllExecutions();
|
||||
expect(result).toEqual([
|
||||
expect.objectContaining({ id: executions[0].id, deletedAt: yesterday }),
|
||||
expect.objectContaining({ id: executions[1].id, deletedAt: null }),
|
||||
]);
|
||||
});
|
||||
|
||||
test.each<[ExecutionStatus, Partial<ExecutionEntity>]>([
|
||||
['warning', { startedAt: yesterday, stoppedAt: yesterday }],
|
||||
['unknown', { startedAt: yesterday, stoppedAt: yesterday }],
|
||||
['canceled', { startedAt: yesterday, stoppedAt: yesterday }],
|
||||
['crashed', { startedAt: yesterday, stoppedAt: yesterday }],
|
||||
['failed', { startedAt: yesterday, stoppedAt: yesterday }],
|
||||
['success', { finished: true, startedAt: yesterday, stoppedAt: yesterday }],
|
||||
])('should prune %s executions', async (status, attributes) => {
|
||||
const execution = await testDb.createExecution({ status, ...attributes }, workflow);
|
||||
|
||||
await pruningService.softDeleteOnPruningCycle();
|
||||
|
||||
const result = await findAllExecutions();
|
||||
expect(result).toEqual([
|
||||
expect.objectContaining({ id: execution.id, deletedAt: expect.any(Date) }),
|
||||
]);
|
||||
});
|
||||
|
||||
test.each<[ExecutionStatus, Partial<ExecutionEntity>]>([
|
||||
['new', {}],
|
||||
['running', { startedAt: yesterday }],
|
||||
['waiting', { startedAt: yesterday, stoppedAt: yesterday, waitTill: yesterday }],
|
||||
])('should not prune %s executions', async (status, attributes) => {
|
||||
const executions = [
|
||||
await testDb.createExecution({ status, ...attributes }, workflow),
|
||||
await testDb.createSuccessfulExecution(workflow),
|
||||
];
|
||||
|
||||
await pruningService.softDeleteOnPruningCycle();
|
||||
|
||||
const result = await findAllExecutions();
|
||||
expect(result).toEqual([
|
||||
expect.objectContaining({ id: executions[0].id, deletedAt: null }),
|
||||
expect.objectContaining({ id: executions[1].id, deletedAt: null }),
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user