docs: enterprise refactor plan with ralph specs
This commit is contained in:
151
tests/server/scheduler.test.ts
Normal file
151
tests/server/scheduler.test.ts
Normal file
@@ -0,0 +1,151 @@
|
||||
/**
|
||||
* Tests for SchedulerService: cron parsing, schedule registration, skip when session active.
|
||||
*/
|
||||
|
||||
import Database from 'better-sqlite3';
|
||||
import { runMigrations } from '../../src/db/migrations';
|
||||
import { ScheduleRepository } from '../../src/db/ScheduleRepository';
|
||||
import { SchedulerService } from '../../src/server/scheduler/SchedulerService';
|
||||
import { SessionStore } from '../../src/server/SessionStore';
|
||||
|
||||
function makeDb(): Database.Database {
|
||||
const db = new Database(':memory:');
|
||||
db.pragma('foreign_keys = ON');
|
||||
runMigrations(db);
|
||||
return db;
|
||||
}
|
||||
|
||||
describe('SchedulerService', () => {
|
||||
let db: Database.Database;
|
||||
let repo: ScheduleRepository;
|
||||
let store: SessionStore;
|
||||
let scheduler: SchedulerService;
|
||||
|
||||
beforeEach(() => {
|
||||
db = makeDb();
|
||||
repo = new ScheduleRepository(db);
|
||||
store = new SessionStore('./reports');
|
||||
scheduler = new SchedulerService(repo, store);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
scheduler.stop();
|
||||
db.close();
|
||||
});
|
||||
|
||||
it('starts without error when no schedules exist', () => {
|
||||
expect(() => scheduler.start()).not.toThrow();
|
||||
});
|
||||
|
||||
it('registers a valid cron job', () => {
|
||||
repo.create({
|
||||
id: 'sched_1',
|
||||
name: 'Daily check',
|
||||
url: 'http://example.com',
|
||||
configJson: '{}',
|
||||
cronExpression: '0 2 * * *',
|
||||
enabled: true,
|
||||
});
|
||||
expect(() => scheduler.register(repo.findById('sched_1')!)).not.toThrow();
|
||||
});
|
||||
|
||||
it('skips invalid cron expressions', () => {
|
||||
repo.create({
|
||||
id: 'sched_bad',
|
||||
name: 'Bad',
|
||||
url: 'http://x.com',
|
||||
configJson: '{}',
|
||||
cronExpression: 'not-valid-cron',
|
||||
enabled: true,
|
||||
});
|
||||
expect(() => scheduler.register(repo.findById('sched_bad')!)).not.toThrow();
|
||||
});
|
||||
|
||||
it('unregisters a schedule job', () => {
|
||||
repo.create({
|
||||
id: 'sched_2',
|
||||
name: 'Test',
|
||||
url: 'http://example.com',
|
||||
configJson: '{}',
|
||||
cronExpression: '0 * * * *',
|
||||
enabled: true,
|
||||
});
|
||||
scheduler.register(repo.findById('sched_2')!);
|
||||
expect(() => scheduler.unregister('sched_2')).not.toThrow();
|
||||
});
|
||||
|
||||
it('computeNextRunAt returns a future timestamp for valid cron', () => {
|
||||
const nextRun = SchedulerService.computeNextRunAt('0 2 * * *');
|
||||
expect(nextRun).not.toBeNull();
|
||||
expect(nextRun!).toBeGreaterThan(Date.now());
|
||||
});
|
||||
|
||||
it('computeNextRunAt returns null for invalid cron', () => {
|
||||
expect(SchedulerService.computeNextRunAt('not-valid')).toBeNull();
|
||||
});
|
||||
|
||||
it('does not register job for disabled schedule', () => {
|
||||
repo.create({
|
||||
id: 'sched_3',
|
||||
name: 'Disabled',
|
||||
url: 'http://example.com',
|
||||
configJson: '{}',
|
||||
cronExpression: '0 * * * *',
|
||||
enabled: false,
|
||||
});
|
||||
// Should not throw and job should not be registered
|
||||
expect(() => scheduler.register(repo.findById('sched_3')!)).not.toThrow();
|
||||
// Unregistering a non-existing job is also safe
|
||||
expect(() => scheduler.unregister('sched_3')).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('ScheduleRepository', () => {
|
||||
let db: Database.Database;
|
||||
let repo: ScheduleRepository;
|
||||
|
||||
beforeEach(() => {
|
||||
db = makeDb();
|
||||
repo = new ScheduleRepository(db);
|
||||
});
|
||||
|
||||
afterEach(() => db.close());
|
||||
|
||||
it('creates and retrieves a schedule', () => {
|
||||
repo.create({
|
||||
id: 's1',
|
||||
name: 'Daily',
|
||||
url: 'http://test.com',
|
||||
configJson: '{"maxStates":10}',
|
||||
cronExpression: '0 2 * * *',
|
||||
});
|
||||
const record = repo.findById('s1');
|
||||
expect(record).toBeDefined();
|
||||
expect(record!.name).toBe('Daily');
|
||||
expect(record!.enabled).toBe(true);
|
||||
});
|
||||
|
||||
it('findAll returns all schedules', () => {
|
||||
repo.create({ id: 's1', name: 'A', url: 'http://a.com', configJson: '{}', cronExpression: '0 * * * *' });
|
||||
repo.create({ id: 's2', name: 'B', url: 'http://b.com', configJson: '{}', cronExpression: '0 2 * * *' });
|
||||
expect(repo.findAll()).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('findAll(true) returns only enabled schedules', () => {
|
||||
repo.create({ id: 's1', name: 'Enabled', url: 'http://a.com', configJson: '{}', cronExpression: '0 * * * *', enabled: true });
|
||||
repo.create({ id: 's2', name: 'Disabled', url: 'http://b.com', configJson: '{}', cronExpression: '0 * * * *', enabled: false });
|
||||
expect(repo.findAll(true)).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('updates enabled field', () => {
|
||||
repo.create({ id: 's1', name: 'A', url: 'http://a.com', configJson: '{}', cronExpression: '0 * * * *' });
|
||||
repo.update('s1', { enabled: false });
|
||||
expect(repo.findById('s1')!.enabled).toBe(false);
|
||||
});
|
||||
|
||||
it('deletes a schedule', () => {
|
||||
repo.create({ id: 's1', name: 'A', url: 'http://a.com', configJson: '{}', cronExpression: '0 * * * *' });
|
||||
repo.delete('s1');
|
||||
expect(repo.findById('s1')).toBeUndefined();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user