"use strict";

const { PasswordGenerator } = ChromeUtils.importESModule(
  "resource://gre/modules/shared/PasswordGenerator.sys.mjs"
);

add_task(async function test_shuffleString() {
  let original = "1234567890";
  let shuffled = PasswordGenerator._shuffleString(original);
  notEqual(original, shuffled, "String should have been shuffled");
});

add_task(async function test_randomUInt8Index() {
  throws(
    () => PasswordGenerator._randomUInt8Index(256),
    /uint8/,
    "Should throw for larger than uint8"
  );
  Assert.ok(
    Number.isSafeInteger(PasswordGenerator._randomUInt8Index(255)),
    "Check integer returned"
  );
});

add_task(async function test_generatePassword_classes() {
  let password = PasswordGenerator.generatePassword(
    /* REQUIRED_CHARACTER_CLASSES */

    { length: 4 }
  );
  info(password);
  equal(password.length, 4, "Check length is correct");
  Assert.ok(
    password.match(/[a-km-np-z]/),
    "Minimal password should include at least one lowercase character"
  );
  Assert.ok(
    password.match(/[A-HJ-NP-Z]/),
    "Minimal password should include at least one uppercase character"
  );
  Assert.ok(
    password.match(/[2-9]/),
    "Minimal password should include at least one digit"
  );
  Assert.ok(
    password.match(/[-~!@#$%^&*_+=)}:;"'>,.?\]]/),
    "Minimal password should include at least one special character"
  );
  Assert.ok(
    password.match(/^[a-km-np-zA-HJ-NP-Z2-9-~!@#$%^&*_+=)}:;"'>,.?\]]+$/),
    "All characters should be in the expected set"
  );
});

add_task(async function test_generatePassword_length() {
  let password = PasswordGenerator.generatePassword({ length: 5 });
  info(password);
  equal(password.length, 5, "Check length is correct");

  password = PasswordGenerator.generatePassword({ length: 3 });
  equal(password.length, 4, "Minimum generated length is 4");

  password = PasswordGenerator.generatePassword({ length: Math.pow(2, 8) });
  equal(
    password.length,
    Math.pow(2, 8) - 1,
    "Maximum generated length is Math.pow(2, 8) - 1 "
  );

  Assert.ok(
    password.match(/^[a-km-np-zA-HJ-NP-Z2-9-~!@#$%^&*_+=)}:;"'>,.?\]]+$/),
    "All characters should be in the expected set"
  );
});

add_task(async function test_generatePassword_defaultLength() {
  let password = PasswordGenerator.generatePassword({});
  info(password);
  equal(password.length, 15, "Check default length is correct");
  Assert.ok(
    password.match(/^[a-km-np-zA-HJ-NP-Z2-9-~!@#$%^&*_+=)}:;"'>,.?\]]{15}$/),
    "All characters should be in the expected set"
  );
});

add_task(
  async function test_generatePassword_immutableDefaultRequiredClasses() {
    // We need to escape our special characters since some of them
    // have special meaning in regex.
    let specialCharacters = PasswordGenerator._getSpecialCharacters();
    let escapedSpecialCharacters = specialCharacters.replace(
      /[.*\-+?^${}()|[\]\\]/g,
      "\\$&"
    );
    specialCharacters = new RegExp(`[${escapedSpecialCharacters}]`);
    let rules = new Map();
    rules.set("required", ["special"]);
    let password = PasswordGenerator.generatePassword({ rules });
    equal(password.length, 15, "Check default length is correct");
    Assert.ok(
      password.match(specialCharacters),
      "Password should include special character."
    );
    let allCharacters = new RegExp(
      `[a-km-np-zA-HJ-NP-Z2-9 ${escapedSpecialCharacters}]{15}`
    );
    Assert.ok(
      password.match(allCharacters),
      "All characters should be in the expected set"
    );
    password = PasswordGenerator.generatePassword({});
    equal(password.length, 15, "Check default length is correct");
    Assert.ok(
      password.match(specialCharacters),
      "Password should include special character."
    );
    Assert.ok(
      password.match(/^[a-km-np-zA-HJ-NP-Z2-9-~!@#$%^&*_+=)}:;"'>,.?\]]{15}$/),
      "All characters, minus special characters, should be in the expected set"
    );
  }
);
