import {isValidCron, getCronDescription} from "./cron";

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);
      });
    }
  });

  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 ,, * * *", "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("getCronDescription", () => {
    it(`Produces a sentence for valid cron`, () => {
      expect(getCronDescription("0 * * * *")).toEqual(
        "Runs every hour\n\n" +
          "Actions schedules run at most every 5 minutes. [Learn more](https://docs.github.com/actions/using-workflows/workflow-syntax-for-github-actions#onschedule)"
      );
    });

    it(`Returns nothing for invalid cron`, () => {
      expect(getCronDescription("* * * * * *")).toBeUndefined();
    });
  });
});
