mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-21 01:32:44 +08:00
172 lines
4.3 KiB
JavaScript
172 lines
4.3 KiB
JavaScript
const assert = require('node:assert/strict');
|
|
const { test } = require('node:test');
|
|
|
|
const {
|
|
BUILTIN_RUN_EVENT_LIST_DEFAULT_LIMIT,
|
|
BUILTIN_RUN_EVENT_LIST_MAX_LIMIT,
|
|
BUILTIN_RUN_EVENT_LIST_TOOL,
|
|
BUILTIN_RUN_EVENT_LIST_TOOL_DEFINITION,
|
|
BuiltInRunEventListToolUnavailableError,
|
|
InvalidBuiltInRunEventListToolError,
|
|
executeBuiltInRunEventListTool,
|
|
} = require('../dist/tool-projection/runEventList.js');
|
|
|
|
function run(projectId = 'default') {
|
|
return Object.freeze({ id: 'run-1', projectId });
|
|
}
|
|
|
|
function event(sequence, overrides = {}) {
|
|
return Object.freeze({
|
|
id: `event-${sequence}`,
|
|
runId: 'run-1',
|
|
sequence,
|
|
type: `run.event.${sequence}`,
|
|
dedupeKey: `private-dedupe-${sequence}`,
|
|
actorType: 'system',
|
|
actorId: 'private-actor',
|
|
attemptId: 'private-attempt',
|
|
stepRunId: 'private-step',
|
|
payload: Object.freeze({ secret: 'must-not-leak' }),
|
|
createdAtMs: 1_000 + sequence,
|
|
...overrides,
|
|
});
|
|
}
|
|
|
|
test('defines one bounded low-risk Run event list Tool', () => {
|
|
assert.deepEqual(BUILTIN_RUN_EVENT_LIST_TOOL, {
|
|
name: 'qinglong.run.events.list',
|
|
version: '1.0.0',
|
|
});
|
|
assert.equal(BUILTIN_RUN_EVENT_LIST_TOOL_DEFINITION.effect, 'read');
|
|
assert.equal(BUILTIN_RUN_EVENT_LIST_TOOL_DEFINITION.risk, 'low');
|
|
assert.deepEqual(
|
|
BUILTIN_RUN_EVENT_LIST_TOOL_DEFINITION.requiredPermissions,
|
|
['run.read'],
|
|
);
|
|
assert.equal(BUILTIN_RUN_EVENT_LIST_DEFAULT_LIMIT, 32);
|
|
assert.equal(BUILTIN_RUN_EVENT_LIST_MAX_LIMIT, 64);
|
|
});
|
|
|
|
test('returns an ordered payload-free page and a stable cursor', async () => {
|
|
const calls = [];
|
|
const result = await executeBuiltInRunEventListTool(
|
|
{
|
|
async findRunById(runId) {
|
|
calls.push(['run', runId]);
|
|
return run();
|
|
},
|
|
async listEvents(runId, options) {
|
|
calls.push(['events', runId, options]);
|
|
return [event(3), event(4), event(5)];
|
|
},
|
|
},
|
|
'default',
|
|
{ runId: 'run-1', afterSequence: 2, limit: 2 },
|
|
);
|
|
assert.deepEqual(result, {
|
|
found: true,
|
|
events: [
|
|
{
|
|
sequence: 3,
|
|
type: 'run.event.3',
|
|
actorType: 'system',
|
|
createdAtMs: 1_003,
|
|
},
|
|
{
|
|
sequence: 4,
|
|
type: 'run.event.4',
|
|
actorType: 'system',
|
|
createdAtMs: 1_004,
|
|
},
|
|
],
|
|
hasMore: true,
|
|
nextAfterSequence: 4,
|
|
});
|
|
assert.deepEqual(calls, [
|
|
['run', 'run-1'],
|
|
['events', 'run-1', { afterSequence: 2, limit: 3 }],
|
|
]);
|
|
assert.equal(JSON.stringify(result).includes('must-not-leak'), false);
|
|
assert.equal(JSON.stringify(result).includes('private-'), false);
|
|
});
|
|
|
|
test('masks absent and cross-Project Runs without reading events', async () => {
|
|
for (const value of [null, run('other')]) {
|
|
let eventReads = 0;
|
|
const result = await executeBuiltInRunEventListTool(
|
|
{
|
|
async findRunById() {
|
|
return value;
|
|
},
|
|
async listEvents() {
|
|
eventReads += 1;
|
|
return [];
|
|
},
|
|
},
|
|
'default',
|
|
{ runId: 'run-1', afterSequence: 7 },
|
|
);
|
|
assert.deepEqual(result, {
|
|
found: false,
|
|
events: [],
|
|
hasMore: false,
|
|
nextAfterSequence: 7,
|
|
});
|
|
assert.equal(eventReads, 0);
|
|
}
|
|
});
|
|
|
|
test('rejects malformed input and fails closed on storage or event corruption', async () => {
|
|
const runs = {
|
|
async findRunById() {
|
|
return run();
|
|
},
|
|
async listEvents() {
|
|
return [];
|
|
},
|
|
};
|
|
for (const input of [
|
|
null,
|
|
{},
|
|
{ runId: '' },
|
|
{ runId: 'run-1', limit: 65 },
|
|
{ runId: 'run-1', afterSequence: -1 },
|
|
{ runId: 'run-1', unexpected: true },
|
|
]) {
|
|
await assert.rejects(
|
|
executeBuiltInRunEventListTool(runs, 'default', input),
|
|
InvalidBuiltInRunEventListToolError,
|
|
);
|
|
}
|
|
await assert.rejects(
|
|
executeBuiltInRunEventListTool(
|
|
{
|
|
async findRunById() {
|
|
throw new Error('hidden');
|
|
},
|
|
async listEvents() {
|
|
return [];
|
|
},
|
|
},
|
|
'default',
|
|
{ runId: 'run-1' },
|
|
),
|
|
BuiltInRunEventListToolUnavailableError,
|
|
);
|
|
await assert.rejects(
|
|
executeBuiltInRunEventListTool(
|
|
{
|
|
async findRunById() {
|
|
return run();
|
|
},
|
|
async listEvents() {
|
|
return [event(2), event(1)];
|
|
},
|
|
},
|
|
'default',
|
|
{ runId: 'run-1' },
|
|
),
|
|
BuiltInRunEventListToolUnavailableError,
|
|
);
|
|
});
|