Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: GuillaumeRochat/cron-validator
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: master
Choose a base ref
...
head repository: lobaro/cron-validator
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref
Can’t automatically merge. Don’t worry, you can still create the pull request.
  • 17 commits
  • 3 files changed
  • 2 contributors

Commits on Jul 19, 2023

  1. Update README.md

    Kniggebrot authored Jul 19, 2023
    Copy the full SHA
    5285532 View commit details
  2. Tests for H acceptance

    From Jenkins CRONs:
    Is replaced with a pseudo-random number, to allow load spreading for regular jobs (builds etc.)
    That number stays consistent as long as the Jenkins job title stays the same.
    
    Plan is to accept H (if the right option is set), and validate its ranges.
    Maybe even multiple H in one field?
    
    More infos here:
    https://github.com/jenkinsci/jenkins/blob/master/core/src/main/resources/hudson/triggers/TimerTrigger/help-spec.jelly
    Kniggebrot committed Jul 19, 2023
    Copy the full SHA
    8923132 View commit details

Commits on Jul 21, 2023

  1. More H tests

    Should H be allowed as a step value?
    Kniggebrot committed Jul 21, 2023
    Copy the full SHA
    1bcdc69 View commit details

Commits on Jul 24, 2023

  1. H test improvements

    Add tests with H in ranges -> invalid CRONs
    Add tests with H as step size -> invalid CRONs
    Fix tests with (in)valid H ranges
    Remove test with wrong step value; currently not validated
    Kniggebrot committed Jul 24, 2023
    Copy the full SHA
    8894ec3 View commit details
  2. Add H validation

    All fields are checked for 'H' present in it if "allowHashed" is set in options.
    If there is one, it is split into its range and accepted if its range is ok,
    or checked if it's only 'H'.
    Kniggebrot committed Jul 24, 2023
    Copy the full SHA
    02c5d0b View commit details

Commits on Jul 25, 2023

  1. Tests for L acceptance

    L can be used in the DOM or DOW field.
    In DOM it means the last day of the month, with an offset if set (to second-last, ... day
    In DOW it means either Saturday or the last x-day of the month, e. g. 2L means the last Monday of the month.
    Kniggebrot committed Jul 25, 2023
    Copy the full SHA
    43e7c47 View commit details

Commits on Jul 26, 2023

  1. Add check for reversed range in H

    Also verify the range used in H starts from the lower value of the range.
    Kniggebrot committed Jul 26, 2023
    Copy the full SHA
    c6b48cd View commit details
  2. More tests for L

    Kniggebrot committed Jul 26, 2023
    Copy the full SHA
    cfc3e8e View commit details
  3. Fix test for LW

    Forgot to pass allowWeekday to hasValidDays
    Kniggebrot committed Jul 26, 2023
    Copy the full SHA
    a5991c7 View commit details
  4. Tests for W

    W can be used to trigger on the next working week day (MON-FRI) to the day of month specified in front of it.
    Kniggebrot committed Jul 26, 2023
    Copy the full SHA
    87a49e6 View commit details
  5. W validation

    Try to validate W if allowWeekday is set, by ensuring it is not in a range and the DOM in front of it is valid.
    Kniggebrot committed Jul 26, 2023
    Copy the full SHA
    d082dd9 View commit details

Commits on Jul 27, 2023

  1. Update README.md

    Kniggebrot committed Jul 27, 2023
    Copy the full SHA
    2076f65 View commit details

Commits on Jul 31, 2023

  1. Merge pull request #1 from lobaro/hwl-validator

    H, W, and L validator
    
    Allows validation of CRONs using H, W and L in cron-validator:
    
    - H will be a pseudo-random number for a field, linked to the project name or something similar. It allows easier load balancing. See Jenkins doc
    - W can be written after a day of month to ensure the CRON is executed on the closest working week day (Mon-Fri).
    - L can either mean the last day of month/day of week, or x days before that if used with an offset (L-x), or the last working week day of a month if used with W (LW).
    Kniggebrot authored Jul 31, 2023
    Copy the full SHA
    e7336c6 View commit details

Commits on Aug 11, 2023

  1. Copy the full SHA
    c02293c View commit details
  2. Merge pull request #2 from lobaro/hwl-validator

    Add test for incomplete H range
    Kniggebrot authored Aug 11, 2023
    Copy the full SHA
    6d20cee View commit details
  3. Update README.md

    Kniggebrot committed Aug 11, 2023
    Copy the full SHA
    1588a31 View commit details

Commits on Aug 14, 2023

  1. Add tests for LW in iterators

    L and W flags cannot be used in iterators/as step values.
    Add tests for wrong usage of these in CRONs, and a check for these in validateForRange.
    Kniggebrot committed Aug 14, 2023
    Copy the full SHA
    7702970 View commit details
Showing with 320 additions and 27 deletions.
  1. +1 −0 README.md
  2. +244 −0 src/index.test.ts
  3. +75 −27 src/index.ts
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -88,6 +88,7 @@ cron.isValidCron('* * * * 7', { allowSevenAsSunday: true });
- [x] Support blank day notation with `?` symbol.
- [x] Support both 0-6 and 1-7 ranges for weekdays.
- [ ] ~~Have an explain mode returning the fragments in error.~~
- [x] Support 'H', 'L' and 'W' usage.

## Motivations

244 changes: 244 additions & 0 deletions src/index.test.ts
Original file line number Diff line number Diff line change
@@ -479,4 +479,248 @@ describe('validate', () => {
const validWithAliases = isValidCron('* * ? * ?', { alias: true, allowBlankDay: true })
expect(validWithAliases).toBeFalsy()
})

it('should not accept H if allowHashed is not set', () => {
const valid = isValidCron('H * * * *')
expect(valid).toBeFalsy()

const validWithSeconds = isValidCron('* * H * * *', {seconds: true} )
expect(validWithSeconds).toBeFalsy()

const validWithAlias = isValidCron('* * H * THU', {alias: true} )
expect(validWithAlias).toBeFalsy()
})

it('should accept H if allowHashed is set', () => {
const valid = isValidCron('H * * * *', {allowHashed: true})
expect(valid).toBeTruthy()

const validWithSeconds = isValidCron('* * H * * *', {allowHashed: true, seconds: true} )
expect(validWithSeconds).toBeTruthy()

const validWithAlias = isValidCron('* * H * THU', {allowHashed: true, alias: true} )
expect(validWithAlias).toBeTruthy()
})

it('should not accept H with ranges if allowHashed is not set', () => {
const valid = isValidCron('H(0-20) * * * *')
expect(valid).toBeFalsy()

const validWithSeconds = isValidCron('* * H(10-15) * * *', {seconds: true} )
expect(validWithSeconds).toBeFalsy()

const validWithAlias = isValidCron('* * H(1-8) * THU', {alias: true} )
expect(validWithAlias).toBeFalsy()

const validWithRange = isValidCron('* * H(1-8)-10 * *')
expect(validWithRange).toBeFalsy()
})

it('should accept H with ranges if allowHashed is set', () => {
const valid = isValidCron('H(0-20) * * * *', {allowHashed: true})
expect(valid).toBeTruthy()

const validWithSeconds = isValidCron('* * H(10-15) * * *', {allowHashed: true, seconds: true} )
expect(validWithSeconds).toBeTruthy()

const validWithAlias = isValidCron('* * H(1-8) * THU', {allowHashed: true, alias: true} )
expect(validWithAlias).toBeTruthy()

const validWithNoRange = isValidCron('* * H( * THU', {allowHashed: true, alias: true} )
expect(validWithNoRange).toBeFalsy()

const validWithWrongRange = isValidCron('* * H(1-12 * THU', {allowHashed: true, alias: true} )
expect(validWithWrongRange).toBeFalsy()
})

it('should not accept H with invalid ranges even if allowHashed is set', () => {
const valid = isValidCron('H(0-61) * * * *', {allowHashed: true})
expect(valid).toBeFalsy()

const validWithSeconds = isValidCron('* * H(0-25) * * *', {allowHashed: true, seconds: true} )
expect(validWithSeconds).toBeFalsy()

const validWithAlias = isValidCron('* * H(0-8) * THU', {allowHashed: true, alias: true} )
expect(validWithAlias).toBeFalsy()

const validWithWrongDOM = isValidCron('* * H(1-32) * THU', {allowHashed: true, alias: true} )
expect(validWithWrongDOM).toBeFalsy()

const validWithRange = isValidCron('H(0-30)-50 * * * *', {allowHashed: true})
expect(validWithRange).toBeFalsy()

const validWithReverse = isValidCron('H(30-0) * * * *', {allowHashed: true})
expect(validWithReverse).toBeFalsy()
})

it('should accept H with iterators if allowHashed is set', () => {
const valid = isValidCron('H/8 * * * *', {allowHashed: true})
expect(valid).toBeTruthy()

const validWithSeconds = isValidCron('* * H(10-15)/2 * * *', {allowHashed: true, seconds: true} )
expect(validWithSeconds).toBeTruthy()

const validWithAlias = isValidCron('* * H/4 * THU', {allowHashed: true, alias: true} )
expect(validWithAlias).toBeTruthy()

// Steps are always valid if they're integers above 0. Maybe behaviour changes in the future
//const validWithWrongIterator = isValidCron('* * H/32 * THU', {allowHashed: true, alias: true} )
//expect(validWithWrongIterator).toBeFalsy()

// H cannot be a valid step value
const validWithHstep = isValidCron('* * 4/H * *', {allowHashed: true} )
expect(validWithHstep).toBeFalsy()

const validWithHstep2 = isValidCron('* * 4/H(1-5) * *', {allowHashed: true} )
expect(validWithHstep2).toBeFalsy()
})

it('should not accept H in a range if allowHashed is set', () => {
const valid = isValidCron('H-8 * * * *', {allowHashed: true})
expect(valid).toBeFalsy()

const validWithSeconds = isValidCron('* * H-2 * * *', {allowHashed: true, seconds: true} )
expect(validWithSeconds).toBeFalsy()

const validWithAlias = isValidCron('* * H-4 * THU', {allowHashed: true, alias: true} )
expect(validWithAlias).toBeFalsy()
})

// TODO: Decide if multiple H in one field should be supported? Would only work properly if their ranges were starting at different points

it('should not accept L if allowLast is not set', () => {
const valid = isValidCron('* * L * *')
expect(valid).toBeFalsy()

const validWithSeconds = isValidCron('* * * L * *', {seconds: true})
expect(validWithSeconds).toBeFalsy()

const validWithAlias = isValidCron('* * L * WED', {alias: true})
expect(validWithAlias).toBeFalsy()

const validWithWrongField = isValidCron('* L * * *')
expect(validWithWrongField).toBeFalsy()
})

it('should accept L in DOM and DOW if allowLast is set', () => {
const validDOM = isValidCron('* * L * *', {allowLast: true})
expect(validDOM).toBeTruthy()

const validLW = isValidCron('* * LW * *', {allowLast: true, allowWeekday: true})
expect(validLW).toBeTruthy()

const validLWDOW = isValidCron('* * * * LW', {allowLast: true, allowWeekday: true})
expect(validLWDOW).toBeFalsy()

const validDOMAlias = isValidCron('* * L * WED', {allowLast: true, alias:true})
expect(validDOMAlias).toBeTruthy()

const validDOMsec = isValidCron('* * * L * THU', {allowLast: true, alias: true, seconds: true})
expect(validDOMsec).toBeTruthy()

const validDOMOffset = isValidCron('* * L-2 * *', {allowLast: true})
expect(validDOMOffset).toBeTruthy()

const validDOW = isValidCron('* * * * L', {allowLast: true})
expect(validDOW).toBeTruthy()

const validDOWDay = isValidCron('* * * * 2L', {allowLast: true})
expect(validDOWDay).toBeTruthy()

const validWithWrongField = isValidCron('* L * * *', {allowLast: true})
expect(validWithWrongField).toBeFalsy()

const validWithSeconds = isValidCron('L * * * * *', {allowLast: true, seconds: true})
expect(validWithSeconds).toBeFalsy()
})

it('should not accept L in invalid places or with invalid offsets/values', () => {
const validDOM = isValidCron('* L L * *', {allowLast: true})
expect(validDOM).toBeFalsy()

const validDOMOffset = isValidCron('* L-2 L-3 * *', {allowLast: true})
expect(validDOMOffset).toBeFalsy()

const validDOW = isValidCron('* * * * L *', {allowLast: true, seconds: true})
expect(validDOW).toBeFalsy()

const validDOWDay = isValidCron('* * * * 2L-2', {allowLast: true})
expect(validDOWDay).toBeFalsy()

const validDOMDay = isValidCron('* * * 3L * 2L', {allowLast: true, seconds: true})
expect(validDOMDay).toBeFalsy()
})

it('should not accept W if allowWeekdays is not set', () => {
const validDOM = isValidCron('* * 1W * *')
expect(validDOM).toBeFalsy()

const validDOW = isValidCron('* * * 5W * *', {seconds: true})
expect(validDOW).toBeFalsy()

const validDOWDay = isValidCron('* * 2W * THU', {alias: true})
expect(validDOWDay).toBeFalsy()

const validDOMDay = isValidCron('* * * 3W * 2L', {allowLast: true, seconds: true})
expect(validDOMDay).toBeFalsy()
})

it('should accept W if allowWeekdays is set', () => {
const validDOM = isValidCron('* * 1W * *', {allowWeekday: true})
expect(validDOM).toBeTruthy()

const validDOW = isValidCron('* * * 5W * *', {allowWeekday: true, seconds: true})
expect(validDOW).toBeTruthy()

const validDOWDay = isValidCron('* * 2W * THU', {allowWeekday: true, alias: true})
expect(validDOWDay).toBeTruthy()

const validDOMDay = isValidCron('* * * 3W * *', {allowWeekday: true, seconds: true})
expect(validDOMDay).toBeTruthy()
})

it('should not accept W in invalid fields or combinations', () => {
const validDOM = isValidCron('* * * 1W *', {allowWeekday: true})
expect(validDOM).toBeFalsy()

const validDOW = isValidCron('* * * 32W * *', {allowWeekday: true, seconds: true})
expect(validDOW).toBeFalsy()

const validDOWDay = isValidCron('* * 2W-5 * THU', {allowWeekday: true, alias: true})
expect(validDOWDay).toBeFalsy()

const validDOWDay2 = isValidCron('* * 2-5W * THU', {allowWeekday: true, alias: true})
expect(validDOWDay2).toBeFalsy()

const validDOMDay = isValidCron('* * * * * 2W', {allowWeekday: true, seconds: true})
expect(validDOMDay).toBeFalsy()
})

it('should not accept L in iterators', () => {
const validDOM = isValidCron('* * L/5 * *', {allowLast: true})
expect(validDOM).toBeFalsy()

const validDOMOffset = isValidCron('* * 3/L * *', {allowLast: true})
expect(validDOMOffset).toBeFalsy()

const validDOW = isValidCron('* * * * * L/2', {allowLast: true, seconds: true})
expect(validDOW).toBeFalsy()

const validDOW2 = isValidCron('* * * * * 2/L', {allowLast: true, seconds: true})
expect(validDOW2).toBeFalsy()
})

it('should not accept W in iterators', () => {
const validDOM = isValidCron('* * 1W/5 * *')
expect(validDOM).toBeFalsy()

const validDOW = isValidCron('* * * 2/5W * *', {seconds: true})
expect(validDOW).toBeFalsy()

const validDOWDay = isValidCron('* * 2W/5 * THU', {alias: true})
expect(validDOWDay).toBeFalsy()

const validDOMDay = isValidCron('* * * 3/3W * 2L', {allowLast: true, seconds: true})
expect(validDOMDay).toBeFalsy()
})
})
Loading