124 lines
4.0 KiB
TypeScript
124 lines
4.0 KiB
TypeScript
import { describe, it, expect } from 'vitest'
|
|
import { describeCronFrequency, validateCronExpression, generateCloneName } from '../cron-utils'
|
|
|
|
describe('describeCronFrequency', () => {
|
|
it('returns "every minute" for * * * * *', () => {
|
|
expect(describeCronFrequency('* * * * *')).toBe('every minute')
|
|
})
|
|
|
|
it('returns "every 5m" for */5 * * * *', () => {
|
|
expect(describeCronFrequency('*/5 * * * *')).toBe('every 5m')
|
|
})
|
|
|
|
it('returns "hourly at :00" for 0 * * * *', () => {
|
|
expect(describeCronFrequency('0 * * * *')).toBe('hourly at :00')
|
|
})
|
|
|
|
it('returns "daily at 14:30" for 30 14 * * *', () => {
|
|
expect(describeCronFrequency('30 14 * * *')).toBe('daily at 14:30')
|
|
})
|
|
|
|
it('returns "monthly" for 0 0 1 * *', () => {
|
|
expect(describeCronFrequency('0 0 1 * *')).toBe('monthly')
|
|
})
|
|
|
|
it('returns time with select days for 0 0 * * 1 (specific hour+dow)', () => {
|
|
expect(describeCronFrequency('0 0 * * 1')).toBe('00:00 (select days)')
|
|
})
|
|
|
|
it('returns "weekly" when dow is set and no earlier branch matches', () => {
|
|
expect(describeCronFrequency('1-30 * * * 1')).toBe('weekly')
|
|
})
|
|
|
|
it('falls through to raw schedule for */10 */2 * * *', () => {
|
|
expect(describeCronFrequency('*/10 */2 * * *')).toBe('*/10 */2 * * *')
|
|
})
|
|
|
|
it('returns "every 2h" for 0 */2 * * *', () => {
|
|
expect(describeCronFrequency('0 */2 * * *')).toBe('every 2h')
|
|
})
|
|
|
|
it('returns the raw schedule for non-standard expressions', () => {
|
|
expect(describeCronFrequency('invalid')).toBe('invalid')
|
|
})
|
|
|
|
it('strips trailing parenthetical comments', () => {
|
|
expect(describeCronFrequency('*/5 * * * * (some note)')).toBe('every 5m')
|
|
})
|
|
|
|
it('handles time with select days', () => {
|
|
expect(describeCronFrequency('0 9 * * 1,3,5')).toBe('09:00 (select days)')
|
|
})
|
|
})
|
|
|
|
describe('validateCronExpression', () => {
|
|
it('returns null for valid expression * * * * *', () => {
|
|
expect(validateCronExpression('* * * * *')).toBeNull()
|
|
})
|
|
|
|
it('returns null for valid step expression */5 * * * *', () => {
|
|
expect(validateCronExpression('*/5 * * * *')).toBeNull()
|
|
})
|
|
|
|
it('returns error for minute > 59', () => {
|
|
const result = validateCronExpression('60 * * * *')
|
|
expect(result).not.toBeNull()
|
|
expect(result).toContain('minute')
|
|
})
|
|
|
|
it('returns error for hour > 23', () => {
|
|
const result = validateCronExpression('* 25 * * *')
|
|
expect(result).not.toBeNull()
|
|
expect(result).toContain('hour')
|
|
})
|
|
|
|
it('returns error for wrong number of fields', () => {
|
|
const result = validateCronExpression('invalid')
|
|
expect(result).not.toBeNull()
|
|
expect(result).toContain('Expected 5 fields')
|
|
})
|
|
|
|
it('returns error for empty string', () => {
|
|
const result = validateCronExpression('')
|
|
expect(result).not.toBeNull()
|
|
})
|
|
|
|
it('returns null for valid range expression 1-5 * * * *', () => {
|
|
expect(validateCronExpression('1-5 * * * *')).toBeNull()
|
|
})
|
|
|
|
it('returns null for valid comma-separated values', () => {
|
|
expect(validateCronExpression('0,15,30,45 * * * *')).toBeNull()
|
|
})
|
|
|
|
it('returns error for day-of-month 0', () => {
|
|
const result = validateCronExpression('0 0 0 * *')
|
|
expect(result).not.toBeNull()
|
|
expect(result).toContain('day of month')
|
|
})
|
|
|
|
it('returns error for month > 12', () => {
|
|
const result = validateCronExpression('0 0 1 13 *')
|
|
expect(result).not.toBeNull()
|
|
expect(result).toContain('month')
|
|
})
|
|
})
|
|
|
|
describe('generateCloneName', () => {
|
|
it('returns "Name (copy)" when no conflicts', () => {
|
|
expect(generateCloneName('My Job', [])).toBe('My Job (copy)')
|
|
})
|
|
|
|
it('returns "Name (copy 2)" when "(copy)" exists', () => {
|
|
expect(generateCloneName('My Job', ['My Job (copy)'])).toBe('My Job (copy 2)')
|
|
})
|
|
|
|
it('returns "Name (copy 3)" when "(copy)" and "(copy 2)" exist', () => {
|
|
expect(generateCloneName('My Job', ['My Job (copy)', 'My Job (copy 2)'])).toBe('My Job (copy 3)')
|
|
})
|
|
|
|
it('is case-insensitive when checking existing names', () => {
|
|
expect(generateCloneName('My Job', ['my job (copy)'])).toBe('My Job (copy 2)')
|
|
})
|
|
})
|