@elaraai/e3-api-tests 0.0.2-beta.31 → 0.0.2-beta.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -21
- package/dist/src/index.d.ts +18 -23
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +13 -47
- package/dist/src/index.js.map +1 -1
- package/dist/src/setup.d.ts +11 -0
- package/dist/src/setup.d.ts.map +1 -0
- package/dist/src/setup.js +6 -0
- package/dist/src/setup.js.map +1 -0
- package/dist/src/suites/cli.d.ts +3 -2
- package/dist/src/suites/cli.d.ts.map +1 -1
- package/dist/src/suites/cli.js +47 -37
- package/dist/src/suites/cli.js.map +1 -1
- package/dist/src/suites/dataflow.d.ts +3 -2
- package/dist/src/suites/dataflow.d.ts.map +1 -1
- package/dist/src/suites/dataflow.js +128 -206
- package/dist/src/suites/dataflow.js.map +1 -1
- package/dist/src/suites/datasets.d.ts +3 -2
- package/dist/src/suites/datasets.d.ts.map +1 -1
- package/dist/src/suites/datasets.js +22 -22
- package/dist/src/suites/datasets.js.map +1 -1
- package/dist/src/suites/packages.d.ts +3 -2
- package/dist/src/suites/packages.d.ts.map +1 -1
- package/dist/src/suites/packages.js +24 -25
- package/dist/src/suites/packages.js.map +1 -1
- package/dist/src/suites/platform.d.ts +3 -2
- package/dist/src/suites/platform.d.ts.map +1 -1
- package/dist/src/suites/platform.js +21 -21
- package/dist/src/suites/platform.js.map +1 -1
- package/dist/src/suites/repository.d.ts +3 -2
- package/dist/src/suites/repository.d.ts.map +1 -1
- package/dist/src/suites/repository.js +19 -19
- package/dist/src/suites/repository.js.map +1 -1
- package/dist/src/suites/transfer.d.ts +3 -2
- package/dist/src/suites/transfer.d.ts.map +1 -1
- package/dist/src/suites/transfer.js +43 -36
- package/dist/src/suites/transfer.js.map +1 -1
- package/dist/src/suites/workspaces.d.ts +3 -2
- package/dist/src/suites/workspaces.d.ts.map +1 -1
- package/dist/src/suites/workspaces.js +31 -42
- package/dist/src/suites/workspaces.js.map +1 -1
- package/package.json +1 -1
|
@@ -7,31 +7,65 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Tests: start, execute (blocking), poll for completion, logs
|
|
9
9
|
*/
|
|
10
|
-
import { describe, it
|
|
10
|
+
import { describe, it } from 'node:test';
|
|
11
11
|
import assert from 'node:assert/strict';
|
|
12
12
|
import { readFileSync } from 'node:fs';
|
|
13
13
|
import { packageImport, workspaceCreate, workspaceDeploy, workspaceStatus, dataflowStart, dataflowExecute, dataflowExecution, dataflowCancel, dataflowGraph, taskLogs, ApiError, } from '@elaraai/e3-api-client';
|
|
14
14
|
import { createPackageZip, createDiamondPackageZip, createFailingPackageZip, createSlowPackageZip, createParallelMixedPackageZip, createFailingDiamondPackageZip, createWideParallelPackageZip, } from '../fixtures.js';
|
|
15
|
+
/** Helper: import package, create workspace, deploy */
|
|
16
|
+
function withDeployed(setup, createZip, pkgName, wsName) {
|
|
17
|
+
return async (t) => {
|
|
18
|
+
const ctx = await setup(t);
|
|
19
|
+
const opts = await ctx.opts();
|
|
20
|
+
const zipPath = await createZip(ctx.tempDir, pkgName, '1.0.0');
|
|
21
|
+
const packageZip = readFileSync(zipPath);
|
|
22
|
+
await packageImport(ctx.config.baseUrl, ctx.repoName, packageZip, opts);
|
|
23
|
+
await workspaceCreate(ctx.config.baseUrl, ctx.repoName, wsName, opts);
|
|
24
|
+
await workspaceDeploy(ctx.config.baseUrl, ctx.repoName, wsName, `${pkgName}@1.0.0`, opts);
|
|
25
|
+
return ctx;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
15
28
|
/**
|
|
16
29
|
* Register dataflow execution tests.
|
|
17
30
|
*
|
|
18
|
-
* @param
|
|
31
|
+
* @param setup - Factory that creates a fresh test context per test
|
|
19
32
|
*/
|
|
20
|
-
export function dataflowTests(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
export function dataflowTests(setup) {
|
|
34
|
+
const withSimpleExec = withDeployed(setup, createPackageZip, 'exec-pkg', 'exec-ws');
|
|
35
|
+
const withDiamond = withDeployed(setup, createDiamondPackageZip, 'diamond-pkg', 'diamond-ws');
|
|
36
|
+
const withFailing = withDeployed(setup, createFailingPackageZip, 'fail-pkg', 'fail-ws');
|
|
37
|
+
const withMixed = withDeployed(setup, createParallelMixedPackageZip, 'mixed-pkg', 'mixed-ws');
|
|
38
|
+
const withFailingDiamond = withDeployed(setup, createFailingDiamondPackageZip, 'fdiamond-pkg', 'fdiamond-ws');
|
|
39
|
+
const withWideParallel = async (t) => {
|
|
40
|
+
const ctx = await setup(t);
|
|
41
|
+
const opts = await ctx.opts();
|
|
42
|
+
const zipPath = await createWideParallelPackageZip(ctx.tempDir, 'wide-pkg', '1.0.0', 6);
|
|
43
|
+
const packageZip = readFileSync(zipPath);
|
|
44
|
+
await packageImport(ctx.config.baseUrl, ctx.repoName, packageZip, opts);
|
|
45
|
+
await workspaceCreate(ctx.config.baseUrl, ctx.repoName, 'wide-ws', opts);
|
|
46
|
+
await workspaceDeploy(ctx.config.baseUrl, ctx.repoName, 'wide-ws', 'wide-pkg@1.0.0', opts);
|
|
47
|
+
return ctx;
|
|
48
|
+
};
|
|
49
|
+
const withSlow = async (t) => {
|
|
50
|
+
const ctx = await setup(t);
|
|
51
|
+
const opts = await ctx.opts();
|
|
52
|
+
const zipPath = await createSlowPackageZip(ctx.tempDir, 'slow-pkg', '1.0.0', 30);
|
|
53
|
+
const packageZip = readFileSync(zipPath);
|
|
54
|
+
await packageImport(ctx.config.baseUrl, ctx.repoName, packageZip, opts);
|
|
55
|
+
await workspaceCreate(ctx.config.baseUrl, ctx.repoName, 'slow-ws', opts);
|
|
56
|
+
await workspaceDeploy(ctx.config.baseUrl, ctx.repoName, 'slow-ws', 'slow-pkg@1.0.0', opts);
|
|
57
|
+
return ctx;
|
|
58
|
+
};
|
|
59
|
+
const withNoExec = withDeployed(setup, createPackageZip, 'noexec-pkg', 'noexec-ws');
|
|
60
|
+
const withCache = withDeployed(setup, createPackageZip, 'cache-pkg', 'cache-ws');
|
|
61
|
+
const withFilter = withDeployed(setup, createDiamondPackageZip, 'filter-pkg', 'filter-ws');
|
|
62
|
+
const withGraph = withDeployed(setup, createDiamondPackageZip, 'graph-pkg', 'graph-ws');
|
|
63
|
+
const withLogPag = withDeployed(setup, createPackageZip, 'logpag-pkg', 'logpag-ws');
|
|
64
|
+
const withEvtPag = withDeployed(setup, createDiamondPackageZip, 'evtpag-pkg', 'evtpag-ws');
|
|
65
|
+
describe('dataflow', { concurrency: true }, () => {
|
|
66
|
+
describe('simple execution', { concurrency: true }, () => {
|
|
67
|
+
it('dataflowExecute runs tasks and returns result (blocking)', async (t) => {
|
|
68
|
+
const ctx = await withSimpleExec(t);
|
|
35
69
|
const opts = await ctx.opts();
|
|
36
70
|
const result = await dataflowExecute(ctx.config.baseUrl, ctx.repoName, 'exec-ws', { force: true }, opts);
|
|
37
71
|
// Verify execution result
|
|
@@ -50,8 +84,8 @@ export function dataflowTests(getContext) {
|
|
|
50
84
|
assert.ok(outputDataset, 'Output dataset .tasks.compute.output should exist');
|
|
51
85
|
assert.strictEqual(outputDataset.status.type, 'up-to-date');
|
|
52
86
|
});
|
|
53
|
-
it('dataflowStart triggers execution (non-blocking)', async () => {
|
|
54
|
-
const ctx =
|
|
87
|
+
it('dataflowStart triggers execution (non-blocking)', async (t) => {
|
|
88
|
+
const ctx = await withSimpleExec(t);
|
|
55
89
|
const opts = await ctx.opts();
|
|
56
90
|
// Should return immediately
|
|
57
91
|
await dataflowStart(ctx.config.baseUrl, ctx.repoName, 'exec-ws', { force: true }, opts);
|
|
@@ -71,8 +105,8 @@ export function dataflowTests(getContext) {
|
|
|
71
105
|
// Verify execution completed
|
|
72
106
|
assert.strictEqual(status.tasks[0].status.type, 'up-to-date');
|
|
73
107
|
});
|
|
74
|
-
it('dataflowExecution returns execution state', async () => {
|
|
75
|
-
const ctx =
|
|
108
|
+
it('dataflowExecution returns execution state', async (t) => {
|
|
109
|
+
const ctx = await withSimpleExec(t);
|
|
76
110
|
const opts = await ctx.opts();
|
|
77
111
|
// Start execution
|
|
78
112
|
await dataflowStart(ctx.config.baseUrl, ctx.repoName, 'exec-ws', { force: true }, opts);
|
|
@@ -97,8 +131,8 @@ export function dataflowTests(getContext) {
|
|
|
97
131
|
}
|
|
98
132
|
assert.fail('Execution did not complete in time');
|
|
99
133
|
});
|
|
100
|
-
it('taskLogs returns logs after execution', async () => {
|
|
101
|
-
const ctx =
|
|
134
|
+
it('taskLogs returns logs after execution', async (t) => {
|
|
135
|
+
const ctx = await withSimpleExec(t);
|
|
102
136
|
const opts = await ctx.opts();
|
|
103
137
|
// Execute first
|
|
104
138
|
await dataflowExecute(ctx.config.baseUrl, ctx.repoName, 'exec-ws', { force: true }, opts);
|
|
@@ -112,19 +146,9 @@ export function dataflowTests(getContext) {
|
|
|
112
146
|
assert.ok(typeof logs.complete === 'boolean');
|
|
113
147
|
});
|
|
114
148
|
});
|
|
115
|
-
describe('diamond dependency execution', () => {
|
|
116
|
-
|
|
117
|
-
const ctx =
|
|
118
|
-
const opts = await ctx.opts();
|
|
119
|
-
// Create and import diamond package (left, right, merge tasks)
|
|
120
|
-
const zipPath = await createDiamondPackageZip(ctx.tempDir, 'diamond-pkg', '1.0.0');
|
|
121
|
-
const packageZip = readFileSync(zipPath);
|
|
122
|
-
await packageImport(ctx.config.baseUrl, ctx.repoName, packageZip, opts);
|
|
123
|
-
await workspaceCreate(ctx.config.baseUrl, ctx.repoName, 'diamond-ws', opts);
|
|
124
|
-
await workspaceDeploy(ctx.config.baseUrl, ctx.repoName, 'diamond-ws', 'diamond-pkg@1.0.0', opts);
|
|
125
|
-
});
|
|
126
|
-
it('executes diamond dependency graph correctly', async () => {
|
|
127
|
-
const ctx = getContext();
|
|
149
|
+
describe('diamond dependency execution', { concurrency: true }, () => {
|
|
150
|
+
it('executes diamond dependency graph correctly', async (t) => {
|
|
151
|
+
const ctx = await withDiamond(t);
|
|
128
152
|
const opts = await ctx.opts();
|
|
129
153
|
const result = await dataflowExecute(ctx.config.baseUrl, ctx.repoName, 'diamond-ws', { force: true }, opts);
|
|
130
154
|
// Should execute all three tasks
|
|
@@ -137,8 +161,8 @@ export function dataflowTests(getContext) {
|
|
|
137
161
|
assert.strictEqual(task.state.type, 'success', `Task ${task.name} should succeed`);
|
|
138
162
|
}
|
|
139
163
|
});
|
|
140
|
-
it('tracks events during execution', async () => {
|
|
141
|
-
const ctx =
|
|
164
|
+
it('tracks events during execution', async (t) => {
|
|
165
|
+
const ctx = await withDiamond(t);
|
|
142
166
|
const opts = await ctx.opts();
|
|
143
167
|
// Start execution
|
|
144
168
|
await dataflowStart(ctx.config.baseUrl, ctx.repoName, 'diamond-ws', { force: true }, opts);
|
|
@@ -162,19 +186,9 @@ export function dataflowTests(getContext) {
|
|
|
162
186
|
assert.ok(completeEvents.length >= 3, `Expected at least 3 complete events, got ${completeEvents.length}`);
|
|
163
187
|
});
|
|
164
188
|
});
|
|
165
|
-
describe('failed execution', () => {
|
|
166
|
-
|
|
167
|
-
const ctx =
|
|
168
|
-
const opts = await ctx.opts();
|
|
169
|
-
// Create and import a package with a failing task
|
|
170
|
-
const zipPath = await createFailingPackageZip(ctx.tempDir, 'fail-pkg', '1.0.0');
|
|
171
|
-
const packageZip = readFileSync(zipPath);
|
|
172
|
-
await packageImport(ctx.config.baseUrl, ctx.repoName, packageZip, opts);
|
|
173
|
-
await workspaceCreate(ctx.config.baseUrl, ctx.repoName, 'fail-ws', opts);
|
|
174
|
-
await workspaceDeploy(ctx.config.baseUrl, ctx.repoName, 'fail-ws', 'fail-pkg@1.0.0', opts);
|
|
175
|
-
});
|
|
176
|
-
it('dataflowExecute returns failure result when task fails', async () => {
|
|
177
|
-
const ctx = getContext();
|
|
189
|
+
describe('failed execution', { concurrency: true }, () => {
|
|
190
|
+
it('dataflowExecute returns failure result when task fails', async (t) => {
|
|
191
|
+
const ctx = await withFailing(t);
|
|
178
192
|
const opts = await ctx.opts();
|
|
179
193
|
const result = await dataflowExecute(ctx.config.baseUrl, ctx.repoName, 'fail-ws', { force: true }, opts);
|
|
180
194
|
// Execution should report failure
|
|
@@ -185,8 +199,8 @@ export function dataflowTests(getContext) {
|
|
|
185
199
|
assert.strictEqual(result.tasks[0].name, 'failing');
|
|
186
200
|
assert.strictEqual(result.tasks[0].state.type, 'failed');
|
|
187
201
|
});
|
|
188
|
-
it('dataflowExecution shows failed status after task failure', async () => {
|
|
189
|
-
const ctx =
|
|
202
|
+
it('dataflowExecution shows failed status after task failure', async (t) => {
|
|
203
|
+
const ctx = await withFailing(t);
|
|
190
204
|
const opts = await ctx.opts();
|
|
191
205
|
// Start execution
|
|
192
206
|
await dataflowStart(ctx.config.baseUrl, ctx.repoName, 'fail-ws', { force: true }, opts);
|
|
@@ -210,8 +224,8 @@ export function dataflowTests(getContext) {
|
|
|
210
224
|
}
|
|
211
225
|
assert.fail('Execution did not complete in time');
|
|
212
226
|
});
|
|
213
|
-
it('can restart execution after failure', async () => {
|
|
214
|
-
const ctx =
|
|
227
|
+
it('can restart execution after failure', async (t) => {
|
|
228
|
+
const ctx = await withFailing(t);
|
|
215
229
|
const opts = await ctx.opts();
|
|
216
230
|
// First execution - should fail
|
|
217
231
|
const result1 = await dataflowExecute(ctx.config.baseUrl, ctx.repoName, 'fail-ws', { force: true }, opts);
|
|
@@ -222,19 +236,10 @@ export function dataflowTests(getContext) {
|
|
|
222
236
|
assert.strictEqual(result2.failed, 1n);
|
|
223
237
|
});
|
|
224
238
|
});
|
|
225
|
-
describe('parallel task failures', () => {
|
|
226
|
-
describe('mixed success/failure', () => {
|
|
227
|
-
|
|
228
|
-
const ctx =
|
|
229
|
-
const opts = await ctx.opts();
|
|
230
|
-
const zipPath = await createParallelMixedPackageZip(ctx.tempDir, 'mixed-pkg', '1.0.0');
|
|
231
|
-
const packageZip = readFileSync(zipPath);
|
|
232
|
-
await packageImport(ctx.config.baseUrl, ctx.repoName, packageZip, opts);
|
|
233
|
-
await workspaceCreate(ctx.config.baseUrl, ctx.repoName, 'mixed-ws', opts);
|
|
234
|
-
await workspaceDeploy(ctx.config.baseUrl, ctx.repoName, 'mixed-ws', 'mixed-pkg@1.0.0', opts);
|
|
235
|
-
});
|
|
236
|
-
it('parallel tasks with mixed success/failure complete without stalling', async () => {
|
|
237
|
-
const ctx = getContext();
|
|
239
|
+
describe('parallel task failures', { concurrency: true }, () => {
|
|
240
|
+
describe('mixed success/failure', { concurrency: true }, () => {
|
|
241
|
+
it('parallel tasks with mixed success/failure complete without stalling', async (t) => {
|
|
242
|
+
const ctx = await withMixed(t);
|
|
238
243
|
const opts = await ctx.opts();
|
|
239
244
|
const result = await dataflowExecute(ctx.config.baseUrl, ctx.repoName, 'mixed-ws', { force: true }, opts);
|
|
240
245
|
// Dataflow should complete (not stall) and report failure
|
|
@@ -245,16 +250,14 @@ export function dataflowTests(getContext) {
|
|
|
245
250
|
assert.ok(failC, 'fail_c task should be in results');
|
|
246
251
|
assert.strictEqual(failC.state.type, 'failed');
|
|
247
252
|
// Tasks that did execute should have succeeded
|
|
248
|
-
// (orchestrator may stop launching new tasks after a failure,
|
|
249
|
-
// so not all succeed tasks are guaranteed to have run)
|
|
250
253
|
for (const task of result.tasks) {
|
|
251
254
|
if (task.name !== 'fail_c') {
|
|
252
255
|
assert.strictEqual(task.state.type, 'success', `Task ${task.name} should succeed`);
|
|
253
256
|
}
|
|
254
257
|
}
|
|
255
258
|
});
|
|
256
|
-
it('failed task logs are accessible', async () => {
|
|
257
|
-
const ctx =
|
|
259
|
+
it('failed task logs are accessible', async (t) => {
|
|
260
|
+
const ctx = await withMixed(t);
|
|
258
261
|
const opts = await ctx.opts();
|
|
259
262
|
// Execute first to generate logs
|
|
260
263
|
await dataflowExecute(ctx.config.baseUrl, ctx.repoName, 'mixed-ws', { force: true }, opts);
|
|
@@ -263,8 +266,8 @@ export function dataflowTests(getContext) {
|
|
|
263
266
|
assert.ok(typeof logs.data === 'string', 'logs.data should be a string');
|
|
264
267
|
assert.ok(typeof logs.complete === 'boolean', 'logs.complete should be a boolean');
|
|
265
268
|
});
|
|
266
|
-
it('workspace status reflects failed tasks correctly', async () => {
|
|
267
|
-
const ctx =
|
|
269
|
+
it('workspace status reflects failed tasks correctly', async (t) => {
|
|
270
|
+
const ctx = await withMixed(t);
|
|
268
271
|
const opts = await ctx.opts();
|
|
269
272
|
await dataflowExecute(ctx.config.baseUrl, ctx.repoName, 'mixed-ws', { force: true }, opts);
|
|
270
273
|
const status = await workspaceStatus(ctx.config.baseUrl, ctx.repoName, 'mixed-ws', opts);
|
|
@@ -278,18 +281,9 @@ export function dataflowTests(getContext) {
|
|
|
278
281
|
}
|
|
279
282
|
});
|
|
280
283
|
});
|
|
281
|
-
describe('diamond with upstream failure', () => {
|
|
282
|
-
|
|
283
|
-
const ctx =
|
|
284
|
-
const opts = await ctx.opts();
|
|
285
|
-
const zipPath = await createFailingDiamondPackageZip(ctx.tempDir, 'fdiamond-pkg', '1.0.0');
|
|
286
|
-
const packageZip = readFileSync(zipPath);
|
|
287
|
-
await packageImport(ctx.config.baseUrl, ctx.repoName, packageZip, opts);
|
|
288
|
-
await workspaceCreate(ctx.config.baseUrl, ctx.repoName, 'fdiamond-ws', opts);
|
|
289
|
-
await workspaceDeploy(ctx.config.baseUrl, ctx.repoName, 'fdiamond-ws', 'fdiamond-pkg@1.0.0', opts);
|
|
290
|
-
});
|
|
291
|
-
it('diamond with upstream failure skips dependents', async () => {
|
|
292
|
-
const ctx = getContext();
|
|
284
|
+
describe('diamond with upstream failure', { concurrency: true }, () => {
|
|
285
|
+
it('diamond with upstream failure skips dependents', async (t) => {
|
|
286
|
+
const ctx = await withFailingDiamond(t);
|
|
293
287
|
const opts = await ctx.opts();
|
|
294
288
|
const result = await dataflowExecute(ctx.config.baseUrl, ctx.repoName, 'fdiamond-ws', { force: true }, opts);
|
|
295
289
|
assert.strictEqual(result.success, false);
|
|
@@ -306,8 +300,8 @@ export function dataflowTests(getContext) {
|
|
|
306
300
|
assert.strictEqual(rightTask.state.type, 'failed');
|
|
307
301
|
assert.strictEqual(mergeTask.state.type, 'skipped');
|
|
308
302
|
});
|
|
309
|
-
it('taskLogs returns execution_not_found for skipped task', async () => {
|
|
310
|
-
const ctx =
|
|
303
|
+
it('taskLogs returns execution_not_found for skipped task', async (t) => {
|
|
304
|
+
const ctx = await withFailingDiamond(t);
|
|
311
305
|
const opts = await ctx.opts();
|
|
312
306
|
// Execute — merge will be skipped because right fails
|
|
313
307
|
await dataflowExecute(ctx.config.baseUrl, ctx.repoName, 'fdiamond-ws', { force: true }, opts);
|
|
@@ -321,18 +315,9 @@ export function dataflowTests(getContext) {
|
|
|
321
315
|
}
|
|
322
316
|
});
|
|
323
317
|
});
|
|
324
|
-
describe('wide parallel execution', () => {
|
|
325
|
-
|
|
326
|
-
const ctx =
|
|
327
|
-
const opts = await ctx.opts();
|
|
328
|
-
const zipPath = await createWideParallelPackageZip(ctx.tempDir, 'wide-pkg', '1.0.0', 6);
|
|
329
|
-
const packageZip = readFileSync(zipPath);
|
|
330
|
-
await packageImport(ctx.config.baseUrl, ctx.repoName, packageZip, opts);
|
|
331
|
-
await workspaceCreate(ctx.config.baseUrl, ctx.repoName, 'wide-ws', opts);
|
|
332
|
-
await workspaceDeploy(ctx.config.baseUrl, ctx.repoName, 'wide-ws', 'wide-pkg@1.0.0', opts);
|
|
333
|
-
});
|
|
334
|
-
it('wide parallel execution completes correctly', async () => {
|
|
335
|
-
const ctx = getContext();
|
|
318
|
+
describe('wide parallel execution', { concurrency: true }, () => {
|
|
319
|
+
it('wide parallel execution completes correctly', async (t) => {
|
|
320
|
+
const ctx = await withWideParallel(t);
|
|
336
321
|
const opts = await ctx.opts();
|
|
337
322
|
const result = await dataflowExecute(ctx.config.baseUrl, ctx.repoName, 'wide-ws', { force: true }, opts);
|
|
338
323
|
assert.strictEqual(result.success, true);
|
|
@@ -345,19 +330,11 @@ export function dataflowTests(getContext) {
|
|
|
345
330
|
});
|
|
346
331
|
});
|
|
347
332
|
});
|
|
333
|
+
// Concurrent execution tests must remain serial within their describe
|
|
334
|
+
// because they test locking behavior with timing-sensitive operations
|
|
348
335
|
describe('concurrent execution', () => {
|
|
349
|
-
|
|
350
|
-
const ctx =
|
|
351
|
-
const opts = await ctx.opts();
|
|
352
|
-
// Create and import a slow package
|
|
353
|
-
const zipPath = await createSlowPackageZip(ctx.tempDir, 'slow-pkg', '1.0.0', 30);
|
|
354
|
-
const packageZip = readFileSync(zipPath);
|
|
355
|
-
await packageImport(ctx.config.baseUrl, ctx.repoName, packageZip, opts);
|
|
356
|
-
await workspaceCreate(ctx.config.baseUrl, ctx.repoName, 'slow-ws', opts);
|
|
357
|
-
await workspaceDeploy(ctx.config.baseUrl, ctx.repoName, 'slow-ws', 'slow-pkg@1.0.0', opts);
|
|
358
|
-
});
|
|
359
|
-
it('rejects second dataflowStart while execution is running', async () => {
|
|
360
|
-
const ctx = getContext();
|
|
336
|
+
it('rejects second dataflowStart while execution is running', async (t) => {
|
|
337
|
+
const ctx = await withSlow(t);
|
|
361
338
|
const opts = await ctx.opts();
|
|
362
339
|
// Start first execution (non-blocking)
|
|
363
340
|
await dataflowStart(ctx.config.baseUrl, ctx.repoName, 'slow-ws', { force: true }, opts);
|
|
@@ -375,8 +352,8 @@ export function dataflowTests(getContext) {
|
|
|
375
352
|
assert.ok(message.includes('lock') || message.includes('running') || message.includes('busy'), `Expected lock-related error, got: ${err.message}`);
|
|
376
353
|
}
|
|
377
354
|
});
|
|
378
|
-
it('rejects dataflowExecute while execution is running', async () => {
|
|
379
|
-
const ctx =
|
|
355
|
+
it('rejects dataflowExecute while execution is running', async (t) => {
|
|
356
|
+
const ctx = await withSlow(t);
|
|
380
357
|
const opts = await ctx.opts();
|
|
381
358
|
// Start first execution (non-blocking)
|
|
382
359
|
await dataflowStart(ctx.config.baseUrl, ctx.repoName, 'slow-ws', { force: true }, opts);
|
|
@@ -393,8 +370,8 @@ export function dataflowTests(getContext) {
|
|
|
393
370
|
assert.ok(message.includes('lock') || message.includes('running') || message.includes('busy'), `Expected lock-related error, got: ${err.message}`);
|
|
394
371
|
}
|
|
395
372
|
});
|
|
396
|
-
it('dataflowCancel stops a running execution', async () => {
|
|
397
|
-
const ctx =
|
|
373
|
+
it('dataflowCancel stops a running execution', async (t) => {
|
|
374
|
+
const ctx = await withSlow(t);
|
|
398
375
|
const opts = await ctx.opts();
|
|
399
376
|
// Start slow execution
|
|
400
377
|
await dataflowStart(ctx.config.baseUrl, ctx.repoName, 'slow-ws', { force: true }, opts);
|
|
@@ -406,8 +383,8 @@ export function dataflowTests(getContext) {
|
|
|
406
383
|
const state = await dataflowExecution(ctx.config.baseUrl, ctx.repoName, 'slow-ws', {}, opts);
|
|
407
384
|
assert.strictEqual(state.status.type, 'aborted');
|
|
408
385
|
});
|
|
409
|
-
it('dataflowCancel returns error when no execution is running', async () => {
|
|
410
|
-
const ctx =
|
|
386
|
+
it('dataflowCancel returns error when no execution is running', async (t) => {
|
|
387
|
+
const ctx = await withSlow(t);
|
|
411
388
|
const opts = await ctx.opts();
|
|
412
389
|
// Try to cancel when nothing is running
|
|
413
390
|
try {
|
|
@@ -420,19 +397,9 @@ export function dataflowTests(getContext) {
|
|
|
420
397
|
}
|
|
421
398
|
});
|
|
422
399
|
});
|
|
423
|
-
describe('execution not found', () => {
|
|
424
|
-
|
|
425
|
-
const ctx =
|
|
426
|
-
const opts = await ctx.opts();
|
|
427
|
-
// Create and import a simple package, deploy but do NOT execute
|
|
428
|
-
const zipPath = await createPackageZip(ctx.tempDir, 'noexec-pkg', '1.0.0');
|
|
429
|
-
const packageZip = readFileSync(zipPath);
|
|
430
|
-
await packageImport(ctx.config.baseUrl, ctx.repoName, packageZip, opts);
|
|
431
|
-
await workspaceCreate(ctx.config.baseUrl, ctx.repoName, 'noexec-ws', opts);
|
|
432
|
-
await workspaceDeploy(ctx.config.baseUrl, ctx.repoName, 'noexec-ws', 'noexec-pkg@1.0.0', opts);
|
|
433
|
-
});
|
|
434
|
-
it('taskLogs returns execution_not_found for never-executed task', async () => {
|
|
435
|
-
const ctx = getContext();
|
|
400
|
+
describe('execution not found', { concurrency: true }, () => {
|
|
401
|
+
it('taskLogs returns execution_not_found for never-executed task', async (t) => {
|
|
402
|
+
const ctx = await withNoExec(t);
|
|
436
403
|
const opts = await ctx.opts();
|
|
437
404
|
try {
|
|
438
405
|
await taskLogs(ctx.config.baseUrl, ctx.repoName, 'noexec-ws', 'compute', { stream: 'stdout' }, opts);
|
|
@@ -443,8 +410,8 @@ export function dataflowTests(getContext) {
|
|
|
443
410
|
assert.strictEqual(err.code, 'execution_not_found');
|
|
444
411
|
}
|
|
445
412
|
});
|
|
446
|
-
it('taskLogs returns task_not_found for non-existent task', async () => {
|
|
447
|
-
const ctx =
|
|
413
|
+
it('taskLogs returns task_not_found for non-existent task', async (t) => {
|
|
414
|
+
const ctx = await withNoExec(t);
|
|
448
415
|
const opts = await ctx.opts();
|
|
449
416
|
try {
|
|
450
417
|
await taskLogs(ctx.config.baseUrl, ctx.repoName, 'noexec-ws', 'no_such_task', { stream: 'stdout' }, opts);
|
|
@@ -455,8 +422,8 @@ export function dataflowTests(getContext) {
|
|
|
455
422
|
assert.strictEqual(err.code, 'task_not_found');
|
|
456
423
|
}
|
|
457
424
|
});
|
|
458
|
-
it('taskLogs returns workspace_not_found for non-existent workspace', async () => {
|
|
459
|
-
const ctx =
|
|
425
|
+
it('taskLogs returns workspace_not_found for non-existent workspace', async (t) => {
|
|
426
|
+
const ctx = await withNoExec(t);
|
|
460
427
|
const opts = await ctx.opts();
|
|
461
428
|
try {
|
|
462
429
|
await taskLogs(ctx.config.baseUrl, ctx.repoName, 'no_such_ws', 'compute', { stream: 'stdout' }, opts);
|
|
@@ -468,9 +435,9 @@ export function dataflowTests(getContext) {
|
|
|
468
435
|
}
|
|
469
436
|
});
|
|
470
437
|
});
|
|
471
|
-
describe('workspace error handling', () => {
|
|
472
|
-
it('dataflowExecute returns error for non-existent workspace', async () => {
|
|
473
|
-
const ctx =
|
|
438
|
+
describe('workspace error handling', { concurrency: true }, () => {
|
|
439
|
+
it('dataflowExecute returns error for non-existent workspace', async (t) => {
|
|
440
|
+
const ctx = await setup(t);
|
|
474
441
|
const opts = await ctx.opts();
|
|
475
442
|
try {
|
|
476
443
|
await dataflowExecute(ctx.config.baseUrl, ctx.repoName, 'no_such_ws', { force: true }, opts);
|
|
@@ -481,8 +448,8 @@ export function dataflowTests(getContext) {
|
|
|
481
448
|
assert.ok(err.code === 'workspace_not_found' || err.code === 'workspace_not_deployed', `Expected workspace_not_found or workspace_not_deployed, got ${err.code}`);
|
|
482
449
|
}
|
|
483
450
|
});
|
|
484
|
-
it('dataflowGraph returns error for non-existent workspace', async () => {
|
|
485
|
-
const ctx =
|
|
451
|
+
it('dataflowGraph returns error for non-existent workspace', async (t) => {
|
|
452
|
+
const ctx = await setup(t);
|
|
486
453
|
const opts = await ctx.opts();
|
|
487
454
|
try {
|
|
488
455
|
await dataflowGraph(ctx.config.baseUrl, ctx.repoName, 'no_such_ws', opts);
|
|
@@ -493,8 +460,8 @@ export function dataflowTests(getContext) {
|
|
|
493
460
|
assert.ok(err.code === 'workspace_not_found' || err.code === 'workspace_not_deployed', `Expected workspace_not_found or workspace_not_deployed, got ${err.code}`);
|
|
494
461
|
}
|
|
495
462
|
});
|
|
496
|
-
it('workspaceStatus returns error for non-existent workspace', async () => {
|
|
497
|
-
const ctx =
|
|
463
|
+
it('workspaceStatus returns error for non-existent workspace', async (t) => {
|
|
464
|
+
const ctx = await setup(t);
|
|
498
465
|
const opts = await ctx.opts();
|
|
499
466
|
try {
|
|
500
467
|
await workspaceStatus(ctx.config.baseUrl, ctx.repoName, 'no_such_ws', opts);
|
|
@@ -506,18 +473,9 @@ export function dataflowTests(getContext) {
|
|
|
506
473
|
}
|
|
507
474
|
});
|
|
508
475
|
});
|
|
509
|
-
describe('cache behavior', () => {
|
|
510
|
-
|
|
511
|
-
const ctx =
|
|
512
|
-
const opts = await ctx.opts();
|
|
513
|
-
const zipPath = await createPackageZip(ctx.tempDir, 'cache-pkg', '1.0.0');
|
|
514
|
-
const packageZip = readFileSync(zipPath);
|
|
515
|
-
await packageImport(ctx.config.baseUrl, ctx.repoName, packageZip, opts);
|
|
516
|
-
await workspaceCreate(ctx.config.baseUrl, ctx.repoName, 'cache-ws', opts);
|
|
517
|
-
await workspaceDeploy(ctx.config.baseUrl, ctx.repoName, 'cache-ws', 'cache-pkg@1.0.0', opts);
|
|
518
|
-
});
|
|
519
|
-
it('second execution uses cached results', async () => {
|
|
520
|
-
const ctx = getContext();
|
|
476
|
+
describe('cache behavior', { concurrency: true }, () => {
|
|
477
|
+
it('second execution uses cached results', async (t) => {
|
|
478
|
+
const ctx = await withCache(t);
|
|
521
479
|
const opts = await ctx.opts();
|
|
522
480
|
// First execution - should execute the task
|
|
523
481
|
const result1 = await dataflowExecute(ctx.config.baseUrl, ctx.repoName, 'cache-ws', { force: false }, opts);
|
|
@@ -529,8 +487,8 @@ export function dataflowTests(getContext) {
|
|
|
529
487
|
assert.ok(result2.cached > 0n, `Expected cached > 0, got ${result2.cached}`);
|
|
530
488
|
assert.strictEqual(result2.executed, 0n);
|
|
531
489
|
});
|
|
532
|
-
it('force bypasses cache', async () => {
|
|
533
|
-
const ctx =
|
|
490
|
+
it('force bypasses cache', async (t) => {
|
|
491
|
+
const ctx = await withCache(t);
|
|
534
492
|
const opts = await ctx.opts();
|
|
535
493
|
// First execution
|
|
536
494
|
await dataflowExecute(ctx.config.baseUrl, ctx.repoName, 'cache-ws', { force: false }, opts);
|
|
@@ -540,18 +498,9 @@ export function dataflowTests(getContext) {
|
|
|
540
498
|
assert.ok(result.executed > 0n, `Expected executed > 0, got ${result.executed}`);
|
|
541
499
|
});
|
|
542
500
|
});
|
|
543
|
-
describe('task filter', () => {
|
|
544
|
-
|
|
545
|
-
const ctx =
|
|
546
|
-
const opts = await ctx.opts();
|
|
547
|
-
const zipPath = await createDiamondPackageZip(ctx.tempDir, 'filter-pkg', '1.0.0');
|
|
548
|
-
const packageZip = readFileSync(zipPath);
|
|
549
|
-
await packageImport(ctx.config.baseUrl, ctx.repoName, packageZip, opts);
|
|
550
|
-
await workspaceCreate(ctx.config.baseUrl, ctx.repoName, 'filter-ws', opts);
|
|
551
|
-
await workspaceDeploy(ctx.config.baseUrl, ctx.repoName, 'filter-ws', 'filter-pkg@1.0.0', opts);
|
|
552
|
-
});
|
|
553
|
-
it('filter runs only the specified task', async () => {
|
|
554
|
-
const ctx = getContext();
|
|
501
|
+
describe('task filter', { concurrency: true }, () => {
|
|
502
|
+
it('filter runs only the specified task', async (t) => {
|
|
503
|
+
const ctx = await withFilter(t);
|
|
555
504
|
const opts = await ctx.opts();
|
|
556
505
|
const result = await dataflowExecute(ctx.config.baseUrl, ctx.repoName, 'filter-ws', { force: true, filter: 'left' }, opts);
|
|
557
506
|
assert.strictEqual(result.success, true);
|
|
@@ -560,8 +509,8 @@ export function dataflowTests(getContext) {
|
|
|
560
509
|
assert.strictEqual(executedTasks.length, 1, `Expected 1 executed task, got ${executedTasks.length}`);
|
|
561
510
|
assert.strictEqual(executedTasks[0].name, 'left');
|
|
562
511
|
});
|
|
563
|
-
it('filter with non-existent task returns error', async () => {
|
|
564
|
-
const ctx =
|
|
512
|
+
it('filter with non-existent task returns error', async (t) => {
|
|
513
|
+
const ctx = await withFilter(t);
|
|
565
514
|
const opts = await ctx.opts();
|
|
566
515
|
try {
|
|
567
516
|
await dataflowExecute(ctx.config.baseUrl, ctx.repoName, 'filter-ws', { force: true, filter: 'no_such_task' }, opts);
|
|
@@ -573,18 +522,9 @@ export function dataflowTests(getContext) {
|
|
|
573
522
|
}
|
|
574
523
|
});
|
|
575
524
|
});
|
|
576
|
-
describe('dependency graph', () => {
|
|
577
|
-
|
|
578
|
-
const ctx =
|
|
579
|
-
const opts = await ctx.opts();
|
|
580
|
-
const zipPath = await createDiamondPackageZip(ctx.tempDir, 'graph-pkg', '1.0.0');
|
|
581
|
-
const packageZip = readFileSync(zipPath);
|
|
582
|
-
await packageImport(ctx.config.baseUrl, ctx.repoName, packageZip, opts);
|
|
583
|
-
await workspaceCreate(ctx.config.baseUrl, ctx.repoName, 'graph-ws', opts);
|
|
584
|
-
await workspaceDeploy(ctx.config.baseUrl, ctx.repoName, 'graph-ws', 'graph-pkg@1.0.0', opts);
|
|
585
|
-
});
|
|
586
|
-
it('dataflowGraph returns correct structure', async () => {
|
|
587
|
-
const ctx = getContext();
|
|
525
|
+
describe('dependency graph', { concurrency: true }, () => {
|
|
526
|
+
it('dataflowGraph returns correct structure', async (t) => {
|
|
527
|
+
const ctx = await withGraph(t);
|
|
588
528
|
const opts = await ctx.opts();
|
|
589
529
|
const graph = await dataflowGraph(ctx.config.baseUrl, ctx.repoName, 'graph-ws', opts);
|
|
590
530
|
// Should have 3 tasks: left, right, merge
|
|
@@ -604,18 +544,9 @@ export function dataflowTests(getContext) {
|
|
|
604
544
|
assert.strictEqual(merge.dependsOn.length, 2);
|
|
605
545
|
});
|
|
606
546
|
});
|
|
607
|
-
describe('log pagination', () => {
|
|
608
|
-
|
|
609
|
-
const ctx =
|
|
610
|
-
const opts = await ctx.opts();
|
|
611
|
-
const zipPath = await createPackageZip(ctx.tempDir, 'logpag-pkg', '1.0.0');
|
|
612
|
-
const packageZip = readFileSync(zipPath);
|
|
613
|
-
await packageImport(ctx.config.baseUrl, ctx.repoName, packageZip, opts);
|
|
614
|
-
await workspaceCreate(ctx.config.baseUrl, ctx.repoName, 'logpag-ws', opts);
|
|
615
|
-
await workspaceDeploy(ctx.config.baseUrl, ctx.repoName, 'logpag-ws', 'logpag-pkg@1.0.0', opts);
|
|
616
|
-
});
|
|
617
|
-
it('taskLogs supports offset and limit', async () => {
|
|
618
|
-
const ctx = getContext();
|
|
547
|
+
describe('log pagination', { concurrency: true }, () => {
|
|
548
|
+
it('taskLogs supports offset and limit', async (t) => {
|
|
549
|
+
const ctx = await withLogPag(t);
|
|
619
550
|
const opts = await ctx.opts();
|
|
620
551
|
// Execute to generate logs
|
|
621
552
|
await dataflowExecute(ctx.config.baseUrl, ctx.repoName, 'logpag-ws', { force: true }, opts);
|
|
@@ -635,18 +566,9 @@ export function dataflowTests(getContext) {
|
|
|
635
566
|
assert.strictEqual(chunk2.data, full.data.slice(5));
|
|
636
567
|
});
|
|
637
568
|
});
|
|
638
|
-
describe('event pagination', () => {
|
|
639
|
-
|
|
640
|
-
const ctx =
|
|
641
|
-
const opts = await ctx.opts();
|
|
642
|
-
const zipPath = await createDiamondPackageZip(ctx.tempDir, 'evtpag-pkg', '1.0.0');
|
|
643
|
-
const packageZip = readFileSync(zipPath);
|
|
644
|
-
await packageImport(ctx.config.baseUrl, ctx.repoName, packageZip, opts);
|
|
645
|
-
await workspaceCreate(ctx.config.baseUrl, ctx.repoName, 'evtpag-ws', opts);
|
|
646
|
-
await workspaceDeploy(ctx.config.baseUrl, ctx.repoName, 'evtpag-ws', 'evtpag-pkg@1.0.0', opts);
|
|
647
|
-
});
|
|
648
|
-
it('dataflowExecution supports event offset and limit', async () => {
|
|
649
|
-
const ctx = getContext();
|
|
569
|
+
describe('event pagination', { concurrency: true }, () => {
|
|
570
|
+
it('dataflowExecution supports event offset and limit', async (t) => {
|
|
571
|
+
const ctx = await withEvtPag(t);
|
|
650
572
|
const opts = await ctx.opts();
|
|
651
573
|
// Execute and wait for completion
|
|
652
574
|
await dataflowExecute(ctx.config.baseUrl, ctx.repoName, 'evtpag-ws', { force: true }, opts);
|