ci: Fix WorkflowHistoryManager tests (no-changelog) (#7356)

[DB Tests](https://github.com/n8n-io/n8n/actions/runs/6418058186)
This commit is contained in:
कारतोफ्फेलस्क्रिप्ट™ 2023-10-05 13:14:57 +02:00 committed by GitHub
parent 7ebf8f327a
commit 169175080a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 77 additions and 164 deletions

View file

@ -1,9 +1,9 @@
import { WorkflowHistoryRepository } from '@/databases/repositories';
import { Service } from 'typedi'; import { Service } from 'typedi';
import { LessThan } from 'typeorm';
import { DateTime } from 'luxon';
import { WorkflowHistoryRepository } from '@/databases/repositories';
import { WORKFLOW_HISTORY_PRUNE_INTERVAL } from './constants'; import { WORKFLOW_HISTORY_PRUNE_INTERVAL } from './constants';
import { getWorkflowHistoryPruneTime, isWorkflowHistoryEnabled } from './workflowHistoryHelper.ee'; import { getWorkflowHistoryPruneTime, isWorkflowHistoryEnabled } from './workflowHistoryHelper.ee';
import { DateTime } from 'luxon';
import { LessThan } from 'typeorm';
@Service() @Service()
export class WorkflowHistoryManager { export class WorkflowHistoryManager {

View file

@ -118,7 +118,7 @@ export async function terminate() {
export async function truncate(collections: CollectionName[]) { export async function truncate(collections: CollectionName[]) {
const [tag, rest] = separate(collections, (c) => c === 'Tag'); const [tag, rest] = separate(collections, (c) => c === 'Tag');
if (tag) { if (tag.length) {
await Container.get(TagRepository).delete({}); await Container.get(TagRepository).delete({});
await Container.get(WorkflowTagMappingRepository).delete({}); await Container.get(WorkflowTagMappingRepository).delete({});
} }

View file

@ -1,203 +1,94 @@
import { WorkflowHistoryRepository } from '@/databases/repositories';
import * as testDb from './shared/testDb';
import { License } from '@/License';
import { mockInstance } from './shared/utils';
import { WorkflowHistoryManager } from '@/workflows/workflowHistory/workflowHistoryManager.ee';
import Container from 'typedi'; import Container from 'typedi';
import config from '@/config';
import { DateTime } from 'luxon';
import { In } from 'typeorm'; import { In } from 'typeorm';
import { DateTime } from 'luxon';
let licenseMock: License; import config from '@/config';
let licensePruneTime = -1; import { WorkflowHistoryRepository } from '@/databases/repositories';
let licenseEnabled = true; import { License } from '@/License';
let manager: WorkflowHistoryManager; import { WorkflowHistoryManager } from '@/workflows/workflowHistory/workflowHistoryManager.ee';
beforeAll(async () => { import * as testDb from './shared/testDb';
await testDb.init(); import { mockInstance } from './shared/utils';
licenseMock = mockInstance(License, {
isWorkflowHistoryLicensed() {
return licenseEnabled;
},
getWorkflowHistoryPruneLimit() {
return licensePruneTime;
},
});
});
beforeEach(async () => {
await testDb.truncate([WorkflowHistoryRepository]);
jest.useRealTimers();
jest.clearAllMocks();
config.set('workflowHistory.enabled', true);
config.set('workflowHistory.pruneTime', -1);
licensePruneTime = -1;
licenseEnabled = true;
});
afterEach(() => {
manager?.shutdown();
});
describe('Workflow History Manager', () => { describe('Workflow History Manager', () => {
test('should prune on interval', () => { const license = mockInstance(License);
jest.useFakeTimers(); let repo: WorkflowHistoryRepository;
let manager: WorkflowHistoryManager;
manager = new WorkflowHistoryManager(Container.get(WorkflowHistoryRepository)); beforeAll(async () => {
manager.init(); await testDb.init();
repo = Container.get(WorkflowHistoryRepository);
manager = Container.get(WorkflowHistoryManager);
});
beforeEach(async () => {
await testDb.truncate(['Workflow']);
jest.clearAllMocks();
config.set('workflowHistory.enabled', true);
config.set('workflowHistory.pruneTime', -1);
license.isWorkflowHistoryLicensed.mockReturnValue(true);
license.getWorkflowHistoryPruneLimit.mockReturnValue(-1);
});
test('should prune on interval', () => {
const pruneSpy = jest.spyOn(manager, 'prune'); const pruneSpy = jest.spyOn(manager, 'prune');
const currentCount = pruneSpy.mock.calls.length; const currentCount = pruneSpy.mock.calls.length;
jest.runOnlyPendingTimers(); jest.useFakeTimers();
manager.init();
jest.runOnlyPendingTimers();
expect(pruneSpy).toBeCalledTimes(currentCount + 1); expect(pruneSpy).toBeCalledTimes(currentCount + 1);
jest.runOnlyPendingTimers(); jest.runOnlyPendingTimers();
expect(pruneSpy).toBeCalledTimes(currentCount + 2); expect(pruneSpy).toBeCalledTimes(currentCount + 2);
manager.shutdown();
jest.clearAllTimers();
jest.useRealTimers();
pruneSpy.mockRestore();
}); });
test('should not prune when not licensed', async () => { test('should not prune when not licensed', async () => {
// Set a prune time just to make sure it gets to the delete license.isWorkflowHistoryLicensed.mockReturnValue(false);
config.set('workflowHistory.pruneTime', 24); await createWorkflowHistory();
await pruneAndAssertCount();
licenseEnabled = false;
const repo = Container.get(WorkflowHistoryRepository);
manager = new WorkflowHistoryManager(repo);
manager.init();
const workflow = await testDb.createWorkflow();
await testDb.createManyWorkflowHistoryItems(
workflow.id,
10,
DateTime.now().minus({ days: 2 }).toJSDate(),
);
expect(await repo.count()).toBe(10);
const deleteSpy = jest.spyOn(repo, 'delete');
await manager.prune();
expect(deleteSpy).not.toBeCalled();
expect(await repo.count()).toBe(10);
}); });
test('should not prune when licensed but disabled', async () => { test('should not prune when licensed but disabled', async () => {
// Set a prune time just to make sure it gets to the delete
config.set('workflowHistory.pruneTime', 24);
config.set('workflowHistory.enabled', false); config.set('workflowHistory.enabled', false);
await createWorkflowHistory();
const repo = Container.get(WorkflowHistoryRepository); await pruneAndAssertCount();
manager = new WorkflowHistoryManager(repo);
manager.init();
const workflow = await testDb.createWorkflow();
await testDb.createManyWorkflowHistoryItems(
workflow.id,
10,
DateTime.now().minus({ days: 2 }).toJSDate(),
);
expect(await repo.count()).toBe(10);
const deleteSpy = jest.spyOn(repo, 'delete');
await manager.prune();
expect(deleteSpy).not.toBeCalled();
expect(await repo.count()).toBe(10);
}); });
test('should not prune when both prune times are -1 (infinite)', async () => { test('should not prune when both prune times are -1 (infinite)', async () => {
config.set('workflowHistory.pruneTime', -1); await createWorkflowHistory();
licensePruneTime = -1; await pruneAndAssertCount();
const repo = Container.get(WorkflowHistoryRepository);
manager = new WorkflowHistoryManager(repo);
manager.init();
const workflow = await testDb.createWorkflow();
await testDb.createManyWorkflowHistoryItems(
workflow.id,
10,
DateTime.now().minus({ days: 2 }).toJSDate(),
);
expect(await repo.count()).toBe(10);
const deleteSpy = jest.spyOn(repo, 'delete');
await manager.prune();
expect(deleteSpy).not.toBeCalled();
expect(await repo.count()).toBe(10);
}); });
test('should prune when config prune time is not -1 (infinite)', async () => { test('should prune when config prune time is not -1 (infinite)', async () => {
config.set('workflowHistory.pruneTime', 24); config.set('workflowHistory.pruneTime', 24);
licensePruneTime = -1; await createWorkflowHistory();
await pruneAndAssertCount(0);
const repo = Container.get(WorkflowHistoryRepository);
manager = new WorkflowHistoryManager(repo);
manager.init();
const workflow = await testDb.createWorkflow();
await testDb.createManyWorkflowHistoryItems(
workflow.id,
10,
DateTime.now().minus({ days: 2 }).toJSDate(),
);
expect(await repo.count()).toBe(10);
const deleteSpy = jest.spyOn(repo, 'delete');
await manager.prune();
expect(deleteSpy).toBeCalled();
expect(await repo.count()).toBe(0);
}); });
test('should prune when license prune time is not -1 (infinite)', async () => { test('should prune when license prune time is not -1 (infinite)', async () => {
config.set('workflowHistory.pruneTime', -1); license.getWorkflowHistoryPruneLimit.mockReturnValue(24);
licensePruneTime = 24;
const repo = Container.get(WorkflowHistoryRepository); await createWorkflowHistory();
manager = new WorkflowHistoryManager(repo); await pruneAndAssertCount(0);
manager.init();
const workflow = await testDb.createWorkflow();
await testDb.createManyWorkflowHistoryItems(
workflow.id,
10,
DateTime.now().minus({ days: 2 }).toJSDate(),
);
expect(await repo.count()).toBe(10);
const deleteSpy = jest.spyOn(repo, 'delete');
await manager.prune();
expect(deleteSpy).toBeCalled();
expect(await repo.count()).toBe(0);
}); });
test('should only prune versions older than prune time', async () => { test('should only prune versions older than prune time', async () => {
config.set('workflowHistory.pruneTime', 24); config.set('workflowHistory.pruneTime', 24);
licensePruneTime = -1;
const repo = Container.get(WorkflowHistoryRepository); const recentVersions = await createWorkflowHistory(0);
manager = new WorkflowHistoryManager(repo); const oldVersions = await createWorkflowHistory();
manager.init();
const workflow = await testDb.createWorkflow(); await pruneAndAssertCount(10, 20);
const recentVersions = await testDb.createManyWorkflowHistoryItems(workflow.id, 10);
const oldVersions = await testDb.createManyWorkflowHistoryItems(
workflow.id,
10,
DateTime.now().minus({ days: 2 }).toJSDate(),
);
expect(await repo.count()).toBe(20);
const deleteSpy = jest.spyOn(repo, 'delete');
await manager.prune();
expect(deleteSpy).toBeCalled();
expect(await repo.count()).toBe(10);
expect( expect(
await repo.count({ where: { versionId: In(recentVersions.map((i) => i.versionId)) } }), await repo.count({ where: { versionId: In(recentVersions.map((i) => i.versionId)) } }),
).toBe(10); ).toBe(10);
@ -205,4 +96,26 @@ describe('Workflow History Manager', () => {
await repo.count({ where: { versionId: In(oldVersions.map((i) => i.versionId)) } }), await repo.count({ where: { versionId: In(oldVersions.map((i) => i.versionId)) } }),
).toBe(0); ).toBe(0);
}); });
const createWorkflowHistory = async (ageInDays = 2) => {
const workflow = await testDb.createWorkflow();
const time = DateTime.now().minus({ days: ageInDays }).toJSDate();
return testDb.createManyWorkflowHistoryItems(workflow.id, 10, time);
};
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);
};
}); });