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

const { AbuseReporter, AbuseReportError } = ChromeUtils.importESModule(
  "resource://gre/modules/AbuseReporter.sys.mjs"
);

const { ClientID } = ChromeUtils.importESModule(
  "resource://gre/modules/ClientID.sys.mjs"
);
const { TelemetryController } = ChromeUtils.importESModule(
  "resource://gre/modules/TelemetryController.sys.mjs"
);
const { TelemetryTestUtils } = ChromeUtils.importESModule(
  "resource://testing-common/TelemetryTestUtils.sys.mjs"
);

const APPNAME = "XPCShell";
const APPVERSION = "1";
const ADDON_ID = "test-addon@tests.mozilla.org";
const ADDON_ID2 = "test-addon2@tests.mozilla.org";
const FAKE_INSTALL_INFO = {
  source: "fake-Install:Source",
  method: "fake:install method",
};
const PREF_REQUIRED_LOCALE = "intl.locale.requested";
const REPORT_OPTIONS = { reportEntryPoint: "menu" };
const TELEMETRY_EVENTS_FILTERS = {
  category: "addonsManager",
  method: "report",
};

const FAKE_AMO_DETAILS = {
  name: {
    "en-US": "fake name",
    "it-IT": "fake it-IT name",
  },
  current_version: { version: "1.0" },
  type: "extension",
  is_recommended: true,
};

createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "49");

const server = createHttpServer({ hosts: ["test.addons.org"] });

// Mock abuse report API endpoint.
let apiRequestHandler;
server.registerPathHandler("/api/report/", (request, response) => {
  const stream = request.bodyInputStream;
  const buffer = NetUtil.readInputStream(stream, stream.available());
  const data = new TextDecoder().decode(buffer);
  apiRequestHandler({ data, request, response });
});

// Mock addon details API endpoint.
const amoAddonDetailsMap = new Map();
server.registerPrefixHandler("/api/addons/addon/", (request, response) => {
  const addonId = request.path.split("/").pop();
  if (!amoAddonDetailsMap.has(addonId)) {
    response.setStatusLine(request.httpVersion, 404, "Not Found");
    response.write(JSON.stringify({ detail: "Not found." }));
  } else {
    response.setStatusLine(request.httpVersion, 200, "Success");
    response.write(JSON.stringify(amoAddonDetailsMap.get(addonId)));
  }
});

function getProperties(obj, propNames) {
  return propNames.reduce((acc, el) => {
    acc[el] = obj[el];
    return acc;
  }, {});
}

function handleSubmitRequest({ request, response }) {
  response.setStatusLine(request.httpVersion, 200, "OK");
  response.setHeader("Content-Type", "application/json", false);
  response.write("{}");
}

function clearAbuseReportState() {
  // Clear the timestamp of the last submission.
  AbuseReporter._lastReportTimestamp = null;
}

async function installTestExtension(overrideOptions = {}) {
  const extOptions = {
    manifest: {
      browser_specific_settings: { gecko: { id: ADDON_ID } },
      name: "Test Extension",
    },
    useAddonManager: "permanent",
    amInstallTelemetryInfo: FAKE_INSTALL_INFO,
    ...overrideOptions,
  };

  const extension = ExtensionTestUtils.loadExtension(extOptions);
  await extension.startup();

  const addon = await AddonManager.getAddonByID(ADDON_ID);

  return { extension, addon };
}

async function assertRejectsAbuseReportError(promise, errorType, errorInfo) {
  let error;

  await Assert.rejects(
    promise,
    err => {
      // Log the actual error to make investigating test failures easier.
      Cu.reportError(err);
      error = err;
      return err instanceof AbuseReportError;
    },
    `Got an AbuseReportError`
  );

  equal(error.errorType, errorType, "Got the expected errorType");
  equal(error.errorInfo, errorInfo, "Got the expected errorInfo");
  ok(
    error.message.includes(errorType),
    "errorType should be included in the error message"
  );
  if (errorInfo) {
    ok(
      error.message.includes(errorInfo),
      "errorInfo should be included in the error message"
    );
  }
}

async function assertBaseReportData({ reportData, addon }) {
  // Report properties related to addon metadata.
  equal(reportData.addon, ADDON_ID, "Got expected 'addon'");
  equal(
    reportData.addon_version,
    addon.version,
    "Got expected 'addon_version'"
  );
  equal(
    reportData.install_date,
    addon.installDate.toISOString(),
    "Got expected 'install_date' in ISO format"
  );
  equal(
    reportData.addon_install_origin,
    addon.sourceURI.spec,
    "Got expected 'addon_install_origin'"
  );
  equal(
    reportData.addon_install_source,
    "fake_install_source",
    "Got expected 'addon_install_source'"
  );
  equal(
    reportData.addon_install_method,
    "fake_install_method",
    "Got expected 'addon_install_method'"
  );
  equal(
    reportData.addon_signature,
    "privileged",
    "Got expected 'addon_signature'"
  );

  // Report properties related to the environment.
  equal(
    reportData.client_id,
    await ClientID.getClientIdHash(),
    "Got the expected 'client_id'"
  );
  equal(reportData.app, APPNAME.toLowerCase(), "Got expected 'app'");
  equal(reportData.appversion, APPVERSION, "Got expected 'appversion'");
  equal(
    reportData.lang,
    Services.locale.appLocaleAsBCP47,
    "Got expected 'lang'"
  );
  equal(
    reportData.operating_system,
    AppConstants.platform,
    "Got expected 'operating_system'"
  );
  equal(
    reportData.operating_system_version,
    Services.sysinfo.getProperty("version"),
    "Got expected 'operating_system_version'"
  );
}

add_task(async function test_setup() {
  Services.prefs.setCharPref(
    "extensions.abuseReport.url",
    "http://test.addons.org/api/report/"
  );

  Services.prefs.setCharPref(
    "extensions.abuseReport.amoDetailsURL",
    "http://test.addons.org/api/addons/addon"
  );

  await promiseStartupManager();
  // Telemetry test setup needed to ensure that the builtin events are defined
  // and they can be collected and verified.
  await TelemetryController.testSetup();

  // This is actually only needed on Android, because it does not properly support unified telemetry
  // and so, if not enabled explicitly here, it would make these tests to fail when running on a
  // non-Nightly build.
  const oldCanRecordBase = Services.telemetry.canRecordBase;
  Services.telemetry.canRecordBase = true;
  registerCleanupFunction(() => {
    Services.telemetry.canRecordBase = oldCanRecordBase;
  });

  // Register a fake it-IT locale (used to test localized AMO details in some
  // of the test case defined in this test file).
  L10nRegistry.getInstance().registerSources([
    L10nFileSource.createMock(
      "mock",
      "app",
      ["it-IT", "fr-FR"],
      "resource://fake/locales/{locale}",
      []
    ),
  ]);
});

add_task(async function test_addon_report_data() {
  info("Verify report property for a privileged extension");
  const { addon, extension } = await installTestExtension();
  const data = await AbuseReporter.getReportData(addon);
  await assertBaseReportData({ reportData: data, addon });
  await extension.unload();

  info("Verify 'addon_signature' report property for non privileged extension");
  AddonTestUtils.usePrivilegedSignatures = false;
  const { addon: addon2, extension: extension2 } = await installTestExtension();
  const data2 = await AbuseReporter.getReportData(addon2);
  equal(
    data2.addon_signature,
    "signed",
    "Got expected 'addon_signature' for non privileged extension"
  );
  await extension2.unload();

  info("Verify 'addon_install_method' report property on temporary install");
  const { addon: addon3, extension: extension3 } = await installTestExtension({
    useAddonManager: "temporary",
  });
  const data3 = await AbuseReporter.getReportData(addon3);
  equal(
    data3.addon_install_source,
    "temporary_addon",
    "Got expected 'addon_install_method' on temporary install"
  );
  await extension3.unload();
});

add_task(async function test_report_on_not_installed_addon() {
  Services.telemetry.clearEvents();

  // Make sure that the AMO addons details API endpoint is going to
  // return a 404 status for the not installed addon.
  amoAddonDetailsMap.delete(ADDON_ID);

  await assertRejectsAbuseReportError(
    AbuseReporter.createAbuseReport(ADDON_ID, REPORT_OPTIONS),
    "ERROR_ADDON_NOTFOUND"
  );

  TelemetryTestUtils.assertEvents(
    [
      {
        object: REPORT_OPTIONS.reportEntryPoint,
        value: ADDON_ID,
        extra: { error_type: "ERROR_AMODETAILS_NOTFOUND" },
      },
      {
        object: REPORT_OPTIONS.reportEntryPoint,
        value: ADDON_ID,
        extra: { error_type: "ERROR_ADDON_NOTFOUND" },
      },
    ],
    TELEMETRY_EVENTS_FILTERS
  );

  Services.telemetry.clearEvents();
});

// This tests verifies how the addon installTelemetryInfo values are being
// normalized into the addon_install_source and addon_install_method
// expected by the API endpoint.
add_task(async function test_normalized_addon_install_source_and_method() {
  async function assertAddonInstallMethod(amInstallTelemetryInfo, expected) {
    const { addon, extension } = await installTestExtension({
      amInstallTelemetryInfo,
    });
    const {
      addon_install_method,
      addon_install_source,
      addon_install_source_url,
    } = await AbuseReporter.getReportData(addon);

    Assert.deepEqual(
      {
        addon_install_method,
        addon_install_source,
        addon_install_source_url,
      },
      {
        addon_install_method: expected.method,
        addon_install_source: expected.source,
        addon_install_source_url: expected.sourceURL,
      },
      `Got the expected report data for ${JSON.stringify(
        amInstallTelemetryInfo
      )}`
    );
    await extension.unload();
  }

  // Array of testcases: the `test` property contains the installTelemetryInfo value
  // and the `expect` contains the expected normalized values.
  const TEST_CASES = [
    // Explicitly verify normalized values on missing telemetry info.
    {
      test: null,
      expect: { source: null, method: null },
    },

    // Verify expected normalized values for some common install telemetry info.
    {
      test: { source: "about:addons", method: "drag-and-drop" },
      expect: { source: "about_addons", method: "drag_and_drop" },
    },
    {
      test: { source: "amo", method: "amWebAPI" },
      expect: { source: "amo", method: "amwebapi" },
    },
    {
      test: { source: "app-profile", method: "sideload" },
      expect: { source: "app_profile", method: "sideload" },
    },
    {
      test: { source: "distribution" },
      expect: { source: "distribution", method: null },
    },
    {
      test: {
        method: "installTrigger",
        source: "test-host",
        sourceURL: "http://host.triggered.install/example?test=1",
      },
      expect: {
        method: "installtrigger",
        source: "test_host",
        sourceURL: "http://host.triggered.install/example?test=1",
      },
    },
    {
      test: {
        method: "link",
        source: "unknown",
        sourceURL: "https://another.host/installExtension?name=ext1",
      },
      expect: {
        method: "link",
        source: "unknown",
        sourceURL: "https://another.host/installExtension?name=ext1",
      },
    },
  ];

  for (const { expect, test } of TEST_CASES) {
    await assertAddonInstallMethod(test, expect);
  }
});

add_task(async function test_report_create_and_submit() {
  Services.telemetry.clearEvents();

  // Override the test api server request handler, to be able to
  // intercept the submittions to the test api server.
  let reportSubmitted;
  apiRequestHandler = ({ data, request, response }) => {
    reportSubmitted = JSON.parse(data);
    handleSubmitRequest({ request, response });
  };

  const { addon, extension } = await installTestExtension();

  const reportEntryPoint = "menu";
  const report = await AbuseReporter.createAbuseReport(ADDON_ID, {
    reportEntryPoint,
  });

  equal(report.addon, addon, "Got the expected addon property");
  equal(
    report.reportEntryPoint,
    reportEntryPoint,
    "Got the expected reportEntryPoint"
  );

  const baseReportData = await AbuseReporter.getReportData(addon);
  const reportProperties = {
    message: "test message",
    reason: "test-reason",
  };

  info("Submitting report");
  report.setMessage(reportProperties.message);
  report.setReason(reportProperties.reason);
  await report.submit();

  const expectedEntries = Object.entries({
    report_entry_point: reportEntryPoint,
    ...baseReportData,
    ...reportProperties,
  });

  for (const [expectedKey, expectedValue] of expectedEntries) {
    equal(
      reportSubmitted[expectedKey],
      expectedValue,
      `Got the expected submitted value for "${expectedKey}"`
    );
  }

  TelemetryTestUtils.assertEvents(
    [
      {
        object: reportEntryPoint,
        value: ADDON_ID,
        extra: { addon_type: "extension" },
      },
    ],
    TELEMETRY_EVENTS_FILTERS
  );

  await extension.unload();
});

add_task(async function test_error_recent_submit() {
  Services.telemetry.clearEvents();
  clearAbuseReportState();

  let reportSubmitted;
  apiRequestHandler = ({ data, request, response }) => {
    reportSubmitted = JSON.parse(data);
    handleSubmitRequest({ request, response });
  };

  const { extension } = await installTestExtension();
  const report = await AbuseReporter.createAbuseReport(ADDON_ID, {
    reportEntryPoint: "uninstall",
  });

  const { extension: extension2 } = await installTestExtension({
    manifest: {
      browser_specific_settings: { gecko: { id: ADDON_ID2 } },
      name: "Test Extension2",
    },
  });
  const report2 = await AbuseReporter.createAbuseReport(
    ADDON_ID2,
    REPORT_OPTIONS
  );

  // Submit the two reports in fast sequence.
  report.setReason("reason1");
  report2.setReason("reason2");
  await report.submit();
  await assertRejectsAbuseReportError(report2.submit(), "ERROR_RECENT_SUBMIT");
  equal(
    reportSubmitted.reason,
    "reason1",
    "Server only received the data from the first submission"
  );

  TelemetryTestUtils.assertEvents(
    [
      {
        object: "uninstall",
        value: ADDON_ID,
        extra: { addon_type: "extension" },
      },
      {
        object: REPORT_OPTIONS.reportEntryPoint,
        value: ADDON_ID2,
        extra: {
          addon_type: "extension",
          error_type: "ERROR_RECENT_SUBMIT",
        },
      },
    ],
    TELEMETRY_EVENTS_FILTERS
  );

  await extension.unload();
  await extension2.unload();
});

add_task(async function test_submission_server_error() {
  const { extension } = await installTestExtension();

  async function testErrorCode({
    responseStatus,
    responseText = "",
    expectedErrorType,
    expectedErrorInfo,
    expectRequest = true,
  }) {
    info(
      `Test expected AbuseReportError on response status "${responseStatus}"`
    );
    Services.telemetry.clearEvents();
    clearAbuseReportState();

    let requestReceived = false;
    apiRequestHandler = ({ request, response }) => {
      requestReceived = true;
      response.setStatusLine(request.httpVersion, responseStatus, "Error");
      response.write(responseText);
    };

    const report = await AbuseReporter.createAbuseReport(
      ADDON_ID,
      REPORT_OPTIONS
    );
    report.setReason("a-reason");
    const promiseSubmit = report.submit();
    if (typeof expectedErrorType === "string") {
      // Assert a specific AbuseReportError errorType.
      await assertRejectsAbuseReportError(
        promiseSubmit,
        expectedErrorType,
        expectedErrorInfo
      );
    } else {
      // Assert on a given Error class.
      await Assert.rejects(promiseSubmit, expectedErrorType);
    }
    equal(
      requestReceived,
      expectRequest,
      `${expectRequest ? "" : "Not "}received a request as expected`
    );

    TelemetryTestUtils.assertEvents(
      [
        {
          object: REPORT_OPTIONS.reportEntryPoint,
          value: ADDON_ID,
          extra: {
            addon_type: "extension",
            error_type:
              typeof expectedErrorType === "string"
                ? expectedErrorType
                : "ERROR_UNKNOWN",
          },
        },
      ],
      TELEMETRY_EVENTS_FILTERS
    );
  }

  await testErrorCode({
    responseStatus: 500,
    responseText: "A server error",
    expectedErrorType: "ERROR_SERVER",
    expectedErrorInfo: JSON.stringify({
      status: 500,
      responseText: "A server error",
    }),
  });
  await testErrorCode({
    responseStatus: 404,
    responseText: "Not found error",
    expectedErrorType: "ERROR_CLIENT",
    expectedErrorInfo: JSON.stringify({
      status: 404,
      responseText: "Not found error",
    }),
  });
  // Test response with unexpected status code.
  await testErrorCode({
    responseStatus: 604,
    responseText: "An unexpected status code",
    expectedErrorType: "ERROR_UNKNOWN",
    expectedErrorInfo: JSON.stringify({
      status: 604,
      responseText: "An unexpected status code",
    }),
  });
  // Test response status 200 with invalid json data.
  await testErrorCode({
    responseStatus: 200,
    expectedErrorType: /SyntaxError: JSON.parse/,
  });

  // Test on invalid url.
  Services.prefs.setCharPref(
    "extensions.abuseReport.url",
    "invalid-protocol://abuse-report"
  );
  await testErrorCode({
    expectedErrorType: "ERROR_NETWORK",
    expectRequest: false,
  });

  await extension.unload();
});

add_task(async function set_test_abusereport_url() {
  Services.prefs.setCharPref(
    "extensions.abuseReport.url",
    "http://test.addons.org/api/report/"
  );
});

add_task(async function test_submission_aborting() {
  Services.telemetry.clearEvents();
  clearAbuseReportState();

  const { extension } = await installTestExtension();

  // override the api request handler with one that is never going to reply.
  let receivedRequestsCount = 0;
  let resolvePendingResponses;
  const waitToReply = new Promise(
    resolve => (resolvePendingResponses = resolve)
  );

  const onRequestReceived = new Promise(resolve => {
    apiRequestHandler = ({ request, response }) => {
      response.processAsync();
      response.setStatusLine(request.httpVersion, 200, "OK");
      receivedRequestsCount++;
      resolve();

      // Keep the request pending until resolvePendingResponses have been
      // called.
      waitToReply.then(() => {
        response.finish();
      });
    };
  });

  const report = await AbuseReporter.createAbuseReport(
    ADDON_ID,
    REPORT_OPTIONS
  );
  report.setReason("a-reason");
  const promiseResult = report.submit();

  await onRequestReceived;

  ok(receivedRequestsCount > 0, "Got the expected number of requests");
  ok(
    (await Promise.race([promiseResult, Promise.resolve("pending")])) ===
      "pending",
    "Submission fetch request should still be pending"
  );

  report.abort();

  await assertRejectsAbuseReportError(promiseResult, "ERROR_ABORTED_SUBMIT");

  TelemetryTestUtils.assertEvents(
    [
      {
        object: REPORT_OPTIONS.reportEntryPoint,
        value: ADDON_ID,
        extra: { addon_type: "extension", error_type: "ERROR_ABORTED_SUBMIT" },
      },
    ],
    TELEMETRY_EVENTS_FILTERS
  );

  await extension.unload();

  // Unblock pending requests on the server request handler side, so that the
  // test file can shutdown (otherwise the test run will be stuck after this
  // task completed).
  resolvePendingResponses();
});

add_task(async function test_truncated_string_properties() {
  const generateString = len => new Array(len).fill("a").join("");

  const LONG_STRINGS_ADDON_ID = "addon-with-long-strings-props@mochi.test";
  const { extension } = await installTestExtension({
    manifest: {
      name: generateString(400),
      description: generateString(400),
      browser_specific_settings: { gecko: { id: LONG_STRINGS_ADDON_ID } },
    },
  });

  // Override the test api server request handler, to be able to
  // intercept the properties actually submitted.
  let reportSubmitted;
  apiRequestHandler = ({ data, request, response }) => {
    reportSubmitted = JSON.parse(data);
    handleSubmitRequest({ request, response });
  };

  const report = await AbuseReporter.createAbuseReport(
    LONG_STRINGS_ADDON_ID,
    REPORT_OPTIONS
  );

  report.setMessage("fake-message");
  report.setReason("fake-reason");
  await report.submit();

  const expected = {
    addon_name: generateString(255),
    addon_summary: generateString(255),
  };

  Assert.deepEqual(
    {
      addon_name: reportSubmitted.addon_name,
      addon_summary: reportSubmitted.addon_summary,
    },
    expected,
    "Got the long strings truncated as expected"
  );

  await extension.unload();
});

add_task(async function test_report_recommended() {
  const NON_RECOMMENDED_ADDON_ID = "non-recommended-addon@mochi.test";
  const RECOMMENDED_ADDON_ID = "recommended-addon@mochi.test";

  const now = Date.now();
  const not_before = new Date(now - 3600000).toISOString();
  const not_after = new Date(now + 3600000).toISOString();

  const { extension: nonRecommended } = await installTestExtension({
    manifest: {
      name: "Fake non recommended addon",
      browser_specific_settings: { gecko: { id: NON_RECOMMENDED_ADDON_ID } },
    },
  });

  const { extension: recommended } = await installTestExtension({
    manifest: {
      name: "Fake recommended addon",
      browser_specific_settings: { gecko: { id: RECOMMENDED_ADDON_ID } },
    },
    files: {
      "mozilla-recommendation.json": {
        addon_id: RECOMMENDED_ADDON_ID,
        states: ["recommended"],
        validity: { not_before, not_after },
      },
    },
  });

  // Override the test api server request handler, to be able to
  // intercept the properties actually submitted.
  let reportSubmitted;
  apiRequestHandler = ({ data, request, response }) => {
    reportSubmitted = JSON.parse(data);
    handleSubmitRequest({ request, response });
  };

  async function checkReportedSignature(addonId, expectedAddonSignature) {
    clearAbuseReportState();
    const report = await AbuseReporter.createAbuseReport(
      addonId,
      REPORT_OPTIONS
    );
    report.setMessage("fake-message");
    report.setReason("fake-reason");
    await report.submit();
    equal(
      reportSubmitted.addon_signature,
      expectedAddonSignature,
      `Got the expected addon_signature for ${addonId}`
    );
  }

  await checkReportedSignature(NON_RECOMMENDED_ADDON_ID, "signed");
  await checkReportedSignature(RECOMMENDED_ADDON_ID, "curated");

  await nonRecommended.unload();
  await recommended.unload();
});

add_task(async function test_query_amo_details() {
  async function assertReportOnAMODetails({
    addonId,
    addonType = "extension",
    expectedReport,
  } = {}) {
    // Clear last report timestamp and any telemetry event recorded so far.
    clearAbuseReportState();
    Services.telemetry.clearEvents();

    const report = await AbuseReporter.createAbuseReport(addonId, {
      reportEntryPoint: "menu",
    });

    let reportSubmitted;
    apiRequestHandler = ({ data, request, response }) => {
      reportSubmitted = JSON.parse(data);
      handleSubmitRequest({ request, response });
    };

    report.setMessage("fake message");
    report.setReason("reason1");
    await report.submit();

    Assert.deepEqual(
      expectedReport,
      getProperties(reportSubmitted, Object.keys(expectedReport)),
      "Got the expected report properties"
    );

    // Telemetry recorded for the successfully submitted report.
    TelemetryTestUtils.assertEvents(
      [
        {
          object: "menu",
          value: addonId,
          extra: { addon_type: FAKE_AMO_DETAILS.type },
        },
      ],
      TELEMETRY_EVENTS_FILTERS
    );

    clearAbuseReportState();
  }

  // Add the expected AMO addons details.
  const addonId = "not-installed-addon@mochi.test";
  amoAddonDetailsMap.set(addonId, FAKE_AMO_DETAILS);

  // Test on the default en-US locale.
  Services.prefs.setCharPref(PREF_REQUIRED_LOCALE, "en-US");
  let locale = Services.locale.appLocaleAsBCP47;
  equal(locale, "en-US", "Got the expected app locale set");

  let expectedReport = {
    addon: addonId,
    addon_name: FAKE_AMO_DETAILS.name[locale],
    addon_version: FAKE_AMO_DETAILS.current_version.version,
    addon_install_source: "not_installed",
    addon_install_method: null,
    addon_signature: "curated",
  };

  await assertReportOnAMODetails({ addonId, expectedReport });

  // Test with a non-default locale also available in the AMO details.
  Services.prefs.setCharPref(PREF_REQUIRED_LOCALE, "it-IT");
  locale = Services.locale.appLocaleAsBCP47;
  equal(locale, "it-IT", "Got the expected app locale set");

  expectedReport = {
    ...expectedReport,
    addon_name: FAKE_AMO_DETAILS.name[locale],
  };
  await assertReportOnAMODetails({ addonId, expectedReport });

  // Test with a non-default locale not available in the AMO details.
  Services.prefs.setCharPref(PREF_REQUIRED_LOCALE, "fr-FR");
  locale = Services.locale.appLocaleAsBCP47;
  equal(locale, "fr-FR", "Got the expected app locale set");

  expectedReport = {
    ...expectedReport,
    // Fallbacks on en-US for non available locales.
    addon_name: FAKE_AMO_DETAILS.name["en-US"],
  };
  await assertReportOnAMODetails({ addonId, expectedReport });

  Services.prefs.clearUserPref(PREF_REQUIRED_LOCALE);

  amoAddonDetailsMap.clear();
});

add_task(async function test_statictheme_normalized_into_type_theme() {
  const themeId = "not-installed-statictheme@mochi.test";
  amoAddonDetailsMap.set(themeId, {
    ...FAKE_AMO_DETAILS,
    type: "statictheme",
  });

  const report = await AbuseReporter.createAbuseReport(themeId, REPORT_OPTIONS);

  equal(report.addon.id, themeId, "Got a report for the expected theme id");
  equal(report.addon.type, "theme", "Got the expected addon type");

  amoAddonDetailsMap.clear();
});
