/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/
 */

// Tests AddonRepository.jsm

var gServer = createHttpServer({hosts: ["example.com"]});

const PREF_GETADDONS_BROWSEADDONS        = "extensions.getAddons.browseAddons";
const PREF_GETADDONS_BROWSESEARCHRESULTS = "extensions.getAddons.search.browseURL";

const BASE_URL      = "http://example.com";
const DEFAULT_URL   = "about:blank";

const ADDONS = [
  {
    manifest: {
      name: "XPI Add-on 1",
      version: "1.1",
      applications: {gecko: {id: "test_AddonRepository_1@tests.mozilla.org" }},
    },
  },
  {
    manifest: {
      name: "XPI Add-on 2",
      version: "1.2",
      theme: { },
      applications: {gecko: {id: "test_AddonRepository_2@tests.mozilla.org"}},
    },
  },
  {
    manifest: {
      name: "XPI Add-on 3",
      version: "1.3",
      theme: { },
      applications: {gecko: {id: "test_AddonRepository_3@tests.mozilla.org"}},
    },
  },
];

// Path to source URI of installing add-on
const INSTALL_URL2  = "/addons/test_AddonRepository_2.xpi";
// Path to source URI of non-active add-on (state = STATE_AVAILABLE)
const INSTALL_URL3  = "/addons/test_AddonRepository_3.xpi";

// Properties of an individual add-on that should be checked
// Note: name is checked separately
var ADDON_PROPERTIES = ["id", "type", "version", "creator", "developers",
                        "description", "fullDescription",
                        "iconURL", "icons", "screenshots",
                        "supportURL", "contributionURL",
                        "averageRating", "reviewCount", "reviewURL",
                        "weeklyDownloads", "dailyUsers",
                        "sourceURI", "updateDate"];

// Results of getAddonsByIDs
var GET_RESULTS = [{
  id:                     "test1@tests.mozilla.org",
  type:                   "extension",
  version:                "1.1",
  creator:                {
                            name: "Test Creator 1",
                            url:  BASE_URL + "/creator1.html",
                          },
  developers:             [{
                            name: "Test Developer 1",
                            url:  BASE_URL + "/developer1.html",
                          }],
  description:            "Test Summary 1",
  fullDescription:        "Test Description 1",
  iconURL:                BASE_URL + "/icon1.png",
  icons:                  { "32": BASE_URL + "/icon1.png" },
  screenshots:            [{
                            url:             BASE_URL + "/full1-1.png",
                            width:           400,
                            height:          300,
                            thumbnailURL:    BASE_URL + "/thumbnail1-1.png",
                            thumbnailWidth:  200,
                            thumbnailHeight: 150,
                            caption:         "Caption 1 - 1",
                          }, {
                            url:          BASE_URL + "/full2-1.png",
                            thumbnailURL: BASE_URL + "/thumbnail2-1.png",
                            caption:      "Caption 2 - 1",
                          }],
  supportURL:             BASE_URL + "/support1.html",
  contributionURL:        BASE_URL + "/contribution1.html",
  averageRating:          4,
  reviewCount:            1111,
  reviewURL:              BASE_URL + "/review1.html",
  weeklyDownloads:        3333,
  sourceURI:              BASE_URL + INSTALL_URL2,
  updateDate:             new Date(1265033045000),
}, {
  id:                     "test2@tests.mozilla.org",
  type:                   "extension",
  version:                "2.0",
  icons:                  {},
  sourceURI:              "http://example.com/addons/bleah.xpi",
}, {
  id:                     "test_AddonRepository_1@tests.mozilla.org",
  type:                   "theme",
  version:                "1.4",
  icons:                  {},
}];

// Values for testing AddonRepository.getAddonsByIDs()
var GET_TEST = {
  preference:       PREF_GETADDONS_BYIDS,
  preferenceValue:  BASE_URL + "/%OS%/%VERSION%/%IDS%",
  failedIDs:      ["test1@tests.mozilla.org"],
  failedURL:        "/XPCShell/1/test1%40tests.mozilla.org",
  successfulIDs:  ["test1@tests.mozilla.org",
                   "test2@tests.mozilla.org",
                   "{00000000-1111-2222-3333-444444444444}",
                   "test_AddonRepository_1@tests.mozilla.org"],
  successfulURL:    "/XPCShell/1/test1%40tests.mozilla.org%2C" +
                    "test2%40tests.mozilla.org%2C" +
                    "%7B00000000-1111-2222-3333-444444444444%7D%2C" +
                    "test_AddonRepository_1%40tests.mozilla.org",
  successfulRTAURL: "/XPCShell/1/rta%3AdGVzdDFAdGVzdHMubW96aWxsYS5vcmc%2C" +
                    "test2%40tests.mozilla.org%2C" +
                    "%7B00000000-1111-2222-3333-444444444444%7D%2C" +
                    "test_AddonRepository_1%40tests.mozilla.org",
};

// Test that actual results and expected results are equal
function check_results(aActualAddons, aExpectedAddons) {
  do_check_addons(aActualAddons, aExpectedAddons, ADDON_PROPERTIES);

  // Additional tests
  aActualAddons.forEach(function check_each_addon(aActualAddon) {
    // Separately check name so better messages are output when test fails
    if (aActualAddon.name == "FAIL")
      do_throw(aActualAddon.id + " - " + aActualAddon.description);
    if (aActualAddon.name != "PASS")
      do_throw(aActualAddon.id + " - invalid add-on name " + aActualAddon.name);
  });
}

add_task(async function setup() {
  // Setup for test
  createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");

  let xpis = ADDONS.map(addon => createTempWebExtensionFile(addon));

  // Register other add-on XPI files
  gServer.registerFile(INSTALL_URL2, xpis[1]);
  gServer.registerFile(INSTALL_URL3, xpis[2]);

  // Register files used to test search failure
  gServer.registerFile(GET_TEST.failedURL,
                       do_get_file("data/test_AddonRepository_fail.json"));

  // Register files used to test search success
  gServer.registerFile(GET_TEST.successfulURL,
                       do_get_file("data/test_AddonRepository_getAddonsByIDs.json"));
  // Register file for RTA test
  gServer.registerFile(GET_TEST.successfulRTAURL,
                       do_get_file("data/test_AddonRepository_getAddonsByIDs.json"));

  await promiseStartupManager();

  // Install an add-on so can check that it isn't returned in the results
  await promiseInstallFile(xpis[0]);
  await promiseRestartManager();

  // Create an active AddonInstall so can check that it isn't returned in the results
  let install = await AddonManager.getInstallForURL(BASE_URL + INSTALL_URL2);
  let promise = promiseCompleteInstall(install);
  registerCleanupFunction(() => promise);

  // Create a non-active AddonInstall so can check that it is returned in the results
  await AddonManager.getInstallForURL(BASE_URL + INSTALL_URL3);
});

// Tests homepageURL and getSearchURL()
add_task(async function test_1() {
  function check_urls(aPreference, aGetURL, aTests) {
    aTests.forEach(function(aTest) {
      Services.prefs.setCharPref(aPreference, aTest.preferenceValue);
      Assert.equal(aGetURL(aTest), aTest.expectedURL);
    });
  }

  var urlTests = [{
    preferenceValue:  BASE_URL,
    expectedURL:      BASE_URL,
  }, {
    preferenceValue:  BASE_URL + "/%OS%/%VERSION%",
    expectedURL:      BASE_URL + "/XPCShell/1",
  }];

  // Extra tests for AddonRepository.getSearchURL();
  var searchURLTests = [{
    searchTerms:      "test",
    preferenceValue:  BASE_URL + "/search?q=%TERMS%",
    expectedURL:      BASE_URL + "/search?q=test",
  }, {
    searchTerms:      "test search",
    preferenceValue:  BASE_URL + "/%TERMS%",
    expectedURL:      BASE_URL + "/test%20search",
  }, {
    searchTerms:      "odd=search:with&weird\"characters",
    preferenceValue:  BASE_URL + "/%TERMS%",
    expectedURL:      BASE_URL + "/odd%3Dsearch%3Awith%26weird%22characters",
  }];

  // Setup tests for homepageURL and getSearchURL()
  var tests = [{
    initiallyUndefined: true,
    preference:         PREF_GETADDONS_BROWSEADDONS,
    urlTests,
    getURL:             () => AddonRepository.homepageURL,
  }, {
    initiallyUndefined: false,
    preference:         PREF_GETADDONS_BROWSESEARCHRESULTS,
    urlTests:           urlTests.concat(searchURLTests),
    getURL:             function getSearchURL(aTest) {
                          var searchTerms = aTest && aTest.searchTerms ? aTest.searchTerms
                                                                       : "unused terms";
                          return AddonRepository.getSearchURL(searchTerms);
                        },
  }];

  tests.forEach(function url_test(aTest) {
    if (aTest.initiallyUndefined) {
      // Preference is not defined by default
      Assert.equal(Services.prefs.getPrefType(aTest.preference),
                   Services.prefs.PREF_INVALID);
      Assert.equal(aTest.getURL(), DEFAULT_URL);
    }

    check_urls(aTest.preference, aTest.getURL, aTest.urlTests);
  });
});

// Tests failure of AddonRepository.getAddonsByIDs()
add_task(async function test_getAddonsByID_fails() {
  Services.prefs.setCharPref(GET_TEST.preference, GET_TEST.preferenceValue);

  await Assert.rejects(
    AddonRepository.getAddonsByIDs(GET_TEST.failedIDs),
    /Error: GET.*?failed/);
});

// Tests success of AddonRepository.getAddonsByIDs()
add_task(async function test_getAddonsByID_succeeds() {
  let result = await AddonRepository.getAddonsByIDs(GET_TEST.successfulIDs);

  check_results(result, GET_RESULTS);
});

// Tests success of AddonRepository.getAddonsByIDs() with rta ID.
add_task(async function test_getAddonsByID_rta() {
  let id = `rta:${btoa(GET_TEST.successfulIDs[0])}`.slice(0, -1);
  GET_TEST.successfulIDs[0] = id;
  let result = await AddonRepository.getAddonsByIDs(GET_TEST.successfulIDs);

  check_results(result, GET_RESULTS);
});
