All files / services/scheduler-service/src/__tests__ scheduler.test.js

0% Statements 0/72
0% Branches 0/15
0% Functions 0/17
0% Lines 0/66

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129                                                                                                                                                                                                                                                                 
const Schedule = require('../models/Schedule');
 
const mockSchedule = {
  organizationId: 'org-123',
  targetId: '507f1f77bcf86cd799439011',
  name: 'Test schedule',
  cronExpression: '0 */6 * * *',
  timezone: 'UTC',
};
 
describe('Schedule Model', () => {
  it('should create a valid schedule', () => {
    const schedule = new Schedule(mockSchedule);
    expect(schedule.organizationId).toBe('org-123');
    expect(schedule.cronExpression).toBe('0 */6 * * *');
    expect(schedule.isActive).toBe(true);
    expect(schedule.runCount).toBe(0);
  });
 
  it('should reject missing organizationId', () => {
    const schedule = new Schedule({ targetId: 'abc', cronExpression: '0 * * * *' });
    const error = schedule.validateSync();
    expect(error.errors.organizationId).toBeDefined();
  });
 
  it('should reject missing cronExpression', () => {
    const schedule = new Schedule({ organizationId: 'org-1', targetId: 'abc' });
    const error = schedule.validateSync();
    expect(error.errors.cronExpression).toBeDefined();
  });
 
  it('should reject invalid lastRunStatus', () => {
    const schedule = new Schedule({
      ...mockSchedule,
      lastRunStatus: 'invalid',
    });
    const error = schedule.validateSync();
    expect(error).toBeDefined();
    expect(error.errors['lastRunStatus']).toBeDefined();
  });
 
  it('should accept valid lastRunStatus values', () => {
    const success = new Schedule({ ...mockSchedule, lastRunStatus: 'success' });
    const failed = new Schedule({ ...mockSchedule, lastRunStatus: 'failed' });
    const pending = new Schedule({ ...mockSchedule, lastRunStatus: 'pending' });
    expect(success.validateSync()).toBeUndefined();
    expect(failed.validateSync()).toBeUndefined();
    expect(pending.validateSync()).toBeUndefined();
  });
 
  it('should default isActive to true', () => {
    const schedule = new Schedule(mockSchedule);
    expect(schedule.isActive).toBe(true);
  });
 
  it('should default runCount to 0', () => {
    const schedule = new Schedule(mockSchedule);
    expect(schedule.runCount).toBe(0);
  });
 
  it('should default timezone to UTC', () => {
    const schedule = new Schedule(mockSchedule);
    expect(schedule.timezone).toBe('UTC');
  });
 
  it('should default type to crawler', () => {
    const schedule = new Schedule(mockSchedule);
    expect(schedule.type).toBe('crawler');
  });
 
  it('should store the crawler id as targetId', () => {
    const schedule = new Schedule(mockSchedule);
    expect(schedule.targetId).toBe(mockSchedule.targetId);
  });
});
 
function isValidCron(expr) {
  if (!expr || typeof expr !== 'string') return false;
  const parts = expr.trim().split(/\s+/);
  if (parts.length !== 5) return false;
  const cronRegex = /^(\*|\d+(-\d+)?)(\/\d+)?$/;
  const rangeChecks = [
    { min: 0, max: 59 },
    { min: 0, max: 23 },
    { min: 1, max: 31 },
    { min: 1, max: 12 },
    { min: 0, max: 7 },
  ];
  for (let i = 0; i < parts.length; i++) {
    if (parts[i] === '*') continue;
    const fields = parts[i].split(',');
    for (const field of fields) {
      if (!cronRegex.test(field)) return false;
      const num = parseInt(field, 10);
      if (!isNaN(num) && (num < rangeChecks[i].min || num > rangeChecks[i].max)) return false;
    }
  }
  return true;
}
 
describe('Cron Expression Validation', () => {
  it('should accept valid cron expressions', () => {
    const valid = [
      '0 */6 * * *',
      '0 0 * * *',
      '*/15 * * * *',
      '0 0 1 1 *',
      '30 4 * * 1-5',
      '* * * * *',
    ];
    valid.forEach((expr) => {
      expect(isValidCron(expr)).toBe(true);
    });
  });
 
  it('should reject invalid cron expressions', () => {
    const invalid = [
      'not-a-cron',
      '0 * * *',
      '* * * * * *',
      '',
      null,
    ];
    invalid.forEach((expr) => {
      expect(isValidCron(expr)).toBe(false);
    });
  });
});