mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-21 00:38:14 +08:00
258 lines
6.3 KiB
JavaScript
258 lines
6.3 KiB
JavaScript
const assert = require('node:assert/strict');
|
|
const { test } = require('node:test');
|
|
|
|
const {
|
|
createPanelCronListRoute,
|
|
} = require('../dist/panel-compatibility/panelCronListRoute.js');
|
|
const {
|
|
createTaskDefinitionRecord,
|
|
} = require('@qinglong/runtime-core/task-definition');
|
|
const {
|
|
createTriggerRecord,
|
|
createBuiltInTriggerSpecSemanticRegistry,
|
|
} = require('@qinglong/runtime-core/trigger');
|
|
|
|
function task(taskId, revision = 2, enabled = true) {
|
|
return createTaskDefinitionRecord(
|
|
{
|
|
projectId: 'default',
|
|
taskId,
|
|
expectedRevision: revision - 1,
|
|
mutationId: `019f7300-0000-4000-8000-${String(revision).padStart(
|
|
12,
|
|
'0',
|
|
)}`,
|
|
name: `Task ${taskId}`,
|
|
kind: 'command',
|
|
spec: {
|
|
schema: 'qinglong/command@v1',
|
|
config: { command: ['/bin/private'] },
|
|
},
|
|
labels: { private: 'redacted' },
|
|
enabled,
|
|
occurredAtMs: 200,
|
|
},
|
|
100,
|
|
);
|
|
}
|
|
|
|
function trigger(taskRecord, triggerId, enabled = true) {
|
|
const semantics = createBuiltInTriggerSpecSemanticRegistry();
|
|
const spec = semantics.normalize({
|
|
projectId: 'default',
|
|
triggerId,
|
|
taskId: taskRecord.taskId,
|
|
taskRevision: taskRecord.revision,
|
|
spec: {
|
|
schema: 'qinglong/cron@v1',
|
|
config: {
|
|
expression: '0 * * * *',
|
|
timezone: 'UTC',
|
|
misfirePolicy: 'skip',
|
|
},
|
|
},
|
|
});
|
|
return createTriggerRecord(
|
|
{
|
|
projectId: 'default',
|
|
triggerId,
|
|
expectedRevision: null,
|
|
mutationId: `019f7300-0000-4000-8001-${
|
|
triggerId.endsWith('b') ? '000000000002' : '000000000001'
|
|
}`,
|
|
taskId: taskRecord.taskId,
|
|
taskRevision: taskRecord.revision,
|
|
taskContentDigest: taskRecord.contentDigest,
|
|
spec,
|
|
enabled,
|
|
occurredAtMs: 400,
|
|
},
|
|
300,
|
|
);
|
|
}
|
|
|
|
test('projects one bounded page of pinned QL3 cron triggers into the legacy panel envelope', async () => {
|
|
const taskA = task('task-a');
|
|
const taskB = task('task-b', 3, false);
|
|
const triggers = [
|
|
trigger(taskA, 'cron:task-a'),
|
|
trigger(taskB, 'cron:task-b'),
|
|
];
|
|
const calls = [];
|
|
const route = createPanelCronListRoute({
|
|
tasks: {
|
|
async findTaskDefinitionRevision(projectId, taskId, revision) {
|
|
calls.push(['task', projectId, taskId, revision]);
|
|
return [taskA, taskB].find(
|
|
(entry) => entry.taskId === taskId && entry.revision === revision,
|
|
);
|
|
},
|
|
},
|
|
triggers: {
|
|
async listTriggers(input) {
|
|
calls.push(['trigger', input]);
|
|
return {
|
|
triggers: triggers.slice(0, input.limit),
|
|
truncated: triggers.length > input.limit,
|
|
...(triggers.length > input.limit
|
|
? { next: { triggerId: triggers[input.limit - 1].triggerId } }
|
|
: {}),
|
|
};
|
|
},
|
|
},
|
|
});
|
|
|
|
const first = await route.handle({
|
|
projectId: 'default',
|
|
page: 1,
|
|
size: 1,
|
|
maximumRows: 64,
|
|
});
|
|
assert.equal(first.statusCode, 200);
|
|
assert.equal(first.body.code, 200);
|
|
assert.equal(first.body.data.total, 2);
|
|
assert.deepEqual(first.body.data.data, [
|
|
{
|
|
id: 'cron:task-a',
|
|
name: 'Task task-a',
|
|
command: 'ql3:command:task-a@2',
|
|
schedule: '0 * * * *',
|
|
extra_schedules: [],
|
|
status: 1,
|
|
isDisabled: 0,
|
|
isPinned: 0,
|
|
createdAt: new Date(300).toISOString(),
|
|
updatedAt: new Date(400).toISOString(),
|
|
ql3: {
|
|
projectId: 'default',
|
|
taskId: 'task-a',
|
|
taskRevision: 2,
|
|
triggerId: 'cron:task-a',
|
|
triggerRevision: 1,
|
|
timezone: 'UTC',
|
|
misfirePolicy: 'skip',
|
|
readOnly: true,
|
|
},
|
|
},
|
|
]);
|
|
|
|
const second = await route.handle({
|
|
projectId: 'default',
|
|
page: 2,
|
|
size: 1,
|
|
maximumRows: 64,
|
|
});
|
|
assert.equal(second.body.data.total, 2);
|
|
assert.equal(second.body.data.data[0].id, 'cron:task-b');
|
|
assert.equal(second.body.data.data[0].status, 2);
|
|
assert.equal(second.body.data.data[0].isDisabled, 1);
|
|
assert.deepEqual(calls[0], ['trigger', { projectId: 'default', limit: 1 }]);
|
|
assert.deepEqual(calls[2], ['trigger', { projectId: 'default', limit: 2 }]);
|
|
|
|
const afterEnd = await route.handle({
|
|
projectId: 'default',
|
|
page: 3,
|
|
size: 1,
|
|
maximumRows: 64,
|
|
});
|
|
assert.deepEqual(afterEnd.body.data, { data: [], total: 2 });
|
|
});
|
|
|
|
test('fails closed for detached pins, unsupported triggers, invalid budgets and unavailable storage', async () => {
|
|
const pinned = task('task-a');
|
|
const cron = trigger(pinned, 'cron:task-a');
|
|
for (const sources of [
|
|
{
|
|
tasks: {
|
|
async findTaskDefinitionRevision() {
|
|
return null;
|
|
},
|
|
},
|
|
triggers: {
|
|
async listTriggers() {
|
|
return { triggers: [cron], truncated: false };
|
|
},
|
|
},
|
|
},
|
|
{
|
|
tasks: {
|
|
async findTaskDefinitionRevision() {
|
|
return pinned;
|
|
},
|
|
},
|
|
triggers: {
|
|
async listTriggers() {
|
|
return {
|
|
triggers: [
|
|
{ ...cron, spec: { schema: 'vendor/event@v1', config: {} } },
|
|
],
|
|
truncated: false,
|
|
};
|
|
},
|
|
},
|
|
},
|
|
{
|
|
tasks: {
|
|
async findTaskDefinitionRevision() {
|
|
return pinned;
|
|
},
|
|
},
|
|
triggers: {
|
|
async listTriggers() {
|
|
throw new Error('offline');
|
|
},
|
|
},
|
|
},
|
|
]) {
|
|
const route = createPanelCronListRoute(sources);
|
|
assert.deepEqual(
|
|
await route.handle({
|
|
projectId: 'default',
|
|
page: 1,
|
|
size: 1,
|
|
maximumRows: 64,
|
|
}),
|
|
{
|
|
statusCode: 503,
|
|
body: { code: 503, message: 'QL3 面板兼容视图暂不可用' },
|
|
},
|
|
);
|
|
}
|
|
const route = createPanelCronListRoute({
|
|
tasks: {
|
|
async findTaskDefinitionRevision() {
|
|
return pinned;
|
|
},
|
|
},
|
|
triggers: {
|
|
async listTriggers() {
|
|
return { triggers: [], truncated: false };
|
|
},
|
|
},
|
|
});
|
|
assert.deepEqual(
|
|
await route.handle({
|
|
projectId: 'default',
|
|
page: 4,
|
|
size: 20,
|
|
maximumRows: 64,
|
|
}),
|
|
{
|
|
statusCode: 200,
|
|
body: { code: 200, data: { data: [], total: 0 } },
|
|
},
|
|
);
|
|
assert.equal(
|
|
(
|
|
await route.handle({
|
|
projectId: 'default',
|
|
page: 5,
|
|
size: 20,
|
|
maximumRows: 64,
|
|
})
|
|
).statusCode,
|
|
400,
|
|
);
|
|
assert.throws(() => createPanelCronListRoute({}), TypeError);
|
|
});
|