Timed jobs move from in-process timers to a job registry run by pg-boss: once per schedule across processes, retries, run history, cron schedules in the church timezone, and an admin Background jobs page with Run now. Playwright end-to-end tests run against their own seeded database. A Gitea Actions workflow runs backend tests, frontend checks and the e2e tests, and deploys v* tags with scripts/deploy.sh once secrets are set. Also fixes the at-the-door check-in count for multi-day tickets. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
55 lines
2.2 KiB
JavaScript
55 lines
2.2 KiB
JavaScript
const { JOBS, isEnabled, cronFor, nextRun, isNoop } = require('../src/jobs');
|
|
|
|
const job = (name) => JOBS.find(j => j.name === name);
|
|
|
|
describe('job registry', () => {
|
|
const saved = { ...process.env };
|
|
afterEach(() => { process.env = { ...saved }; });
|
|
|
|
test('every job has a valid cron schedule and a unique name', () => {
|
|
const names = new Set();
|
|
for (const j of JOBS) {
|
|
expect(names.has(j.name)).toBe(false);
|
|
names.add(j.name);
|
|
expect(() => nextRun(j.cron)).not.toThrow();
|
|
}
|
|
});
|
|
|
|
test('a job is on unless its own flag or JOBS_ENABLED is false', () => {
|
|
delete process.env.WAITLIST_JOB_ENABLED;
|
|
delete process.env.JOBS_ENABLED;
|
|
expect(isEnabled(job('waitlist'))).toBe(true);
|
|
process.env.WAITLIST_JOB_ENABLED = 'false';
|
|
expect(isEnabled(job('waitlist'))).toBe(false);
|
|
process.env.WAITLIST_JOB_ENABLED = 'true';
|
|
process.env.JOBS_ENABLED = 'false';
|
|
expect(isEnabled(job('waitlist'))).toBe(false);
|
|
expect(isEnabled(job('temp-cleanup'))).toBe(false);
|
|
});
|
|
|
|
test('JOB_SCHEDULE_<NAME> overrides the schedule; an invalid one is ignored', () => {
|
|
process.env.JOB_SCHEDULE_ANALYTICS_ROLLUP = '30 5 * * *';
|
|
expect(cronFor(job('analytics-rollup'))).toBe('30 5 * * *');
|
|
process.env.JOB_SCHEDULE_ANALYTICS_ROLLUP = 'every tuesday';
|
|
const warn = jest.spyOn(console, 'warn').mockImplementation(() => {});
|
|
expect(cronFor(job('analytics-rollup'))).toBe(job('analytics-rollup').cron);
|
|
warn.mockRestore();
|
|
});
|
|
|
|
test('daily jobs fire at local (Johannesburg) time', () => {
|
|
// 07:00 SAST = 05:00 UTC
|
|
const next = nextRun('0 7 * * *', new Date('2026-11-04T06:00:00Z'));
|
|
expect(next.toISOString()).toBe('2026-11-05T05:00:00.000Z');
|
|
expect(nextRun('0 7 * * *', new Date('2026-11-04T04:00:00Z')).toISOString()).toBe('2026-11-04T05:00:00.000Z');
|
|
});
|
|
|
|
test('isNoop: quiet when nothing happened', () => {
|
|
expect(isNoop({ expired: 0, offers: 0 })).toBe(true);
|
|
expect(isNoop({ reminders: { sessions: 0, sent: 0 }, certificates: { issued: 0, sent: 0 } })).toBe(true);
|
|
expect(isNoop({ deleted: 0, errors: 0 })).toBe(true);
|
|
expect(isNoop(null)).toBe(true);
|
|
expect(isNoop({ sent: 3 })).toBe(false);
|
|
expect(isNoop({ file: 'backup.sql.gz' })).toBe(false);
|
|
});
|
|
});
|