Files

155 lines
4.0 KiB
JavaScript

const assert = require('node:assert/strict');
const { test } = require('node:test');
const {
BUILTIN_RUN_LIST_DEFAULT_LIMIT,
BUILTIN_RUN_LIST_MAX_LIMIT,
BUILTIN_RUN_LIST_TOOL,
BUILTIN_RUN_LIST_TOOL_DEFINITION,
BuiltInRunListToolUnavailableError,
InvalidBuiltInRunListToolError,
executeBuiltInRunListTool,
} = require('../dist/tool-projection/runList.js');
function run(id, createdAtMs, overrides = {}) {
return Object.freeze({
id,
projectId: 'default',
taskId: `task-${id}`,
taskRevision: 'revision-1',
taskName: 'private-name',
triggerType: 'manual',
executionOrigin: 'manual',
executionOwner: 'runtime',
triggeredBy: 'private-actor',
requestId: 'private-request',
status: 'succeeded',
version: 2,
eventSequence: 3,
priority: 0,
inputRef: 'private-input',
outputRef: 'private-output',
createdAtMs,
finishedAtMs: createdAtMs + 1,
...overrides,
});
}
test('defines one bounded low-risk Project Run discovery Tool', () => {
assert.deepEqual(BUILTIN_RUN_LIST_TOOL, {
name: 'qinglong.run.list',
version: '1.0.0',
});
assert.equal(BUILTIN_RUN_LIST_TOOL_DEFINITION.effect, 'read');
assert.equal(BUILTIN_RUN_LIST_TOOL_DEFINITION.risk, 'low');
assert.deepEqual(BUILTIN_RUN_LIST_TOOL_DEFINITION.requiredPermissions, [
'run.read',
]);
assert.equal(BUILTIN_RUN_LIST_DEFAULT_LIMIT, 32);
assert.equal(BUILTIN_RUN_LIST_MAX_LIMIT, 64);
});
test('returns a descending low-sensitive page and stable continuation', async () => {
const calls = [];
const result = await executeBuiltInRunListTool(
{
async listRunsByProject(query) {
calls.push(query);
return [run('run-c', 30), run('run-b', 20), run('run-a', 10)];
},
},
'default',
{ limit: 2 },
);
assert.deepEqual(calls, [{ projectId: 'default', limit: 3 }]);
assert.deepEqual(result, {
runs: [
{
id: 'run-c',
taskId: 'task-run-c',
taskRevision: 'revision-1',
status: 'succeeded',
version: 2,
eventSequence: 3,
priority: 0,
executionOrigin: 'manual',
executionOwner: 'runtime',
createdAtMs: 30,
finishedAtMs: 31,
},
{
id: 'run-b',
taskId: 'task-run-b',
taskRevision: 'revision-1',
status: 'succeeded',
version: 2,
eventSequence: 3,
priority: 0,
executionOrigin: 'manual',
executionOwner: 'runtime',
createdAtMs: 20,
finishedAtMs: 21,
},
],
hasMore: true,
next: { createdAtMs: 20, runId: 'run-b' },
});
assert.equal(JSON.stringify(result).includes('private-'), false);
});
test('passes an exact cursor and fails closed on malformed or corrupt pages', async () => {
const seen = [];
const reader = {
async listRunsByProject(query) {
seen.push(query);
return [];
},
};
assert.deepEqual(
await executeBuiltInRunListTool(reader, 'default', {
after: { createdAtMs: 20, runId: 'run-b' },
}),
{ runs: [], hasMore: false },
);
assert.deepEqual(seen, [
{
projectId: 'default',
limit: BUILTIN_RUN_LIST_DEFAULT_LIMIT + 1,
after: { createdAtMs: 20, runId: 'run-b' },
},
]);
for (const input of [
null,
{ limit: 65 },
{ unexpected: true },
{ after: null },
{ after: { createdAtMs: -1, runId: 'run-b' } },
{ after: { createdAtMs: 20, runId: 'run-b', extra: true } },
]) {
await assert.rejects(
executeBuiltInRunListTool(reader, 'default', input),
InvalidBuiltInRunListToolError,
);
}
for (const rows of [
[run('run-a', 10, { projectId: 'other' })],
[run('run-a', 10), run('run-b', 20)],
Array.from({ length: 34 }, (_, index) => run(`run-${index}`, 100 - index)),
]) {
await assert.rejects(
executeBuiltInRunListTool(
{
async listRunsByProject() {
return rows;
},
},
'default',
{},
),
BuiltInRunListToolUnavailableError,
);
}
});