288 lines
7.8 KiB
JavaScript
288 lines
7.8 KiB
JavaScript
"use strict";
|
|
|
|
const { IndexedDB } = ChromeUtils.importESModule(
|
|
"resource://gre/modules/IndexedDB.sys.mjs"
|
|
);
|
|
|
|
const { PreferenceRollouts } = ChromeUtils.importESModule(
|
|
"resource://normandy/lib/PreferenceRollouts.sys.mjs"
|
|
);
|
|
const {
|
|
NormandyTestUtils: {
|
|
factories: { preferenceRolloutFactory },
|
|
},
|
|
} = ChromeUtils.importESModule(
|
|
"resource://testing-common/NormandyTestUtils.sys.mjs"
|
|
);
|
|
|
|
decorate_task(
|
|
PreferenceRollouts.withTestMock(),
|
|
async function testGetMissing() {
|
|
ok(
|
|
!(await PreferenceRollouts.get("does-not-exist")),
|
|
"get should return null when the requested rollout does not exist"
|
|
);
|
|
}
|
|
);
|
|
|
|
decorate_task(
|
|
PreferenceRollouts.withTestMock(),
|
|
async function testAddUpdateAndGet() {
|
|
const rollout = {
|
|
slug: "test-rollout",
|
|
state: PreferenceRollouts.STATE_ACTIVE,
|
|
preferences: [],
|
|
};
|
|
await PreferenceRollouts.add(rollout);
|
|
let storedRollout = await PreferenceRollouts.get(rollout.slug);
|
|
Assert.deepEqual(
|
|
rollout,
|
|
storedRollout,
|
|
"get should retrieve a rollout from storage."
|
|
);
|
|
|
|
rollout.state = PreferenceRollouts.STATE_GRADUATED;
|
|
await PreferenceRollouts.update(rollout);
|
|
storedRollout = await PreferenceRollouts.get(rollout.slug);
|
|
Assert.deepEqual(
|
|
rollout,
|
|
storedRollout,
|
|
"get should retrieve a rollout from storage."
|
|
);
|
|
}
|
|
);
|
|
|
|
decorate_task(
|
|
PreferenceRollouts.withTestMock(),
|
|
async function testCantUpdateNonexistent() {
|
|
const rollout = {
|
|
slug: "test-rollout",
|
|
state: PreferenceRollouts.STATE_ACTIVE,
|
|
preferences: [],
|
|
};
|
|
await Assert.rejects(
|
|
PreferenceRollouts.update(rollout),
|
|
/doesn't already exist/,
|
|
"Update should fail if the rollout doesn't exist"
|
|
);
|
|
ok(
|
|
!(await PreferenceRollouts.has("test-rollout")),
|
|
"rollout should not have been added"
|
|
);
|
|
}
|
|
);
|
|
|
|
decorate_task(PreferenceRollouts.withTestMock(), async function testGetAll() {
|
|
const rollout1 = {
|
|
slug: "test-rollout-1",
|
|
preference: [],
|
|
};
|
|
const rollout2 = {
|
|
slug: "test-rollout-2",
|
|
preference: [],
|
|
};
|
|
await PreferenceRollouts.add(rollout1);
|
|
await PreferenceRollouts.add(rollout2);
|
|
|
|
const storedRollouts = await PreferenceRollouts.getAll();
|
|
Assert.deepEqual(
|
|
storedRollouts.sort((a, b) => a.id - b.id),
|
|
[rollout1, rollout2],
|
|
"getAll should return every stored rollout."
|
|
);
|
|
});
|
|
|
|
decorate_task(
|
|
PreferenceRollouts.withTestMock(),
|
|
async function testGetAllActive() {
|
|
const rollout1 = {
|
|
slug: "test-rollout-1",
|
|
state: PreferenceRollouts.STATE_ACTIVE,
|
|
};
|
|
const rollout2 = {
|
|
slug: "test-rollout-2",
|
|
state: PreferenceRollouts.STATE_GRADUATED,
|
|
};
|
|
const rollout3 = {
|
|
slug: "test-rollout-3",
|
|
state: PreferenceRollouts.STATE_ROLLED_BACK,
|
|
};
|
|
await PreferenceRollouts.add(rollout1);
|
|
await PreferenceRollouts.add(rollout2);
|
|
await PreferenceRollouts.add(rollout3);
|
|
|
|
const activeRollouts = await PreferenceRollouts.getAllActive();
|
|
Assert.deepEqual(
|
|
activeRollouts,
|
|
[rollout1],
|
|
"getAllActive should return only active rollouts"
|
|
);
|
|
}
|
|
);
|
|
|
|
decorate_task(PreferenceRollouts.withTestMock(), async function testHas() {
|
|
const rollout = {
|
|
slug: "test-rollout",
|
|
preferences: [],
|
|
};
|
|
await PreferenceRollouts.add(rollout);
|
|
ok(
|
|
await PreferenceRollouts.has(rollout.slug),
|
|
"has should return true for an existing rollout"
|
|
);
|
|
ok(
|
|
!(await PreferenceRollouts.has("does not exist")),
|
|
"has should return false for a missing rollout"
|
|
);
|
|
});
|
|
|
|
// recordOriginalValue should update storage to note the original values
|
|
decorate_task(
|
|
PreferenceRollouts.withTestMock(),
|
|
async function testRecordOriginalValuesUpdatesPreviousValues() {
|
|
await PreferenceRollouts.add({
|
|
slug: "test-rollout",
|
|
state: PreferenceRollouts.STATE_ACTIVE,
|
|
preferences: [
|
|
{ preferenceName: "test.pref", value: 2, previousValue: null },
|
|
],
|
|
});
|
|
|
|
await PreferenceRollouts.recordOriginalValues({ "test.pref": 1 });
|
|
|
|
Assert.deepEqual(
|
|
await PreferenceRollouts.getAll(),
|
|
[
|
|
{
|
|
slug: "test-rollout",
|
|
state: PreferenceRollouts.STATE_ACTIVE,
|
|
preferences: [
|
|
{ preferenceName: "test.pref", value: 2, previousValue: 1 },
|
|
],
|
|
},
|
|
],
|
|
"rollout in database should be updated"
|
|
);
|
|
}
|
|
);
|
|
|
|
// recordOriginalValue should graduate a study when all of its preferences are built-in
|
|
decorate_task(
|
|
withSendEventSpy(),
|
|
PreferenceRollouts.withTestMock(),
|
|
async function testRecordOriginalValuesGraduates({ sendEventSpy }) {
|
|
await PreferenceRollouts.add({
|
|
slug: "test-rollout",
|
|
state: PreferenceRollouts.STATE_ACTIVE,
|
|
preferences: [
|
|
{ preferenceName: "test.pref1", value: 2, previousValue: null },
|
|
{ preferenceName: "test.pref2", value: 2, previousValue: null },
|
|
],
|
|
});
|
|
|
|
// one pref being the same isn't enough to graduate
|
|
await PreferenceRollouts.recordOriginalValues({
|
|
"test.pref1": 1,
|
|
"test.pref2": 2,
|
|
});
|
|
let rollout = await PreferenceRollouts.get("test-rollout");
|
|
is(
|
|
rollout.state,
|
|
PreferenceRollouts.STATE_ACTIVE,
|
|
"rollouts should remain active when only one pref matches the built-in default"
|
|
);
|
|
|
|
sendEventSpy.assertEvents([]);
|
|
|
|
// both prefs is enough
|
|
await PreferenceRollouts.recordOriginalValues({
|
|
"test.pref1": 2,
|
|
"test.pref2": 2,
|
|
});
|
|
rollout = await PreferenceRollouts.get("test-rollout");
|
|
is(
|
|
rollout.state,
|
|
PreferenceRollouts.STATE_GRADUATED,
|
|
"rollouts should graduate when all prefs matches the built-in defaults"
|
|
);
|
|
|
|
sendEventSpy.assertEvents([
|
|
["graduate", "preference_rollout", "test-rollout", {}],
|
|
]);
|
|
}
|
|
);
|
|
|
|
// init should mark active rollouts in telemetry
|
|
decorate_task(
|
|
withStub(TelemetryEnvironment, "setExperimentActive"),
|
|
PreferenceRollouts.withTestMock(),
|
|
async function testInitTelemetry({ setExperimentActiveStub }) {
|
|
await PreferenceRollouts.add({
|
|
slug: "test-rollout-active-1",
|
|
state: PreferenceRollouts.STATE_ACTIVE,
|
|
});
|
|
await PreferenceRollouts.add({
|
|
slug: "test-rollout-active-2",
|
|
state: PreferenceRollouts.STATE_ACTIVE,
|
|
});
|
|
await PreferenceRollouts.add({
|
|
slug: "test-rollout-rolled-back",
|
|
state: PreferenceRollouts.STATE_ROLLED_BACK,
|
|
});
|
|
await PreferenceRollouts.add({
|
|
slug: "test-rollout-graduated",
|
|
state: PreferenceRollouts.STATE_GRADUATED,
|
|
});
|
|
|
|
await PreferenceRollouts.init();
|
|
|
|
Assert.deepEqual(
|
|
setExperimentActiveStub.args,
|
|
[
|
|
["test-rollout-active-1", "active", { type: "normandy-prefrollout" }],
|
|
["test-rollout-active-2", "active", { type: "normandy-prefrollout" }],
|
|
],
|
|
"init should set activate a telemetry experiment for active preferences"
|
|
);
|
|
}
|
|
);
|
|
|
|
// init should graduate rollouts in the graduation set
|
|
decorate_task(
|
|
withStub(TelemetryEnvironment, "setExperimentActive"),
|
|
withSendEventSpy(),
|
|
PreferenceRollouts.withTestMock({
|
|
graduationSet: new Set(["test-rollout"]),
|
|
rollouts: [
|
|
preferenceRolloutFactory({
|
|
slug: "test-rollout",
|
|
state: PreferenceRollouts.STATE_ACTIVE,
|
|
}),
|
|
],
|
|
}),
|
|
async function testInitGraduationSet({
|
|
setExperimentActiveStub,
|
|
sendEventSpy,
|
|
}) {
|
|
await PreferenceRollouts.init();
|
|
const newRollout = await PreferenceRollouts.get("test-rollout");
|
|
Assert.equal(
|
|
newRollout.state,
|
|
PreferenceRollouts.STATE_GRADUATED,
|
|
"the rollout should be graduated"
|
|
);
|
|
Assert.deepEqual(
|
|
setExperimentActiveStub.args,
|
|
[],
|
|
"setExperimentActive should not be called"
|
|
);
|
|
sendEventSpy.assertEvents([
|
|
[
|
|
"graduate",
|
|
"preference_rollout",
|
|
"test-rollout",
|
|
{ reason: "in-graduation-set" },
|
|
],
|
|
]);
|
|
}
|
|
);
|