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' }); });