mirror of
https://github.com/n8n-io/n8n.git
synced 2026-10-19 16:55:02 +02:00
chore(n8n-test): Apply v2 verification findings
Guard mockNode against unknown node names, reset captures per run, refuse Code-node/sub-workflow capabilities with clear messages, pin nodes-base to its release-train pairing (2.16.0 — avoids a nested second n8n-workflow copy), and fix the mock-persistence wording.
This commit is contained in:
parent
1c44da8eda
commit
5e51b07b68
|
|
@ -122,6 +122,10 @@ Feasibility is proven, but these are the edges the production version must own:
|
|||
interception point — mocking there would also catch non-HTTP-module clients.
|
||||
- **Speed is a non-issue.** The lazy loader brings node-type load to ~1s per
|
||||
process; the full demo suite runs in under 3s.
|
||||
- **(v2) Code nodes and sub-workflows need mocking.** Task-runner-backed nodes
|
||||
(the Code node) and Execute Sub-workflow call engine capabilities a test
|
||||
harness cannot provide; `runWorkflow` refuses them with a clear message —
|
||||
`mockNode()` is the answer for both.
|
||||
- **(v2) Released-engine drift is the real prebake tax.** Three shims were
|
||||
needed to run on npm releases: `vitest-mock-extended`'s CJS build cannot be
|
||||
`require`d outside Vite's pipeline (hence the plain `additionalData` object);
|
||||
|
|
|
|||
|
|
@ -27,10 +27,12 @@ const externals = [
|
|||
// Pinned to current npm releases; bump deliberately, the engine API is the risk surface.
|
||||
// The @n8n helper pins MUST match what the released n8n-core depends on, or npm installs
|
||||
// a second copy and the DI container splits (`npm view n8n-core@<v> dependencies`).
|
||||
// nodes-base must be the release-train pairing for this core/workflow (check
|
||||
// `npm view n8n@<train> dependencies`) or it nests its own second n8n-workflow copy.
|
||||
const runtimeVersions = {
|
||||
'n8n-core': '2.16.1',
|
||||
'n8n-workflow': '2.16.0',
|
||||
'n8n-nodes-base': '2.15.1',
|
||||
'n8n-nodes-base': '2.16.0',
|
||||
'@n8n/di': '0.10.0',
|
||||
'@n8n/decorators': '1.16.0',
|
||||
};
|
||||
|
|
@ -112,8 +114,9 @@ export declare function runWorkflow(
|
|||
): Promise<Record<string, unknown>>;
|
||||
|
||||
/**
|
||||
* Replaces the named node's execution with a canned output for the next runWorkflow of
|
||||
* this workflow object. Multiple nodes may be mocked; last mock per node name wins.
|
||||
* Replaces the named node's execution with a canned output for every subsequent
|
||||
* runWorkflow of this workflow object, until clearNodeMocks() runs. Multiple nodes may
|
||||
* be mocked; last mock per node name wins.
|
||||
*/
|
||||
export declare function mockNode(
|
||||
workflowJson: unknown,
|
||||
|
|
|
|||
|
|
@ -27,17 +27,23 @@ interface MockEntry {
|
|||
const registry = new Map<WorkflowJson, Map<string, MockEntry>>();
|
||||
|
||||
/**
|
||||
* Replaces the named node's execution with a canned output for the next
|
||||
* {@link runWorkflow} of this workflow object. The JSON itself is never mutated.
|
||||
* Mocking the same node again replaces the previous mock (last one wins).
|
||||
* Replaces the named node's execution with a canned output for every subsequent
|
||||
* {@link runWorkflow} of this workflow object, until {@link clearNodeMocks} runs.
|
||||
* The JSON itself is never mutated. Mocking the same node again replaces the
|
||||
* previous mock (last one wins).
|
||||
*
|
||||
* The returned handle captures what the node received, for assertions.
|
||||
* The returned handle captures what the node received on the latest run.
|
||||
*/
|
||||
export function mockNode(
|
||||
workflow: WorkflowJson,
|
||||
nodeName: string,
|
||||
output: IDataObject,
|
||||
): MockedNodeHandle {
|
||||
if (!workflow.nodes.some((node) => node.name === nodeName)) {
|
||||
throw new UnexpectedError(
|
||||
`Cannot mock node "${nodeName}": the workflow has no node with that name`,
|
||||
);
|
||||
}
|
||||
let mocks = registry.get(workflow);
|
||||
if (!mocks) {
|
||||
mocks = new Map();
|
||||
|
|
@ -54,7 +60,10 @@ export function clearNodeMocks(): void {
|
|||
}
|
||||
|
||||
export function getNodeMocks(workflow: WorkflowJson): Map<string, MockEntry> | undefined {
|
||||
return registry.get(workflow);
|
||||
const mocks = registry.get(workflow);
|
||||
// A fresh run means fresh captures: a handle must never report a previous run's input.
|
||||
if (mocks) for (const entry of mocks.values()) entry.capturedInput = undefined;
|
||||
return mocks;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -126,6 +126,18 @@ export async function runWorkflow(
|
|||
instanceBaseUrl: 'http://localhost',
|
||||
variables: {},
|
||||
currentNodeExecutionIndex: 0,
|
||||
// Friendly refusals for engine capabilities a test harness cannot provide — without
|
||||
// these, nodes fail with raw TypeErrors ("... is not a function").
|
||||
executeWorkflow: () => {
|
||||
throw new UnexpectedError(
|
||||
'Sub-workflow execution is not supported by n8n-test — mock the node with mockNode() instead',
|
||||
);
|
||||
},
|
||||
startRunnerTask: () => {
|
||||
throw new UnexpectedError(
|
||||
'Task-runner nodes (e.g. the Code node) are not supported by n8n-test — mock them with mockNode() instead',
|
||||
);
|
||||
},
|
||||
} as unknown as IWorkflowExecuteAdditionalData;
|
||||
|
||||
const runExecutionData = createRunExecutionData({
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user