summaryrefslogtreecommitdiffstats
path: root/toolkit/components/normandy/test/browser/browser_actions_AddonRolloutAction.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/normandy/test/browser/browser_actions_AddonRolloutAction.js')
-rw-r--r--toolkit/components/normandy/test/browser/browser_actions_AddonRolloutAction.js512
1 files changed, 512 insertions, 0 deletions
diff --git a/toolkit/components/normandy/test/browser/browser_actions_AddonRolloutAction.js b/toolkit/components/normandy/test/browser/browser_actions_AddonRolloutAction.js
new file mode 100644
index 0000000000..cbac8524f3
--- /dev/null
+++ b/toolkit/components/normandy/test/browser/browser_actions_AddonRolloutAction.js
@@ -0,0 +1,512 @@
+"use strict";
+
+const { AddonRolloutAction } = ChromeUtils.importESModule(
+ "resource://normandy/actions/AddonRolloutAction.sys.mjs"
+);
+const { BaseAction } = ChromeUtils.importESModule(
+ "resource://normandy/actions/BaseAction.sys.mjs"
+);
+const { AddonRollouts } = ChromeUtils.importESModule(
+ "resource://normandy/lib/AddonRollouts.sys.mjs"
+);
+const { NormandyTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/NormandyTestUtils.sys.mjs"
+);
+
+// Test that a simple recipe enrolls as expected
+decorate_task(
+ AddonRollouts.withTestMock(),
+ ensureAddonCleanup(),
+ withMockNormandyApi(),
+ withStub(TelemetryEnvironment, "setExperimentActive"),
+ withSendEventSpy(),
+ async function simple_recipe_enrollment({
+ mockNormandyApi,
+ setExperimentActiveStub,
+ sendEventSpy,
+ }) {
+ const recipe = {
+ id: 1,
+ arguments: {
+ slug: "test-rollout",
+ extensionApiId: 1,
+ },
+ };
+ mockNormandyApi.extensionDetails = {
+ [recipe.arguments.extensionApiId]: extensionDetailsFactory({
+ id: recipe.arguments.extensionApiId,
+ }),
+ };
+
+ const webExtStartupPromise =
+ AddonTestUtils.promiseWebExtensionStartup(FIXTURE_ADDON_ID);
+
+ const action = new AddonRolloutAction();
+ await action.processRecipe(recipe, BaseAction.suitability.FILTER_MATCH);
+ is(action.lastError, null, "lastError should be null");
+
+ await webExtStartupPromise;
+
+ // addon was installed
+ const addon = await AddonManager.getAddonByID(FIXTURE_ADDON_ID);
+ is(addon.id, FIXTURE_ADDON_ID, "addon should be installed");
+
+ // rollout was stored
+ const rollouts = await AddonRollouts.getAll();
+ Assert.deepEqual(
+ rollouts,
+ [
+ {
+ recipeId: recipe.id,
+ slug: "test-rollout",
+ state: AddonRollouts.STATE_ACTIVE,
+ extensionApiId: 1,
+ addonId: FIXTURE_ADDON_ID,
+ addonVersion: "1.0",
+ xpiUrl: FIXTURE_ADDON_DETAILS["normandydriver-a-1.0"].url,
+ xpiHash: FIXTURE_ADDON_DETAILS["normandydriver-a-1.0"].hash,
+ xpiHashAlgorithm: "sha256",
+ },
+ ],
+ "Rollout should be stored in db"
+ );
+
+ sendEventSpy.assertEvents([
+ ["enroll", "addon_rollout", recipe.arguments.slug],
+ ]);
+ ok(
+ setExperimentActiveStub.calledWithExactly("test-rollout", "active", {
+ type: "normandy-addonrollout",
+ }),
+ "a telemetry experiment should be activated"
+ );
+
+ // cleanup installed addon
+ await addon.uninstall();
+ }
+);
+
+// Test that a rollout can update the addon
+decorate_task(
+ AddonRollouts.withTestMock(),
+ ensureAddonCleanup(),
+ withMockNormandyApi(),
+ withSendEventSpy(),
+ async function update_rollout({ mockNormandyApi, sendEventSpy }) {
+ // first enrollment
+ const recipe = {
+ id: 1,
+ arguments: {
+ slug: "test-rollout",
+ extensionApiId: 1,
+ },
+ };
+ mockNormandyApi.extensionDetails = {
+ [recipe.arguments.extensionApiId]: extensionDetailsFactory({
+ id: recipe.arguments.extensionApiId,
+ }),
+ 2: extensionDetailsFactory({
+ id: 2,
+ xpi: FIXTURE_ADDON_DETAILS["normandydriver-a-2.0"].url,
+ version: "2.0",
+ hash: FIXTURE_ADDON_DETAILS["normandydriver-a-2.0"].hash,
+ }),
+ };
+
+ let webExtStartupPromise =
+ AddonTestUtils.promiseWebExtensionStartup(FIXTURE_ADDON_ID);
+
+ let action = new AddonRolloutAction();
+ await action.processRecipe(recipe, BaseAction.suitability.FILTER_MATCH);
+ is(action.lastError, null, "lastError should be null");
+
+ await webExtStartupPromise;
+
+ // addon was installed
+ let addon = await AddonManager.getAddonByID(FIXTURE_ADDON_ID);
+ is(addon.id, FIXTURE_ADDON_ID, "addon should be installed");
+ is(addon.version, "1.0", "addon should be the correct version");
+
+ // update existing enrollment
+ recipe.arguments.extensionApiId = 2;
+ webExtStartupPromise =
+ AddonTestUtils.promiseWebExtensionStartup(FIXTURE_ADDON_ID);
+ action = new AddonRolloutAction();
+ await action.processRecipe(recipe, BaseAction.suitability.FILTER_MATCH);
+ is(action.lastError, null, "lastError should be null");
+
+ await webExtStartupPromise;
+
+ addon = await AddonManager.getAddonByID(FIXTURE_ADDON_ID);
+ is(addon.id, FIXTURE_ADDON_ID, "addon should still be installed");
+ is(addon.version, "2.0", "addon should be the correct version");
+
+ // rollout in the DB has been updated
+ const rollouts = await AddonRollouts.getAll();
+ Assert.deepEqual(
+ rollouts,
+ [
+ {
+ recipeId: recipe.id,
+ slug: "test-rollout",
+ state: AddonRollouts.STATE_ACTIVE,
+ extensionApiId: 2,
+ addonId: FIXTURE_ADDON_ID,
+ addonVersion: "2.0",
+ xpiUrl: FIXTURE_ADDON_DETAILS["normandydriver-a-2.0"].url,
+ xpiHash: FIXTURE_ADDON_DETAILS["normandydriver-a-2.0"].hash,
+ xpiHashAlgorithm: "sha256",
+ },
+ ],
+ "Rollout should be stored in db"
+ );
+
+ sendEventSpy.assertEvents([
+ ["enroll", "addon_rollout", "test-rollout"],
+ ["update", "addon_rollout", "test-rollout"],
+ ]);
+
+ // Cleanup
+ await addon.uninstall();
+ }
+);
+
+// Re-running a recipe does nothing
+decorate_task(
+ AddonRollouts.withTestMock(),
+ ensureAddonCleanup(),
+ withMockNormandyApi(),
+ withSendEventSpy(),
+ async function rerun_recipe({ mockNormandyApi, sendEventSpy }) {
+ const recipe = {
+ id: 1,
+ arguments: {
+ slug: "test-rollout",
+ extensionApiId: 1,
+ },
+ };
+ mockNormandyApi.extensionDetails = {
+ [recipe.arguments.extensionApiId]: extensionDetailsFactory({
+ id: recipe.arguments.extensionApiId,
+ }),
+ };
+
+ const webExtStartupPromise =
+ AddonTestUtils.promiseWebExtensionStartup(FIXTURE_ADDON_ID);
+
+ let action = new AddonRolloutAction();
+ await action.processRecipe(recipe, BaseAction.suitability.FILTER_MATCH);
+ is(action.lastError, null, "lastError should be null");
+
+ await webExtStartupPromise;
+
+ // addon was installed
+ let addon = await AddonManager.getAddonByID(FIXTURE_ADDON_ID);
+ is(addon.id, FIXTURE_ADDON_ID, "addon should be installed");
+ is(addon.version, "1.0", "addon should be the correct version");
+
+ // re-run the same recipe
+ action = new AddonRolloutAction();
+ await action.processRecipe(recipe, BaseAction.suitability.FILTER_MATCH);
+ is(action.lastError, null, "lastError should be null");
+
+ addon = await AddonManager.getAddonByID(FIXTURE_ADDON_ID);
+ is(addon.id, FIXTURE_ADDON_ID, "addon should still be installed");
+ is(addon.version, "1.0", "addon should be the correct version");
+
+ // rollout in the DB has not been updated
+ const rollouts = await AddonRollouts.getAll();
+ Assert.deepEqual(
+ rollouts,
+ [
+ {
+ recipeId: recipe.id,
+ slug: "test-rollout",
+ state: AddonRollouts.STATE_ACTIVE,
+ extensionApiId: 1,
+ addonId: FIXTURE_ADDON_ID,
+ addonVersion: "1.0",
+ xpiUrl: FIXTURE_ADDON_DETAILS["normandydriver-a-1.0"].url,
+ xpiHash: FIXTURE_ADDON_DETAILS["normandydriver-a-1.0"].hash,
+ xpiHashAlgorithm: "sha256",
+ },
+ ],
+ "Rollout should be stored in db"
+ );
+
+ sendEventSpy.assertEvents([["enroll", "addon_rollout", "test-rollout"]]);
+
+ // Cleanup
+ await addon.uninstall();
+ }
+);
+
+// Conflicting rollouts
+decorate_task(
+ AddonRollouts.withTestMock(),
+ ensureAddonCleanup(),
+ withMockNormandyApi(),
+ withSendEventSpy(),
+ async function conflicting_rollout({ mockNormandyApi, sendEventSpy }) {
+ const recipe = {
+ id: 1,
+ arguments: {
+ slug: "test-rollout",
+ extensionApiId: 1,
+ },
+ };
+ mockNormandyApi.extensionDetails = {
+ [recipe.arguments.extensionApiId]: extensionDetailsFactory({
+ id: recipe.arguments.extensionApiId,
+ }),
+ };
+
+ const webExtStartupPromise =
+ AddonTestUtils.promiseWebExtensionStartup(FIXTURE_ADDON_ID);
+
+ let action = new AddonRolloutAction();
+ await action.processRecipe(recipe, BaseAction.suitability.FILTER_MATCH);
+ is(action.lastError, null, "lastError should be null");
+
+ await webExtStartupPromise;
+
+ // addon was installed
+ let addon = await AddonManager.getAddonByID(FIXTURE_ADDON_ID);
+ is(addon.id, FIXTURE_ADDON_ID, "addon should be installed");
+ is(addon.version, "1.0", "addon should be the correct version");
+
+ // update existing enrollment
+ action = new AddonRolloutAction();
+ await action.processRecipe(
+ {
+ ...recipe,
+ id: 2,
+ arguments: {
+ ...recipe.arguments,
+ slug: "test-conflict",
+ },
+ },
+ BaseAction.suitability.FILTER_MATCH
+ );
+ is(action.lastError, null, "lastError should be null");
+
+ addon = await AddonManager.getAddonByID(FIXTURE_ADDON_ID);
+ is(addon.id, FIXTURE_ADDON_ID, "addon should still be installed");
+ is(addon.version, "1.0", "addon should be the correct version");
+
+ // rollout in the DB has not been updated
+ const rollouts = await AddonRollouts.getAll();
+ Assert.deepEqual(
+ rollouts,
+ [
+ {
+ recipeId: recipe.id,
+ slug: "test-rollout",
+ state: AddonRollouts.STATE_ACTIVE,
+ extensionApiId: 1,
+ addonId: FIXTURE_ADDON_ID,
+ addonVersion: "1.0",
+ xpiUrl: FIXTURE_ADDON_DETAILS["normandydriver-a-1.0"].url,
+ xpiHash: FIXTURE_ADDON_DETAILS["normandydriver-a-1.0"].hash,
+ xpiHashAlgorithm: "sha256",
+ },
+ ],
+ "Rollout should be stored in db"
+ );
+
+ sendEventSpy.assertEvents([
+ [
+ "enroll",
+ "addon_rollout",
+ "test-rollout",
+ { addonId: FIXTURE_ADDON_ID },
+ ],
+ [
+ "enrollFailed",
+ "addon_rollout",
+ "test-conflict",
+ { reason: "conflict" },
+ ],
+ ]);
+
+ // Cleanup
+ await addon.uninstall();
+ }
+);
+
+// Add-on ID changed
+decorate_task(
+ AddonRollouts.withTestMock(),
+ ensureAddonCleanup(),
+ withMockNormandyApi(),
+ withSendEventSpy(),
+ async function enroll_failed_addon_id_changed({
+ mockNormandyApi,
+ sendEventSpy,
+ }) {
+ const recipe = {
+ id: 1,
+ arguments: {
+ slug: "test-rollout",
+ extensionApiId: 1,
+ },
+ };
+ mockNormandyApi.extensionDetails = {
+ [recipe.arguments.extensionApiId]: extensionDetailsFactory({
+ id: recipe.arguments.extensionApiId,
+ }),
+ 2: extensionDetailsFactory({
+ id: 2,
+ extension_id: "normandydriver-b@example.com",
+ xpi: FIXTURE_ADDON_DETAILS["normandydriver-b-1.0"].url,
+ version: "1.0",
+ hash: FIXTURE_ADDON_DETAILS["normandydriver-b-1.0"].hash,
+ }),
+ };
+
+ const webExtStartupPromise =
+ AddonTestUtils.promiseWebExtensionStartup(FIXTURE_ADDON_ID);
+
+ let action = new AddonRolloutAction();
+ await action.processRecipe(recipe, BaseAction.suitability.FILTER_MATCH);
+ is(action.lastError, null, "lastError should be null");
+
+ await webExtStartupPromise;
+
+ // addon was installed
+ let addon = await AddonManager.getAddonByID(FIXTURE_ADDON_ID);
+ is(addon.id, FIXTURE_ADDON_ID, "addon should be installed");
+ is(addon.version, "1.0", "addon should be the correct version");
+
+ // update existing enrollment
+ recipe.arguments.extensionApiId = 2;
+ action = new AddonRolloutAction();
+ await action.processRecipe(recipe, BaseAction.suitability.FILTER_MATCH);
+ is(action.lastError, null, "lastError should be null");
+
+ addon = await AddonManager.getAddonByID(FIXTURE_ADDON_ID);
+ is(addon.id, FIXTURE_ADDON_ID, "addon should still be installed");
+ is(addon.version, "1.0", "addon should be the correct version");
+
+ // rollout in the DB has not been updated
+ const rollouts = await AddonRollouts.getAll();
+ Assert.deepEqual(
+ rollouts,
+ [
+ {
+ recipeId: recipe.id,
+ slug: "test-rollout",
+ state: AddonRollouts.STATE_ACTIVE,
+ extensionApiId: 1,
+ addonId: FIXTURE_ADDON_ID,
+ addonVersion: "1.0",
+ xpiUrl: FIXTURE_ADDON_DETAILS["normandydriver-a-1.0"].url,
+ xpiHash: FIXTURE_ADDON_DETAILS["normandydriver-a-1.0"].hash,
+ xpiHashAlgorithm: "sha256",
+ },
+ ],
+ "Rollout should be stored in db"
+ );
+
+ sendEventSpy.assertEvents([
+ ["enroll", "addon_rollout", "test-rollout"],
+ [
+ "updateFailed",
+ "addon_rollout",
+ "test-rollout",
+ { reason: "addon-id-changed" },
+ ],
+ ]);
+
+ // Cleanup
+ await addon.uninstall();
+ }
+);
+
+// Add-on upgrade required
+decorate_task(
+ AddonRollouts.withTestMock(),
+ ensureAddonCleanup(),
+ withMockNormandyApi(),
+ withSendEventSpy(),
+ async function enroll_failed_upgrade_required({
+ mockNormandyApi,
+ sendEventSpy,
+ }) {
+ const recipe = {
+ id: 1,
+ arguments: {
+ slug: "test-rollout",
+ extensionApiId: 1,
+ },
+ };
+ mockNormandyApi.extensionDetails = {
+ [recipe.arguments.extensionApiId]: extensionDetailsFactory({
+ id: recipe.arguments.extensionApiId,
+ xpi: FIXTURE_ADDON_DETAILS["normandydriver-a-2.0"].url,
+ version: "2.0",
+ hash: FIXTURE_ADDON_DETAILS["normandydriver-a-2.0"].hash,
+ }),
+ 2: extensionDetailsFactory({
+ id: 2,
+ }),
+ };
+
+ const webExtStartupPromise =
+ AddonTestUtils.promiseWebExtensionStartup(FIXTURE_ADDON_ID);
+
+ let action = new AddonRolloutAction();
+ await action.processRecipe(recipe, BaseAction.suitability.FILTER_MATCH);
+ is(action.lastError, null, "lastError should be null");
+
+ await webExtStartupPromise;
+
+ // addon was installed
+ let addon = await AddonManager.getAddonByID(FIXTURE_ADDON_ID);
+ is(addon.id, FIXTURE_ADDON_ID, "addon should be installed");
+ is(addon.version, "2.0", "addon should be the correct version");
+
+ // update existing enrollment
+ recipe.arguments.extensionApiId = 2;
+ action = new AddonRolloutAction();
+ await action.processRecipe(recipe, BaseAction.suitability.FILTER_MATCH);
+ is(action.lastError, null, "lastError should be null");
+
+ addon = await AddonManager.getAddonByID(FIXTURE_ADDON_ID);
+ is(addon.id, FIXTURE_ADDON_ID, "addon should still be installed");
+ is(addon.version, "2.0", "addon should be the correct version");
+
+ // rollout in the DB has not been updated
+ const rollouts = await AddonRollouts.getAll();
+ Assert.deepEqual(
+ rollouts,
+ [
+ {
+ recipeId: recipe.id,
+ slug: "test-rollout",
+ state: AddonRollouts.STATE_ACTIVE,
+ extensionApiId: 1,
+ addonId: FIXTURE_ADDON_ID,
+ addonVersion: "2.0",
+ xpiUrl: FIXTURE_ADDON_DETAILS["normandydriver-a-2.0"].url,
+ xpiHash: FIXTURE_ADDON_DETAILS["normandydriver-a-2.0"].hash,
+ xpiHashAlgorithm: "sha256",
+ },
+ ],
+ "Rollout should be stored in db"
+ );
+
+ sendEventSpy.assertEvents([
+ ["enroll", "addon_rollout", "test-rollout"],
+ [
+ "updateFailed",
+ "addon_rollout",
+ "test-rollout",
+ { reason: "upgrade-required" },
+ ],
+ ]);
+
+ // Cleanup
+ await addon.uninstall();
+ }
+);