Files
alighasami 3d5eaf9445
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled
first commit
2026-03-17 16:22:57 +03:30

188 lines
5.1 KiB
TypeScript

import { createWorkflow, testDb } from '@n8n/backend-test-utils';
import { ExecutionDataRepository, ExecutionRepository } from '@n8n/db';
import { Container } from '@n8n/di';
import { stringify } from 'flatted';
import type { IRunExecutionData, IRunExecutionDataAll } from 'n8n-workflow';
describe('ExecutionRepository', () => {
beforeAll(async () => {
await testDb.init();
});
beforeEach(async () => {
await testDb.truncate(['WorkflowEntity', 'ExecutionEntity']);
});
afterAll(async () => {
await testDb.terminate();
});
describe('run execution data migration', () => {
it('should automatically migrate IRunExecutionDataV0 to V1 when reading', async () => {
const executionRepo = Container.get(ExecutionRepository);
const executionDataRepo = Container.get(ExecutionDataRepository);
const workflow = await createWorkflow({ settings: { executionOrder: 'v1' } });
// Create V0 data with string destinationNode
const v0Data: IRunExecutionDataAll = {
version: 0,
startData: { destinationNode: 'TestNode' },
resultData: { runData: {} },
};
// Insert execution with V0 data directly into the database
const { identifiers } = await executionRepo.insert({
workflowId: workflow.id,
mode: 'manual',
startedAt: new Date(),
status: 'success',
finished: true,
createdAt: new Date(),
});
const executionId = identifiers[0].id as string;
await executionDataRepo.insert({
executionId,
workflowData: { id: workflow.id, connections: {}, nodes: [], name: workflow.name },
data: stringify(v0Data),
});
// Read the execution back
const execution = await executionRepo.findSingleExecution(executionId, {
includeData: true,
unflattenData: true,
});
// Verify that the data was migrated to V1
const data = execution?.data as IRunExecutionData;
expect(data.version).toBe(1);
expect(data.startData?.destinationNode).toEqual({
nodeName: 'TestNode',
mode: 'inclusive',
});
});
});
describe('findByStopExecutionsFilter', () => {
it('should find executions by status', async () => {
const executionRepo = Container.get(ExecutionRepository);
const workflow = await createWorkflow();
// Insert executions with different statuses
await executionRepo.insert([
{
workflowId: workflow.id,
mode: 'manual',
startedAt: new Date(),
status: 'running',
finished: false,
createdAt: new Date(),
},
{
workflowId: workflow.id,
mode: 'manual',
startedAt: new Date(),
status: 'success',
finished: true,
createdAt: new Date(),
},
{
workflowId: workflow.id,
mode: 'manual',
startedAt: new Date(),
status: 'error',
finished: false,
createdAt: new Date(),
},
]);
// Find executions with status 'running' and 'error'
const executions = await executionRepo.findByStopExecutionsFilter({
status: ['running', 'error'],
workflowId: workflow.id,
});
expect(executions).toHaveLength(2);
});
it('should find executions by startedAfter and startedBefore', async () => {
const executionRepo = Container.get(ExecutionRepository);
const workflow = await createWorkflow();
// Insert executions with different start times
const now = new Date();
const pastDate = new Date(now.getTime() - 1000 * 60 * 60); // 1 hour ago
const futureDate = new Date(now.getTime() + 1000 * 60 * 60); // 1 hour later
await executionRepo.insert([
{
workflowId: workflow.id,
mode: 'manual',
startedAt: pastDate,
status: 'running',
finished: false,
createdAt: pastDate,
},
{
workflowId: workflow.id,
mode: 'manual',
startedAt: now,
status: 'success',
finished: true,
createdAt: now,
},
{
workflowId: workflow.id,
mode: 'manual',
startedAt: futureDate,
status: 'error',
finished: false,
createdAt: futureDate,
},
]);
// Find executions started between pastDate and now
const executions = await executionRepo.findByStopExecutionsFilter({
startedAfter: new Date(pastDate.getTime() + 1).toISOString(),
startedBefore: new Date(futureDate.getTime() - 1).toISOString(),
status: ['running', 'success', 'error'],
workflowId: workflow.id,
});
expect(executions).toHaveLength(1);
});
it('should find executions for all workflows when workflowId is "all"', async () => {
const executionRepo = Container.get(ExecutionRepository);
const workflow1 = await createWorkflow();
const workflow2 = await createWorkflow();
// Insert executions for different workflows
await executionRepo.insert([
{
workflowId: workflow1.id,
mode: 'manual',
startedAt: new Date(),
status: 'running',
finished: false,
createdAt: new Date(),
},
{
workflowId: workflow2.id,
mode: 'manual',
startedAt: new Date(),
status: 'success',
finished: true,
createdAt: new Date(),
},
]);
// Find executions for all workflows
const executions = await executionRepo.findByStopExecutionsFilter({
status: ['running', 'success'],
workflowId: 'all',
});
expect(executions).toHaveLength(2);
});
});
});