"use strict"; const { ExperimentStore } = ChromeUtils.importESModule( "resource://nimbus/lib/ExperimentStore.sys.mjs" ); const { ProfilesDatastoreService } = ChromeUtils.importESModule( "moz-src:///toolkit/profile/ProfilesDatastoreService.sys.mjs" ); const { SYNC_DATA_PREF_BRANCH, SYNC_DEFAULTS_PREF_BRANCH } = ExperimentStore; add_setup(function () { Services.fog.initializeFOG(); }); async function setupTest({ ...args } = {}) { const ctx = await NimbusTestUtils.setupTest({ ...args }); return { ...ctx, store: ctx.manager.store, }; } add_task(async function test_sharedDataMap_key() { const store = new ExperimentStore(); // Outside of tests we use sharedDataKey for the profile dir filepath // where we store experiments Assert.ok(store._sharedDataKey, "Make sure it's defined"); }); add_task(async function test_usageBeforeInitialization() { const { store, initExperimentAPI, cleanup } = await setupTest({ init: false, }); const experiment = NimbusTestUtils.factories.experiment("foo"); Assert.equal(store.getAll().length, 0, "It should not fail"); await initExperimentAPI(); store.addEnrollment(experiment); Assert.equal( store.getExperimentForFeature("testFeature"), experiment, "should return a matching experiment for the given feature" ); store.updateExperiment(experiment.slug, { active: false }); await cleanup(); }); add_task(async function test_initOnUpdateEventsFire() { let storePath; { const store = NimbusTestUtils.stubs.store(); await store.init(); store.addEnrollment( NimbusTestUtils.factories.experiment.withFeatureConfig("testFeature-1", { featureId: "testFeature", }) ); store.addEnrollment( NimbusTestUtils.factories.rollout.withFeatureConfig("testFeature-2", { featureId: "testFeature", }) ); store.addEnrollment( NimbusTestUtils.factories.experiment.withFeatureConfig( "nimbus-qa-1", { featureId: "nimbus-qa-1" }, { active: false } ) ); store.addEnrollment( NimbusTestUtils.factories.rollout.withFeatureConfig( "nimbus-qa-2", { featureId: "nimbus-qa-2" }, { active: false } ) ); store.addEnrollment( NimbusTestUtils.factories.experiment.withFeatureConfig("coenroll-1", { featureId: "no-feature-firefox-desktop", }) ); store.addEnrollment( NimbusTestUtils.factories.experiment.withFeatureConfig("coenroll-2", { featureId: "no-feature-firefox-desktop", }) ); store.addEnrollment( NimbusTestUtils.factories.rollout.withFeatureConfig("coenroll-3", { featureId: "no-feature-firefox-desktop", }) ); store.addEnrollment( NimbusTestUtils.factories.rollout.withFeatureConfig("coenroll-4", { featureId: "no-feature-firefox-desktop", }) ); storePath = await NimbusTestUtils.saveStore(store); } const { sandbox, initExperimentAPI, cleanup } = await setupTest({ storePath, init: false, }); const onFeatureUpdate = sandbox.stub(); NimbusFeatures.testFeature.onUpdate(onFeatureUpdate); NimbusFeatures["nimbus-qa-1"].onUpdate(onFeatureUpdate); NimbusFeatures["nimbus-qa-2"].onUpdate(onFeatureUpdate); NimbusFeatures["no-feature-firefox-desktop"].onUpdate(onFeatureUpdate); await initExperimentAPI(); Assert.ok( onFeatureUpdate.calledWithExactly( "featureUpdate:testFeature", "feature-enrollments-loaded" ) ); Assert.ok( onFeatureUpdate.calledWithExactly( "featureUpdate:no-feature-firefox-desktop", "feature-enrollments-loaded" ) ); Assert.equal( onFeatureUpdate.callCount, 2, "onFeatureUpdate called once per active feature ID" ); NimbusFeatures.testFeature.offUpdate(onFeatureUpdate); await NimbusTestUtils.cleanupManager([ "testFeature-1", "testFeature-2", "coenroll-1", "coenroll-2", "coenroll-3", "coenroll-4", ]); await cleanup(); }); add_task(async function test_getExperimentForGroup() { const { store, cleanup } = await setupTest(); const experiment = NimbusTestUtils.factories.experiment.withFeatureConfig( "foo", { featureId: "purple", } ); store.addEnrollment(NimbusTestUtils.factories.experiment("bar")); store.addEnrollment(experiment); Assert.equal( store.getExperimentForFeature("purple"), experiment, "should return a matching experiment for the given feature" ); store.updateExperiment("foo", { active: false }); store.updateExperiment("bar", { active: false }); await cleanup(); }); add_task(async function test_hasExperimentForFeature() { const { store, cleanup } = await setupTest(); store.addEnrollment( NimbusTestUtils.factories.experiment.withFeatureConfig("foo", { featureId: "green", }) ); store.addEnrollment( NimbusTestUtils.factories.experiment.withFeatureConfig("foo2", { featureId: "yellow", }) ); store.addEnrollment( NimbusTestUtils.factories.experiment.withFeatureConfig( "bar_expired", { featureId: "purple" }, { active: false, } ) ); Assert.equal( store.hasExperimentForFeature(), false, "should return false if the input is empty" ); Assert.equal( store.hasExperimentForFeature(undefined), false, "should return false if the input is undefined" ); Assert.equal( store.hasExperimentForFeature("green"), true, "should return true if there is an experiment with any of the given groups" ); Assert.equal( store.hasExperimentForFeature("purple"), false, "should return false if there is a non-active experiment with the given groups" ); store.updateExperiment("foo", { active: false }); store.updateExperiment("foo2", { active: false }); await cleanup(); }); add_task(async function test_getAll() { const { store, cleanup } = await setupTest(); store.addEnrollment( NimbusTestUtils.factories.experiment("foo", { active: false }) ); store.addEnrollment( NimbusTestUtils.factories.experiment("bar", { active: false }) ); store.addEnrollment( NimbusTestUtils.factories.experiment("baz", { active: false }) ); store.addEnrollment( NimbusTestUtils.factories.experiment("qux", { active: true }) ); store.addEnrollment( NimbusTestUtils.factories.rollout("quux", { active: false }) ); store.addEnrollment( NimbusTestUtils.factories.rollout("corge", { active: false }) ); store.addEnrollment( NimbusTestUtils.factories.rollout("grault", { active: false }) ); store.addEnrollment( NimbusTestUtils.factories.rollout("garply", { active: true }) ); Assert.deepEqual( store.getAll().map(e => e.slug), ["foo", "bar", "baz", "qux", "quux", "corge", "grault", "garply"], ".getAll() should return all experiments" ); Assert.deepEqual( store.getAllActiveExperiments().map(e => e.slug), ["qux"], "getAllActiveExperiments() should return all experiments that are active" ); Assert.deepEqual( store.getAllActiveRollouts().map(e => e.slug), ["garply"], "getAllActiveRollouts() should return all experiments that are active" ); store.updateExperiment("qux", { active: false }); store.updateExperiment("garply", { active: false }); await cleanup(); }); add_task(async function test_addEnrollment() { const { store, cleanup } = await setupTest(); const experiment = NimbusTestUtils.factories.experiment("experiment"); const rollout = NimbusTestUtils.factories.experiment("rollout"); store.addEnrollment(experiment); store.addEnrollment(rollout); Assert.equal( store.get("experiment"), experiment, "should save experiment by slug" ); Assert.equal(store.get("rollout"), rollout, "should save experiment by slug"); store.updateExperiment("experiment", { active: false }); store.updateExperiment("rollout", { active: false }); await cleanup(); }); add_task(async function test_updateExperiment() { const { store, cleanup } = await setupTest(); const features = [{ featureId: "cfr", value: {} }]; const experiment = Object.freeze( NimbusTestUtils.factories.experiment("foo", { branch: { slug: "treatment", ratio: 1, features, }, active: true, }) ); store.addEnrollment(experiment); store.updateExperiment(experiment.slug, { active: false }); const actual = store.get("foo"); Assert.equal(actual.active, false, "should change updated props"); Assert.deepEqual( actual.branch.features, features, "should not update other props" ); store.updateExperiment("foo", { active: false }); await cleanup(); }); add_task(async function test_sync_access_before_init() { const { store, cleanup } = await setupTest(); Assert.equal(store.getAll().length, 0, "Start with an empty store"); const experiment = NimbusTestUtils.factories.experiment.withFeatureConfig( "foo", { featureId: "newtab", } ); store.addEnrollment(experiment); const prefValue = JSON.parse( Services.prefs.getStringPref(`${SYNC_DATA_PREF_BRANCH}newtab`) ); Assert.ok(prefValue, "Parsed stored experiment"); Assert.equal(prefValue.slug, experiment.slug, "Got back the experiment"); // New un-initialized store that should read the pref value const newStore = NimbusTestUtils.stubs.store(); Assert.equal( newStore.getExperimentForFeature("newtab").slug, "foo", "Returns experiment from pref" ); store.updateExperiment("foo", { active: false }); await cleanup(); await NimbusTestUtils.assert.storeIsEmpty(newStore); }); add_task(async function test_sync_access_update() { const { store, cleanup } = await setupTest(); const experiment = NimbusTestUtils.factories.experiment.withFeatureConfig( "foo", { featureId: "aboutwelcome", } ); store.addEnrollment(experiment); store.updateExperiment("foo", { branch: { ...experiment.branch, features: [ { featureId: "aboutwelcome", value: { bar: "bar", enabled: true }, }, ], }, }); const newStore = NimbusTestUtils.stubs.store(); const cachedExperiment = newStore.getExperimentForFeature("aboutwelcome"); Assert.ok(cachedExperiment, "Got back 1 experiment"); Assert.deepEqual( // `branch.feature` and not `features` because for sync access (early startup) // experiments we only store the `isEarlyStartup` feature cachedExperiment.branch.features[0].value, { bar: "bar", enabled: true }, "Got updated value" ); store.updateExperiment("foo", { active: false }); await cleanup(); await NimbusTestUtils.assert.storeIsEmpty(newStore); }); add_task(async function test_sync_features_only() { const { store, cleanup } = await setupTest(); store.addEnrollment( NimbusTestUtils.factories.experiment.withFeatureConfig("foo", { featureId: "cfr", }) ); const newStore = NimbusTestUtils.stubs.store(); Assert.equal( newStore.getAll().length, 0, "cfr is not a sync access experiment" ); store.updateExperiment("foo", { active: false }); await cleanup(); }); add_task(async function test_sync_access_unenroll() { const { store, cleanup } = await setupTest(); let experiment = NimbusTestUtils.factories.experiment.withFeatureConfig( "foo", { featureId: "aboutwelcome", } ); await store.init(); store.addEnrollment(experiment); store.updateExperiment("foo", { active: false }); const newStore = NimbusTestUtils.stubs.store(); Assert.equal(newStore.getAll().length, 0, "Unenrolled experiment is deleted"); await cleanup(); }); add_task(async function test_sync_access_unenroll_2() { const { store, cleanup } = await setupTest(); let experiment1 = NimbusTestUtils.factories.experiment.withFeatureConfig( "foo", { featureId: "newtab" } ); let experiment2 = NimbusTestUtils.factories.experiment.withFeatureConfig( "bar", { featureId: "aboutwelcome" } ); await store.init(); store.addEnrollment(experiment1); store.addEnrollment(experiment2); Assert.equal(store.getAll().length, 2, "2/2 experiments"); const newStore = NimbusTestUtils.stubs.store(); Assert.ok( newStore.getExperimentForFeature("aboutwelcome"), "Fetches experiment from pref cache even before init (aboutwelcome)" ); store.updateExperiment("bar", { active: false }); Assert.ok( newStore.getExperimentForFeature("newtab").slug, "Fetches experiment from pref cache even before init (newtab)" ); Assert.ok( !newStore.getExperimentForFeature("aboutwelcome")?.slug, "Experiment was updated and should not be found" ); store.updateExperiment("foo", { active: false }); Assert.ok( !newStore.getExperimentForFeature("newtab")?.slug, "Unenrolled from 2/2 experiments" ); Assert.equal( Services.prefs.getStringPref(`${SYNC_DATA_PREF_BRANCH}newtab`, "").length, 0, "Cleared pref 1" ); Assert.equal( Services.prefs.getStringPref(`${SYNC_DATA_PREF_BRANCH}aboutwelcome`, "") .length, 0, "Cleared pref 2" ); await cleanup(); }); add_task(async function test_getRolloutForFeature_fromStore() { const { store, cleanup } = await setupTest(); const rollout = NimbusTestUtils.factories.rollout("foo"); await store.init(); store.addEnrollment(rollout); Assert.deepEqual( store.getRolloutForFeature(rollout.featureIds[0]), rollout, "Should return back the same rollout" ); store.updateExperiment("foo", { active: false }); await cleanup(); }); add_task(async function test_getRolloutForFeature_fromSyncCache() { const { store, cleanup } = await setupTest(); const rollout = NimbusTestUtils.factories.rollout.withFeatureConfig("foo", { featureId: "aboutwelcome", value: { enabled: true }, }); store.addEnrollment(rollout); // New uninitialized store will return data from sync cache // before init const newStore = NimbusTestUtils.stubs.store(); Assert.ok( Services.prefs.getStringPref(`${SYNC_DEFAULTS_PREF_BRANCH}aboutwelcome`), "Sync cache is set" ); Assert.equal( newStore.getRolloutForFeature(rollout.featureIds[0]).slug, rollout.slug, "Should return back the same rollout" ); Assert.deepEqual( newStore.getRolloutForFeature(rollout.featureIds[0]).branch.features[0], rollout.branch.features[0], "Should return back the same feature" ); store.updateExperiment("foo", { active: false }); await cleanup(); }); add_task(async function test_remoteRollout() { const { store, initExperimentAPI, cleanup } = await setupTest({ init: false, }); const featureUpdateStub = sinon.stub(); const rollout = NimbusTestUtils.factories.rollout.withFeatureConfig("foo", { featureId: "aboutwelcome", value: { enabled: true }, }); store.on("featureUpdate:aboutwelcome", featureUpdateStub); await initExperimentAPI(); store.addEnrollment(rollout); Assert.ok( Services.prefs.getStringPref(`${SYNC_DEFAULTS_PREF_BRANCH}aboutwelcome`), "Sync cache is set" ); store.updateExperiment(rollout.slug, { active: false }); Assert.ok(featureUpdateStub.calledTwice, "Called for add and remove"); Assert.ok( store.get(rollout.slug), "Rollout is still in the store just not active" ); Assert.ok( !store.getRolloutForFeature("aboutwelcome"), "Feature rollout should not exist" ); Assert.ok( !Services.prefs.getStringPref( `${SYNC_DEFAULTS_PREF_BRANCH}aboutwelcome`, "" ), "Sync cache is cleared" ); await cleanup(); }); add_task(async function test_syncDataStore_setDefault() { const { store, cleanup } = await setupTest(); Assert.equal( Services.prefs.getStringPref( `${SYNC_DEFAULTS_PREF_BRANCH}aboutwelcome`, "" ), "", "Pref is empty" ); const rollout = NimbusTestUtils.factories.rollout.withFeatureConfig("foo", { featureId: "aboutwelcome", value: { remote: true }, }); store.addEnrollment(rollout); Assert.ok( Services.prefs.getStringPref(`${SYNC_DEFAULTS_PREF_BRANCH}aboutwelcome`), "Stored in pref" ); store.updateExperiment("foo", { active: false }); await cleanup(); }); add_task(async function test_syncDataStore_getDefault() { const { store, cleanup } = await setupTest(); const rollout = NimbusTestUtils.factories.rollout.withFeatureConfig( "aboutwelcome-slug", { featureId: "aboutwelcome", value: { remote: true } } ); await store.addEnrollment(rollout); Assert.ok( Services.prefs.getStringPref(`${SYNC_DEFAULTS_PREF_BRANCH}aboutwelcome`) ); const restoredRollout = store.getRolloutForFeature("aboutwelcome"); Assert.ok(restoredRollout); Assert.ok( restoredRollout.branch.features[0].value.remote, "Restore data from pref" ); store.updateExperiment(rollout.slug, { active: false }); await cleanup(); }); add_task(async function test_addEnrollment_rollout() { const { sandbox, store, initExperimentAPI, cleanup } = await setupTest({ init: false, }); const stub = sandbox.stub(); const value = { bar: true }; const rollout = NimbusTestUtils.factories.rollout.withFeatureConfig("foo", { featureId: "aboutwelcome", value, }); store._onFeatureUpdate("aboutwelcome", stub); await initExperimentAPI(); store.addEnrollment(rollout); Assert.deepEqual( store.getRolloutForFeature("aboutwelcome"), rollout, "should return the stored value" ); Assert.equal(stub.callCount, 1, "Called once on update"); Assert.equal( stub.firstCall.args[1], "rollout-updated", "Called for correct reason" ); store.updateExperiment("foo", { active: false }); await cleanup(); }); add_task(async function test_storeValuePerPref_returnsSameValue_allTypes() { const cleanupFeature = NimbusTestUtils.addTestFeatures( new ExperimentFeature("purple", { isEarlyStartup: true, variables: { string: { type: "string" }, bool: { type: "boolean" }, array: { type: "json" }, number1: { type: "int" }, number2: { type: "int" }, number3: { type: "int" }, json: { type: "json" }, }, }) ); const { store, cleanup } = await setupTest(); const experiment = NimbusTestUtils.factories.experiment.withFeatureConfig( "foo", { // Ensure it gets saved to prefs featureId: "purple", value: { string: "string", bool: true, array: [1, 2, 3], number1: 42, number2: 0, number3: -5, json: { jsonValue: true }, }, } ); store.addEnrollment(experiment); const branch = Services.prefs.getBranch(`${SYNC_DATA_PREF_BRANCH}purple.`); const newStore = NimbusTestUtils.stubs.store(); Assert.deepEqual( newStore.getExperimentForFeature("purple").branch.features[0].value, experiment.branch.features[0].value, "Returns the same value" ); // Cleanup store.updateExperiment(experiment.slug, { active: false }); Assert.ok( !Services.prefs.getStringPref(`${SYNC_DATA_PREF_BRANCH}purple`, ""), "Experiment cleanup" ); Assert.deepEqual(branch.getChildList(""), [], "Variables are also removed"); await cleanup(); cleanupFeature(); }); add_task(async function test_cleanupOldRecipes() { const store = NimbusTestUtils.stubs.store(); await store.init({ cleanupOldRecipes: false }); const NOW = Date.now(); const SIX_HOURS = 6 * 3600 * 1000; const ONE_DAY = 4 * SIX_HOURS; const ONE_YEAR = 365.25 * 24 * 3600 * 1000; const ONE_MONTH = Math.floor(ONE_YEAR / 12); const active = NimbusTestUtils.factories.experiment("active-6hrs", { active: true, lastSeen: new Date(NOW - SIX_HOURS).toJSON(), }); const inactiveToday = NimbusTestUtils.factories.experiment( "inactive-recent", { active: false, unenrollReason: "unknown", lastSeen: new Date(NOW - SIX_HOURS).toJSON(), } ); const inactiveSixMonths = NimbusTestUtils.factories.experiment( "inactive-6mo", { active: false, unenrollReason: "unknown", lastSeen: new Date(NOW - 6 * ONE_MONTH).toJSON(), } ); const inactiveUnderTwelveMonths = NimbusTestUtils.factories.experiment( "inactive-under-12mo", { active: false, unenrollReason: "unknown", lastSeen: new Date(NOW - ONE_YEAR + ONE_DAY).toJSON(), } ); const inactiveOverTwelveMonths = NimbusTestUtils.factories.experiment( "inactive-over-12mo", { active: false, unenrollReason: "unknown", lastSeen: new Date(NOW - ONE_YEAR - ONE_DAY).toJSON(), } ); const inactiveNoLastSeen = NimbusTestUtils.factories.experiment( "inactive-unknown", { active: false, unenrollReason: "unknown", } ); delete inactiveNoLastSeen.lastSeen; store.addEnrollment(active); await store._addEnrollmentToDatabase( active, NimbusTestUtils.factories.recipe.withFeatureConfig( active.slug, active.branch.features[0] ) ); store.addEnrollment(inactiveToday); await store._addEnrollmentToDatabase( inactiveToday, NimbusTestUtils.factories.recipe.withFeatureConfig( inactiveToday.slug, inactiveToday.branch.features[0] ) ); store.addEnrollment(inactiveSixMonths); await store._addEnrollmentToDatabase( inactiveSixMonths, NimbusTestUtils.factories.recipe.withFeatureConfig( inactiveSixMonths.slug, inactiveSixMonths.branch.features[0] ) ); store.addEnrollment(inactiveUnderTwelveMonths); await store._addEnrollmentToDatabase( inactiveUnderTwelveMonths, NimbusTestUtils.factories.recipe.withFeatureConfig( inactiveUnderTwelveMonths.slug, inactiveUnderTwelveMonths.branch.features[0] ) ); store.addEnrollment(inactiveOverTwelveMonths); await store._addEnrollmentToDatabase( inactiveOverTwelveMonths, NimbusTestUtils.factories.recipe.withFeatureConfig( inactiveOverTwelveMonths.slug, inactiveOverTwelveMonths.branch.features[0] ) ); // There is a NOT NULL constraint that prevents adding this enrollment to the // database. store.addEnrollment(inactiveNoLastSeen); // Insert a row belonging to another profile. const otherProfileId = Services.uuid.generateUUID().toString().slice(1, -1); const conn = await ProfilesDatastoreService.getConnection(); await conn.execute( ` INSERT INTO NimbusEnrollments VALUES( null, :profileId, :slug, :branchSlug, null, :active, :unenrollReason, :lastSeen, null, null, :source ); `, { profileId: otherProfileId, slug: inactiveOverTwelveMonths.slug, branchSlug: inactiveOverTwelveMonths.branch.slug, active: false, unenrollReason: inactiveOverTwelveMonths.unenrollReason, lastSeen: inactiveOverTwelveMonths.lastSeen, source: inactiveOverTwelveMonths.source, } ); await store._cleanupOldRecipes(); Assert.equal( store.get(inactiveOverTwelveMonths.slug), null, "Expired enrollment removed from in memory store" ); Assert.equal( store.get(inactiveNoLastSeen.slug), null, "invalid enrollment removed from the store" ); await NimbusTestUtils.assert.enrollmentExists(active.slug, { active: true }); await NimbusTestUtils.assert.enrollmentExists(inactiveToday.slug, { active: false, }); await NimbusTestUtils.assert.enrollmentExists(inactiveSixMonths.slug, { active: false, }); await NimbusTestUtils.assert.enrollmentExists( inactiveUnderTwelveMonths.slug, { active: false } ); await NimbusTestUtils.assert.enrollmentDoesNotExist( inactiveOverTwelveMonths.slug ); // Rows in the other profile should not have been changed. await NimbusTestUtils.assert.enrollmentExists(inactiveOverTwelveMonths.slug, { active: false, profileId: otherProfileId, }); store.updateExperiment(active.slug, { active: false }); await store._deactivateEnrollmentInDatabase(active.slug); await NimbusTestUtils.assert.storeIsEmpty(store); }); add_task(async function test_restore() { let storePath; { const store = NimbusTestUtils.stubs.store(); await store.init(); store.addEnrollment(NimbusTestUtils.factories.experiment("experiment")); store.addEnrollment( NimbusTestUtils.factories.rollout("rollout", { active: true }) ); storePath = await NimbusTestUtils.saveStore(store); } const { store, cleanup } = await setupTest({ storePath }); Assert.ok(store.get("experiment")); Assert.ok(store.get("rollout")); await NimbusTestUtils.cleanupManager(["experiment", "rollout"]); await cleanup(); }); add_task(async function test_restoreDatabaseConsistency() { Services.fog.testResetFOG(); let storePath; { const store = await NimbusTestUtils.stubs.store(); await store.init(); const experimentRecipe = NimbusTestUtils.factories.recipe.withFeatureConfig( "experiment", { featureId: "no-feature-firefox-desktop" } ); const experimentEnrollment = NimbusTestUtils.factories.experiment.withFeatureConfig("experiment", { featureId: "no-feature-firefox-desktop", }); const rolloutRecipe = NimbusTestUtils.factories.recipe.withFeatureConfig( "rollout", { featureId: "no-feature-firefox-desktop" }, { isRollout: true } ); const rolloutEnrollment = NimbusTestUtils.factories.experiment.withFeatureConfig( "rollout", { featureId: "no-feature-firefox-desktop" }, { isRollout: true } ); const inactiveExperimentEnrollment = NimbusTestUtils.factories.experiment.withFeatureConfig( "inactive", { featureId: "no-feature-firefox-desktop" }, { active: false } ); store.addEnrollment(experimentEnrollment); await store._addEnrollmentToDatabase( experimentEnrollment, experimentRecipe ); store.addEnrollment(rolloutEnrollment); await store._addEnrollmentToDatabase(rolloutEnrollment, rolloutRecipe); store.addEnrollment(inactiveExperimentEnrollment); await store._addEnrollmentToDatabase(inactiveExperimentEnrollment, null); // We should expect to see three successful databaseWrite events. const events = Glean.nimbusEvents.databaseWrite .testGetValue("events") .map(ev => ev.extra); Assert.deepEqual(events, [ { success: "true" }, { success: "true" }, { success: "true" }, ]); storePath = await NimbusTestUtils.saveStore(store); } // Initializing the store above will submit the event we care about. Disregard // any metrics previously recorded. Services.fog.testResetFOG(); const { cleanup } = await NimbusTestUtils.setupTest({ storePath, clearTelemetry: true, }); const events = Glean.nimbusEvents.startupDatabaseConsistency .testGetValue("events") .map(ev => ev.extra); Assert.deepEqual(events, [ { total_db_count: "3", total_store_count: "3", db_active_count: "2", store_active_count: "2", }, ]); await NimbusTestUtils.cleanupManager(["rollout", "experiment"]); await cleanup(); });