commit 52a349bba6a928eb6362b0781dd128015e7715ea
parent 819cdd63014d015ee47f1343c339ae36d26a3e20
Author: Beth Rennie <beth@brennie.ca>
Date: Fri, 14 Nov 2025 22:27:58 +0000
Bug 1996359 - Add NimbusTestUtils.createStoreWith r=nimbus-reviewers,relud
This adds a new test helper that creates and saves an ExperimentStore to
disk. Most Nimbus unit tests have been updated to use this helper.
Differential Revision: https://phabricator.services.mozilla.com/D270235
Diffstat:
7 files changed, 115 insertions(+), 174 deletions(-)
diff --git a/toolkit/components/nimbus/test/NimbusTestUtils.sys.mjs b/toolkit/components/nimbus/test/NimbusTestUtils.sys.mjs
@@ -747,6 +747,22 @@ export const NimbusTestUtils = {
} catch (e) {}
},
+ /**
+ * Create a Nimbus store and return its path on disk.
+ *
+ * @param {function(store: ExperimentStore): void} A function that will be
+ * called with the store.
+ *
+ * @returns {string} The path to the Nimbus store, which can be passed to
+ * {@link NimbusTestUtils.setupTest}.
+ */
+ async createStoreWith(fn) {
+ const store = NimbusTestUtils.stubs.store();
+ await store.init();
+ await fn(store);
+ return NimbusTestUtils.saveStore(store);
+ },
+
async deleteEnrollmentsFromProfiles(profileIds) {
const conn = await lazy.ProfilesDatastoreService.getConnection();
if (!conn) {
diff --git a/toolkit/components/nimbus/test/unit/test_ExperimentManager_lifecycle.js b/toolkit/components/nimbus/test/unit/test_ExperimentManager_lifecycle.js
@@ -24,36 +24,27 @@ const { ProfilesDatastoreService } = ChromeUtils.importESModule(
* - should set call setExperimentActive for each active experiment
*/
async function test_onStartup_setExperimentActive_called() {
- let storePath;
-
- {
- const store = NimbusTestUtils.stubs.store();
- await store.init();
-
- NimbusTestUtils.addEnrollmentForRecipe(
- NimbusTestUtils.factories.recipe("foo"),
- { store, branchSlug: "control" }
- );
- NimbusTestUtils.addEnrollmentForRecipe(
- NimbusTestUtils.factories.recipe("bar", { isRollout: true }),
- { store }
- );
- NimbusTestUtils.addEnrollmentForRecipe(
- NimbusTestUtils.factories.recipe("baz"),
- { store, branchSlug: "control", extra: { active: false } }
- );
- NimbusTestUtils.addEnrollmentForRecipe(
- NimbusTestUtils.factories.recipe("qux", { isRollout: true }),
- { store, extra: { active: false } }
- );
-
- storePath = await NimbusTestUtils.saveStore(store);
- }
-
const { sandbox, manager, initExperimentAPI, cleanup } =
await NimbusTestUtils.setupTest({
- storePath,
init: false,
+ storePath: await NimbusTestUtils.createStoreWith(store => {
+ NimbusTestUtils.addEnrollmentForRecipe(
+ NimbusTestUtils.factories.recipe("foo"),
+ { store, branchSlug: "control" }
+ );
+ NimbusTestUtils.addEnrollmentForRecipe(
+ NimbusTestUtils.factories.recipe("bar", { isRollout: true }),
+ { store }
+ );
+ NimbusTestUtils.addEnrollmentForRecipe(
+ NimbusTestUtils.factories.recipe("baz"),
+ { store, branchSlug: "control", extra: { active: false } }
+ );
+ NimbusTestUtils.addEnrollmentForRecipe(
+ NimbusTestUtils.factories.recipe("qux", { isRollout: true }),
+ { store, extra: { active: false } }
+ );
+ }),
migrationState:
NimbusTestUtils.migrationState.IMPORTED_ENROLLMENTS_TO_SQL,
});
@@ -97,21 +88,16 @@ add_task(async function test_onStartup_setExperimentActive_called_db() {
async function test_startup_unenroll() {
Services.prefs.setBoolPref("app.shield.optoutstudies.enabled", false);
- let storePath;
- {
- const store = NimbusTestUtils.stubs.store();
- await store.init();
-
- NimbusTestUtils.addEnrollmentForRecipe(
- NimbusTestUtils.factories.recipe("startup_unenroll"),
- { store, branchSlug: "control" }
- );
-
- storePath = await NimbusTestUtils.saveStore(store);
- }
-
const { sandbox, manager, initExperimentAPI, cleanup } =
- await NimbusTestUtils.setupTest({ storePath, init: false });
+ await NimbusTestUtils.setupTest({
+ init: false,
+ storePath: await NimbusTestUtils.createStoreWith(store => {
+ NimbusTestUtils.addEnrollmentForRecipe(
+ NimbusTestUtils.factories.recipe("startup_unenroll"),
+ { store, branchSlug: "control" }
+ );
+ }),
+ });
sandbox.spy(manager, "_unenroll");
diff --git a/toolkit/components/nimbus/test/unit/test_ExperimentStore.js b/toolkit/components/nimbus/test/unit/test_ExperimentStore.js
@@ -53,12 +53,7 @@ add_task(async function test_usageBeforeInitialization() {
});
async function test_initOnUpdateEventsFire() {
- let storePath;
-
- {
- const store = NimbusTestUtils.stubs.store();
- await store.init();
-
+ const storePath = await NimbusTestUtils.createStoreWith(store => {
NimbusTestUtils.addEnrollmentForRecipe(
NimbusTestUtils.factories.recipe.withFeatureConfig("testFeature-1", {
featureId: "testFeature",
@@ -120,13 +115,11 @@ async function test_initOnUpdateEventsFire() {
}),
{ store }
);
-
- storePath = await NimbusTestUtils.saveStore(store);
- }
+ });
const { sandbox, initExperimentAPI, cleanup } = await setupTest({
- storePath,
init: false,
+ storePath,
migrationState: NimbusTestUtils.migrationState.IMPORTED_ENROLLMENTS_TO_SQL,
});
@@ -893,25 +886,17 @@ add_task(async function test_cleanupOldRecipes() {
});
async function test_restore() {
- let storePath;
- {
- const store = NimbusTestUtils.stubs.store();
- await store.init();
-
- NimbusTestUtils.addEnrollmentForRecipe(
- NimbusTestUtils.factories.recipe("experiment"),
- { store, branchSlug: "control" }
- );
- NimbusTestUtils.addEnrollmentForRecipe(
- NimbusTestUtils.factories.recipe("rollout", { isRollout: true }),
- { store }
- );
-
- storePath = await NimbusTestUtils.saveStore(store);
- }
-
const { store, cleanup } = await setupTest({
- storePath,
+ storePath: await NimbusTestUtils.createStoreWith(store => {
+ NimbusTestUtils.addEnrollmentForRecipe(
+ NimbusTestUtils.factories.recipe("experiment"),
+ { store, branchSlug: "control" }
+ );
+ NimbusTestUtils.addEnrollmentForRecipe(
+ NimbusTestUtils.factories.recipe("rollout", { isRollout: true }),
+ { store }
+ );
+ }),
migrationState: NimbusTestUtils.migrationState.IMPORTED_ENROLLMENTS_TO_SQL,
});
@@ -934,12 +919,7 @@ add_task(async function test_restore_db() {
async function test_restoreDatabaseConsistency(primary = "jsonfile") {
Services.fog.testResetFOG();
- let storePath;
-
- {
- const store = await NimbusTestUtils.stubs.store();
- await store.init();
-
+ const storePath = await NimbusTestUtils.createStoreWith(store => {
const experimentRecipe = NimbusTestUtils.factories.recipe.withFeatureConfig(
"experiment",
{ featureId: "no-feature-firefox-desktop" }
@@ -962,9 +942,9 @@ async function test_restoreDatabaseConsistency(primary = "jsonfile") {
store,
extra: { active: false },
});
+ });
- storePath = await NimbusTestUtils.saveStore(store);
-
+ {
// We should expect to see one successful databaseWrite event.
const events = Glean.nimbusEvents.databaseWrite
.testGetValue("events")
diff --git a/toolkit/components/nimbus/test/unit/test_Migrations.js b/toolkit/components/nimbus/test/unit/test_Migrations.js
@@ -866,22 +866,26 @@ add_task(async function test_migration_firefoxLabsEnrollments_idempotent() {
const recipes = mockLabsRecipes("true");
- // Get the store into a partially migrated state (i.e., we have enrolled in at least one
- // experiment but the migration pref has not updated).
- {
- const manager = NimbusTestUtils.stubs.manager();
- await manager.store.init();
- await manager.onStartup();
-
- manager.enroll(recipes[0], "rs-loader", { branchSlug: "control" });
-
- await NimbusTestUtils.saveStore(manager.store);
-
- removePrefObservers(manager);
- assertNoObservers(manager);
- }
-
const { manager, cleanup } = await setupTest({
+ storePath: await NimbusTestUtils.createStoreWith(store => {
+ // Get the store into a partially migrated state (i.e., we have enrolled in at least one
+ // experiment but the migration pref has not updated).
+ NimbusTestUtils.addEnrollmentForRecipe(recipes[0], {
+ store,
+ branchSlug: "control",
+ extra: {
+ prefs: [
+ {
+ name: prefs[0],
+ featureId: recipes[0].featureIds[0],
+ variable: "enabled",
+ branch: "user",
+ originalValue: false,
+ },
+ ],
+ },
+ });
+ }),
experiments: recipes,
migrations: {
[NimbusMigrations.Phase.AFTER_REMOTE_SETTINGS_UPDATE]: [
@@ -933,7 +937,6 @@ async function testMigrateEnrollmentsToSql(primary = "jsonfile") {
},
},
};
- let storePath;
const experiments = [
NimbusTestUtils.factories.recipe.withFeatureConfig(
@@ -1041,11 +1044,7 @@ async function testMigrateEnrollmentsToSql(primary = "jsonfile") {
),
];
- {
- const store = NimbusTestUtils.stubs.store();
-
- await store.init();
-
+ const storePath = await NimbusTestUtils.createStoreWith(store => {
store.set(
"inactive-1",
NimbusTestUtils.factories.experiment.withFeatureConfig(
@@ -1194,9 +1193,7 @@ async function testMigrateEnrollmentsToSql(primary = "jsonfile") {
}
)
);
-
- storePath = await NimbusTestUtils.saveStore(store);
- }
+ });
let importMigrationError = null;
diff --git a/toolkit/components/nimbus/test/unit/test_RemoteSettingsExperimentLoader_updateRecipes.js b/toolkit/components/nimbus/test/unit/test_RemoteSettingsExperimentLoader_updateRecipes.js
@@ -472,24 +472,16 @@ add_task(async function test_updateRecipes_simpleFeatureInvalidAfterUpdate() {
async function test_updateRecipes_invalidFeatureAfterUpdate() {
const featureConfig = { featureId: "bogus", value: {} };
- let storePath;
- {
- const store = NimbusTestUtils.stubs.store();
- await store.init();
-
- await NimbusTestUtils.addEnrollmentForRecipe(
- NimbusTestUtils.factories.recipe.withFeatureConfig(
- "recipe",
- featureConfig
- ),
- { store }
- );
-
- storePath = await NimbusTestUtils.saveStore(store);
- }
-
const { manager, cleanup } = await setupTest({
- storePath,
+ storePath: await NimbusTestUtils.createStoreWith(store => {
+ NimbusTestUtils.addEnrollmentForRecipe(
+ NimbusTestUtils.factories.recipe.withFeatureConfig(
+ "recipe",
+ featureConfig
+ ),
+ { store }
+ );
+ }),
experiments: [
NimbusTestUtils.factories.recipe.withFeatureConfig(
"recipe",
diff --git a/toolkit/components/nimbus/test/unit/test_policy.js b/toolkit/components/nimbus/test/unit/test_policy.js
@@ -54,26 +54,18 @@ async function doTest({
"Policy engine is active"
);
- let storePath = undefined;
- if (existingEnrollments) {
- const store = NimbusTestUtils.stubs.store();
- await store.init();
-
- for (const slug of existingEnrollments) {
- NimbusTestUtils.addEnrollmentForRecipe(
- RECIPES.find(e => e.slug === slug),
- { store }
- );
- }
-
- storePath = await NimbusTestUtils.saveStore(store);
- }
-
const { initExperimentAPI, cleanup, loader } =
await NimbusTestUtils.setupTest({
init: false,
+ storePath: await NimbusTestUtils.createStoreWith(store => {
+ for (const slug of existingEnrollments) {
+ NimbusTestUtils.addEnrollmentForRecipe(
+ RECIPES.find(e => e.slug === slug),
+ { store }
+ );
+ }
+ }),
experiments: RECIPES,
- storePath,
});
sinon.spy(loader, "updateRecipes");
diff --git a/toolkit/components/nimbus/test/unit/test_prefFlips.js b/toolkit/components/nimbus/test/unit/test_prefFlips.js
@@ -2381,32 +2381,23 @@ async function test_prefFlips_restore_unenroll() {
}
);
- // Set up a previous ExperimentStore on disk.
- let storePath;
- {
- const store = NimbusTestUtils.stubs.store();
- await store.init();
-
- await NimbusTestUtils.addEnrollmentForRecipe(recipe, {
- store,
- extra: {
- source: "rs-loader",
- prefFlips: {
- originalValues: {
- "test.pref.please.ignore": null,
+ const { manager, cleanup } = await setupTest({
+ storePath: await NimbusTestUtils.createStoreWith(store => {
+ NimbusTestUtils.addEnrollmentForRecipe(recipe, {
+ store,
+ extra: {
+ source: "rs-loader",
+ prefFlips: {
+ originalValues: {
+ "test.pref.please.ignore": null,
+ },
},
},
- },
- });
-
- storePath = await NimbusTestUtils.saveStore(store);
- }
-
- // Set the pref controlled by the experiment.
- Services.prefs.setStringPref("test.pref.please.ignore", "test-value");
+ });
- const { manager, cleanup } = await setupTest({
- storePath,
+ // Set the pref controlled by the experiment.
+ Services.prefs.setStringPref("test.pref.please.ignore", "test-value");
+ }),
secureExperiments: [recipe],
migrationState: NimbusTestUtils.migrationState.IMPORTED_ENROLLMENTS_TO_SQL,
});
@@ -2873,17 +2864,12 @@ add_task(async function test_prefFlips_update_failure() {
});
async function test_prefFlips_restore() {
- let storePath;
-
const PREF_1 = "pref.one";
const PREF_2 = "pref.two";
const PREF_3 = "pref.three";
const PREF_4 = "pref.FOUR";
- {
- const store = NimbusTestUtils.stubs.store();
- await store.init();
-
+ const storePath = await NimbusTestUtils.createStoreWith(store => {
NimbusTestUtils.addEnrollmentForRecipe(
NimbusTestUtils.factories.recipe.withFeatureConfig(
"rollout-1",
@@ -2971,9 +2957,7 @@ async function test_prefFlips_restore() {
},
}
);
-
- storePath = await NimbusTestUtils.saveStore(store);
- }
+ });
const { manager, cleanup } = await setupTest({
storePath,
@@ -3045,13 +3029,9 @@ add_task(async function test_prefFlips_restore_db() {
});
async function test_prefFlips_restore_failure_conflict() {
- let storePath;
-
const PREF = "pref.foo.bar";
- {
- const store = NimbusTestUtils.stubs.store();
- await store.init();
+ const storePath = await NimbusTestUtils.createStoreWith(store => {
NimbusTestUtils.addEnrollmentForRecipe(
NimbusTestUtils.factories.recipe.withFeatureConfig("rollout-1", {
featureId: FEATURE_ID,
@@ -3122,9 +3102,7 @@ async function test_prefFlips_restore_failure_conflict() {
},
}
);
-
- storePath = await NimbusTestUtils.saveStore(store);
- }
+ });
const { manager, cleanup } = await setupTest({
storePath,