-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathimport-runner-historical.test.ts
More file actions
89 lines (80 loc) · 3.53 KB
/
Copy pathimport-runner-historical.test.ts
File metadata and controls
89 lines (80 loc) · 3.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
/**
* #3479 — a "historical" import carries curated established facts (a batch of
* already-`closed` tickets, `closed_won` deals), so it must skip the object's
* `state_machine` rule: otherwise `initialStates` rejects every mid-lifecycle
* row on insert. runImport owns the option→context mapping — it sets
* `skipStateMachine` on the write context iff `treatAsHistorical` is set. A
* normal import must NOT set it (the FSM still applies).
*
* The engine's actual exemption behavior (skipStateMachine → skip the rule) is
* tested in @objectstack/objectql (engine.test.ts); this pins the wiring the
* import runner owns.
*/
import { describe, it, expect, vi } from 'vitest';
import { runImport, type ImportProtocolLike } from './import-runner';
import type { ExportFieldMeta } from './export-format.js';
const metaMap = new Map<string, ExportFieldMeta>([['name', { name: 'name', type: 'text' }]]);
const baseOpts = {
objectName: 'ticket',
metaMap,
writeMode: 'insert' as const,
matchFields: [] as string[],
dryRun: false,
runAutomations: false,
trimWhitespace: true,
createMissingOptions: false,
skipBlankMatchKey: false,
};
/** Provider mock that records the write context every persist call received. */
function makeProvider() {
const contexts: any[] = [];
let idc = 0;
const p: ImportProtocolLike = {
findData: vi.fn(async () => []),
createData: vi.fn(async (args: any) => {
contexts.push(args.context);
return { id: `d${++idc}`, ...args.data };
}),
updateData: vi.fn(async (args: any) => {
contexts.push(args.context);
return { id: args.id, ...args.data };
}),
createManyData: vi.fn(async (args: any) => {
contexts.push(args.context);
return { records: args.records.map((r: any) => ({ id: `d${++idc}`, ...r })) };
}),
};
return { p, contexts };
}
describe('runImport — historical import skips the state machine (#3479)', () => {
it('sets skipStateMachine on the write context when treatAsHistorical is true', async () => {
const { p, contexts } = makeProvider();
const summary = await runImport({
...baseOpts, p, rows: [{ name: 'a' }, { name: 'b' }], treatAsHistorical: true,
});
expect(summary.created).toBe(2);
expect(contexts.length).toBeGreaterThan(0);
for (const ctx of contexts) expect(ctx?.skipStateMachine).toBe(true);
});
it('does NOT set skipStateMachine for a normal import (the FSM still applies)', async () => {
const { p, contexts } = makeProvider();
await runImport({ ...baseOpts, p, rows: [{ name: 'a' }], treatAsHistorical: false });
expect(contexts.length).toBeGreaterThan(0);
for (const ctx of contexts) expect(ctx?.skipStateMachine).toBeUndefined();
});
it('defaults to off when the option is omitted (safe default — walk the FSM)', async () => {
const { p, contexts } = makeProvider();
await runImport({ ...baseOpts, p, rows: [{ name: 'a' }] });
expect(contexts.length).toBeGreaterThan(0);
for (const ctx of contexts) expect(ctx?.skipStateMachine).toBeUndefined();
});
it('leaves the automation toggle independent (skipAutomations still tracks runAutomations)', async () => {
const { p, contexts } = makeProvider();
await runImport({ ...baseOpts, p, rows: [{ name: 'a' }], treatAsHistorical: true, runAutomations: true });
for (const ctx of contexts) {
expect(ctx?.skipStateMachine).toBe(true);
expect(ctx?.skipAutomations).toBe(false); // runAutomations:true ⇒ don't skip
}
});
});