mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-12 19:57:37 +00:00
fix(scheduler): reject 6-field cron in Scheduled Operations (#1435)
* fix(scheduler): reject 6-field cron in Scheduled Operations Create and edit validation parsed cron with cron-parser, which accepts both 5- and 6-field expressions, while the form, presets, and docs all describe a 5-field cron. Because the scheduler ticks once per minute, a leading seconds field can never improve precision, so a 6-field expression was silently accepted but never honored on its stated schedule. Add a field-count guard on both sides: the API rejects 6-field input at create and edit with a clear message, and the form surfaces the same error inline and disables save. Cron nicknames such as @daily still pass. Document the five-field requirement in the cron reference. * chore: merge main into scheduled cron validation * fix: avoid logging policy bypass actor in debug output
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { getCronFieldError } from './scheduling';
|
||||
|
||||
describe('getCronFieldError', () => {
|
||||
it('accepts a standard 5-field expression', () => {
|
||||
expect(getCronFieldError('0 3 * * *')).toBeNull();
|
||||
});
|
||||
|
||||
it('rejects a 6-field expression with a seconds field', () => {
|
||||
expect(getCronFieldError('30 0 3 * * *')).toMatch(/5 fields/);
|
||||
});
|
||||
|
||||
it('rejects expressions with extra fields beyond six', () => {
|
||||
expect(getCronFieldError('0 0 3 * * * 2026')).toMatch(/5 fields/);
|
||||
});
|
||||
|
||||
it('accepts cron nicknames such as @daily', () => {
|
||||
expect(getCronFieldError('@daily')).toBeNull();
|
||||
});
|
||||
|
||||
it('ignores empty or whitespace-only input', () => {
|
||||
expect(getCronFieldError('')).toBeNull();
|
||||
expect(getCronFieldError(' ')).toBeNull();
|
||||
});
|
||||
|
||||
it('tolerates irregular spacing between five fields', () => {
|
||||
expect(getCronFieldError(' 0 3 * * * ')).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -8,6 +8,20 @@ export function getCronDescription(expression: string): string {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reject cron expressions with a leading seconds field (6 or more fields). The
|
||||
* scheduler is minute-granular, so the seconds field could never be honored.
|
||||
* Nicknames like `@daily` (one token) pass. Returns an error message or null
|
||||
* when the field count is acceptable.
|
||||
*/
|
||||
export function getCronFieldError(expression: string): string | null {
|
||||
const trimmed = expression.trim();
|
||||
if (trimmed && trimmed.split(/\s+/).length >= 6) {
|
||||
return 'Use 5 fields (minute hour day month weekday). The seconds field is not supported.';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function formatTimestamp(ts: number | null): string {
|
||||
if (ts == null) return '-';
|
||||
return new Date(ts).toLocaleString();
|
||||
|
||||
Reference in New Issue
Block a user