/*
 * Test LoginHelper.dedupeLogins
 */

"use strict";

ChromeUtils.import("resource://gre/modules/LoginHelper.jsm");

const DOMAIN1_HTTP_TO_HTTP_U1_P1 = TestData.formLogin({
  timePasswordChanged: 3000,
  timeLastUsed: 2000,
});
const DOMAIN1_HTTP_TO_HTTP_U1_P2 = TestData.formLogin({
  password: "password two",
});
const DOMAIN1_HTTP_TO_HTTP_U2_P2 = TestData.formLogin({
  password: "password two",
  username: "username two",
});
const DOMAIN1_HTTPS_TO_HTTPS_U1_P1 = TestData.formLogin({
  formSubmitURL: "http://www.example.com",
  hostname: "https://www3.example.com",
  timePasswordChanged: 4000,
  timeLastUsed: 1000,
});
const DOMAIN1_HTTPS_TO_EMPTY_U1_P1 = TestData.formLogin({
  formSubmitURL: "",
  hostname: "https://www3.example.com",
});
const DOMAIN1_HTTPS_TO_EMPTYU_P1 = TestData.formLogin({
  hostname: "https://www3.example.com",
  username: "",
});
const DOMAIN1_HTTP_AUTH = TestData.authLogin({
  hostname: "http://www3.example.com",
});
const DOMAIN1_HTTPS_AUTH = TestData.authLogin({
  hostname: "https://www3.example.com",
});


add_task(function test_dedupeLogins() {
  // [description, expectedOutput, dedupe arg. 0, dedupe arg 1, ...]
  let testcases = [
    [
      "exact dupes",
      [DOMAIN1_HTTP_TO_HTTP_U1_P1],
      [DOMAIN1_HTTP_TO_HTTP_U1_P1, DOMAIN1_HTTP_TO_HTTP_U1_P1],
      undefined,
      [], // force no resolveBy logic to test behavior of preferring the first..
    ],
    [
      "default uniqueKeys is un + pw",
      [DOMAIN1_HTTP_TO_HTTP_U1_P1, DOMAIN1_HTTP_TO_HTTP_U1_P2],
      [DOMAIN1_HTTP_TO_HTTP_U1_P1, DOMAIN1_HTTP_TO_HTTP_U1_P2],
      undefined,
      [],
    ],
    [
      "same usernames, different passwords, dedupe username only",
      [DOMAIN1_HTTP_TO_HTTP_U1_P1],
      [DOMAIN1_HTTP_TO_HTTP_U1_P1, DOMAIN1_HTTP_TO_HTTP_U1_P2],
      ["username"],
      [],
    ],
    [
      "same un+pw, different scheme",
      [DOMAIN1_HTTP_TO_HTTP_U1_P1],
      [DOMAIN1_HTTP_TO_HTTP_U1_P1, DOMAIN1_HTTPS_TO_HTTPS_U1_P1],
      undefined,
      [],
    ],
    [
      "same un+pw, different scheme, reverse order",
      [DOMAIN1_HTTPS_TO_HTTPS_U1_P1],
      [DOMAIN1_HTTPS_TO_HTTPS_U1_P1, DOMAIN1_HTTP_TO_HTTP_U1_P1],
      undefined,
      [],
    ],
    [
      "same un+pw, different scheme, include hostname",
      [DOMAIN1_HTTP_TO_HTTP_U1_P1, DOMAIN1_HTTPS_TO_HTTPS_U1_P1],
      [DOMAIN1_HTTP_TO_HTTP_U1_P1, DOMAIN1_HTTPS_TO_HTTPS_U1_P1],
      ["hostname", "username", "password"],
      [],
    ],
    [
      "empty username is not deduped with non-empty",
      [DOMAIN1_HTTP_TO_HTTP_U1_P1, DOMAIN1_HTTPS_TO_EMPTYU_P1],
      [DOMAIN1_HTTP_TO_HTTP_U1_P1, DOMAIN1_HTTPS_TO_EMPTYU_P1],
      undefined,
      [],
    ],
    [
      "empty username is deduped with same passwords",
      [DOMAIN1_HTTPS_TO_EMPTYU_P1],
      [DOMAIN1_HTTPS_TO_EMPTYU_P1, DOMAIN1_HTTP_TO_HTTP_U1_P1],
      ["password"],
      [],
    ],
    [
      "mix of form and HTTP auth",
      [DOMAIN1_HTTP_TO_HTTP_U1_P1],
      [DOMAIN1_HTTP_TO_HTTP_U1_P1, DOMAIN1_HTTP_AUTH],
      undefined,
      [],
    ],
  ];

  for (let tc of testcases) {
    let description = tc.shift();
    let expected = tc.shift();
    let actual = LoginHelper.dedupeLogins(...tc);
    Assert.strictEqual(actual.length, expected.length, `Check: ${description}`);
    for (let [i, login] of expected.entries()) {
      Assert.strictEqual(actual[i], login, `Check index ${i}`);
    }
  }
});


add_task(async function test_dedupeLogins_resolveBy() {
  Assert.ok(DOMAIN1_HTTP_TO_HTTP_U1_P1.timeLastUsed > DOMAIN1_HTTPS_TO_HTTPS_U1_P1.timeLastUsed,
            "Sanity check timeLastUsed difference");
  Assert.ok(DOMAIN1_HTTP_TO_HTTP_U1_P1.timePasswordChanged < DOMAIN1_HTTPS_TO_HTTPS_U1_P1.timePasswordChanged,
            "Sanity check timePasswordChanged difference");

  let testcases = [
    [
      "default resolveBy is timeLastUsed",
      [DOMAIN1_HTTP_TO_HTTP_U1_P1],
      [DOMAIN1_HTTPS_TO_HTTPS_U1_P1, DOMAIN1_HTTP_TO_HTTP_U1_P1],
    ],
    [
      "default resolveBy is timeLastUsed, reversed input",
      [DOMAIN1_HTTP_TO_HTTP_U1_P1],
      [DOMAIN1_HTTP_TO_HTTP_U1_P1, DOMAIN1_HTTPS_TO_HTTPS_U1_P1],
    ],
    [
      "resolveBy timeLastUsed + timePasswordChanged",
      [DOMAIN1_HTTP_TO_HTTP_U1_P1],
      [DOMAIN1_HTTPS_TO_HTTPS_U1_P1, DOMAIN1_HTTP_TO_HTTP_U1_P1],
      undefined,
      ["timeLastUsed", "timePasswordChanged"],
    ],
    [
      "resolveBy timeLastUsed + timePasswordChanged, reversed input",
      [DOMAIN1_HTTP_TO_HTTP_U1_P1],
      [DOMAIN1_HTTP_TO_HTTP_U1_P1, DOMAIN1_HTTPS_TO_HTTPS_U1_P1],
      undefined,
      ["timeLastUsed", "timePasswordChanged"],
    ],
    [
      "resolveBy timePasswordChanged",
      [DOMAIN1_HTTPS_TO_HTTPS_U1_P1],
      [DOMAIN1_HTTPS_TO_HTTPS_U1_P1, DOMAIN1_HTTP_TO_HTTP_U1_P1],
      undefined,
      ["timePasswordChanged"],
    ],
    [
      "resolveBy timePasswordChanged, reversed",
      [DOMAIN1_HTTPS_TO_HTTPS_U1_P1],
      [DOMAIN1_HTTP_TO_HTTP_U1_P1, DOMAIN1_HTTPS_TO_HTTPS_U1_P1],
      undefined,
      ["timePasswordChanged"],
    ],
    [
      "resolveBy timePasswordChanged + timeLastUsed",
      [DOMAIN1_HTTPS_TO_HTTPS_U1_P1],
      [DOMAIN1_HTTPS_TO_HTTPS_U1_P1, DOMAIN1_HTTP_TO_HTTP_U1_P1],
      undefined,
      ["timePasswordChanged", "timeLastUsed"],
    ],
    [
      "resolveBy timePasswordChanged + timeLastUsed, reversed",
      [DOMAIN1_HTTPS_TO_HTTPS_U1_P1],
      [DOMAIN1_HTTP_TO_HTTP_U1_P1, DOMAIN1_HTTPS_TO_HTTPS_U1_P1],
      undefined,
      ["timePasswordChanged", "timeLastUsed"],
    ],
    [
      "resolveBy scheme + timePasswordChanged, prefer HTTP",
      [DOMAIN1_HTTP_TO_HTTP_U1_P1],
      [DOMAIN1_HTTPS_TO_HTTPS_U1_P1, DOMAIN1_HTTP_TO_HTTP_U1_P1],
      undefined,
      ["scheme", "timePasswordChanged"],
      DOMAIN1_HTTP_TO_HTTP_U1_P1.hostname,
    ],
    [
      "resolveBy scheme + timePasswordChanged, prefer HTTP, reversed input",
      [DOMAIN1_HTTP_TO_HTTP_U1_P1],
      [DOMAIN1_HTTP_TO_HTTP_U1_P1, DOMAIN1_HTTPS_TO_HTTPS_U1_P1],
      undefined,
      ["scheme", "timePasswordChanged"],
      DOMAIN1_HTTP_TO_HTTP_U1_P1.hostname,
    ],
    [
      "resolveBy scheme + timePasswordChanged, prefer HTTPS",
      [DOMAIN1_HTTPS_TO_HTTPS_U1_P1],
      [DOMAIN1_HTTPS_TO_HTTPS_U1_P1, DOMAIN1_HTTP_TO_HTTP_U1_P1],
      undefined,
      ["scheme", "timePasswordChanged"],
      DOMAIN1_HTTPS_TO_HTTPS_U1_P1.hostname,
    ],
    [
      "resolveBy scheme + timePasswordChanged, prefer HTTPS, reversed input",
      [DOMAIN1_HTTPS_TO_HTTPS_U1_P1],
      [DOMAIN1_HTTP_TO_HTTP_U1_P1, DOMAIN1_HTTPS_TO_HTTPS_U1_P1],
      undefined,
      ["scheme", "timePasswordChanged"],
      DOMAIN1_HTTPS_TO_HTTPS_U1_P1.hostname,
    ],
    [
      "resolveBy scheme HTTP auth",
      [DOMAIN1_HTTPS_AUTH],
      [DOMAIN1_HTTP_AUTH, DOMAIN1_HTTPS_AUTH],
      undefined,
      ["scheme"],
      DOMAIN1_HTTPS_AUTH.hostname,
    ],
    [
      "resolveBy scheme HTTP auth, reversed input",
      [DOMAIN1_HTTPS_AUTH],
      [DOMAIN1_HTTPS_AUTH, DOMAIN1_HTTP_AUTH],
      undefined,
      ["scheme"],
      DOMAIN1_HTTPS_AUTH.hostname,
    ],
    [
      "resolveBy scheme, empty form submit URL",
      [DOMAIN1_HTTPS_TO_HTTPS_U1_P1],
      [DOMAIN1_HTTPS_TO_HTTPS_U1_P1, DOMAIN1_HTTPS_TO_EMPTY_U1_P1],
      undefined,
      ["scheme"],
      DOMAIN1_HTTPS_TO_HTTPS_U1_P1.hostname,
    ],
  ];

  for (let tc of testcases) {
    let description = tc.shift();
    let expected = tc.shift();
    let actual = LoginHelper.dedupeLogins(...tc);
    Assert.strictEqual(actual.length, expected.length, `Check: ${description}`);
    for (let [i, login] of expected.entries()) {
      Assert.strictEqual(actual[i], login, `Check index ${i}`);
    }
  }

});

add_task(async function test_dedupeLogins_preferredOriginMissing() {
  let testcases = [
    [
      "resolveBy scheme + timePasswordChanged, missing preferredOrigin",
      /preferredOrigin/,
      [DOMAIN1_HTTPS_TO_HTTPS_U1_P1, DOMAIN1_HTTP_TO_HTTP_U1_P1],
      undefined,
      ["scheme", "timePasswordChanged"],
    ],
    [
      "resolveBy timePasswordChanged + scheme, missing preferredOrigin",
      /preferredOrigin/,
      [DOMAIN1_HTTPS_TO_HTTPS_U1_P1, DOMAIN1_HTTP_TO_HTTP_U1_P1],
      undefined,
      ["timePasswordChanged", "scheme"],
    ],
    [
      "resolveBy scheme + timePasswordChanged, empty preferredOrigin",
      /preferredOrigin/,
      [DOMAIN1_HTTPS_TO_HTTPS_U1_P1, DOMAIN1_HTTP_TO_HTTP_U1_P1],
      undefined,
      ["scheme", "timePasswordChanged"],
      "",
    ],
  ];

  for (let tc of testcases) {
    let description = tc.shift();
    let expectedException = tc.shift();
    Assert.throws(() => {
      LoginHelper.dedupeLogins(...tc);
    }, expectedException, `Check: ${description}`);
  }
});
