mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-21 01:32:44 +08:00
138 lines
3.8 KiB
JavaScript
138 lines
3.8 KiB
JavaScript
const assert = require('node:assert/strict');
|
|
const { test } = require('node:test');
|
|
|
|
const {
|
|
RUN_CANCELLATION_SCHEMA,
|
|
RunCancellationFenceRejectedError,
|
|
RunCancellationNotFoundError,
|
|
RunCancellationUnavailableError,
|
|
} = require('@qinglong/runtime-core/run-cancellation');
|
|
const {
|
|
createLocalApiRunCancellationRoute,
|
|
} = require('../dist/run/runCancellationRoute.js');
|
|
|
|
const PRINCIPAL = Object.freeze({
|
|
subject: Object.freeze({ type: 'user', id: 'user-1' }),
|
|
authenticationId: 'credential:user-1',
|
|
authenticatedAtMs: 9_000,
|
|
expiresAtMs: 11_000,
|
|
assurance: 'single_factor',
|
|
});
|
|
const FENCE = Object.freeze({ projectVersion: 2, bindingVersion: 3 });
|
|
|
|
function accepted(overrides = {}) {
|
|
return {
|
|
status: 'accepted',
|
|
projectId: 'project-1',
|
|
runId: 'run-1',
|
|
runStatus: 'running',
|
|
runVersion: 5,
|
|
eventSequence: 7,
|
|
cancelRequestedAtMs: 10_000,
|
|
cancelReason: 'user',
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
function request(overrides = {}) {
|
|
return {
|
|
projectId: 'project-1',
|
|
runId: 'run-1',
|
|
body: { schema: RUN_CANCELLATION_SCHEMA, mutationId: 'mutation-1' },
|
|
principal: PRINCIPAL,
|
|
policyFence: FENCE,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
test('publishes one profile-neutral durable cancellation command', async () => {
|
|
let observed;
|
|
const route = createLocalApiRunCancellationRoute(
|
|
{
|
|
async requestUserCancellation(command) {
|
|
observed = command;
|
|
return accepted();
|
|
},
|
|
},
|
|
() => '018f0000-0000-7000-8000-000000000001',
|
|
);
|
|
assert.deepEqual(await route.handle(request()), {
|
|
statusCode: 202,
|
|
body: { schema: RUN_CANCELLATION_SCHEMA, ...accepted() },
|
|
});
|
|
assert.deepEqual(observed, {
|
|
projectId: 'project-1',
|
|
runId: 'run-1',
|
|
mutationId: 'mutation-1',
|
|
eventId: '018f0000-0000-7000-8000-000000000001',
|
|
subject: PRINCIPAL.subject,
|
|
policyFence: FENCE,
|
|
});
|
|
});
|
|
|
|
test('rejects malformed bodies and incomplete authorization fences', async () => {
|
|
let calls = 0;
|
|
const route = createLocalApiRunCancellationRoute(
|
|
{
|
|
async requestUserCancellation() {
|
|
calls += 1;
|
|
return accepted();
|
|
},
|
|
},
|
|
() => '018f0000-0000-7000-8000-000000000001',
|
|
);
|
|
assert.deepEqual(await route.handle(request({ body: { mutationId: 'x' } })), {
|
|
statusCode: 400,
|
|
body: {
|
|
code: 'invalid_run_cancellation_request',
|
|
schema: RUN_CANCELLATION_SCHEMA,
|
|
},
|
|
});
|
|
assert.deepEqual(await route.handle(request({ policyFence: null })), {
|
|
statusCode: 503,
|
|
body: { code: 'run_cancellation_unavailable' },
|
|
});
|
|
assert.equal(calls, 0);
|
|
});
|
|
|
|
test('maps replay, terminal, missing, fence and unavailable outcomes', async () => {
|
|
for (const outcome of [
|
|
accepted({ status: 'already_requested' }),
|
|
{
|
|
status: 'already_terminal',
|
|
projectId: 'project-1',
|
|
runId: 'run-1',
|
|
runStatus: 'succeeded',
|
|
runVersion: 6,
|
|
eventSequence: 8,
|
|
},
|
|
]) {
|
|
const route = createLocalApiRunCancellationRoute(
|
|
{ async requestUserCancellation() { return outcome; } },
|
|
() => '018f0000-0000-7000-8000-000000000001',
|
|
);
|
|
assert.equal((await route.handle(request())).statusCode, 200);
|
|
}
|
|
for (const [error, statusCode, code] of [
|
|
[new RunCancellationNotFoundError(), 404, 'run_not_found'],
|
|
[
|
|
new RunCancellationFenceRejectedError('authorization_changed'),
|
|
409,
|
|
'run_cancellation_fence_rejected',
|
|
],
|
|
[
|
|
new RunCancellationUnavailableError(),
|
|
503,
|
|
'run_cancellation_unavailable',
|
|
],
|
|
]) {
|
|
const route = createLocalApiRunCancellationRoute(
|
|
{ async requestUserCancellation() { throw error; } },
|
|
() => '018f0000-0000-7000-8000-000000000001',
|
|
);
|
|
const response = await route.handle(request());
|
|
assert.equal(response.statusCode, statusCode);
|
|
assert.equal(response.body.code, code);
|
|
}
|
|
});
|