Add tests and correct a lot of validation

This commit is contained in:
Jacob Wallraff
2023-01-09 17:51:44 -08:00
parent 4cd9500523
commit d02fa0bb67
2 changed files with 145 additions and 86 deletions
@@ -0,0 +1,43 @@
import { isValidCron } from "./cron"
describe("isValidCron", () => {
const valid = [
"0 0 * * *",
"15 * * * *",
"2,10 4,5 * * *",
"30 4-6 * * *",
"20/15 * * * *",
"30 5,17 * * *",
"30 5 * * 1,3",
"30 5 * * 2,4",
"28 */4 * * *",
"28 5,*/4 * * *",
"28 5,*/4,6 * * *",
"28 5,*/4,6-8 * * *",
"0 0 * * SUN",
"0 0 * * SUN-TUE",
"0 0 * * SUN-2",
"0 * * */FEB */TUE",
]
for (const cron of valid) {
it(`${cron} should be valid`, () => {
expect(isValidCron(cron)).toBe(true)
})
}
const invalid = [
"0 0 * *",
"0 0 * * BUN",
"0 0 * SUN JAN",
"0 0 * * FRI-TUE",
"0 12-4 * * *",
"0 */0 * * *"
]
for (const cron of invalid) {
it(`${cron} should be invalid`, () => {
expect(isValidCron(cron)).toBe(false)
})
}
})
@@ -1,95 +1,111 @@
const MONTHS = {
jan: 1,
feb: 2,
mar: 3,
apr: 4,
may: 5,
jun: 6,
jul: 7,
aug: 8,
sep: 9,
oct: 10,
nov: 11,
dec: 12,
jan: 1,
feb: 2,
mar: 3,
apr: 4,
may: 5,
jun: 6,
jul: 7,
aug: 8,
sep: 9,
oct: 10,
nov: 11,
dec: 12,
}
const DAYS = {
sun: 0,
mon: 1,
tue: 2,
wed: 3,
thu: 4,
fri: 5,
sat: 6,
}
// TODO: make this parseCron
export function isValidCron(cron: string): boolean {
// https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#schedule
const parts = cron.split(" ")
if (parts.length != 5) {
return false
}
const DAYS = {
sun: 0,
mon: 1,
tue: 2,
wed: 3,
thu: 4,
fri: 5,
sat: 6,
const [minutes, hours, dom, months, dow] = parts
return (
validateRange(minutes, { min: 0, max: 59 }) &&
validateRange(hours, { min: 0, max: 23 }) &&
validateRange(dom, { min: 1, max: 31 }) &&
validateRange(months, { min: 1, max: 12, names: MONTHS }) &&
validateRange(dow, { min: 0, max: 6, names: DAYS })
)
}
type Range = {
min: number
max: number
names?: Record<string, number>
}
function validateRange(
value: string,
range: Range,
allowSeparators = true
): boolean {
if (range.names && range.names[value.toLowerCase()] !== undefined) {
return true
}
// TODO: make this parseCron
export function isValidCron(cron: string): boolean {
// https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#schedule
const parts = cron.split(" ")
if (parts.length != 5) {
if (value === "*") {
return true
}
if (value.includes(",")) {
if (!allowSeparators) {
return false
}
// Allow separators
return value.split(",").every((v) => validateRange(v, range))
}
if (value.includes("/")) {
if (!allowSeparators) {
return false
}
const [start, step, ...rest] = value.split("/")
// Supports */TUE and similar, which nees to be verified with the go cron library
const stepNumber = convertToNumber(range, step)
if (rest.length > 0 || stepNumber <= 0) {
return false
}
const [minutes, hours, dom, months, dow] = parts
return (
validateRange(minutes, { min: 0, max: 59 }) &&
validateRange(hours, { min: 0, max: 23 }) &&
validateRange(dom, { min: 1, max: 31 }) &&
validateRange(months, { min: 1, max: 12, names: MONTHS }) &&
validateRange(dow, { min: 0, max: 6, names: DAYS })
validateRange(start, range, false) && validateRange(step, range, false)
)
}
type Range = {
min: number
max: number
names?: Record<string, number>
if (value.includes("-")) {
const [start, end] = value.split("-")
// Convert name to integers so we can make sure end >= start
const startNumber = convertToNumber(range, start)
const endNumber = convertToNumber(range, end)
return (
validateRange(start, range, false) && validateRange(end, range, false) && endNumber >= startNumber
)
}
function validateRange(
value: string,
range: Range,
allowSeparators = true
): boolean {
if (range.names && range.names[value.toLowerCase()] !== undefined) {
return true
}
if (value === "*") {
return true
}
if (value.includes(",")) {
if (!allowSeparators) {
return false
}
return value.split(",").every((v) => validateRange(v, range, false))
}
if (value.includes("/")) {
if (!allowSeparators) {
return false
}
const [start, step, ...rest] = value.split("/")
if (rest.length > 0) {
return false
}
return (
validateRange(start, range, false) && validateRange(step, range, false)
)
}
if (value.includes("-")) {
const [start, end] = value.split("-")
return (
validateRange(start, range, false) && validateRange(end, range, false)
)
}
const number = parseInt(value, 10)
return !isNaN(number) && number >= range.min && number <= range.max
}
const number = +value
return !isNaN(number) && number >= range.min && number <= range.max
}
function convertToNumber(range: Range, value: string): number {
if (range.names && range.names[value.toLowerCase()] !== undefined) {
return +range.names[value.toLowerCase()]
}
else {
return +value
}
}