Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 86d024d

Browse files
authored
Merge pull request #376 from theGeekist/refactor-pipeline-to-meet-framework-norms
refine pipeline internals with typed coordinators
2 parents 0c97975 + 73200c9 commit 86d024d

File tree

9 files changed

+1732
-859
lines changed

9 files changed

+1732
-859
lines changed

packages/pipeline/src/__tests__/createPipeline.test.ts

Lines changed: 148 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type {
44
HelperApplyOptions,
55
Pipeline,
66
PipelineDiagnostic,
7+
PipelineExtensionRollbackErrorMetadata,
78
PipelineReporter,
89
PipelineRunState,
910
} from '../types.js';
@@ -45,7 +46,28 @@ function createTestReporter(): TestReporter {
4546
return reporter;
4647
}
4748

48-
function createTestPipeline(): {
49+
function createTestPipeline(options?: {
50+
readonly onDiagnostic?: ({
51+
reporter,
52+
diagnostic,
53+
}: {
54+
readonly reporter: TestReporter;
55+
readonly diagnostic: TestDiagnostic;
56+
}) => void;
57+
readonly onExtensionRollbackError?: ({
58+
error,
59+
extensionKeys,
60+
hookSequence,
61+
errorMetadata,
62+
context,
63+
}: {
64+
readonly error: unknown;
65+
readonly extensionKeys: readonly string[];
66+
readonly hookSequence: readonly string[];
67+
readonly errorMetadata: PipelineExtensionRollbackErrorMetadata;
68+
readonly context: TestContext;
69+
}) => void;
70+
}): {
4971
pipeline: TestPipeline;
5072
reporter: TestReporter;
5173
} {
@@ -109,6 +131,8 @@ function createTestPipeline(): {
109131
createRunResult({ artifact, diagnostics, steps }) {
110132
return { artifact, diagnostics, steps } satisfies TestRunResult;
111133
},
134+
onDiagnostic: options?.onDiagnostic,
135+
onExtensionRollbackError: options?.onExtensionRollbackError,
112136
});
113137

114138
pipeline.ir.use(
@@ -184,4 +208,127 @@ describe('createPipeline (extensions)', () => {
184208

185209
await expect(pipeline.run({})).rejects.toThrow('registration failed');
186210
});
211+
212+
it('reports rollback metadata via the extension coordinator when builders fail', async () => {
213+
const rollback = jest.fn(() => {
214+
throw new Error('rollback failure');
215+
});
216+
const onExtensionRollbackError = jest.fn();
217+
const { pipeline, reporter } = createTestPipeline({
218+
onExtensionRollbackError,
219+
});
220+
221+
pipeline.extensions.use({
222+
key: 'test.rollback',
223+
register() {
224+
return ({ artifact }: { artifact: string[] }) => ({
225+
artifact: [...artifact, 'extension'],
226+
rollback,
227+
});
228+
},
229+
});
230+
231+
pipeline.builders.use(
232+
createHelper<
233+
TestContext,
234+
void,
235+
string[],
236+
TestReporter,
237+
typeof pipeline.builderKind
238+
>({
239+
key: 'builder.failure',
240+
kind: pipeline.builderKind,
241+
priority: 1,
242+
apply() {
243+
throw new Error('builder exploded');
244+
},
245+
})
246+
);
247+
248+
try {
249+
await pipeline.run({});
250+
throw new Error('expected pipeline.run() to reject');
251+
} catch (error) {
252+
expect(error).toBeInstanceOf(Error);
253+
expect((error as Error).message).toBe('builder exploded');
254+
}
255+
256+
expect(rollback).toHaveBeenCalledTimes(1);
257+
expect(onExtensionRollbackError).toHaveBeenCalledTimes(1);
258+
259+
const event = onExtensionRollbackError.mock.calls[0][0];
260+
expect(event.error).toBeInstanceOf(Error);
261+
expect((event.error as Error).message).toBe('rollback failure');
262+
expect(event.extensionKeys).toEqual(['test.rollback']);
263+
expect(event.hookSequence).toEqual(['test.rollback']);
264+
expect(event.errorMetadata).toEqual(
265+
expect.objectContaining({
266+
message: 'rollback failure',
267+
name: 'Error',
268+
})
269+
);
270+
expect(event.context.reporter).toBe(reporter);
271+
});
272+
});
273+
274+
describe('createPipeline diagnostics', () => {
275+
it('replays stored diagnostics once when a reporter is available', async () => {
276+
const onDiagnostic = jest.fn();
277+
const { pipeline, reporter } = createTestPipeline({ onDiagnostic });
278+
279+
pipeline.ir.use(
280+
createHelper<
281+
TestContext,
282+
void,
283+
string[],
284+
TestReporter,
285+
typeof pipeline.fragmentKind
286+
>({
287+
key: 'fragment.conflict',
288+
kind: pipeline.fragmentKind,
289+
mode: 'override',
290+
apply({ output }) {
291+
output.push('first');
292+
},
293+
})
294+
);
295+
296+
expect(() =>
297+
pipeline.ir.use(
298+
createHelper<
299+
TestContext,
300+
void,
301+
string[],
302+
TestReporter,
303+
typeof pipeline.fragmentKind
304+
>({
305+
key: 'fragment.conflict',
306+
kind: pipeline.fragmentKind,
307+
mode: 'override',
308+
apply({ output }) {
309+
output.push('second');
310+
},
311+
})
312+
)
313+
).toThrow(
314+
'Multiple overrides registered for helper "fragment.conflict".'
315+
);
316+
317+
expect(onDiagnostic).not.toHaveBeenCalled();
318+
319+
await pipeline.run({});
320+
321+
expect(onDiagnostic).toHaveBeenCalledTimes(1);
322+
expect(onDiagnostic).toHaveBeenCalledWith({
323+
reporter,
324+
diagnostic: expect.objectContaining({
325+
type: 'conflict',
326+
key: 'fragment.conflict',
327+
}),
328+
});
329+
330+
await pipeline.run({});
331+
332+
expect(onDiagnostic).toHaveBeenCalledTimes(1);
333+
});
187334
});

0 commit comments

Comments
 (0)