mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
feat(ql3): establish 3.0 incubation baseline
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
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,
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user