mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-22 19:02:22 +08:00
feat(ql3): establish 3.0 incubation baseline
This commit is contained in:
@@ -0,0 +1,194 @@
|
||||
const assert = require('node:assert/strict');
|
||||
const { test } = require('node:test');
|
||||
|
||||
const {
|
||||
BUILTIN_TRIGGER_LIST_DEFAULT_LIMIT,
|
||||
BUILTIN_TRIGGER_LIST_MAX_LIMIT,
|
||||
BUILTIN_TRIGGER_LIST_TOOL,
|
||||
BUILTIN_TRIGGER_LIST_TOOL_DEFINITION,
|
||||
BuiltInTriggerListToolUnavailableError,
|
||||
InvalidBuiltInTriggerListToolError,
|
||||
executeBuiltInTriggerListTool,
|
||||
} = require('../dist/tool-projection/triggerList.js');
|
||||
|
||||
function trigger(triggerId, overrides = {}) {
|
||||
return Object.freeze({
|
||||
projectId: 'default',
|
||||
triggerId,
|
||||
revision: 2,
|
||||
mutationId: 'private-mutation',
|
||||
taskId: 'task-1',
|
||||
taskRevision: 3,
|
||||
taskContentDigest: 'private-task-digest',
|
||||
spec: Object.freeze({
|
||||
schema: 'qinglong/cron@v1',
|
||||
config: Object.freeze({
|
||||
expression: 'private cron expression',
|
||||
timezone: 'private timezone',
|
||||
misfire: 'private misfire policy',
|
||||
}),
|
||||
}),
|
||||
enabled: true,
|
||||
contentDigest: 'private-trigger-digest',
|
||||
createdAtMs: 10,
|
||||
updatedAtMs: 20,
|
||||
...overrides,
|
||||
});
|
||||
}
|
||||
|
||||
test('defines one bounded low-risk trigger.read Tool', () => {
|
||||
assert.deepEqual(BUILTIN_TRIGGER_LIST_TOOL, {
|
||||
name: 'qinglong.trigger.list',
|
||||
version: '1.0.0',
|
||||
});
|
||||
assert.equal(BUILTIN_TRIGGER_LIST_TOOL_DEFINITION.effect, 'read');
|
||||
assert.equal(BUILTIN_TRIGGER_LIST_TOOL_DEFINITION.risk, 'low');
|
||||
assert.deepEqual(BUILTIN_TRIGGER_LIST_TOOL_DEFINITION.requiredPermissions, [
|
||||
'trigger.read',
|
||||
]);
|
||||
assert.equal(BUILTIN_TRIGGER_LIST_DEFAULT_LIMIT, 32);
|
||||
assert.equal(BUILTIN_TRIGGER_LIST_MAX_LIMIT, 64);
|
||||
});
|
||||
|
||||
test('projects bounded current Triggers without schedule configuration', async () => {
|
||||
let captured;
|
||||
const output = await executeBuiltInTriggerListTool(
|
||||
{
|
||||
async listTriggers(query) {
|
||||
captured = query;
|
||||
return Object.freeze({
|
||||
triggers: Object.freeze([trigger('trigger-b')]),
|
||||
truncated: true,
|
||||
next: Object.freeze({ triggerId: 'trigger-b' }),
|
||||
});
|
||||
},
|
||||
},
|
||||
'default',
|
||||
{ after: { triggerId: 'trigger-a' }, limit: 1 },
|
||||
);
|
||||
assert.deepEqual(captured, {
|
||||
projectId: 'default',
|
||||
limit: 1,
|
||||
after: { triggerId: 'trigger-a' },
|
||||
});
|
||||
assert.deepEqual(output, {
|
||||
triggers: [
|
||||
{
|
||||
triggerId: 'trigger-b',
|
||||
revision: 2,
|
||||
taskId: 'task-1',
|
||||
taskRevision: 3,
|
||||
specSchema: 'qinglong/cron@v1',
|
||||
enabled: true,
|
||||
updatedAtMs: 20,
|
||||
},
|
||||
],
|
||||
hasMore: true,
|
||||
next: { triggerId: 'trigger-b' },
|
||||
});
|
||||
const serialized = JSON.stringify(output);
|
||||
for (const hidden of [
|
||||
'private',
|
||||
'expression',
|
||||
'timezone',
|
||||
'misfire',
|
||||
'contentDigest',
|
||||
'projectId',
|
||||
]) {
|
||||
assert.equal(serialized.includes(hidden), false);
|
||||
}
|
||||
});
|
||||
|
||||
test('defaults to 32 and returns no cursor for a complete page', async () => {
|
||||
let captured;
|
||||
const output = await executeBuiltInTriggerListTool(
|
||||
{
|
||||
async listTriggers(query) {
|
||||
captured = query;
|
||||
return { triggers: [], truncated: false };
|
||||
},
|
||||
},
|
||||
'default',
|
||||
{},
|
||||
);
|
||||
assert.deepEqual(captured, { projectId: 'default', limit: 32 });
|
||||
assert.deepEqual(output, { triggers: [], hasMore: false });
|
||||
});
|
||||
|
||||
test('rejects invalid input before reading', async () => {
|
||||
let reads = 0;
|
||||
const source = {
|
||||
async listTriggers() {
|
||||
reads += 1;
|
||||
return { triggers: [], truncated: false };
|
||||
},
|
||||
};
|
||||
for (const input of [
|
||||
null,
|
||||
{ limit: 65 },
|
||||
{ after: { triggerId: '' } },
|
||||
{ after: { triggerId: 'trigger-a', extra: true } },
|
||||
{ unexpected: true },
|
||||
]) {
|
||||
await assert.rejects(
|
||||
executeBuiltInTriggerListTool(source, 'default', input),
|
||||
InvalidBuiltInTriggerListToolError,
|
||||
);
|
||||
}
|
||||
assert.equal(reads, 0);
|
||||
});
|
||||
|
||||
test('fails closed on cross-Project, unordered, oversized or inconsistent pages', async () => {
|
||||
for (const { page, input = {} } of [
|
||||
{
|
||||
page: {
|
||||
triggers: [trigger('trigger-a', { projectId: 'other' })],
|
||||
truncated: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
page: {
|
||||
triggers: [trigger('trigger-b'), trigger('trigger-a')],
|
||||
truncated: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
page: {
|
||||
triggers: [trigger('trigger-a'), trigger('trigger-b')],
|
||||
truncated: false,
|
||||
},
|
||||
input: { limit: 1 },
|
||||
},
|
||||
{
|
||||
page: { triggers: [trigger('trigger-a')], truncated: true },
|
||||
},
|
||||
{
|
||||
page: {
|
||||
triggers: [trigger('trigger-a')],
|
||||
truncated: true,
|
||||
next: { triggerId: 'trigger-b' },
|
||||
},
|
||||
},
|
||||
{
|
||||
page: {
|
||||
triggers: [
|
||||
trigger('trigger-a', { spec: { schema: 'invalid', config: {} } }),
|
||||
],
|
||||
truncated: false,
|
||||
},
|
||||
},
|
||||
]) {
|
||||
await assert.rejects(
|
||||
executeBuiltInTriggerListTool(
|
||||
{
|
||||
async listTriggers() {
|
||||
return page;
|
||||
},
|
||||
},
|
||||
'default',
|
||||
input,
|
||||
),
|
||||
BuiltInTriggerListToolUnavailableError,
|
||||
);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user