Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

/* Any copyright is dedicated to the Public Domain.
"use strict";
const { AddonsEngine } = ChromeUtils.importESModule(
);
const { Service } = ChromeUtils.importESModule(
);
const { FileUtils } = ChromeUtils.importESModule(
"resource://gre/modules/FileUtils.sys.mjs"
);
const { SyncedRecordsTelemetry } = ChromeUtils.importESModule(
);
const HTTP_PORT = 8888;
Services.prefs.setStringPref(
"extensions.getAddons.get.url",
);
// Note that all compat-override URLs currently 404, but that's OK - the main
// thing is to avoid us hitting the real AMO.
Services.prefs.setStringPref(
"extensions.getAddons.compatOverides.url",
);
Services.prefs.setBoolPref("extensions.install.requireSecureOrigin", false);
Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
AddonTestUtils.init(this);
AddonTestUtils.createAppInfo(
"xpcshell@tests.mozilla.org",
"XPCShell",
"1",
"1.9.2"
);
AddonTestUtils.overrideCertDB();
Services.prefs.setBoolPref("extensions.experiments.enabled", true);
const SYSTEM_ADDON_ID = "system1@tests.mozilla.org";
add_setup(async function setupSystemAddon() {
const distroDir = FileUtils.getDir("ProfD", ["sysfeatures", "app0"]);
distroDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
AddonTestUtils.registerDirectory("XREAppFeat", distroDir);
let xpi = await AddonTestUtils.createTempWebExtensionFile({
manifest: {
browser_specific_settings: { gecko: { id: SYSTEM_ADDON_ID } },
},
});
xpi.copyTo(distroDir, `${SYSTEM_ADDON_ID}.xpi`);
await AddonTestUtils.overrideBuiltIns({ system: [SYSTEM_ADDON_ID] });
await AddonTestUtils.promiseStartupManager();
});
const ID1 = "addon1@tests.mozilla.org";
const ID2 = "addon2@tests.mozilla.org";
const ID3 = "addon3@tests.mozilla.org";
const ADDONS = {
test_addon1: {
manifest: {
browser_specific_settings: {
gecko: {
id: ID1,
},
},
},
},
test_addon2: {
manifest: {
browser_specific_settings: { gecko: { id: ID2 } },
},
},
test_addon3: {
manifest: {
browser_specific_settings: {
gecko: {
id: ID3,
strict_max_version: "0",
},
},
},
},
};
const SEARCH_RESULT = {
next: null,
results: [
{
name: "Test Extension",
type: "extension",
guid: "addon1@tests.mozilla.org",
current_version: {
version: "1.0",
files: [
{
platform: "all",
size: 485,
},
],
},
last_updated: "2018-10-27T04:12:00.826Z",
},
],
};
const MISSING_SEARCH_RESULT = {
next: null,
results: [
{
name: "Test",
type: "extension",
guid: "missing-xpi@tests.mozilla.org",
current_version: {
version: "1.0",
files: [
{
platform: "all",
size: 123,
},
],
},
},
],
};
const AMOSIGNED_SHA1_SEARCH_RESULT = {
next: null,
results: [
{
name: "Test Extension",
type: "extension",
guid: "amosigned-xpi@tests.mozilla.org",
current_version: {
version: "2.1",
files: [
{
platform: "all",
size: 4287,
},
],
},
last_updated: "2024-03-21T16:00:06.640Z",
},
],
};
const XPIS = {};
for (let [name, files] of Object.entries(ADDONS)) {
XPIS[name] = AddonTestUtils.createTempWebExtensionFile(files);
}
let engine;
let store;
let reconciler;
const proxyService = Cc[
"@mozilla.org/network/protocol-proxy-service;1"
].getService(Ci.nsIProtocolProxyService);
const proxyFilter = {
proxyInfo: proxyService.newProxyInfo(
"http",
"localhost",
HTTP_PORT,
"",
"",
0,
4096,
null
),
applyFilter(channel, defaultProxyInfo, callback) {
if (channel.URI.host === "example.com") {
callback.onProxyFilterResult(this.proxyInfo);
} else {
callback.onProxyFilterResult(defaultProxyInfo);
}
},
};
proxyService.registerChannelFilter(proxyFilter, 0);
registerCleanupFunction(() => {
proxyService.unregisterChannelFilter(proxyFilter);
});
/**
* Create a AddonsRec for this application with the fields specified.
*
* @param id Sync GUID of record
* @param addonId ID of add-on
* @param enabled Boolean whether record is enabled
* @param deleted Boolean whether record was deleted
*/
function createRecordForThisApp(id, addonId, enabled, deleted) {
return {
id,
addonID: addonId,
enabled,
deleted: !!deleted,
applicationID: Services.appinfo.ID,
source: "amo",
};
}
function createAndStartHTTPServer(port) {
try {
let server = new HttpServer();
server.registerPathHandler(
"/search/guid:addon1%40tests.mozilla.org",
(req, resp) => {
resp.setHeader("Content-type", "application/json", true);
resp.write(JSON.stringify(SEARCH_RESULT));
}
);
server.registerPathHandler(
"/search/guid:missing-xpi%40tests.mozilla.org",
(req, resp) => {
resp.setHeader("Content-type", "application/json", true);
resp.write(JSON.stringify(MISSING_SEARCH_RESULT));
}
);
server.registerFile("/addon1.xpi", XPIS.test_addon1);
server.registerPathHandler(
"/search/guid:amosigned-xpi%40tests.mozilla.org",
(req, resp) => {
resp.setHeader("Content-type", "application/json", true);
resp.write(JSON.stringify(AMOSIGNED_SHA1_SEARCH_RESULT));
}
);
server.registerFile(
"/amosigned-sha1only.xpi",
do_get_file("amosigned-sha1only.xpi")
);
server.start(port);
return server;
} catch (ex) {
_("Got exception starting HTTP server on port " + port);
_("Error: " + Log.exceptionStr(ex));
do_throw(ex);
}
return null; /* not hit, but keeps eslint happy! */
}
// A helper function to ensure that the reconciler's current view of the addon
// is the same as the addon itself. If it's not, then the reconciler missed a
// change, and is likely to re-upload the addon next sync because of the change
// it missed.
async function checkReconcilerUpToDate(addon) {
let stateBefore = Object.assign({}, store.reconciler.addons[addon.id]);
await store.reconciler.rectifyStateFromAddon(addon);
let stateAfter = store.reconciler.addons[addon.id];
deepEqual(stateBefore, stateAfter);
}
add_setup(async function setup() {
await Service.engineManager.register(AddonsEngine);
engine = Service.engineManager.get("addons");
store = engine._store;
reconciler = engine._reconciler;
reconciler.startListening();
// Don't flush to disk in the middle of an event listener!
// This causes test hangs on WinXP.
reconciler._shouldPersist = false;
});
add_task(async function test_remove() {
_("Ensure removing add-ons from deleted records works.");
let addon = await installAddon(XPIS.test_addon1, reconciler);
let record = createRecordForThisApp(addon.syncGUID, ID1, true, true);
let countTelemetry = new SyncedRecordsTelemetry();
let failed = await store.applyIncomingBatch([record], countTelemetry);
Assert.equal(0, failed.length);
Assert.equal(null, countTelemetry.failedReasons);
Assert.equal(0, countTelemetry.incomingCounts.failed);
let newAddon = await AddonManager.getAddonByID(ID1);
Assert.equal(null, newAddon);
});
add_task(async function test_apply_enabled() {
let countTelemetry = new SyncedRecordsTelemetry();
_("Ensures that changes to the userEnabled flag apply.");
let addon = await installAddon(XPIS.test_addon1, reconciler);
Assert.ok(addon.isActive);
Assert.ok(!addon.userDisabled);
_("Ensure application of a disable record works as expected.");
let records = [];
records.push(createRecordForThisApp(addon.syncGUID, ID1, false, false));
let [failed] = await Promise.all([
store.applyIncomingBatch(records, countTelemetry),
AddonTestUtils.promiseAddonEvent("onDisabled"),
]);
Assert.equal(0, failed.length);
Assert.equal(0, countTelemetry.incomingCounts.failed);
addon = await AddonManager.getAddonByID(ID1);
Assert.ok(addon.userDisabled);
await checkReconcilerUpToDate(addon);
records = [];
_("Ensure enable record works as expected.");
records.push(createRecordForThisApp(addon.syncGUID, ID1, true, false));
[failed] = await Promise.all([
store.applyIncomingBatch(records, countTelemetry),
AddonTestUtils.promiseWebExtensionStartup(ID1),
]);
Assert.equal(0, failed.length);
Assert.equal(0, countTelemetry.incomingCounts.failed);
addon = await AddonManager.getAddonByID(ID1);
Assert.ok(!addon.userDisabled);
await checkReconcilerUpToDate(addon);
records = [];
_("Ensure enabled state updates don't apply if the ignore pref is set.");
records.push(createRecordForThisApp(addon.syncGUID, ID1, false, false));
Svc.PrefBranch.setBoolPref("addons.ignoreUserEnabledChanges", true);
failed = await store.applyIncomingBatch(records, countTelemetry);
Assert.equal(0, failed.length);
Assert.equal(0, countTelemetry.incomingCounts.failed);
addon = await AddonManager.getAddonByID(ID1);
Assert.ok(!addon.userDisabled);
records = [];
await uninstallAddon(addon, reconciler);
Svc.PrefBranch.clearUserPref("addons.ignoreUserEnabledChanges");
});
add_task(async function test_apply_enabled_appDisabled() {
_(
"Ensures that changes to the userEnabled flag apply when the addon is appDisabled."
);
// this addon is appDisabled by default.
let addon = await installAddon(XPIS.test_addon3);
Assert.ok(addon.appDisabled);
Assert.ok(!addon.isActive);
Assert.ok(!addon.userDisabled);
_("Ensure application of a disable record works as expected.");
store.reconciler.pruneChangesBeforeDate(Date.now() + 10);
store.reconciler._changes = [];
let records = [];
let countTelemetry = new SyncedRecordsTelemetry();
records.push(createRecordForThisApp(addon.syncGUID, ID3, false, false));
let failed = await store.applyIncomingBatch(records, countTelemetry);
Assert.equal(0, failed.length);
Assert.equal(0, countTelemetry.incomingCounts.failed);
addon = await AddonManager.getAddonByID(ID3);
Assert.ok(addon.userDisabled);
await checkReconcilerUpToDate(addon);
records = [];
_("Ensure enable record works as expected.");
records.push(createRecordForThisApp(addon.syncGUID, ID3, true, false));
failed = await store.applyIncomingBatch(records, countTelemetry);
Assert.equal(0, failed.length);
Assert.equal(0, countTelemetry.incomingCounts.failed);
addon = await AddonManager.getAddonByID(ID3);
Assert.ok(!addon.userDisabled);
await checkReconcilerUpToDate(addon);
records = [];
await uninstallAddon(addon, reconciler);
});
add_task(async function test_ignore_different_appid() {
_(
"Ensure that incoming records with a different application ID are ignored."
);
// We test by creating a record that should result in an update.
let addon = await installAddon(XPIS.test_addon1, reconciler);
Assert.ok(!addon.userDisabled);
let record = createRecordForThisApp(addon.syncGUID, ID1, false, false);
record.applicationID = "FAKE_ID";
let countTelemetry = new SyncedRecordsTelemetry();
let failed = await store.applyIncomingBatch([record], countTelemetry);
Assert.equal(0, failed.length);
let newAddon = await AddonManager.getAddonByID(ID1);
Assert.ok(!newAddon.userDisabled);
await uninstallAddon(addon, reconciler);
});
add_task(async function test_ignore_unknown_source() {
_("Ensure incoming records with unknown source are ignored.");
let addon = await installAddon(XPIS.test_addon1, reconciler);
let record = createRecordForThisApp(addon.syncGUID, ID1, false, false);
record.source = "DUMMY_SOURCE";
let countTelemetry = new SyncedRecordsTelemetry();
let failed = await store.applyIncomingBatch([record], countTelemetry);
Assert.equal(0, failed.length);
let newAddon = await AddonManager.getAddonByID(ID1);
Assert.ok(!newAddon.userDisabled);
await uninstallAddon(addon, reconciler);
});
add_task(async function test_apply_uninstall() {
_("Ensures that uninstalling an add-on from a record works.");
let addon = await installAddon(XPIS.test_addon1, reconciler);
let records = [];
let countTelemetry = new SyncedRecordsTelemetry();
records.push(createRecordForThisApp(addon.syncGUID, ID1, true, true));
let failed = await store.applyIncomingBatch(records, countTelemetry);
Assert.equal(0, failed.length);
Assert.equal(0, countTelemetry.incomingCounts.failed);
addon = await AddonManager.getAddonByID(ID1);
Assert.equal(null, addon);
});
add_task(async function test_addon_syncability() {
_("Ensure isAddonSyncable functions properly.");
Svc.PrefBranch.setStringPref(
"addons.trustedSourceHostnames",
"addons.mozilla.org,other.example.com"
);
Assert.ok(!(await store.isAddonSyncable(null)));
let addon = await installAddon(XPIS.test_addon1, reconciler);
Assert.ok(await store.isAddonSyncable(addon));
let dummy = {};
const KEYS = [
"id",
"syncGUID",
"type",
"scope",
"foreignInstall",
"isSyncable",
];
for (let k of KEYS) {
dummy[k] = addon[k];
}
Assert.ok(await store.isAddonSyncable(dummy));
dummy.type = "UNSUPPORTED";
Assert.ok(!(await store.isAddonSyncable(dummy)));
dummy.type = addon.type;
dummy.scope = 0;
Assert.ok(!(await store.isAddonSyncable(dummy)));
dummy.scope = addon.scope;
dummy.isSyncable = false;
Assert.ok(!(await store.isAddonSyncable(dummy)));
dummy.isSyncable = addon.isSyncable;
dummy.foreignInstall = true;
Assert.ok(!(await store.isAddonSyncable(dummy)));
dummy.foreignInstall = false;
await uninstallAddon(addon, reconciler);
Assert.ok(!store.isSourceURITrusted(null));
let trusted = [
];
let untrusted = [
"https://untrusted.example.com/foo", // non-trusted hostname`
];
for (let uri of trusted) {
Assert.ok(store.isSourceURITrusted(Services.io.newURI(uri)));
}
for (let uri of untrusted) {
Assert.ok(!store.isSourceURITrusted(Services.io.newURI(uri)));
}
Svc.PrefBranch.setStringPref("addons.trustedSourceHostnames", "");
for (let uri of trusted) {
Assert.ok(!store.isSourceURITrusted(Services.io.newURI(uri)));
}
Svc.PrefBranch.setStringPref(
"addons.trustedSourceHostnames",
"addons.mozilla.org"
);
Assert.ok(
store.isSourceURITrusted(
Services.io.newURI("https://addons.mozilla.org/foo")
)
);
Svc.PrefBranch.clearUserPref("addons.trustedSourceHostnames");
});
add_task(async function test_get_all_ids() {
_("Ensures that getAllIDs() returns an appropriate set.");
_("Installing two addons.");
// XXX - this test seems broken - at this point, before we've installed the
// addons below, store.getAllIDs() returns all addons installed by previous
// tests, even though those tests uninstalled the addon.
// So if any tests above ever add a new addon ID, they are going to need to
// be added here too.
// Assert.equal(0, Object.keys(store.getAllIDs()).length);
let addon1 = await installAddon(XPIS.test_addon1, reconciler);
let addon2 = await installAddon(XPIS.test_addon2, reconciler);
let addon3 = await installAddon(XPIS.test_addon3, reconciler);
_("Ensure they're syncable.");
Assert.ok(await store.isAddonSyncable(addon1));
Assert.ok(await store.isAddonSyncable(addon2));
Assert.ok(await store.isAddonSyncable(addon3));
let ids = await store.getAllIDs();
Assert.equal("object", typeof ids);
Assert.equal(3, Object.keys(ids).length);
Assert.ok(addon1.syncGUID in ids);
Assert.ok(addon2.syncGUID in ids);
Assert.ok(addon3.syncGUID in ids);
await uninstallAddon(addon1, reconciler);
await uninstallAddon(addon2, reconciler);
await uninstallAddon(addon3, reconciler);
});
add_task(async function test_change_item_id() {
_("Ensures that changeItemID() works properly.");
let addon = await installAddon(XPIS.test_addon1, reconciler);
let oldID = addon.syncGUID;
let newID = Utils.makeGUID();
await store.changeItemID(oldID, newID);
let newAddon = await AddonManager.getAddonByID(ID1);
Assert.notEqual(null, newAddon);
Assert.equal(newID, newAddon.syncGUID);
await uninstallAddon(newAddon, reconciler);
});
add_task(async function test_create() {
_("Ensure creating/installing an add-on from a record works.");
let server = createAndStartHTTPServer(HTTP_PORT);
let guid = Utils.makeGUID();
let record = createRecordForThisApp(guid, ID1, true, false);
let countTelemetry = new SyncedRecordsTelemetry();
let failed = await store.applyIncomingBatch([record], countTelemetry);
Assert.equal(0, failed.length);
let newAddon = await AddonManager.getAddonByID(ID1);
Assert.notEqual(null, newAddon);
Assert.equal(guid, newAddon.syncGUID);
Assert.ok(!newAddon.userDisabled);
await uninstallAddon(newAddon, reconciler);
await promiseStopServer(server);
});
add_task(async function test_weak_signature_restrictions() {
_("Ensure installing add-ons with a weak signature fails when restricted.");
// Ensure restrictions on weak signatures are enabled (this should be removed when
// the new behavior is riding the train).
const resetWeakSignaturePref =
AddonTestUtils.setWeakSignatureInstallAllowed(false);
const server = createAndStartHTTPServer(HTTP_PORT);
const ID_TEST_SHA1 = "amosigned-xpi@tests.mozilla.org";
const guidKO = Utils.makeGUID();
const guidOK = Utils.makeGUID();
const recordKO = createRecordForThisApp(guidKO, ID_TEST_SHA1, true, false);
const recordOK = createRecordForThisApp(guidOK, ID1, true, false);
const countTelemetry = new SyncedRecordsTelemetry();
let failed;
const { messages } = await AddonTestUtils.promiseConsoleOutput(async () => {
failed = await store.applyIncomingBatch(
[recordKO, recordOK],
countTelemetry
);
});
Assert.equal(
1,
failed.length,
"Expect only 1 on the two synced add-ons to fail"
);
resetWeakSignaturePref();
let addonKO = await AddonManager.getAddonByID(ID_TEST_SHA1);
Assert.equal(null, addonKO, `Expect ${ID_TEST_SHA1} to NOT be installed`);
let addonOK = await AddonManager.getAddonByID(ID1);
Assert.notEqual(null, addonOK, `Expect ${ID1} to be installed`);
await uninstallAddon(addonOK, reconciler);
await promiseStopServer(server);
AddonTestUtils.checkMessages(messages, {
expected: [
{
message:
/Download of .*\/amosigned-sha1only.xpi failed: install rejected due to the package not including a strong cryptographic signature/,
},
],
});
});
add_task(async function test_create_missing_search() {
_("Ensures that failed add-on searches are handled gracefully.");
let server = createAndStartHTTPServer(HTTP_PORT);
// The handler for this ID is not installed, so a search should 404.
const id = "missing@tests.mozilla.org";
let guid = Utils.makeGUID();
let record = createRecordForThisApp(guid, id, true, false);
let countTelemetry = new SyncedRecordsTelemetry();
let failed = await store.applyIncomingBatch([record], countTelemetry);
Assert.equal(1, failed.length);
Assert.equal(guid, failed[0]);
Assert.equal(
countTelemetry.incomingCounts.failedReasons[0].name,
"GET <URL> failed (status 404)"
);
Assert.equal(countTelemetry.incomingCounts.failedReasons[0].count, 1);
let addon = await AddonManager.getAddonByID(id);
Assert.equal(null, addon);
await promiseStopServer(server);
});
add_task(async function test_create_bad_install() {
_("Ensures that add-ons without a valid install are handled gracefully.");
let server = createAndStartHTTPServer(HTTP_PORT);
// The handler returns a search result but the XPI will 404.
const id = "missing-xpi@tests.mozilla.org";
let guid = Utils.makeGUID();
let record = createRecordForThisApp(guid, id, true, false);
let countTelemetry = new SyncedRecordsTelemetry();
/* let failed = */ await store.applyIncomingBatch([record], countTelemetry);
// This addon had no source URI so was skipped - but it's not treated as
// failure.
// XXX - this test isn't testing what we thought it was. Previously the addon
// was not being installed due to requireSecureURL checking *before* we'd
// attempted to get the XPI.
// With requireSecureURL disabled we do see a download failure, but the addon
// *does* get added to |failed|.
// FTR: onDownloadFailed() is called with ERROR_NETWORK_FAILURE, so it's going
// to be tricky to distinguish a 404 from other transient network errors
// where we do want the addon to end up in |failed|.
// This is being tracked in bug 1284778.
// Assert.equal(0, failed.length);
let addon = await AddonManager.getAddonByID(id);
Assert.equal(null, addon);
await promiseStopServer(server);
});
add_task(async function test_ignore_system() {
_("Ensure we ignore system addons");
// Our system addon should not appear in getAllIDs
await engine._refreshReconcilerState();
let num = 0;
let ids = await store.getAllIDs();
for (let guid in ids) {
num += 1;
let addon = reconciler.getAddonStateFromSyncGUID(guid);
Assert.notEqual(addon.id, SYSTEM_ADDON_ID);
}
Assert.greater(num, 1, "should have seen at least one.");
});
add_task(async function test_incoming_system() {
_("Ensure we handle incoming records that refer to a system addon");
// eg, loop initially had a normal addon but it was then "promoted" to be a
// system addon but wanted to keep the same ID. The server record exists due
// to this.
// before we start, ensure the system addon isn't disabled.
Assert.ok(!(await AddonManager.getAddonByID(SYSTEM_ADDON_ID).userDisabled));
// Now simulate an incoming record with the same ID as the system addon,
// but flagged as disabled - it should not be applied.
let server = createAndStartHTTPServer(HTTP_PORT);
// We make the incoming record flag the system addon as disabled - it should
// be ignored.
let guid = Utils.makeGUID();
let record = createRecordForThisApp(guid, SYSTEM_ADDON_ID, false, false);
let countTelemetry = new SyncedRecordsTelemetry();
let failed = await store.applyIncomingBatch([record], countTelemetry);
Assert.equal(0, failed.length);
// The system addon should still not be userDisabled.
Assert.ok(!(await AddonManager.getAddonByID(SYSTEM_ADDON_ID).userDisabled));
await promiseStopServer(server);
});
add_task(async function test_wipe() {
_("Ensures that wiping causes add-ons to be uninstalled.");
await installAddon(XPIS.test_addon1, reconciler);
await store.wipe();
let addon = await AddonManager.getAddonByID(ID1);
Assert.equal(null, addon);
});
add_task(async function test_wipe_and_install() {
_("Ensure wipe followed by install works.");
// This tests the reset sync flow where remote data is replaced by local. The
// receiving client will see a wipe followed by a record which should undo
// the wipe.
let installed = await installAddon(XPIS.test_addon1, reconciler);
let record = createRecordForThisApp(installed.syncGUID, ID1, true, false);
await store.wipe();
let deleted = await AddonManager.getAddonByID(ID1);
Assert.equal(null, deleted);
// Re-applying the record can require re-fetching the XPI.
let server = createAndStartHTTPServer(HTTP_PORT);
await store.applyIncoming(record);
let fetched = await AddonManager.getAddonByID(record.addonID);
Assert.ok(!!fetched);
// wipe again to we are left with a clean slate.
await store.wipe();
await promiseStopServer(server);
});
// STR for what this is testing:
// * Either:
// * Install then remove an addon, then delete addons.json from the profile
// or corrupt it (in which case the addon manager will remove it)
// * Install then remove an addon while addon caching is disabled, then
// re-enable addon caching.
// * Install the same addon in a different profile, sync it.
// * Sync this profile
// Before bug 1467904, the addon would fail to install because this profile
// has a copy of the addon in our addonsreconciler.json, but the addon manager
// does *not* have a copy in its cache, and repopulating that cache would not
// re-add it as the addon is no longer installed locally.
add_task(async function test_incoming_reconciled_but_not_cached() {
_(
"Ensure we handle incoming records our reconciler has but the addon cache does not"
);
// Make sure addon is not installed.
let addon = await AddonManager.getAddonByID(ID1);
Assert.equal(null, addon);
Services.prefs.setBoolPref("extensions.getAddons.cache.enabled", false);
addon = await installAddon(XPIS.test_addon1, reconciler);
Assert.notEqual(await AddonManager.getAddonByID(ID1), null);
await uninstallAddon(addon, reconciler);
Services.prefs.setBoolPref("extensions.getAddons.cache.enabled", true);
// now pretend it is incoming.
let server = createAndStartHTTPServer(HTTP_PORT);
let guid = Utils.makeGUID();
let record = createRecordForThisApp(guid, ID1, true, false);
let countTelemetry = new SyncedRecordsTelemetry();
let failed = await store.applyIncomingBatch([record], countTelemetry);
Assert.equal(0, failed.length);
Assert.notEqual(await AddonManager.getAddonByID(ID1), null);
await promiseStopServer(server);
});
// NOTE: The test above must be the last test run due to the addon cache
// being trashed. It is probably possible to fix that by running, eg,
// AddonRespository.backgroundUpdateCheck() to rebuild the cache, but that
// requires implementing more AMO functionality in our test server
add_task(async function cleanup() {
// There's an xpcom-shutdown hook for this, but let's give this a shot.
reconciler.stopListening();
});