Add constants and re-add tests:

This commit is contained in:
Jacob Wallraff
2023-01-23 14:18:05 -08:00
parent 245277c18d
commit 08273a85d8
2 changed files with 184 additions and 57 deletions
@@ -0,0 +1,32 @@
// Constants for parsing and validating cron expressions
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,
}
const DAYS = {
sun: 0,
mon: 1,
tue: 2,
wed: 3,
thu: 4,
fri: 5,
sat: 6,
}
export const MINUTE_RANGE = { min: 0, max: 59 }
export const HOUR_RANGE = { min: 0, max: 23 }
export const DOM_RANGE = { min: 1, max: 31 }
export const MONTH_RANGE = { min: 1, max: 12, names: MONTHS }
export const DOW_RANGE = { min: 0, max: 6, names: DAYS }
@@ -1,63 +1,158 @@
import { isValidCron } from "./cron"
import { isValidCron, getSchedule, getSentence } from "./cron"
describe("isValidCron", () => {
const valid = [
["0 0 * * *", "every day at midnight"],
["0 000 001 * *", "accepts leading zeros"],
["15 * * * *", "accepts numbers in range"],
["2,10 4,5 * * *", "accepts comma separated values"],
["30 4-6 * * *", "accepts range"],
["0 4-4 * * *", "accepts range with two equal values"],
["20/15 * * * *", "accepts step with numerical values"],
["30 5,17 * * *", "accepts numbers and ranges"],
["28 */4 * * *", "accepts step with * and numerical value"],
["28 5,*/4 * * *", "accepts comma separated value with step"],
["28 5,*/4,6-8 * * *", "accepts comma separated value with step and range"],
["0 0 * * SUN", "accepts day of week short name"],
["0 0 * * SUN-TUE", "accepts day of week short name range"],
["0 0 * * SUN-2", "accepts day of week range combined with number"],
["0 2-4/5 * * *", "accepts range with step"],
["0 0 * * *", "accepts multiple spaces"],
]
describe("cron", () => {
describe("valid cron", () => {
const valid = [
["0 0 * * *", "every day at midnight"],
["0 000 001 * *", "accepts leading zeros"],
["15 * * * *", "accepts numbers in range"],
["2,10 4,5 * * *", "accepts comma separated values"],
["30 4-6 * * *", "accepts range"],
["0 4-4 * * *", "accepts range with two equal values"],
["20/15 * * * *", "accepts step with numerical values"],
["30 5,17 * * *", "accepts numbers and ranges"],
["28 */4 * * *", "accepts step with * and numerical value"],
["28 5,*/4 * * *", "accepts comma separated value with step"],
["28 5,*/4,6-8 * * *", "accepts comma separated value with step and range"],
["0 0 * * SUN", "accepts day of week short name"],
["0 0 * * SUN-TUE", "accepts day of week short name range"],
["0 0 * * SUN-2", "accepts day of week range combined with number"],
["0 2-4/5 * * *", "accepts range with step"],
["0 0 * * *", "accepts multiple spaces"],
]
for (const [cron, reason] of valid) {
it(`${cron} should be valid: ${reason}`, () => {
expect(isValidCron(cron)).toBe(true)
for (const [cron, reason] of valid) {
it(`${cron} should be valid: ${reason}`, () => {
expect(isValidCron(cron)).toBe(true)
})
}
})
describe("invalid cron", () => {
const invalid = [
["0 0 * *", "too few parts"],
["0 0 * * * * *", "too many parts"],
["0 -1 * * *", "should not accept negative numbers"],
["0 1- * * *", "should not accept trailing -"],
["0 /1 * * *", "should not accept leading / (empty value)"],
["0 1/ * * *", "should not accept trailing / (empty value)"],
["0 ,1 * * *", "should not accept leading , (empty value)"],
["0 1, * * *", "should not accept trailing , (empty value)"],
["0 5--5 * * *", "should not accept multiple -"],
["0 *//5 * * *", "should not accept multiple /"],
["0 */* * * *", "step start and size may not both be *"],
["0 5/* * * *", "steps size may not be *"],
["0 *-4 5-* *-* *", "range may not contain *"],
["0 *//5 * * *", "should not accept multiple /"],
["0 ,, * * *", "should not accept multiple ,"],
[", , , , ,", "comma is not a valid part"],
["0 ** * * *", "should not accept multiple *"],
["0 0 * * BUN", "invalid short name"],
["0 0 * SUN JAN", "short name in incorrect position"],
["0 0 * * FRI-TUE", "should not accept short name range with start > end"],
["0 12-4 * * *", "should not accept nuerical range with start > end"],
["0 */0 * * *", "step size may not be 0"],
["0 2/4-5 * * *", "step size may not be a range"],
["0 2-4-6 * * *", "range may not contain multiple -"],
["0 2/4/6 * * *", "step may not contain multiple /"],
["0 * * */FEB */TUE", "step size may not be a short name"],
]
for (const [cron, reason] of invalid) {
it(`${cron} should be invalid: ${reason}`, () => {
expect(isValidCron(cron)).toBe(false)
})
}
})
describe("getSchedule", () => {
const testCases = [
{
cron: "0 1 2 3 4",
expected: {
minutes: [0],
hours: [1],
dom: [2],
months: [3],
dow: [4],
}
},
{
cron: "2,4 * * * *",
expected: {
minutes: [2, 4],
hours: undefined,
dom: undefined,
months: undefined,
dow: undefined,
}
},
{
cron: "2-4 * * * *",
expected: {
minutes: [2, 3, 4],
hours: undefined,
dom: undefined,
months: undefined,
dow: undefined,
}
},
{
cron: "*/15 * * * *",
expected: {
minutes: [0, 15, 30, 45],
hours: undefined,
dom: undefined,
months: undefined,
dow: undefined,
}
},
{
cron: "10/15 * * * *",
expected: {
minutes: [10, 25, 40, 55],
hours: undefined,
dom: undefined,
months: undefined,
dow: undefined,
}
},
{
cron: "5,*/20 * * * *",
expected: {
minutes: [0, 5, 20, 40],
hours: undefined,
dom: undefined,
months: undefined,
dow: undefined,
}
},
{
cron: "* * * JAN SUN",
expected: {
minutes: undefined,
hours: undefined,
dom: undefined,
months: [1],
dow: [0],
}
},
]
for (const testCase of testCases) {
it(`getSchedule '${testCase.cron}'`, () => {
expect(getSchedule(testCase.cron)).toEqual(testCase.expected)
})
}
})
describe("getSentence", () => {
it(`Produces a sentence for valid cron`, () => {
expect(getSentence("0 * * * *")).toEqual("Every hour")
})
}
const invalid = [
["0 0 * *", "too few parts"],
["0 0 * * * * *", "too many parts"],
["0 -1 * * *", "should not accept negative numbers"],
["0 1- * * *", "should not accept trailing -"],
["0 /1 * * *", "should not accept leading / (empty value)"],
["0 1/ * * *", "should not accept trailing / (empty value)"],
["0 ,1 * * *", "should not accept leading , (empty value)"],
["0 1, * * *", "should not accept trailing , (empty value)"],
["0 5--5 * * *", "should not accept multiple -"],
["0 *//5 * * *", "should not accept multiple /"],
["0 */* * * *", "step start and size may not both be *"],
["0 5/* * * *", "steps size may not be *"],
["0 *-4 5-* *-* *", "range may not contain *"],
["0 *//5 * * *", "should not accept multiple /"],
["0 ,, * * *", "should not accept multiple ,"],
[", , , , ,", "comma is not a valid part"],
["0 ** * * *", "should not accept multiple *"],
["0 0 * * BUN", "invalid short name"],
["0 0 * SUN JAN", "short name in incorrect position"],
["0 0 * * FRI-TUE", "should not accept short name range with start > end"],
["0 12-4 * * *", "should not accept nuerical range with start > end"],
["0 */0 * * *", "step size may not be 0"],
["0 2/4-5 * * *", "step size may not be a range"],
["0 2-4-6 * * *", "range may not contain multiple -"],
["0 2/4/6 * * *", "step may not contain multiple /"],
["0 * * */FEB */TUE", "step size may not be a short name"],
]
for (const [cron, reason] of invalid) {
it(`${cron} should be invalid: ${reason}`, () => {
expect(isValidCron(cron)).toBe(false)
it(`Returns nothing for invalid cron`, () => {
expect(getSentence("* * * * * *")).toBeUndefined()
})
}
})
})