90 lines
3.6 KiB
JavaScript
90 lines
3.6 KiB
JavaScript
import { db, load, makeUser } from './setup.mjs';
|
|
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
|
|
const { createCredential } = await load('../models/credentialModel.js');
|
|
const { createHost } = await load('../models/hostModel.js');
|
|
const { updateSettings } = await load('../models/settingsModel.js');
|
|
const {
|
|
computeNextRun,
|
|
createRunPlanSchedule,
|
|
listDueRunPlanSchedules,
|
|
listRunPlanScheduleRuns,
|
|
previewScheduleOccurrences
|
|
} = await load('../models/runPlanScheduleModel.js');
|
|
const { dispatchDueRunPlanSchedules } = await load('../services/runPlanScheduleWorker.js');
|
|
|
|
const owner = makeUser({ email: 'schedule-owner@test.local' });
|
|
const ts = new Date().toISOString();
|
|
|
|
function createRunnableRunPlan() {
|
|
const scriptId = 'scr_schedule_test';
|
|
db.prepare(`INSERT INTO scripts (id, name, content, visibility, owner_user_id, version, created_at, updated_at)
|
|
VALUES (?, 'Scheduled Script.ps1', 'Write-Output scheduled', 'personal', ?, 1, ?, ?)`).run(scriptId, owner, ts, ts);
|
|
const credential = createCredential({
|
|
name: 'Schedule Credential',
|
|
kind: 'username_password',
|
|
username: 'TEST\\svc',
|
|
secret: 'secret',
|
|
visibility: 'personal'
|
|
}, owner);
|
|
const host = createHost({
|
|
name: 'Schedule Host',
|
|
address: 'schedule.local',
|
|
transport: 'local',
|
|
osFamily: 'linux',
|
|
credentialId: credential.id,
|
|
visibility: 'personal'
|
|
}, owner);
|
|
const runplanId = 'run_schedule_test';
|
|
db.prepare(`INSERT INTO runplans (id, name, script_id, visibility, owner_user_id, parallel, created_at, updated_at)
|
|
VALUES (?, 'Scheduled RunPlan', ?, 'personal', ?, 1, ?, ?)`).run(runplanId, scriptId, owner, ts, ts);
|
|
db.prepare('INSERT INTO runplan_hosts (runplan_id, host_id) VALUES (?, ?)').run(runplanId, host.id);
|
|
return runplanId;
|
|
}
|
|
|
|
test('recurrence preview covers weekly and interval schedules', () => {
|
|
const weekly = {
|
|
scheduleType: 'weekly',
|
|
startAt: '2026-06-01T14:00:00.000Z',
|
|
timeOfDay: '09:30',
|
|
daysOfWeek: [1, 3]
|
|
};
|
|
const preview = previewScheduleOccurrences(weekly, 3, new Date('2026-06-02T00:00:00.000Z'));
|
|
assert.equal(preview.length, 3);
|
|
assert.ok(preview.every((date) => [1, 3].includes(new Date(date).getDay())));
|
|
|
|
const intervalNext = computeNextRun({
|
|
scheduleType: 'interval',
|
|
startAt: '2026-06-01T00:00:00.000Z',
|
|
intervalValue: 6,
|
|
intervalUnit: 'hours'
|
|
}, new Date('2026-06-01T07:00:00.000Z'));
|
|
assert.equal(intervalNext, '2026-06-01T12:00:00.000Z');
|
|
});
|
|
|
|
test('due RunPlan schedules dispatch jobs and record scheduler history', async () => {
|
|
updateSettings({ allow_script_execution: 'false' });
|
|
const runplanId = createRunnableRunPlan();
|
|
const schedule = createRunPlanSchedule({
|
|
name: 'Every minute schedule',
|
|
runplanId,
|
|
scheduleType: 'interval',
|
|
startAt: '2026-01-01T00:00:00.000Z',
|
|
intervalValue: 1,
|
|
intervalUnit: 'minutes',
|
|
visibility: 'personal'
|
|
}, owner);
|
|
db.prepare('UPDATE runplan_schedules SET next_run_at = ? WHERE id = ?').run('2026-01-01T00:02:00.000Z', schedule.id);
|
|
|
|
assert.ok(listDueRunPlanSchedules('2026-01-01T00:02:00.000Z').some((row) => row.id === schedule.id));
|
|
const summary = await dispatchDueRunPlanSchedules({ nowIso: '2026-01-01T00:02:00.000Z' });
|
|
|
|
assert.equal(summary.dispatched, 1);
|
|
const job = db.prepare('SELECT * FROM jobs WHERE runplan_id = ? ORDER BY created_at DESC').get(runplanId);
|
|
assert.ok(job);
|
|
const history = listRunPlanScheduleRuns(owner, false, 20);
|
|
assert.ok(history.some((event) => event.scheduleId === schedule.id && event.jobId === job.id));
|
|
updateSettings({ allow_script_execution: 'true' });
|
|
});
|