2024-02-08 06:13:29 -08:00
|
|
|
import { In } from '@n8n/typeorm';
|
2023-10-05 04:14:57 -07:00
|
|
|
import { DateTime } from 'luxon';
|
2024-09-12 09:07:18 -07:00
|
|
|
import Container from 'typedi';
|
2023-10-05 04:14:57 -07:00
|
|
|
|
|
|
|
import config from '@/config';
|
2024-08-27 08:24:20 -07:00
|
|
|
import { WorkflowHistoryRepository } from '@/databases/repositories/workflow-history.repository';
|
2024-08-22 02:10:37 -07:00
|
|
|
import { License } from '@/license';
|
|
|
|
import { WorkflowHistoryManager } from '@/workflows/workflow-history/workflow-history-manager.ee';
|
2023-10-04 05:57:21 -07:00
|
|
|
|
2024-08-28 04:59:27 -07:00
|
|
|
import { createManyWorkflowHistoryItems } from './shared/db/workflow-history';
|
2024-09-12 09:07:18 -07:00
|
|
|
import { createWorkflow } from './shared/db/workflows';
|
|
|
|
import * as testDb from './shared/test-db';
|
|
|
|
import { mockInstance } from '../shared/mocking';
|
2023-10-05 04:14:57 -07:00
|
|
|
|
|
|
|
describe('Workflow History Manager', () => {
|
|
|
|
const license = mockInstance(License);
|
|
|
|
let repo: WorkflowHistoryRepository;
|
|
|
|
let manager: WorkflowHistoryManager;
|
|
|
|
|
|
|
|
beforeAll(async () => {
|
|
|
|
await testDb.init();
|
|
|
|
repo = Container.get(WorkflowHistoryRepository);
|
|
|
|
manager = Container.get(WorkflowHistoryManager);
|
2023-10-04 05:57:21 -07:00
|
|
|
});
|
|
|
|
|
2023-10-05 04:14:57 -07:00
|
|
|
beforeEach(async () => {
|
|
|
|
await testDb.truncate(['Workflow']);
|
|
|
|
jest.clearAllMocks();
|
2023-10-04 05:57:21 -07:00
|
|
|
|
2023-10-05 04:14:57 -07:00
|
|
|
config.set('workflowHistory.enabled', true);
|
|
|
|
config.set('workflowHistory.pruneTime', -1);
|
2023-10-04 05:57:21 -07:00
|
|
|
|
2023-10-05 04:14:57 -07:00
|
|
|
license.isWorkflowHistoryLicensed.mockReturnValue(true);
|
|
|
|
license.getWorkflowHistoryPruneLimit.mockReturnValue(-1);
|
|
|
|
});
|
2023-10-04 05:57:21 -07:00
|
|
|
|
2024-05-17 01:53:15 -07:00
|
|
|
afterAll(async () => {
|
|
|
|
await testDb.terminate();
|
|
|
|
});
|
|
|
|
|
2023-10-05 04:14:57 -07:00
|
|
|
test('should prune on interval', () => {
|
2023-10-04 05:57:21 -07:00
|
|
|
const pruneSpy = jest.spyOn(manager, 'prune');
|
|
|
|
const currentCount = pruneSpy.mock.calls.length;
|
|
|
|
|
2023-10-05 04:14:57 -07:00
|
|
|
jest.useFakeTimers();
|
|
|
|
manager.init();
|
2023-10-04 05:57:21 -07:00
|
|
|
|
2023-10-05 04:14:57 -07:00
|
|
|
jest.runOnlyPendingTimers();
|
2023-10-04 05:57:21 -07:00
|
|
|
expect(pruneSpy).toBeCalledTimes(currentCount + 1);
|
|
|
|
|
|
|
|
jest.runOnlyPendingTimers();
|
|
|
|
expect(pruneSpy).toBeCalledTimes(currentCount + 2);
|
2023-10-05 04:14:57 -07:00
|
|
|
|
|
|
|
manager.shutdown();
|
|
|
|
jest.clearAllTimers();
|
|
|
|
jest.useRealTimers();
|
|
|
|
pruneSpy.mockRestore();
|
2023-10-04 05:57:21 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
test('should not prune when not licensed', async () => {
|
2023-10-05 04:14:57 -07:00
|
|
|
license.isWorkflowHistoryLicensed.mockReturnValue(false);
|
|
|
|
await createWorkflowHistory();
|
|
|
|
await pruneAndAssertCount();
|
2023-10-04 05:57:21 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
test('should not prune when licensed but disabled', async () => {
|
|
|
|
config.set('workflowHistory.enabled', false);
|
2023-10-05 04:14:57 -07:00
|
|
|
await createWorkflowHistory();
|
|
|
|
await pruneAndAssertCount();
|
2023-10-04 05:57:21 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
test('should not prune when both prune times are -1 (infinite)', async () => {
|
2023-10-05 04:14:57 -07:00
|
|
|
await createWorkflowHistory();
|
|
|
|
await pruneAndAssertCount();
|
2023-10-04 05:57:21 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
test('should prune when config prune time is not -1 (infinite)', async () => {
|
|
|
|
config.set('workflowHistory.pruneTime', 24);
|
2023-10-05 04:14:57 -07:00
|
|
|
await createWorkflowHistory();
|
|
|
|
await pruneAndAssertCount(0);
|
2023-10-04 05:57:21 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
test('should prune when license prune time is not -1 (infinite)', async () => {
|
2023-10-05 04:14:57 -07:00
|
|
|
license.getWorkflowHistoryPruneLimit.mockReturnValue(24);
|
2023-10-04 05:57:21 -07:00
|
|
|
|
2023-10-05 04:14:57 -07:00
|
|
|
await createWorkflowHistory();
|
|
|
|
await pruneAndAssertCount(0);
|
2023-10-04 05:57:21 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
test('should only prune versions older than prune time', async () => {
|
|
|
|
config.set('workflowHistory.pruneTime', 24);
|
|
|
|
|
2023-10-05 04:14:57 -07:00
|
|
|
const recentVersions = await createWorkflowHistory(0);
|
|
|
|
const oldVersions = await createWorkflowHistory();
|
2023-10-04 05:57:21 -07:00
|
|
|
|
2023-10-05 04:14:57 -07:00
|
|
|
await pruneAndAssertCount(10, 20);
|
2023-10-04 05:57:21 -07:00
|
|
|
|
|
|
|
expect(
|
|
|
|
await repo.count({ where: { versionId: In(recentVersions.map((i) => i.versionId)) } }),
|
|
|
|
).toBe(10);
|
|
|
|
expect(
|
|
|
|
await repo.count({ where: { versionId: In(oldVersions.map((i) => i.versionId)) } }),
|
|
|
|
).toBe(0);
|
|
|
|
});
|
2023-10-05 04:14:57 -07:00
|
|
|
|
|
|
|
const createWorkflowHistory = async (ageInDays = 2) => {
|
2023-11-08 07:29:39 -08:00
|
|
|
const workflow = await createWorkflow();
|
2023-10-05 04:14:57 -07:00
|
|
|
const time = DateTime.now().minus({ days: ageInDays }).toJSDate();
|
2024-01-17 07:08:50 -08:00
|
|
|
return await createManyWorkflowHistoryItems(workflow.id, 10, time);
|
2023-10-05 04:14:57 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
const pruneAndAssertCount = async (finalCount = 10, initialCount = 10) => {
|
|
|
|
expect(await repo.count()).toBe(initialCount);
|
|
|
|
|
|
|
|
const deleteSpy = jest.spyOn(repo, 'delete');
|
|
|
|
await manager.prune();
|
|
|
|
|
|
|
|
if (initialCount === finalCount) {
|
|
|
|
expect(deleteSpy).not.toBeCalled();
|
|
|
|
} else {
|
|
|
|
expect(deleteSpy).toBeCalled();
|
|
|
|
}
|
|
|
|
deleteSpy.mockRestore();
|
|
|
|
|
|
|
|
expect(await repo.count()).toBe(finalCount);
|
|
|
|
};
|
2023-10-04 05:57:21 -07:00
|
|
|
});
|