Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/toolkit/components/nimbus/test/unit/   (Firefox Browser Version 153.0.1©)  Datei vom 27.6.2026 mit Größe 96 kB image not shown  

Quelle  test_prefFlips.js   Sprache: JAVA

 

/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/
 */


const { PrefUtils } = ChromeUtils.importESModule(
  "moz-src:///toolkit/modules/PrefUtils.sys.mjs"
);
const { JsonSchema } = ChromeUtils.importESModule(
  "resource://gre/modules/JsonSchema.sys.mjs"
);

const {
  NimbusTelemetry: { UnenrollReason },
} = ChromeUtils.importESModule("resource://nimbus/lib/Telemetry.sys.mjs");

const { ProfilesDatastoreService } = ChromeUtils.importESModule(
  "moz-src:///toolkit/profile/ProfilesDatastoreService.sys.mjs"
);

const USER = "user";
const DEFAULT = "default";

const STRING_PREF = "test.nimbus.prefFlips.string";
const INT_PREF = "test.nimbus.prefFlips.int";
const BOOL_PREF = "test.nimbus.prefFlips.boolean";

const FEATURE_ID = "prefFlips";

const SET_BEFORE_VALUE = "set-before-value";
const USER_VALUE = "user-value";
const DEFAULT_VALUE = "default-value";

const PREF_FEATURES = {
  [USER]: new ExperimentFeature("test-set-pref-user-1", {
    description: "Test feature that sets prefs on the user branch via setPref",
    owner: "test@test.test",
    hasExposure: false,
    variables: {
      foo: {
        type: "string",
        description: "test variable",
        setPref: {
          branch: USER,
          pref: "nimbus.test-only.foo",
        },
      },
    },
  }),
  [DEFAULT]: new ExperimentFeature("test-set-pref-default-1", {
    description:
      "Test feature that sets prefs on the default branch via setPref",
    owner: "test@test.test",
    hasExposure: false,
    variables: {
      foo: {
        type: "string",
        description: "test variable",
        setPref: {
          branch: DEFAULT,
          pref: "nimbus.test-only.foo",
        },
      },
    },
  }),
};

function setPrefs(prefs) {
  for (const [name, { userBranchValue, defaultBranchValue }] of Object.entries(
    prefs
  )) {
    // If the different prefs have the same value, we must set the user branch
    // value first. Otherwise when we try to set the user branch value after
    // the default value, it will see the value already set for the user
    // branch (because it falls back to the default branch value) and will not
    // set it, leaving only a default branch pref.
    if (typeof userBranchValue !== "undefined") {
      PrefUtils.setPref(name, userBranchValue);
    }

    if (typeof defaultBranchValue !== "undefined") {
      PrefUtils.setPref(name, defaultBranchValue, { branch: DEFAULT });
    }
  }
}

function cleanupPrefs(prefs) {
  for (const name of Object.keys(prefs)) {
    Services.prefs.deleteBranch(name);
  }
}

function checkExpectedPrefs(prefs) {
  for (const [name, value] of Object.entries(prefs)) {
    Assert.equal(
      PrefUtils.getPref(name),
      value,
      `Pref ${name} has correct value`
    );
  }
}

function checkExpectedPrefBranches(prefs) {
  for (const [
    name,
    { defaultBranchValue = null, userBranchValue = null },
  ] of Object.entries(prefs)) {
    if (userBranchValue === null) {
      Assert.ok(
        !Services.prefs.prefHasUserValue(name),
        `Pref ${name} has no value on user branch`
      );
    } else {
      Assert.equal(
        PrefUtils.getPref(name, { branch: USER }),
        userBranchValue,
        `Pref ${name} has correct value on user branch`
      );
    }

    if (defaultBranchValue === null) {
      Assert.ok(
        !Services.prefs.prefHasDefaultValue(name),
        `Pref ${name} has no value on default branch`
      );
    } else {
      Assert.equal(
        PrefUtils.getPref(name, { branch: DEFAULT }),
        defaultBranchValue,
        `Pref ${name} has correct value on default branch`
      );
    }
  }
}

/**
 * Generate a comparison function for `Array.prototype.sort`.
 *
 * @param {string} property
 *
 * @returns {(a: object, b: object) => number} The comparison function.
 */

function compareBy(property) {
  return (a, b) => a[property].localeCompare(b[property]);
}

const compareByExperiment = compareBy("experiment");

add_setup(function setup() {
  Services.fog.initializeFOG();
  Services.telemetry.clearEvents();

  registerCleanupFunction(
    NimbusTestUtils.addTestFeatures(PREF_FEATURES[USER], PREF_FEATURES[DEFAULT])
  );
});

async function setupTest({ ...args } = {}) {
  const { cleanup: baseCleanup, ...ctx } = await NimbusTestUtils.setupTest({
    ...args,
    clearTelemetry: true,
  });

  return {
    ...ctx,
    async cleanup() {
      assertNoObservers(ctx.manager);
      await baseCleanup();
    },
  };
}

add_task(async function test_schema() {
  const schema = await fetch(
    "resource://nimbus/schemas/PrefFlipsFeature.schema.json"
  ).then(rsp => rsp.json());
  const validator = new JsonSchema.Validator(schema);

  const ALLOWED_TEST_CASES = [
    { prefs: {} },
    {
      prefs: {
        "foo.string": {
          branch: USER,
          value: "value",
        },
        "foo.int": {
          branch: USER,
          value: 123,
        },
        "foo.bool": {
          branch: USER,
          value: true,
        },
        "bar.string": {
          branch: DEFAULT,
          value: "value",
        },
        "bar.int": {
          branch: DEFAULT,
          value: 345,
        },
        "bar.bool": {
          branch: DEFAULT,
          value: false,
        },
      },
    },
  ];

  for (const obj of ALLOWED_TEST_CASES) {
    const result = validator.validate(obj);
    Assert.ok(
      result.valid,
      `validated: ${JSON.stringify(result.errors, null, 2)}`
    );
  }

  const DISALLOWED_TEST_CASES = [
    {},
    {
      prefs: {
        "foo.bar.baz": {
          branch: "other",
          value: "value",
        },
      },
    },
    {
      prefs: {
        "foo.bar.baz": {},
      },
    },
    {
      prefs: {
        "foo.bar.baz": {
          branch: USER,
        },
      },
    },
    {
      prefs: {
        "foo.bar.baz": {
          branch: DEFAULT,
        },
      },
    },
    {
      prefs: {
        "foo.bar.baz": {
          value: "value",
        },
      },
    },
    {
      prefs: {
        "foo.bar.baz": {
          branch: DEFAULT,
          value: null,
        },
      },
    },
  ];

  for (const obj of DISALLOWED_TEST_CASES) {
    const result = validator.validate(obj);
    Assert.ok(!result.valid);
  }
});

add_task(async function test_prefFlips() {
  const setUserPrefs = {
    prefs: {
      [STRING_PREF]: {
        branch: USER,
        value: "hello, world",
      },
      [INT_PREF]: {
        branch: USER,
        value: 123,
      },
      [BOOL_PREF]: {
        branch: USER,
        value: true,
      },
    },
  };
  const setDefaultPrefs = {
    prefs: {
      [STRING_PREF]: {
        branch: DEFAULT,
        value: "hello, world",
      },
      [INT_PREF]: {
        branch: DEFAULT,
        value: 123,
      },
      [BOOL_PREF]: {
        branch: DEFAULT,
        value: true,
      },
    },
  };

  const clearUserPrefs = {
    prefs: {
      [STRING_PREF]: {
        branch: USER,
        value: null,
      },
      [INT_PREF]: {
        branch: USER,
        value: null,
      },
      [BOOL_PREF]: {
        branch: USER,
        value: null,
      },
    },
  };

  const PRE_SET_PREFS = {
    [USER]: {
      [STRING_PREF]: { userBranchValue: "goodbye, world" },
      [INT_PREF]: { userBranchValue: 234 },
      [BOOL_PREF]: { userBranchValue: false },
    },
    [DEFAULT]: {
      [STRING_PREF]: { defaultBranchValue: "goodbye, world" },
      [INT_PREF]: { defaultBranchValue: 234 },
      [BOOL_PREF]: { defaultBranchValue: false },
    },
    BOTH_BRANCHES: {
      [STRING_PREF]: {
        userBranchValue: USER_VALUE,
        defaultBranchValue: DEFAULT_VALUE,
      },
      [INT_PREF]: { userBranchValue: 2, defaultBranchValue: 3 },
      [BOOL_PREF]: { userBranchValue: false, defaultBranchValue: false },
    },
  };

  const TEST_CASES = [
    {
      name: "Set prefs on the user branch",
      featureValue: setUserPrefs,
    },
    {
      name: "Set prefs on the user branch with pre-existing values on the user branch",
      featureValue: setUserPrefs,
      setPrefsBefore: PRE_SET_PREFS[USER],
    },
    {
      name: "Set prefs on the user branch with pre-existing values on the default branch",
      featureValue: setUserPrefs,
      setPrefsBefore: PRE_SET_PREFS[DEFAULT],
    },
    {
      name: "Set prefs on the user branch with pre-existing values on both branches",
      featureValue: setUserPrefs,
      setPrefsBefore: PRE_SET_PREFS.BOTH_BRANCHES,
    },
    {
      name: "Set prefs on the default branch",
      featureValue: setDefaultPrefs,
    },
    {
      name: "Set prefs on the default branch with pre-existing values on the default branch",
      featureValue: setDefaultPrefs,
      setPrefsBefore: PRE_SET_PREFS[DEFAULT],
    },
    {
      name: "Set prefs on the default branch with pre-existing values on the user branch",
      featureValue: setDefaultPrefs,
      setPrefsBefore: PRE_SET_PREFS[USER],
      expectedPrefs: {
        [STRING_PREF]: PRE_SET_PREFS[USER][STRING_PREF].userBranchValue,
        [INT_PREF]: PRE_SET_PREFS[USER][INT_PREF].userBranchValue,
        [BOOL_PREF]: PRE_SET_PREFS[USER][BOOL_PREF].userBranchValue,
      },
    },
    {
      name: "Set prefs on the default branch with pre-existing values on both branches",
      featureValue: setDefaultPrefs,
      setPrefsBefore: PRE_SET_PREFS.BOTH_BRANCHES,
      expectedPrefs: {
        [STRING_PREF]: PRE_SET_PREFS.BOTH_BRANCHES[STRING_PREF].userBranchValue,
        [INT_PREF]: PRE_SET_PREFS.BOTH_BRANCHES[INT_PREF].userBranchValue,
        [BOOL_PREF]: PRE_SET_PREFS.BOTH_BRANCHES[BOOL_PREF].userBranchValue,
      },
    },
    {
      name: "Clearing prefs on the user branch (with value null) without pre-existing values",
      featureValue: clearUserPrefs,
    },
    {
      name: "Clearing prefs on the user branch (with value null) with pre-existing values on the user branch",
      featureValue: clearUserPrefs,
      setPrefsBefore: PRE_SET_PREFS[USER],
    },
    {
      name: "Clearing prefs on the user branch (with value null) with pre-existing values on the default branch",
      featureValue: clearUserPrefs,
      setPrefsBefore: PRE_SET_PREFS[DEFAULT],
      // This will not affect the default branch prefs.
      expectedPrefs: {
        [STRING_PREF]: PRE_SET_PREFS[DEFAULT][STRING_PREF].defaultBranchValue,
        [INT_PREF]: PRE_SET_PREFS[DEFAULT][INT_PREF].defaultBranchValue,
        [BOOL_PREF]: PRE_SET_PREFS[DEFAULT][BOOL_PREF].defaultBranchValue,
      },
    },
    {
      name: "Clearing prefs on the user branch (with value null) with pre-existing values on both branches",
      featureValue: clearUserPrefs,
      setPrefsBefore: PRE_SET_PREFS.BOTH_BRANCHES,
      expectedPrefs: {
        [STRING_PREF]:
          PRE_SET_PREFS.BOTH_BRANCHES[STRING_PREF].defaultBranchValue,
        [INT_PREF]: PRE_SET_PREFS.BOTH_BRANCHES[INT_PREF].defaultBranchValue,
        [BOOL_PREF]: PRE_SET_PREFS.BOTH_BRANCHES[BOOL_PREF].defaultBranchValue,
      },
    },
  ];

  for (const [i, { name, ...testCase }] of TEST_CASES.entries()) {
    info(`Running test case ${i}: ${name}`);

    const {
      // The feature config to enroll.
      featureValue,

      // Prefs that should be set before enrollment. These will be undone after
      // each test case.
      setPrefsBefore = {},

      // Additional prefs to check after enrollment. They will be checked on the
      // user branch.
      expectedPrefs = {},
    } = testCase;

    const { manager, cleanup } = await setupTest();

    info("Setting initial values of prefs...");
    setPrefs(setPrefsBefore);

    // Collect the values of any prefs that will be set by the enrollment so we
    // can compare their values after unenrollment.
    const prefValuesBeforeEnrollment = Object.fromEntries(
      Object.keys(featureValue.prefs).map(prefName => [
        prefName,
        PrefUtils.getPref(prefName),
      ])
    );

    info("Enrolling...");
    const cleanupExperiment = await NimbusTestUtils.enrollWithFeatureConfig(
      {
        featureId: FEATURE_ID,
        value: featureValue,
      },
      {
        manager,
        isRollout: true,
      }
    );

    info("Checking prefs were set by enrollment...");
    for (const [prefName, { branch, value }] of Object.entries(
      featureValue.prefs
    )) {
      if (typeof value === "undefined" || value === null) {
        if (branch === USER) {
          Assert.ok(
            !Services.prefs.prefHasUserValue(prefName),
            `${prefName} was cleared on the user branch`
          );
        } else if (prefValuesBeforeEnrollment[prefName] !== null) {
          // Can't clear the user branch.
          Assert.equal(
            PrefUtils.getPref(prefName, { branch }),
            prefValuesBeforeEnrollment
          );
        } else {
          Assert.equal(PrefUtils.getPref(prefName, { branch }), value);
        }
      } else {
        Assert.equal(PrefUtils.getPref(prefName, { branch }), value);
      }
    }

    if (expectedPrefs) {
      info("Checking expected prefs...");
      checkExpectedPrefs(expectedPrefs);
    }

    info("Unenrolling...");
    await cleanupExperiment();

    info("Checking prefs were restored after unenrollment...");
    // After unenrollment, the prefs should have been restored to their values
    // before enrollment.
    for (const [prefName, originalValue] of Object.entries(
      prefValuesBeforeEnrollment
    )) {
      // If the pref was set on the default branch, it won't be cleared. It will
      // persist until the next restart.
      const expectedValue =
        featureValue.prefs[prefName].branch === "default" &&
        originalValue === null
          ? featureValue.prefs[prefName].value
          : originalValue;
      Assert.equal(PrefUtils.getPref(prefName), expectedValue);
    }

    info("Cleaning up...");
    // Clear all the prefs we specified in `setPrefsBefore`.
    cleanupPrefs(setPrefsBefore);

    // Clear all prefs specified by the enrollment.
    for (const prefName of Object.keys(featureValue.prefs)) {
      Services.prefs.deleteBranch(prefName);
    }

    await cleanup();
  }
});

add_task(async function test_prefFlips_unenrollment() {
  const PREF_FOO = "nimbus.test-only.foo";
  const PREF_BAR = "nimbus.test-only.bar";

  const SLUG_1 = "slug-1";
  const SLUG_2 = "slug-2";
  const SLUG_3 = "slug-3";

  const EXPERIMENT_VALUE = "experiment-value";

  const TEST_CASES = [
    // Single enrollment case (experiments)
    {
      name: "set pref on the user branch with a prefFlips experiment and change that pref on the user branch",
      enrollmentOrder: [
        {
          slug: SLUG_1,
          featureId: FEATURE_ID,
          value: {
            prefs: { [PREF_FOO]: { value: EXPERIMENT_VALUE, branch: USER } },
          },
        },
      ],
      setPrefsAfter: { [PREF_FOO]: { userBranchValue: USER_VALUE } },
      expectedUnenrollments: [SLUG_1],
      expectedPrefs: { [PREF_FOO]: USER_VALUE },
      unenrollReason: UnenrollReason.CHANGED_PREF,
      unenrollTelemetry: {
        changed_pref: PREF_FOO,
        about_config_change: "false",
      },
    },
    {
      name: "set pref on the user branch with a prefFlips experiment and change that pref on the default branch",
      enrollmentOrder: [
        {
          slug: SLUG_1,
          featureId: FEATURE_ID,
          value: {
            prefs: { [PREF_FOO]: { value: EXPERIMENT_VALUE, branch: USER } },
          },
        },
      ],
      setPrefsAfter: { [PREF_FOO]: { defaultBranchValue: DEFAULT_VALUE } },
      expectedEnrollments: [SLUG_1],
      expectedPrefs: { [PREF_FOO]: EXPERIMENT_VALUE },
    },
    {
      name: "set pref on the default branch with a prefFlips experiment and change that pref on the user branch",
      enrollmentOrder: [
        {
          slug: SLUG_1,
          featureId: FEATURE_ID,
          value: {
            prefs: { [PREF_FOO]: { value: EXPERIMENT_VALUE, branch: DEFAULT } },
          },
        },
      ],
      setPrefsAfter: { [PREF_FOO]: { userBranchValue: USER_VALUE } },
      expectedUnenrollments: [SLUG_1],
      expectedPrefs: { [PREF_FOO]: USER_VALUE },
      unenrollReason: UnenrollReason.CHANGED_PREF,
      unenrollTelemetry: {
        changed_pref: PREF_FOO,
        about_config_change: "false",
      },
    },
    {
      name: "set pref on the default branch with a prefFlips experiment and change that pref on the default branch",
      enrollmentOrder: [
        {
          slug: SLUG_1,
          featureId: FEATURE_ID,
          value: {
            prefs: { [PREF_FOO]: { value: EXPERIMENT_VALUE, branch: DEFAULT } },
          },
        },
      ],
      setPrefsAfter: { [PREF_FOO]: { defaultBranchValue: DEFAULT_VALUE } },
      expectedUnenrollments: [SLUG_1],
      expectedPrefs: { [PREF_FOO]: DEFAULT_VALUE },
      unenrollReason: UnenrollReason.CHANGED_PREF,
      unenrollTelemetry: {
        changed_pref: PREF_FOO,
        about_config_change: "false",
      },
    },
    // Single enrollment case, multiple prefs being reset
    {
      name: "set prefs on the user branch with a prefFlips experiment and change one pref on the user branch",
      setPrefsBefore: { [PREF_FOO]: { userBranchValue: SET_BEFORE_VALUE } },
      enrollmentOrder: [
        {
          slug: SLUG_1,
          featureId: FEATURE_ID,
          value: {
            prefs: {
              [PREF_FOO]: { value: EXPERIMENT_VALUE, branch: USER },
              [PREF_BAR]: { value: EXPERIMENT_VALUE, branch: USER },
            },
          },
        },
      ],
      setPrefsAfter: { [PREF_BAR]: { userBranchValue: USER_VALUE } },
      expectedUnenrollments: [SLUG_1],
      expectedPrefs: { [PREF_FOO]: SET_BEFORE_VALUE, [PREF_BAR]: USER_VALUE },
      unenrollReason: UnenrollReason.CHANGED_PREF,
      unenrollTelemetry: {
        changed_pref: PREF_BAR,
        about_config_change: "false",
      },
    },
    {
      name: "set prefs on the user branch with a prefFlips experiment and change one pref on the default branch",
      setPrefsBefore: { [PREF_FOO]: { userBranchValue: SET_BEFORE_VALUE } },
      enrollmentOrder: [
        {
          slug: SLUG_1,
          featureId: FEATURE_ID,
          value: {
            prefs: {
              [PREF_FOO]: { value: EXPERIMENT_VALUE, branch: USER },
              [PREF_BAR]: { value: EXPERIMENT_VALUE, branch: USER },
            },
          },
        },
      ],
      setPrefsAfter: { [PREF_BAR]: { defaultBranchValue: DEFAULT_VALUE } },
      expectedEnrollments: [SLUG_1],
      expectedPrefs: {
        [PREF_FOO]: EXPERIMENT_VALUE,
        [PREF_BAR]: EXPERIMENT_VALUE,
      },
    },
    {
      name: "set prefs on the default branch with a prefFlips experiment and change one pref on the user branch",
      setPrefsBefore: { [PREF_FOO]: { defaultBranchValue: SET_BEFORE_VALUE } },
      enrollmentOrder: [
        {
          slug: SLUG_1,
          featureId: FEATURE_ID,
          value: {
            prefs: {
              [PREF_FOO]: { value: EXPERIMENT_VALUE, branch: DEFAULT },
              [PREF_BAR]: { value: EXPERIMENT_VALUE, branch: DEFAULT },
            },
          },
        },
      ],
      setPrefsAfter: { [PREF_BAR]: { userBranchValue: USER_VALUE } },
      expectedUnenrollments: [SLUG_1],
      expectedPrefs: { [PREF_FOO]: SET_BEFORE_VALUE, [PREF_BAR]: USER_VALUE },
      unenrollReason: UnenrollReason.CHANGED_PREF,
      unenrollTelemetry: {
        changed_pref: PREF_BAR,
        about_config_change: "false",
      },
    },
    {
      name: "set prefs on the default branch with a prefFlips experiment and change one pref on the default branch",
      setPrefsBefore: { [PREF_FOO]: { defaultBranchValue: SET_BEFORE_VALUE } },
      enrollmentOrder: [
        {
          slug: SLUG_1,
          featureId: FEATURE_ID,
          value: {
            prefs: {
              [PREF_FOO]: { value: EXPERIMENT_VALUE, branch: DEFAULT },
              [PREF_BAR]: { value: EXPERIMENT_VALUE, branch: DEFAULT },
            },
          },
        },
      ],
      setPrefsAfter: { [PREF_BAR]: { defaultBranchValue: DEFAULT_VALUE } },
      expectedUnenrollments: [SLUG_1],
      expectedPrefs: {
        [PREF_FOO]: SET_BEFORE_VALUE,
        [PREF_BAR]: DEFAULT_VALUE,
      },
      unenrollReason: UnenrollReason.CHANGED_PREF,
      unenrollTelemetry: {
        changed_pref: PREF_BAR,
        about_config_change: "false",
      },
    },
    // Multiple enrollment cases
    // - test that we leave enrollments that do not conflict with the set pref
    {
      name: "set pref on the user branch with two prefFlips experiments and then change a pref controlled by only one experiment on the user branch",
      setPrefsBefore: { [PREF_FOO]: { userBranchValue: SET_BEFORE_VALUE } },
      enrollmentOrder: [
        {
          slug: SLUG_1,
          featureId: FEATURE_ID,
          value: {
            prefs: { [PREF_FOO]: { value: EXPERIMENT_VALUE, branch: USER } },
          },
        },
        {
          slug: SLUG_2,
          featureId: FEATURE_ID,
          value: {
            prefs: {
              [PREF_FOO]: { value: EXPERIMENT_VALUE, branch: USER },
              [PREF_BAR]: { value: EXPERIMENT_VALUE, branch: USER },
            },
          },
        },
      ],
      setPrefsAfter: { [PREF_BAR]: { userBranchValue: USER_VALUE } },
      expectedEnrollments: [SLUG_1],
      expectedUnenrollments: [SLUG_2],
      expectedPrefs: { [PREF_FOO]: EXPERIMENT_VALUE, [PREF_BAR]: USER_VALUE },
      unenrollReason: UnenrollReason.CHANGED_PREF,
      unenrollTelemetry: {
        changed_pref: PREF_BAR,
        about_config_change: "false",
      },
    },
    {
      name: "set pref on the user branch two prefFlips experiments and then change a pref controlled by only one experiment on the default branch",
      setPrefsBefore: { [PREF_FOO]: { userBranchValue: SET_BEFORE_VALUE } },
      enrollmentOrder: [
        {
          slug: SLUG_1,
          featureId: FEATURE_ID,
          value: {
            prefs: { [PREF_FOO]: { value: EXPERIMENT_VALUE, branch: USER } },
          },
        },
        {
          slug: SLUG_2,
          featureId: FEATURE_ID,
          value: {
            prefs: {
              [PREF_FOO]: { value: EXPERIMENT_VALUE, branch: USER },
              [PREF_BAR]: { value: EXPERIMENT_VALUE, branch: USER },
            },
          },
        },
      ],
      setPrefsAfter: { [PREF_BAR]: { defaultBranchValue: DEFAULT_VALUE } },
      expectedEnrollments: [SLUG_1, SLUG_2],
      expectedPrefs: {
        [PREF_FOO]: EXPERIMENT_VALUE,
        [PREF_BAR]: EXPERIMENT_VALUE,
      },
      unenrollReason: UnenrollReason.CHANGED_PREF,
      unenrollTelemetry: {
        changed_pref: PREF_BAR,
        about_config_change: "false",
      },
    },
    // - test that we unenroll from all conflicting experiments
    {
      name: "set pref on the default branch with two prefFlips experiments and then change that pref on the user branch",
      setPrefsBefore: { [PREF_FOO]: { defaultBranchValue: SET_BEFORE_VALUE } },
      enrollmentOrder: [
        {
          slug: SLUG_1,
          featureId: FEATURE_ID,
          value: {
            prefs: { [PREF_FOO]: { value: EXPERIMENT_VALUE, branch: DEFAULT } },
          },
        },
        {
          slug: SLUG_2,
          featureId: FEATURE_ID,
          value: {
            prefs: { [PREF_FOO]: { value: EXPERIMENT_VALUE, branch: DEFAULT } },
          },
        },
      ],
      setPrefsAfter: { [PREF_FOO]: { userBranchValue: USER_VALUE } },
      expectedUnenrollments: [SLUG_1, SLUG_2],
      expectedPrefs: { [PREF_FOO]: USER_VALUE },
      unenrollReason: UnenrollReason.CHANGED_PREF,
      unenrollTelemetry: {
        changed_pref: PREF_FOO,
        about_config_change: "false",
      },
    },
    {
      name: "set pref on the default branch with two prefFlips experiments and then change that pref on the default branch",
      setPrefsBefore: { [PREF_FOO]: { defaultBranchValue: SET_BEFORE_VALUE } },
      enrollmentOrder: [
        {
          slug: SLUG_1,
          featureId: FEATURE_ID,
          value: {
            prefs: { [PREF_FOO]: { value: EXPERIMENT_VALUE, branch: DEFAULT } },
          },
        },
        {
          slug: SLUG_2,
          featureId: FEATURE_ID,
          value: {
            prefs: { [PREF_FOO]: { value: EXPERIMENT_VALUE, branch: DEFAULT } },
          },
        },
      ],
      setPrefsAfter: { [PREF_FOO]: { defaultBranchValue: DEFAULT_VALUE } },
      expectedUnenrollments: [SLUG_1, SLUG_2],
      expectedPrefs: { [PREF_FOO]: DEFAULT_VALUE },
      unenrollReason: UnenrollReason.CHANGED_PREF,
      unenrollTelemetry: {
        changed_pref: PREF_FOO,
        about_config_change: "false",
      },
    },
    // - test we unenroll when the experiments conflict with eachother.
    {
      name: "set pref on the user branch with two experiments with conflicting values",
      enrollmentOrder: [
        {
          slug: SLUG_1,
          featureId: FEATURE_ID,
          value: {
            prefs: { [PREF_FOO]: { value: SLUG_1, branch: USER } },
          },
        },
        {
          slug: SLUG_2,
          featureId: FEATURE_ID,
          value: {
            prefs: { [PREF_FOO]: { value: SLUG_2, branch: USER } },
          },
        },
      ],
      expectedEnrollments: [SLUG_1],
      expectedUnenrollments: [SLUG_2],
      expectedPrefs: { [PREF_FOO]: SLUG_1 },
      unenrollReason: UnenrollReason.PREF_FLIPS_FAILED,
      unenrollTelemetry: { pref_name: PREF_FOO, pref_type: "string" },
    },
    {
      name: "set pref on the default branch with two experiments with conflicting values",
      enrollmentOrder: [
        {
          slug: SLUG_1,
          featureId: FEATURE_ID,
          value: {
            prefs: { [PREF_FOO]: { value: SLUG_1, branch: DEFAULT } },
          },
        },
        {
          slug: SLUG_2,
          featureId: FEATURE_ID,
          value: {
            prefs: { [PREF_FOO]: { value: SLUG_2, branch: DEFAULT } },
          },
        },
      ],
      expectedEnrollments: [SLUG_1],
      expectedUnenrollments: [SLUG_2],
      expectedPrefs: { [PREF_FOO]: SLUG_1 },
      unenrollReason: UnenrollReason.PREF_FLIPS_FAILED,
      unenrollTelemetry: { pref_name: PREF_FOO, pref_type: "string" },
    },
    {
      name: "set pref on the user branch with an experiment and set it on the default branch with another",
      enrollmentOrder: [
        {
          slug: SLUG_1,
          featureId: FEATURE_ID,
          value: {
            prefs: { [PREF_FOO]: { value: EXPERIMENT_VALUE, branch: USER } },
          },
        },
        {
          slug: SLUG_2,
          featureId: FEATURE_ID,
          value: {
            prefs: { [PREF_FOO]: { value: EXPERIMENT_VALUE, branch: DEFAULT } },
          },
        },
      ],
      expectedEnrollments: [SLUG_1],
      expectedUnenrollments: [SLUG_2],
      expectedPrefs: { [PREF_FOO]: EXPERIMENT_VALUE },
      unenrollReason: UnenrollReason.PREF_FLIPS_FAILED,
      unenrollTelemetry: { pref_name: PREF_FOO, pref_type: "string" },
    },
    {
      name: "set pref on the default branch with an experiment and set it on the user branch with another",
      enrollmentOrder: [
        {
          slug: SLUG_1,
          featureId: FEATURE_ID,
          value: {
            prefs: { [PREF_FOO]: { value: EXPERIMENT_VALUE, branch: DEFAULT } },
          },
        },
        {
          slug: SLUG_2,
          featureId: FEATURE_ID,
          value: {
            prefs: { [PREF_FOO]: { value: EXPERIMENT_VALUE, branch: USER } },
          },
        },
      ],
      expectedEnrollments: [SLUG_1],
      expectedUnenrollments: [SLUG_2],
      expectedPrefs: { [PREF_FOO]: EXPERIMENT_VALUE },
      unenrollReason: UnenrollReason.PREF_FLIPS_FAILED,
      unenrollTelemetry: { pref_name: PREF_FOO, pref_type: "string" },
    },

    // Multiple enrollment cases (prefFlips -> setPref)

    // NB: We don't need to test setPref experiments/rollouts on both branches
    // for the same pref because that configuration is prohibited by
    // gen_feature_manifests.py

    // * prefFlip experiments -> setPref experiment
    {
      name: "enroll in prefFlips experiments on the user branch and then a setPref experiment on the user branch",
      enrollmentOrder: [
        {
          slug: SLUG_1,
          featureId: FEATURE_ID,
          value: {
            prefs: { [PREF_FOO]: { value: EXPERIMENT_VALUE, branch: USER } },
          },
        },
        {
          slug: SLUG_2,
          featureId: FEATURE_ID,
          value: {
            prefs: { [PREF_FOO]: { value: EXPERIMENT_VALUE, branch: USER } },
          },
        },
        {
          slug: SLUG_3,
          featureId: PREF_FEATURES[USER].featureId,
          value: {
            foo: EXPERIMENT_VALUE,
          },
        },
      ],
      expectedEnrollments: [SLUG_3],
      expectedUnenrollments: [SLUG_1, SLUG_2],
      expectedPrefs: { [PREF_FOO]: EXPERIMENT_VALUE },
      unenrollReason: UnenrollReason.PREF_FLIPS_CONFLICT,
      unenrollTelemetry: { conflicting_slug: SLUG_3 },
    },
    {
      name: "enroll in prefFlips experiments on the default branch and then a setPref experiment on the user branch",
      enrollmentOrder: [
        {
          slug: SLUG_1,
          featureId: FEATURE_ID,
          value: {
            prefs: { [PREF_FOO]: { value: EXPERIMENT_VALUE, branch: DEFAULT } },
          },
        },
        {
          slug: SLUG_2,
          featureId: FEATURE_ID,
          value: {
            prefs: { [PREF_FOO]: { value: EXPERIMENT_VALUE, branch: DEFAULT } },
          },
        },
        {
          slug: SLUG_3,
          featureId: PREF_FEATURES[USER].featureId,
          value: {
            foo: EXPERIMENT_VALUE,
          },
        },
      ],
      expectedEnrollments: [SLUG_3],
      expectedUnenrollments: [SLUG_1, SLUG_2],
      expectedPrefs: { [PREF_FOO]: EXPERIMENT_VALUE },
      unenrollReason: UnenrollReason.PREF_FLIPS_CONFLICT,
      unenrollTelemetry: { conflicting_slug: SLUG_3 },
    },
    {
      name: "enroll in prefFlips experiments on the user branch and then a setPref experiment on the default branch",
      enrollmentOrder: [
        {
          slug: SLUG_1,
          featureId: FEATURE_ID,
          value: {
            prefs: { [PREF_FOO]: { value: EXPERIMENT_VALUE, branch: USER } },
          },
        },
        {
          slug: SLUG_2,
          featureId: FEATURE_ID,
          value: {
            prefs: { [PREF_FOO]: { value: EXPERIMENT_VALUE, branch: USER } },
          },
        },
        {
          slug: SLUG_3,
          featureId: PREF_FEATURES[DEFAULT].featureId,
          value: {
            foo: EXPERIMENT_VALUE,
          },
        },
      ],
      expectedEnrollments: [SLUG_3],
      expectedUnenrollments: [SLUG_1, SLUG_2],
      expectedPrefs: { [PREF_FOO]: EXPERIMENT_VALUE },
      unenrollReason: UnenrollReason.PREF_FLIPS_CONFLICT,
      unenrollTelemetry: { conflicting_slug: SLUG_3 },
    },
    {
      name: "enroll in prefFlips experiments on the default branch and then a setPref experiment on the default branch",
      enrollmentOrder: [
        {
          slug: SLUG_1,
          featureId: FEATURE_ID,
          value: {
            prefs: { [PREF_FOO]: { value: EXPERIMENT_VALUE, branch: DEFAULT } },
          },
        },
        {
          slug: SLUG_2,
          featureId: FEATURE_ID,
          value: {
            prefs: { [PREF_FOO]: { value: EXPERIMENT_VALUE, branch: DEFAULT } },
          },
        },
        {
          slug: SLUG_3,
          featureId: PREF_FEATURES[DEFAULT].featureId,
          value: {
            foo: EXPERIMENT_VALUE,
          },
        },
      ],
      expectedEnrollments: [SLUG_3],
      expectedUnenrollments: [SLUG_1, SLUG_2],
      expectedPrefs: { [PREF_FOO]: EXPERIMENT_VALUE },
      unenrollReason: UnenrollReason.PREF_FLIPS_CONFLICT,
      unenrollTelemetry: { conflicting_slug: SLUG_3 },
    },
    {
      name: "enroll in prefFlip experiment on the user branch and then a setPref experiment on the user branch and unenroll to check if original values are restored (no original value)",
      enrollmentOrder: [
        {
          slug: SLUG_1,
          featureId: FEATURE_ID,
          value: {
            prefs: {
              [PREF_FOO]: { value: SLUG_1, branch: USER },
            },
          },
        },
        {
          slug: SLUG_2,
          featureId: PREF_FEATURES[USER].featureId,
          value: {
            foo: SLUG_2,
          },
        },
      ],
      expectedEnrollments: [SLUG_2],
      expectedUnenrollments: [SLUG_1],
      unenrollmentOrder: [SLUG_2],
      expectedPrefs: {
        [PREF_FOO]: null,
      },
      unenrollReason: UnenrollReason.PREF_FLIPS_CONFLICT,
      unenrollTelemetry: { conflicting_slug: SLUG_2 },
    },
    {
      name: "enroll in prefFlip experiment on the user branch and then a setPref experiment on the default branch and unenroll to check if original values are restored (no original value)",
      enrollmentOrder: [
        {
          slug: SLUG_1,
          featureId: FEATURE_ID,
          value: {
            prefs: {
              [PREF_FOO]: { value: SLUG_1, branch: USER },
            },
          },
        },
        {
          slug: SLUG_2,
          featureId: PREF_FEATURES[DEFAULT].featureId,
          value: {
            foo: SLUG_2,
          },
        },
      ],
      expectedEnrollments: [SLUG_2],
      expectedUnenrollments: [SLUG_1],
      unenrollmentOrder: [SLUG_2],
      expectedPrefs: {
        [PREF_FOO]: SLUG_2, // we can't clear the default branch
      },
      unenrollReason: UnenrollReason.PREF_FLIPS_CONFLICT,
      unenrollTelemetry: { conflicting_slug: SLUG_2 },
    },
    {
      name: "enroll in prefFlip experiment on the default branch and then a setPref experiment on the user branch and unenroll to check if original values are restored (no original value)",
      enrollmentOrder: [
        {
          slug: SLUG_1,
          featureId: FEATURE_ID,
          value: {
            prefs: {
              [PREF_FOO]: { value: SLUG_1, branch: USER },
            },
          },
        },
        {
          slug: SLUG_2,
          featureId: PREF_FEATURES[USER].featureId,
          value: {
            foo: SLUG_2,
          },
        },
      ],
      expectedEnrollments: [SLUG_2],
      expectedUnenrollments: [SLUG_1],
      unenrollmentOrder: [SLUG_2],
      expectedPrefs: {
        [PREF_FOO]: null,
      },
      unenrollReason: UnenrollReason.PREF_FLIPS_CONFLICT,
      unenrollTelemetry: { conflicting_slug: SLUG_2 },
    },
    {
      name: "enroll in prefFlip experiment on the default branch and then a setPref experiment on the default branch and unenroll to check if original values are restored (no original value)",
      enrollmentOrder: [
        {
          slug: SLUG_1,
          featureId: FEATURE_ID,
          value: {
            prefs: {
              [PREF_FOO]: { value: SLUG_1, branch: USER },
            },
          },
        },
        {
          slug: SLUG_2,
          featureId: PREF_FEATURES[DEFAULT].featureId,
          value: {
            foo: SLUG_2,
          },
        },
      ],
      expectedEnrollments: [SLUG_2],
      expectedUnenrollments: [SLUG_1],
      unenrollmentOrder: [SLUG_2],
      expectedPrefs: {
        [PREF_FOO]: SLUG_2,
      },
      unenrollReason: UnenrollReason.PREF_FLIPS_CONFLICT,
      unenrollTelemetry: { conflicting_slug: SLUG_2 },
    },
    {
      name: "enroll in prefFlip experiment on the user branch and then a setPref experiment on the user branch and unenroll to check if original values are restored",
      setPrefsBefore: { [PREF_FOO]: { userBranchValue: SET_BEFORE_VALUE } },
      enrollmentOrder: [
        {
          slug: SLUG_1,
          featureId: FEATURE_ID,
          value: {
            prefs: {
              [PREF_FOO]: { value: SLUG_1, branch: USER },
            },
          },
        },
        {
          slug: SLUG_2,
          featureId: PREF_FEATURES[USER].featureId,
          value: {
            foo: SLUG_2,
          },
        },
      ],
      expectedEnrollments: [SLUG_2],
      expectedUnenrollments: [SLUG_1],
      unenrollmentOrder: [SLUG_2],
      expectedPrefs: {
        [PREF_FOO]: SET_BEFORE_VALUE,
      },
      unenrollReason: UnenrollReason.PREF_FLIPS_CONFLICT,
      unenrollTelemetry: { conflicting_slug: SLUG_2 },
    },
    {
      name: "enroll in prefFlip experiment on the user branch and then a setPref experiment on the default branch and unenroll to check if original values are restored",
      setPrefsBefore: { [PREF_FOO]: { defaultBranchValue: SET_BEFORE_VALUE } },
      enrollmentOrder: [
        {
          slug: SLUG_1,
          featureId: FEATURE_ID,
          value: {
            prefs: {
              [PREF_FOO]: { value: SLUG_1, branch: USER },
            },
          },
        },
        {
          slug: SLUG_2,
          featureId: PREF_FEATURES[DEFAULT].featureId,
          value: {
            foo: SLUG_2,
          },
        },
      ],
      expectedEnrollments: [SLUG_2],
      expectedUnenrollments: [SLUG_1],
      unenrollmentOrder: [SLUG_2],
      expectedPrefs: {
        [PREF_FOO]: SET_BEFORE_VALUE,
      },
      unenrollReason: UnenrollReason.PREF_FLIPS_CONFLICT,
      unenrollTelemetry: { conflicting_slug: SLUG_2 },
    },
    {
      name: "enroll in prefFlip experiment on the default branch and then a setPref experiment on the user branch and unenroll to check if original values are restored",
      setPrefsBefore: { [PREF_FOO]: { defaultBranchValue: SET_BEFORE_VALUE } },
      enrollmentOrder: [
        {
          slug: SLUG_1,
          featureId: FEATURE_ID,
          value: {
            prefs: {
              [PREF_FOO]: { value: SLUG_1, branch: USER },
            },
          },
        },
        {
          slug: SLUG_2,
          featureId: PREF_FEATURES[USER].featureId,
          value: {
            foo: SLUG_2,
          },
        },
      ],
      expectedEnrollments: [SLUG_2],
      expectedUnenrollments: [SLUG_1],
      unenrollmentOrder: [SLUG_2],
      expectedPrefs: {
        [PREF_FOO]: SET_BEFORE_VALUE,
      },
      unenrollReason: UnenrollReason.PREF_FLIPS_CONFLICT,
      unenrollTelemetry: { conflicting_slug: SLUG_2 },
    },
    {
      name: "enroll in prefFlip experiment on the default branch and then a setPref experiment on the default branch and unenroll to check if original values are restored",
      setPrefsBefore: { [PREF_FOO]: { defaultBranchValue: SET_BEFORE_VALUE } },
      enrollmentOrder: [
        {
          slug: SLUG_1,
          featureId: FEATURE_ID,
          value: {
            prefs: {
              [PREF_FOO]: { value: SLUG_1, branch: USER },
            },
          },
        },
        {
          slug: SLUG_2,
          featureId: PREF_FEATURES[DEFAULT].featureId,
          value: {
            foo: SLUG_2,
          },
        },
      ],
      expectedEnrollments: [SLUG_2],
      expectedUnenrollments: [SLUG_1],
      unenrollmentOrder: [SLUG_2],
      expectedPrefs: {
        [PREF_FOO]: SET_BEFORE_VALUE,
      },
      unenrollReason: UnenrollReason.PREF_FLIPS_CONFLICT,
      unenrollTelemetry: { conflicting_slug: SLUG_2 },
    },
    // * setPref experiment, rollout -> prefFlips experiment
    {
      name: "enroll in a setPref experiment and rollout on the user branch then a prefFlips experiment on the user branch",
      enrollmentOrder: [
        {
          slug: SLUG_1,
          featureId: PREF_FEATURES[USER].featureId,
          value: {
            foo: EXPERIMENT_VALUE,
          },
        },
        {
          slug: SLUG_2,
          isRollout: true,
          featureId: PREF_FEATURES[USER].featureId,
          value: {
            foo: EXPERIMENT_VALUE,
          },
        },
        {
          slug: SLUG_3,
          featureId: FEATURE_ID,
          value: {
            prefs: { [PREF_FOO]: { value: EXPERIMENT_VALUE, branch: USER } },
          },
        },
      ],
      expectedEnrollments: [SLUG_3],
      expectedUnenrollments: [SLUG_1, SLUG_2],
      expectedPrefs: { [PREF_FOO]: EXPERIMENT_VALUE },
      unenrollReason: UnenrollReason.PREF_FLIPS_CONFLICT,
      unenrollTelemetry: { conflicting_slug: SLUG_3 },
    },
    {
      name: "enroll in a setPref experiment and rollout on the user branch then a prefFlips experiment on the default branch",
      enrollmentOrder: [
        {
          slug: SLUG_1,
          featureId: PREF_FEATURES[USER].featureId,
          value: {
            foo: EXPERIMENT_VALUE,
          },
        },
        {
          slug: SLUG_2,
          isRollout: true,
          featureId: PREF_FEATURES[USER].featureId,
          value: {
            foo: EXPERIMENT_VALUE,
          },
        },
        {
          slug: SLUG_3,
          featureId: FEATURE_ID,
          value: {
            prefs: { [PREF_FOO]: { value: EXPERIMENT_VALUE, branch: DEFAULT } },
          },
        },
      ],
      expectedEnrollments: [SLUG_3],
      expectedUnenrollments: [SLUG_1, SLUG_2],
      expectedPrefs: { [PREF_FOO]: EXPERIMENT_VALUE },
      unenrollReason: UnenrollReason.PREF_FLIPS_CONFLICT,
      unenrollTelemetry: { conflicting_slug: SLUG_3 },
    },
    {
      name: "enroll in a setPref experiment and rollout on the default branch then a prefFlips experiment on the user branch",
      enrollmentOrder: [
        {
          slug: SLUG_1,
          featureId: PREF_FEATURES[DEFAULT].featureId,
          value: {
            foo: EXPERIMENT_VALUE,
          },
        },
        {
          slug: SLUG_2,
          isRollout: true,
          featureId: PREF_FEATURES[DEFAULT].featureId,
          value: {
            foo: EXPERIMENT_VALUE,
          },
        },
        {
          slug: SLUG_3,
          featureId: FEATURE_ID,
          value: {
            prefs: { [PREF_FOO]: { value: EXPERIMENT_VALUE, branch: USER } },
          },
        },
      ],
      expectedEnrollments: [SLUG_3],
      expectedUnenrollments: [SLUG_1, SLUG_2],
      expectedPrefs: { [PREF_FOO]: EXPERIMENT_VALUE },
      unenrollReason: UnenrollReason.PREF_FLIPS_CONFLICT,
      unenrollTelemetry: { conflicting_slug: SLUG_3 },
    },
    {
      name: "enroll in a setPref experiment and rollout on the default branch then a prefFlips experiment on the default branch",
      enrollmentOrder: [
        {
          slug: SLUG_1,
          featureId: PREF_FEATURES[DEFAULT].featureId,
          value: {
            foo: EXPERIMENT_VALUE,
          },
        },
        {
          slug: SLUG_2,
          isRollout: true,
          featureId: PREF_FEATURES[DEFAULT].featureId,
          value: {
            foo: EXPERIMENT_VALUE,
          },
        },
        {
          slug: SLUG_3,
          featureId: FEATURE_ID,
          value: {
            prefs: { [PREF_FOO]: { value: EXPERIMENT_VALUE, branch: DEFAULT } },
          },
        },
      ],
      expectedEnrollments: [SLUG_3],
      expectedUnenrollments: [SLUG_1, SLUG_2],
      expectedPrefs: { [PREF_FOO]: EXPERIMENT_VALUE },
      unenrollReason: UnenrollReason.PREF_FLIPS_CONFLICT,
      unenrollTelemetry: { conflicting_slug: SLUG_3 },
    },
    {
      name: "enroll in a setPref experiment on the user branch and a prefFlips experiment on the user branch and unenroll to check if original values are restored (no original value)",
      enrollmentOrder: [
        {
          slug: SLUG_1,
          featureId: PREF_FEATURES[USER].featureId,
          value: {
            foo: SLUG_1,
          },
        },
        {
          slug: SLUG_2,
          featureId: FEATURE_ID,
          value: {
            prefs: {
              [PREF_FOO]: { value: SLUG_2, branch: USER },
            },
          },
        },
      ],
      expectedEnrollments: [SLUG_2],
      expectedUnenrollments: [SLUG_1],
      unenrollmentOrder: [SLUG_2],
      expectedPrefs: {
        [PREF_FOO]: null, // we can't clear the default branch
      },
      unenrollReason: UnenrollReason.PREF_FLIPS_CONFLICT,
      unenrollTelemetry: { conflicting_slug: SLUG_2 },
    },
    {
      name: "enroll in a setPref experiment on the user branch and a prefFlips experiment on the default branch and unenroll to check if original values are restored (no original value)",
      enrollmentOrder: [
        {
          slug: SLUG_1,
          featureId: PREF_FEATURES[USER].featureId,
          value: {
            foo: SLUG_1,
          },
        },
        {
          slug: SLUG_2,
          featureId: FEATURE_ID,
          value: {
            prefs: {
              [PREF_FOO]: { value: SLUG_2, branch: DEFAULT },
            },
          },
        },
      ],
      expectedEnrollments: [SLUG_2],
      expectedUnenrollments: [SLUG_1],
      unenrollmentOrder: [SLUG_2],
      expectedPrefs: {
        [PREF_FOO]: SLUG_2, // Cannot clear the default branch
      },
      unenrollReason: UnenrollReason.PREF_FLIPS_CONFLICT,
      unenrollTelemetry: { conflicting_slug: SLUG_2 },
    },
    {
      name: "enroll in a setPref experiment on the default branch and a prefFlips experiment on the user branch and unenroll to check if original values are restored (no original value)",
      enrollmentOrder: [
        {
          slug: SLUG_1,
          featureId: PREF_FEATURES[DEFAULT].featureId,
          value: {
            foo: SLUG_1,
          },
        },
        {
          slug: SLUG_2,
          featureId: FEATURE_ID,
          value: {
            prefs: {
              [PREF_FOO]: { value: SLUG_2, branch: USER },
            },
          },
        },
      ],
      expectedEnrollments: [SLUG_2],
      expectedUnenrollments: [SLUG_1],
      unenrollmentOrder: [SLUG_2],
      expectedPrefs: {
        [PREF_FOO]: SLUG_1, // cannot clear the default branch
      },
      unenrollReason: UnenrollReason.PREF_FLIPS_CONFLICT,
      unenrollTelemetry: { conflicting_slug: SLUG_2 },
    },
    {
      name: "enroll in a setPref experiment on the default branch and a prefFlips experiment on the default branch and unenroll to check if original values are restored (no original value)",
      enrollmentOrder: [
        {
          slug: SLUG_1,
          featureId: PREF_FEATURES[DEFAULT].featureId,
          value: {
            foo: SLUG_1,
          },
        },
        {
          slug: SLUG_2,
          featureId: FEATURE_ID,
          value: {
            prefs: {
              [PREF_FOO]: { value: SLUG_2, branch: DEFAULT },
            },
          },
        },
      ],
      expectedEnrollments: [SLUG_2],
      expectedUnenrollments: [SLUG_1],
      unenrollmentOrder: [SLUG_2],
      expectedPrefs: {
        [PREF_FOO]: SLUG_2, // cannot clear the default branch
      },
      unenrollReason: UnenrollReason.PREF_FLIPS_CONFLICT,
      unenrollTelemetry: { conflicting_slug: SLUG_2 },
    },
    {
      name: "enroll in a setPref experiment on the user branch and a prefFlips experiment on the user branch and unenroll to check if original values are restored",
      setPrefsBefore: {
        [PREF_FOO]: { userBranchValue: SET_BEFORE_VALUE },
      },
      enrollmentOrder: [
        {
          slug: SLUG_1,
          featureId: PREF_FEATURES[USER].featureId,
          value: {
            foo: EXPERIMENT_VALUE,
          },
        },
        {
          slug: SLUG_2,
          featureId: FEATURE_ID,
          value: {
            prefs: {
              [PREF_FOO]: { value: EXPERIMENT_VALUE, branch: USER },
            },
          },
        },
      ],
      expectedEnrollments: [SLUG_2],
      expectedUnenrollments: [SLUG_1],
      unenrollmentOrder: [SLUG_2],
      expectedPrefs: {
        [PREF_FOO]: SET_BEFORE_VALUE,
      },
      unenrollReason: UnenrollReason.PREF_FLIPS_CONFLICT,
      unenrollTelemetry: { conflicting_slug: SLUG_2 },
    },
    {
      name: "enroll in a setPref experiment on the user branch and a prefFlips experiment on the default branch and unenroll to check if original values are restored",
      setPrefsBefore: {
        [PREF_FOO]: { userBranchValue: SET_BEFORE_VALUE },
      },
      enrollmentOrder: [
        {
          slug: SLUG_1,
          featureId: PREF_FEATURES[USER].featureId,
          value: {
            foo: EXPERIMENT_VALUE,
          },
        },
        {
          slug: SLUG_2,
          featureId: FEATURE_ID,
          value: {
            prefs: {
              [PREF_FOO]: { value: EXPERIMENT_VALUE, branch: DEFAULT },
            },
          },
        },
      ],
      expectedEnrollments: [SLUG_2],
      expectedUnenrollments: [SLUG_1],
      unenrollmentOrder: [SLUG_2],
      expectedPrefs: {
        [PREF_FOO]: SET_BEFORE_VALUE,
      },
      unenrollReason: UnenrollReason.PREF_FLIPS_CONFLICT,
      unenrollTelemetry: { conflicting_slug: SLUG_2 },
    },
    {
      name: "enroll in a setPref experiment on the default branch and a prefFlips experiment on the user branch and unenroll to check if original values are restored",
      setPrefsBefore: {
        [PREF_FOO]: { defaultBranchValue: SET_BEFORE_VALUE },
      },
      enrollmentOrder: [
        {
          slug: SLUG_1,
          featureId: PREF_FEATURES[DEFAULT].featureId,
          value: {
            foo: EXPERIMENT_VALUE,
          },
        },
        {
          slug: SLUG_2,
          featureId: FEATURE_ID,
          value: {
            prefs: {
              [PREF_FOO]: { value: EXPERIMENT_VALUE, branch: USER },
            },
          },
        },
      ],
      expectedEnrollments: [SLUG_2],
      expectedUnenrollments: [SLUG_1],
      unenrollmentOrder: [SLUG_2],
      expectedPrefs: {
        [PREF_FOO]: SET_BEFORE_VALUE,
      },
      unenrollReason: UnenrollReason.PREF_FLIPS_CONFLICT,
      unenrollTelemetry: { conflicting_slug: SLUG_2 },
    },
    {
      name: "enroll in a setPref experiment on the default branch and a prefFlips experiment on the default branch and unenroll to check if original values are restored",
      setPrefsBefore: {
        [PREF_FOO]: { defaultBranchValue: SET_BEFORE_VALUE },
      },
      enrollmentOrder: [
        {
          slug: SLUG_1,
          featureId: PREF_FEATURES[DEFAULT].featureId,
          value: {
            foo: EXPERIMENT_VALUE,
          },
        },
        {
          slug: SLUG_2,
          featureId: FEATURE_ID,
          value: {
            prefs: {
              [PREF_FOO]: { value: EXPERIMENT_VALUE, branch: DEFAULT },
            },
          },
        },
      ],
      expectedEnrollments: [SLUG_2],
      expectedUnenrollments: [SLUG_1],
      unenrollmentOrder: [SLUG_2],
      expectedPrefs: {
        [PREF_FOO]: SET_BEFORE_VALUE,
      },
      unenrollReason: UnenrollReason.PREF_FLIPS_CONFLICT,
      unenrollTelemetry: { conflicting_slug: SLUG_2 },
    },
  ];

  for (const [i, { name, ...testCase }] of TEST_CASES.entries()) {
    info(`Running test case ${i}: ${name}`);

    const {
      // Prefs that should be set after enrollment. These will be undone after
      // each test case.
      setPrefsBefore = {},
      // The slugs to enroll in the order they should be enrolled in, and
      // whether or not they should enroll as rollouts.
      enrollmentOrder,
      // Prefs that should be set after enrollment. These will be undone
      // after each test case.
      setPrefsAfter = {},
      // The expected active enrollments after all enrollments have finished.
      expectedEnrollments = [],
      // The expected inactive enrollments after all enrollments have finished.
      expectedUnenrollments = [],
      // The slugs to unenroll from after enrolling and settings prefs but
      // before checking pref values.
      unenrollmentOrder,
      // Prefs to check after enrollment. They will be checked on the user
      // branch.
      expectedPrefs,
      // The reason for unenrollment.
      unenrollReason,
      // Extra keys in the unenrollment telemetry.
      unenrollTelemetry = {},
    } = testCase;

    info("Setting prefs before enrollment...");
    setPrefs(setPrefsBefore);

    const { manager, cleanup } = await setupTest();

    info("Enrolling...");
    for (const {
      slug,
      isRollout = false,
      ...featureConfig
    } of enrollmentOrder) {
      await NimbusTestUtils.enrollWithFeatureConfig(featureConfig, {
        slug,
        manager,
        isRollout,
      });
    }

    info("Setting prefs after enrollment...");
    setPrefs(setPrefsAfter);

    info("Checking expected enrollments...");
    for (const slug of expectedEnrollments) {
      const enrollment = manager.store.get(slug);

      Assert.ok(
        enrollment !== null && typeof enrollment !== "undefined",
        `An enrollment for ${slug} should exist`
      );
      Assert.ok(enrollment.active, `It should still be active`);
    }

    await NimbusTestUtils.assert.activeEnrollments(expectedEnrollments);

    info("Checking expected unenrollments...");
    for (const slug of expectedUnenrollments) {
      const enrollment = manager.store.get(slug);

      Assert.ok(!!enrollment, `An enrollment for ${slug} should exist`);
      Assert.ok(!enrollment.active, "It should no longer be active");
      Assert.equal(
        enrollment.unenrollReason,
        unenrollReason,
        "It should have unenrolled for the correct reason"
      );
    }

    Assert.deepEqual(
      (Glean.nimbusEvents.unenrollment.testGetValue("events") ?? [])
        .map(event => event.extra)
        .sort(compareByExperiment),

      expectedUnenrollments
        .map(slug => ({
          experiment: slug,
          reason: unenrollReason,
          branch: "control",
          ...unenrollTelemetry,
        }))
        .sort(compareByExperiment),

      "Saw expected unenrollment telemetry"
    );

    let expectedCurrentEnrollments = new Set(expectedEnrollments).difference(
      new Set(expectedUnenrollments)
    );
    await NimbusTestUtils.assert.activeEnrollments(
      Array.from(expectedCurrentEnrollments)
    );

    if (unenrollmentOrder) {
      info("Unenrolling from specific experiments before checking prefs...");
      for (const slug of unenrollmentOrder ?? []) {
        manager.unenroll(slug);
      }
    }

    expectedCurrentEnrollments = expectedCurrentEnrollments.difference(
      new Set(unenrollmentOrder)
    );
    await NimbusTestUtils.assert.activeEnrollments(
      Array.from(expectedCurrentEnrollments)
    );

    if (expectedPrefs) {
      info("Checking expected prefs...");
      checkExpectedPrefs(expectedPrefs);
    }

    info("Unenrolling from active experiments...");
    for (const slug of expectedEnrollments) {
      if (!(unenrollmentOrder ?? []).includes(slug)) {
        info(`Unenrolling from ${slug}\n`);
        manager.unenroll(slug);
      }
    }

    await NimbusTestUtils.assert.activeEnrollments([]);

    info("Cleaning up prefs...");
    Services.prefs.deleteBranch(PREF_FOO);
    Services.prefs.deleteBranch(PREF_BAR);

    await cleanup();
  }
});

add_task(async function test_prefFlip_setPref_restore() {
  const PREF = "nimbus.test-only.foo";

  const SET_PREF_USER = "set-pref-user";
  const SET_PREF_DEFAULT = "set-pref-default";
  const PREF_FLIPS_USER = "pref-flips-user";
  const PREF_FLIPS_DEFAULT = "pref-flips-default";

  const FEATURE_CONFIGS = {
    [SET_PREF_USER]: {
      featureId: PREF_FEATURES[USER].featureId,
      value: {
        foo: SET_PREF_USER,
      },
    },
    [SET_PREF_DEFAULT]: {
      featureId: PREF_FEATURES[DEFAULT].featureId,
      value: {
        foo: SET_PREF_DEFAULT,
      },
    },
    [PREF_FLIPS_USER]: {
      featureId: FEATURE_ID,
      value: {
        prefs: {
          [PREF]: {
            branch: USER,
            value: PREF_FLIPS_USER,
          },
        },
      },
    },
    [PREF_FLIPS_DEFAULT]: {
      featureId: FEATURE_ID,
      value: {
        prefs: {
          [PREF]: {
            branch: DEFAULT,
            value: PREF_FLIPS_DEFAULT,
          },
        },
      },
    },
  };

  const TEST_CASES = [
    // 1. No prefs set beforehand.
    // - setPref first
    {
      name: "enroll in setPref on user branch and prefFlips on user branch",
      enrollmentOrder: [SET_PREF_USER, PREF_FLIPS_USER],
      expectedPrefs: { [PREF]: {} },
    },
    {
      name: "enroll in setPref on user branch and prefFlips on default branch",
      enrollmentOrder: [SET_PREF_USER, PREF_FLIPS_DEFAULT],
      expectedPrefs: { [PREF]: { defaultBranchValue: PREF_FLIPS_DEFAULT } },
    },
    {
      name: "enroll in setPref on default branch and prefFlips on user branch",
      enrollmentOrder: [SET_PREF_DEFAULT, PREF_FLIPS_USER],
      expectedPrefs: { [PREF]: { defaultBranchValue: SET_PREF_DEFAULT } },
    },
    {
      name: "enroll in setPref on default branch and prefFlips on default branch",
      enrollmentOrder: [SET_PREF_DEFAULT, PREF_FLIPS_DEFAULT],
      // We can't clear the default branch
      expectedPrefs: { [PREF]: { defaultBranchValue: PREF_FLIPS_DEFAULT } },
    },
    // - prefFlips first
    {
      name: "enroll in prefFlips on user branch and setPref on user branch",
      enrollmentOrder: [PREF_FLIPS_USER, SET_PREF_USER],
      expectedPrefs: { [PREF]: {} },
    },
    {
      name: "enroll in prefFlips on user branch and setPref on default branch",
      enrollmentOrder: [PREF_FLIPS_USER, SET_PREF_DEFAULT],
      expectedPrefs: { [PREF]: { defaultBranchValue: SET_PREF_DEFAULT } },
    },
    {
      name: "enroll in prefFlips on default branch and setPref on user branch",
      enrollmentOrder: [PREF_FLIPS_DEFAULT, SET_PREF_USER],
      expectedPrefs: { [PREF]: { defaultBranchValue: PREF_FLIPS_DEFAULT } },
    },
    {
      name: "enroll in prefFlips on default branch and setPref on default branch",
      enrollmentOrder: [PREF_FLIPS_DEFAULT, SET_PREF_DEFAULT],
      // We can't clear the default branch
      expectedPrefs: { [PREF]: { defaultBranchValue: SET_PREF_DEFAULT } },
    },
    // 2. User branch prefs set beforehand.
    // - setPref first
    {
      name: "set prefs on user branch and enroll in setPref on user branch and prefFlips on user branch",
      setPrefsBefore: { [PREF]: { userBranchValue: USER_VALUE } },
      enrollmentOrder: [SET_PREF_USER, PREF_FLIPS_USER],
      expectedPrefs: { [PREF]: { userBranchValue: USER_VALUE } },
    },
    {
      name: "set prefs on user branch and enroll in setPref on user branch and prefFlips on default branch",
      setPrefsBefore: { [PREF]: { userBranchValue: USER_VALUE } },
      enrollmentOrder: [SET_PREF_USER, PREF_FLIPS_DEFAULT],
      expectedPrefs: {
        [PREF]: {
          userBranchValue: USER_VALUE,
          defaultBranchValue: PREF_FLIPS_DEFAULT,
        },
      },
    },
    {
      name: "set prefs on user branch and enroll in setPref on default branch and prefFlips on user branch",
      setPrefsBefore: { [PREF]: { userBranchValue: USER_VALUE } },
      enrollmentOrder: [SET_PREF_DEFAULT, PREF_FLIPS_USER],
      expectedPrefs: {
        [PREF]: {
          userBranchValue: USER_VALUE,
          defaultBranchValue: SET_PREF_DEFAULT,
        },
      },
    },
    {
      name: "set prefs on user branch and enroll in setPref on default branch and prefFlips on default branch",
      setPrefsBefore: { [PREF]: { userBranchValue: USER_VALUE } },
      enrollmentOrder: [SET_PREF_DEFAULT, PREF_FLIPS_DEFAULT],
      expectedPrefs: {
        [PREF]: {
          userBranchValue: USER_VALUE,
          defaultBranchValue: PREF_FLIPS_DEFAULT, // We can't clear the default branch
        },
      },
    },
    // - prefFlips first
    {
      name: "set prefs on user branch and enroll in prefFlips on user branch and setPref on user branch",
      setPrefsBefore: { [PREF]: { userBranchValue: USER_VALUE } },
      enrollmentOrder: [PREF_FLIPS_USER, SET_PREF_USER],
      expectedPrefs: { [PREF]: { userBranchValue: USER_VALUE } },
    },
    {
      name: "set prefs on user branch and enroll in prefFlips on user branch and setPref on default branch",
      setPrefsBefore: { [PREF]: { userBranchValue: USER_VALUE } },
      enrollmentOrder: [PREF_FLIPS_USER, SET_PREF_DEFAULT],
      expectedPrefs: {
        [PREF]: {
          userBranchValue: USER_VALUE,
          defaultBranchValue: SET_PREF_DEFAULT,
        },
      },
    },
    {
      name: "set prefs on user branch and enroll in prefFlips on default branch and setPref on user branch",
      setPrefsBefore: { [PREF]: { userBranchValue: USER_VALUE } },
      enrollmentOrder: [PREF_FLIPS_DEFAULT, SET_PREF_USER],
      expectedPrefs: {
        [PREF]: {
          userBranchValue: USER_VALUE,
          defaultBranchValue: PREF_FLIPS_DEFAULT,
        },
      },
    },
    {
      name: "set prefs on user branch and enroll in prefFlips on default branch and setPref on default branch",
      setPrefsBefore: { [PREF]: { userBranchValue: USER_VALUE } },
      enrollmentOrder: [PREF_FLIPS_DEFAULT, SET_PREF_DEFAULT],
      expectedPrefs: {
        [PREF]: {
          userBranchValue: USER_VALUE,
          defaultBranchValue: SET_PREF_DEFAULT, // We can't clear the default branch
        },
      },
    },
    // 3. Default branch prefs set beforehand.
    // - setPref first
    {
      setPrefsBefore: { [PREF]: { defaultBranchValue: DEFAULT_VALUE } },
      name: "set prefs on default branch and enroll branch in setPref on user branch and prefFlips on user branch",
      enrollmentOrder: [SET_PREF_USER, PREF_FLIPS_USER],
      expectedPrefs: { [PREF]: { defaultBranchValue: DEFAULT_VALUE } },
    },
    {
      setPrefsBefore: { [PREF]: { defaultBranchValue: DEFAULT_VALUE } },
      name: "set prefs on default branch and enroll branch in setPref on user branch and prefFlips on default branch",
      enrollmentOrder: [SET_PREF_USER, PREF_FLIPS_DEFAULT],
      expectedPrefs: { [PREF]: { defaultBranchValue: DEFAULT_VALUE } },
    },
    {
      setPrefsBefore: { [PREF]: { defaultBranchValue: DEFAULT_VALUE } },
      name: "set prefs on default branch and enroll branch in setPref on default branch and prefFlips on user branch",
      enrollmentOrder: [SET_PREF_DEFAULT, PREF_FLIPS_USER],
      expectedPrefs: { [PREF]: { defaultBranchValue: DEFAULT_VALUE } },
    },
    {
      setPrefsBefore: { [PREF]: { defaultBranchValue: DEFAULT_VALUE } },
      name: "set prefs on default branch and enroll branch in setPref on default branch and prefFlips on default branch",
      enrollmentOrder: [SET_PREF_DEFAULT, PREF_FLIPS_DEFAULT],
      expectedPrefs: { [PREF]: { defaultBranchValue: DEFAULT_VALUE } },
    },
    // - prefFlips first
    {
      setPrefsBefore: { [PREF]: { defaultBranchValue: DEFAULT_VALUE } },
      name: "set prefs on default branch and enroll branch in prefFlips on user branch and setPref on user branch",
      enrollmentOrder: [PREF_FLIPS_USER, SET_PREF_USER],
      expectedPrefs: { [PREF]: { defaultBranchValue: DEFAULT_VALUE } },
    },
    {
      name: "set prefs on default branch and enroll branch in prefFlips on user branch and setPref on default branch",
      setPrefsBefore: { [PREF]: { defaultBranchValue: DEFAULT_VALUE } },
      enrollmentOrder: [PREF_FLIPS_USER, SET_PREF_DEFAULT],
      expectedPrefs: { [PREF]: { defaultBranchValue: DEFAULT_VALUE } },
    },
    {
      name: "set prefs on default branch and enroll branch in prefFlips on default branch and setPref on user branch",
      setPrefsBefore: { [PREF]: { defaultBranchValue: DEFAULT_VALUE } },
      enrollmentOrder: [PREF_FLIPS_DEFAULT, SET_PREF_USER],
      expectedPrefs: { [PREF]: { defaultBranchValue: DEFAULT_VALUE } },
    },
    {
      name: "set prefs on default branch and enroll branch in prefFlips on default branch and setPref on default branch",
      setPrefsBefore: { [PREF]: { defaultBranchValue: DEFAULT_VALUE } },
      enrollmentOrder: [PREF_FLIPS_DEFAULT, SET_PREF_DEFAULT],
      expectedPrefs: { [PREF]: { defaultBranchValue: DEFAULT_VALUE } },
    },
    {
      name: "set prefs on default branch and enroll branch in prefFlips on default branch and setPref on default branch, unenrolling in reverse order",
      setPrefsBefore: { [PREF]: { defaultBranchValue: DEFAULT_VALUE } },
      enrollmentOrder: [PREF_FLIPS_DEFAULT, SET_PREF_DEFAULT],
      unenrollInReverseOrder: true,
      expectedPrefs: { [PREF]: { defaultBranchValue: DEFAULT_VALUE } },
    },
    // 4. Both user and default branch prefs set beforehand.
    // - setPref first
    {
      setPrefsBefore: {
        [PREF]: {
          userBranchValue: USER_VALUE,
          defaultBranchValue: DEFAULT_VALUE,
        },
      },
      name: "set prefs on both branches and enroll branch in setPref on user branch and prefFlips on user branch",
      enrollmentOrder: [SET_PREF_USER, PREF_FLIPS_USER],
      expectedPrefs: {
        [PREF]: {
          userBranchValue: USER_VALUE,
          defaultBranchValue: DEFAULT_VALUE,
        },
      },
    },
    {
      setPrefsBefore: {
        [PREF]: {
          userBranchValue: USER_VALUE,
          defaultBranchValue: DEFAULT_VALUE,
        },
      },
      name: "set prefs on both branches and enroll branch in setPref on user branch and prefFlips on default branch",
      enrollmentOrder: [SET_PREF_USER, PREF_FLIPS_DEFAULT],
      expectedPrefs: {
        [PREF]: {
          userBranchValue: USER_VALUE,
          defaultBranchValue: DEFAULT_VALUE,
        },
      },
    },
    {
      setPrefsBefore: {
        [PREF]: {
          userBranchValue: USER_VALUE,
          defaultBranchValue: DEFAULT_VALUE,
        },
      },
      name: "set prefs on both branches and enroll branch in setPref on default branch and prefFlips on user branch",
      enrollmentOrder: [SET_PREF_DEFAULT, PREF_FLIPS_USER],
      expectedPrefs: {
        [PREF]: {
          userBranchValue: USER_VALUE,
          defaultBranchValue: DEFAULT_VALUE,
        },
      },
    },
    {
      setPrefsBefore: {
        [PREF]: {
          userBranchValue: USER_VALUE,
          defaultBranchValue: DEFAULT_VALUE,
        },
      },
      name: "set prefs on both branches and enroll branch in setPref on default branch and prefFlips on default branch",
      enrollmentOrder: [SET_PREF_DEFAULT, PREF_FLIPS_DEFAULT],
      expectedPrefs: {
        [PREF]: {
          userBranchValue: USER_VALUE,
          defaultBranchValue: DEFAULT_VALUE,
        },
      },
    },
    // - prefFlips first
    {
      setPrefsBefore: {
        [PREF]: {
          userBranchValue: USER_VALUE,
          defaultBranchValue: DEFAULT_VALUE,
        },
      },
      name: "set prefs on both branches and enroll branch in prefFlips on user branch and setPref on user branch",
      enrollmentOrder: [PREF_FLIPS_USER, SET_PREF_USER],
      expectedPrefs: {
        [PREF]: {
          userBranchValue: USER_VALUE,
          defaultBranchValue: DEFAULT_VALUE,
        },
      },
    },
    {
      name: "set prefs on both branches and enroll branch in prefFlips on user branch and setPref on default branch",
      setPrefsBefore: {
        [PREF]: {
          userBranchValue: USER_VALUE,
          defaultBranchValue: DEFAULT_VALUE,
        },
      },
      enrollmentOrder: [PREF_FLIPS_USER, SET_PREF_DEFAULT],
      expectedPrefs: {
        [PREF]: {
          userBranchValue: USER_VALUE,
          defaultBranchValue: DEFAULT_VALUE,
        },
      },
    },
    {
      name: "set prefs on both branches and enroll branch in prefFlips on default branch and setPref on user branch",
      setPrefsBefore: {
        [PREF]: {
          userBranchValue: USER_VALUE,
          defaultBranchValue: DEFAULT_VALUE,
        },
      },
      enrollmentOrder: [PREF_FLIPS_DEFAULT, SET_PREF_USER],
      expectedPrefs: {
        [PREF]: {
          userBranchValue: USER_VALUE,
          defaultBranchValue: DEFAULT_VALUE,
        },
      },
    },
    {
      name: "set prefs on both branches and enroll branch in prefFlips on default branch and setPref on default branch",
      setPrefsBefore: {
        [PREF]: {
          userBranchValue: USER_VALUE,
          defaultBranchValue: DEFAULT_VALUE,
        },
      },
      enrollmentOrder: [PREF_FLIPS_DEFAULT, SET_PREF_DEFAULT],
      expectedPrefs: {
        [PREF]: {
          userBranchValue: USER_VALUE,
          defaultBranchValue: DEFAULT_VALUE,
        },
      },
    },
  ];

  for (const [i, { name, ...testCase }] of TEST_CASES.entries()) {
    info(`Running test case ${i}: ${name}`);

    const { setPrefsBefore = {}, enrollmentOrder, expectedPrefs } = testCase;

    info("Setting prefs before enrollment...");
    setPrefs(setPrefsBefore);

    const { manager, cleanup } = await setupTest();

    info("Enrolling...");
    for (const slug of enrollmentOrder) {
      await NimbusTestUtils.enrollWithFeatureConfig(FEATURE_CONFIGS[slug], {
        manager,
        slug,
      });
    }

    info("Checking expected enrollments...");
    {
      const enrollment = manager.store.get(enrollmentOrder[0]);
      Assert.notStrictEqual(
        enrollment,
        null,
        `An enrollment for ${enrollmentOrder[0]} should exist`
      );
      Assert.ok(!enrollment.active, "It should no longer be active.");
    }
    {
      const enrollment = manager.store.get(enrollmentOrder[1]);
      Assert.notStrictEqual(
        enrollment,
        null,
        `An enrollment for ${enrollmentOrder[1]} should exist`
      );
      Assert.ok(enrollment.active, "It should be active.");
    }

    info("Checking submitted telemetry...");
    TelemetryTestUtils.assertEvents(
      [
        {
          value: enrollmentOrder[0],
          extra: {
            reason: "prefFlips-conflict",
            conflictingSlug: enrollmentOrder[1],
          },
        },
      ],
      {
        category: "normandy",
        object: "nimbus_experiment",
        method: "unenroll",
      }
    );
    Assert.deepEqual(
      Glean.nimbusEvents.unenrollment.testGetValue("events").map(event => ({
        reason: event.extra.reason,
        experiment: event.extra.experiment,
        conflicting_slug: event.extra.conflicting_slug,
      })),
      [
        {
          reason: "prefFlips-conflict",
          experiment: enrollmentOrder[0],
          conflicting_slug: enrollmentOrder[1],
        },
      ]
    );
    Assert.deepEqual(
      Glean.nimbusEvents.enrollmentStatus
        .testGetValue("nimbus-targeting-context")
        ?.map(ev => ev.extra),
      [
        {
          slug: enrollmentOrder[0],
          branch: "control",
          status: "Enrolled",
          reason: "Qualified",
        },
        {
          slug: enrollmentOrder[0],
          branch: "control",
          status: "Disqualified",
          reason: "PrefFlipsConflict",
          conflict_slug: enrollmentOrder[1],
        },
        {
          slug: enrollmentOrder[1],
          branch: "control",
          status: "Enrolled",
          reason: "Qualified",
        },
      ]
    );

    info("Unenrolling...");
    manager.unenroll(enrollmentOrder[1]);

    info("Checking expected prefs...");
    checkExpectedPrefBranches(expectedPrefs);

    await cleanup();

    info("Cleaning up prefs...");
    Services.prefs.deleteBranch(PREF);
  }
});

add_task(async function test_prefFlips_cacheOriginalValues() {
  const recipe = NimbusTestUtils.factories.recipe.withFeatureConfig(
    "prefFlips-test",
    {
      featureId: FEATURE_ID,
      value: {
        prefs: {
          "test.pref.please.ignore": {
            branch: "user",
            value: "test-value",
          },
        },
      },
    }
  );

  const { manager, cleanup } = await setupTest();

  await manager.enroll(recipe, "test");

  const activeEnrollment = manager.store.getExperimentForFeature(FEATURE_ID);

  Assert.deepEqual(activeEnrollment.prefFlips, {
    originalValues: {
      "test.pref.please.ignore": null,
    },
  });

  const storePath = await NimbusTestUtils.saveStore(manager.store);
  const storeContents = await IOUtils.readJSON(storePath);

  Assert.ok(
    Object.hasOwn(storeContents, "prefFlips-test"),
    "enrollment present in serialized store"
  );
  Assert.ok(
    Object.hasOwn(storeContents["prefFlips-test"], "prefFlips"),
    "prefFlips cache preset in serialized enrollment"
  );

  Assert.deepEqual(
    storeContents["prefFlips-test"].prefFlips,
    {
      originalValues: {
        "test.pref.please.ignore": null,
      },
    },
    "originalValues cached on serialized enrollment"
  );

  const dbEnrollment = await NimbusTestUtils.queryEnrollment(recipe.slug);
  Assert.deepEqual(
    storeContents["prefFlips-test"].prefFlips,
    dbEnrollment.prefFlips
  );

  manager.unenroll(recipe.slug);
  Assert.ok(
    !Services.prefs.prefHasUserValue("test.pref.please.ignore"),
    "pref unset after unenrollment"
  );

  await cleanup();
});

add_task(async function test_prefFlips_restore_unenroll() {
  const recipe = NimbusTestUtils.factories.recipe.withFeatureConfig(
    "prefFlips-test",
    {
      featureId: FEATURE_ID,
      value: {
        prefs: {
          "test.pref.please.ignore": {
            branch: "user",
            value: "test-value",
          },
        },
      },
    }
  );

  const { manager, cleanup } = await setupTest({
    storePath: await NimbusTestUtils.createStoreWith(store => {
      NimbusTestUtils.addEnrollmentForRecipe(recipe, {
        store,
        extra: {
          source: "test",
          prefFlips: {
            originalValues: {
              "test.pref.please.ignore": null,
            },
          },
        },
      });

      // Set the pref controlled by the experiment.
      Services.prefs.setStringPref("test.pref.please.ignore", "test-value");
    }),
    secureExperiments: [recipe],
    migrationState: NimbusTestUtils.migrationState.LATEST,
  });

  const activeEnrollment = manager.store.getExperimentForFeature(FEATURE_ID);
  Assert.equal(activeEnrollment.slug, recipe.slug, "enrollment restored");

  Assert.equal(
    manager._prefFlips._getOriginalValue("test.pref.please.ignore", "user"),
    null
  );

  manager.unenroll(recipe.slug);
  Assert.ok(
    !Services.prefs.prefHasUserValue("test.pref.please.ignore"),
    "pref unset after unenrollment"
  );

  await cleanup();
});

add_task(async function test_prefFlips_failed() {
  const PREF = "test.pref.please.ignore";

  Services.prefs.getDefaultBranch(null).setStringPref(PREF, "test-value");

  const recipe = NimbusTestUtils.factories.recipe.withFeatureConfig(
    "prefFlips-test",
    {
      featureId: FEATURE_ID,
      value: {
        prefs: {
          [PREF]: { branch: "user", value: 123 },
        },
      },
    }
  );

  const { manager, cleanup } = await setupTest();
  await manager.enroll(recipe, "test");

  // Unenrolling is triggered by the PrefFlipsFeature in response to the
  // enrollment being added to the store and triggering the feature update
  // callback.
  //
  // That callback triggers an async unenroll() without awaiting (because it
  // wouldn't block the ExperimentStore anyway) so we have to wait for the
  // unenroll to be propagated to the database first.
  await NimbusTestUtils.assert.enrollmentExists(recipe.slug, { active: false });

  const enrollment = manager.store.get(recipe.slug);
  Assert.ok(!enrollment.active, "Experiment should not be active");

  Assert.equal(Services.prefs.getStringPref(PREF), "test-value");

  TelemetryTestUtils.assertEvents(
    [
      {
        value: recipe.slug,
        extra: {
          reason: "prefFlips-failed",
          prefName: PREF,
          prefType: "string",
        },
      },
    ],
    {
      category: "normandy",
      object: "nimbus_experiment",
      method: "unenroll",
    }
  );
  Assert.deepEqual(
    Glean.nimbusEvents.unenrollment.testGetValue("events").map(event => ({
      reason: event.extra.reason,
      experiment: event.extra.experiment,
      pref_name: event.extra.pref_name,
      pref_type: event.extra.pref_type,
    })),
    [
      {
        reason: "prefFlips-failed",
        experiment: recipe.slug,
        pref_name: PREF,
        pref_type: "string",
      },
    ]
  );

  Services.prefs.deleteBranch(PREF);

  await cleanup();
});

add_task(async function test_prefFlips_failed_multiple_prefs() {
  const GOOD_PREF = "test.pref.please.ignore";
  const BAD_PREF = "this.one.too";

  Services.prefs.getDefaultBranch(null).setStringPref(BAD_PREF, "test-value");

  const recipe = NimbusTestUtils.factories.recipe.withFeatureConfig(
    "prefFlips-test",
    {
      featureId: FEATURE_ID,
      value: {
        prefs: {
          [GOOD_PREF]: { branch: USER, value: 123 },
          [BAD_PREF]: { branch: USER, value: 123 },
        },
      },
    }
  );

  const { sandbox, manager, cleanup } = await setupTest();

  const setPrefSpy = sandbox.spy(PrefUtils, "setPref");

  await manager.enroll(recipe, "test");

  // Unenrolling is triggered by the PrefFlipsFeature in response to the
  // enrollment being added to the store and triggering the feature update
  // callback.
  //
  // That callback triggers an async unenroll() without awaiting (because it
  // wouldn't block the ExperimentStore anyway) so we have to wait for the
  // unenroll to be propagated to the database first.
  await NimbusTestUtils.assert.enrollmentExists(recipe.slug, { active: false });

  const enrollment = manager.store.get(recipe.slug);
  Assert.ok(!enrollment.active, "Experiment should not be active");

  Assert.deepEqual(
    setPrefSpy.getCall(0).args,
    [GOOD_PREF, 123, { branch: USER }],
    `should set ${GOOD_PREF}`
  );
  Assert.deepEqual(
    setPrefSpy.getCall(1).args,
    [BAD_PREF, 123, { branch: USER }],
    `should have attempted to set ${BAD_PREF}`
  );
  Assert.notStrictEqual(
    typeof setPrefSpy.getCall(1).exception,
    "undefined",
    `Attempting to set ${BAD_PREF} threw`
  );
  Assert.deepEqual(
    setPrefSpy.getCall(2).args,
    [GOOD_PREF, null, { branch: USER }],
    `should reset ${GOOD_PREF}`
  );
  Assert.equal(
    setPrefSpy.callCount,
    3,
    "should have 3 calls to PrefUtils.setPref"
  );

  Assert.ok(
    !Services.prefs.prefHasUserValue(GOOD_PREF),
    `${GOOD_PREF} should not be set`
  );
  Assert.equal(Services.prefs.getStringPref(BAD_PREF), "test-value");

  Services.prefs.deleteBranch(GOOD_PREF);
  Services.prefs.deleteBranch(BAD_PREF);

  await cleanup();
});

add_task(async function test_prefFlips_failed_experiment_and_rollout_1() {
  const ROLLOUT = "rollout";
  const EXPERIMENT = "experiment";

  const PREFS = {
    [ROLLOUT]: "test.nimbus.prefs.rollout-1",
    [EXPERIMENT]: "test.nimbus.prefs.experiment-1",
  };

  const VALUES = {
    [ROLLOUT]: "rollout-value",
    [EXPERIMENT]: "experiment-value",
  };

  const BOGUS_VALUE = 123;

  const TEST_CASES = [
    {
      name: "Enrolling in an experiment and then a rollout with errors",
      setPrefsBefore: {
        [PREFS[ROLLOUT]]: { defaultBranchValue: BOGUS_VALUE },
      },
      enrollmentOrder: [EXPERIMENT, ROLLOUT],
      expectedEnrollments: [EXPERIMENT],
      expectedUnenrollments: [ROLLOUT],
      expectedPrefs: {
        [PREFS[EXPERIMENT]]: VALUES[EXPERIMENT],
        [PREFS[ROLLOUT]]: BOGUS_VALUE,
      },
    },
  ];

  const FEATURE_VALUES = {
    [EXPERIMENT]: {
      prefs: {
        [PREFS[EXPERIMENT]]: {
          value: VALUES[EXPERIMENT],
          branch: USER,
        },
      },
    },
    [ROLLOUT]: {
      prefs: {
        [PREFS[ROLLOUT]]: {
          value: VALUES[ROLLOUT],
          branch: USER,
        },
      },
    },
  };

  for (const [i, { name, ...testCase }] of TEST_CASES.entries()) {
    info(`Running test case ${i}: ${name}`);

    const {
      setPrefsBefore,
      enrollmentOrder,
      expectedEnrollments,
      expectedUnenrollments,
      expectedPrefs,
    } = testCase;

    const { manager, cleanup } = await setupTest();

    info("Setting initial values of prefs...");
    setPrefs(setPrefsBefore);

    info("Enrolling...");
    for (const slug of enrollmentOrder) {
      await NimbusTestUtils.enrollWithFeatureConfig(
        {
          featureId: FEATURE_ID,
          value: FEATURE_VALUES[slug],
        },
        {
          manager,
          slug,
          isRollout: slug === ROLLOUT,
        }
      );
    }

    info("Checking expected enrollments...");
    for (const slug of expectedEnrollments) {
      const enrollment = manager.store.get(slug);
      Assert.ok(enrollment.active, `The enrollment for ${slug} is active`);
    }

    await NimbusTestUtils.assert.activeEnrollments(expectedEnrollments);

    info("Checking expected unenrollments...");
    for (const slug of expectedUnenrollments) {
      await NimbusTestUtils.assert.enrollmentExists(slug, { active: false });
      const enrollment = manager.store.get(slug);
      Assert.ok(!enrollment.active, "The enrollment is no longer active.");
    }

    info("Checking expected prefs...");
    checkExpectedPrefs(expectedPrefs);

    info("Unenrolling...");
    if (expectedEnrollments.includes(ROLLOUT)) {
      manager.unenroll(ROLLOUT);
    }
    if (expectedEnrollments.includes(EXPERIMENT)) {
      manager.unenroll(EXPERIMENT);
    }

    info("Cleaning up...");
    Services.prefs.deleteBranch(PREFS[ROLLOUT]);
    Services.prefs.deleteBranch(PREFS[EXPERIMENT]);

    await cleanup();
  }
});

add_task(async function test_prefFlips_failed_experiment_and_rollout_2() {
  const ROLLOUT = "rollout";
  const EXPERIMENT = "experiment";

  const PREFS = {
    [ROLLOUT]: "test.nimbus.prefs.rollout-2",
    [EXPERIMENT]: "test.nimbus.prefs.experiment-2",
  };

  const VALUES = {
    [ROLLOUT]: "rollout-value",
    [EXPERIMENT]: "experiment-value",
  };

  const BOGUS_VALUE = 123;

  const TEST_CASES = [
    {
      name: "Enrolling in a rollout and then an experiment with errors",
      setPrefsBefore: {
        [PREFS[EXPERIMENT]]: { defaultBranchValue: BOGUS_VALUE },
      },
      enrollmentOrder: [ROLLOUT, EXPERIMENT],
      expectedEnrollments: [ROLLOUT],
      expectedUnenrollments: [EXPERIMENT],
      expectedPrefs: {
        [PREFS[ROLLOUT]]: VALUES[ROLLOUT],
        [PREFS[EXPERIMENT]]: BOGUS_VALUE,
      },
    },
  ];

  const FEATURE_VALUES = {
    [EXPERIMENT]: {
      prefs: {
        [PREFS[EXPERIMENT]]: {
          value: VALUES[EXPERIMENT],
          branch: USER,
        },
      },
    },
    [ROLLOUT]: {
      prefs: {
        [PREFS[ROLLOUT]]: {
          value: VALUES[ROLLOUT],
          branch: USER,
        },
      },
    },
  };

  for (const [i, { name, ...testCase }] of TEST_CASES.entries()) {
    info(`Running test case ${i}: ${name}`);

    const {
      setPrefsBefore,
      enrollmentOrder,
      expectedEnrollments,
      expectedUnenrollments,
      expectedPrefs,
    } = testCase;

    const { manager, cleanup } = await setupTest();

    info("Setting initial values of prefs...");
    setPrefs(setPrefsBefore);

    info("Enrolling...");
    for (const slug of enrollmentOrder) {
      await NimbusTestUtils.enrollWithFeatureConfig(
        {
          featureId: FEATURE_ID,
          value: FEATURE_VALUES[slug],
        },
        {
          manager,
          slug,
          isRollout: slug === ROLLOUT,
        }
      );
    }

    await NimbusTestUtils.assert.activeEnrollments(expectedEnrollments);

    info("Checking expected enrollments...");
    for (const slug of expectedEnrollments) {
      const enrollment = manager.store.get(slug);
      Assert.ok(enrollment.active, `The enrollment for ${slug} is active`);
    }

    info("Checking expected unenrollments...");
    for (const slug of expectedUnenrollments) {
      await NimbusTestUtils.assert.enrollmentExists(slug, { active: false });
      const enrollment = manager.store.get(slug);
      Assert.ok(!enrollment.active, "The enrollment is no longer active.");
    }

    info("Checking expected prefs...");
    checkExpectedPrefs(expectedPrefs);

    info("Unenrolling...");
    if (expectedEnrollments.includes(ROLLOUT)) {
      manager.unenroll(ROLLOUT);
    }
    if (expectedEnrollments.includes(EXPERIMENT)) {
      manager.unenroll(EXPERIMENT);
    }

    info("Cleaning up...");
    Services.prefs.deleteBranch(PREFS[ROLLOUT]);
    Services.prefs.deleteBranch(PREFS[EXPERIMENT]);

    await cleanup();
  }
});

add_task(async function test_prefFlips_update_failure() {
  const { manager, cleanup } = await setupTest();

  PrefUtils.setPref("pref.one", "default-value", { branch: DEFAULT });
  PrefUtils.setPref("pref.two", "default-value", { branch: DEFAULT });

  const cleanupExperiment = await NimbusTestUtils.enrollWithFeatureConfig(
    {
      featureId: FEATURE_ID,
      value: {
        prefs: {
          "pref.one": { value: "one", branch: USER },
          "pref.two": { value: "two", branch: USER },
        },
      },
    },
    { manager, isRollout: true, slug: "rollout" }
  );

  Assert.equal(Services.prefs.getStringPref("pref.one"), "one");
  Assert.equal(Services.prefs.getStringPref("pref.two"), "two");

  await NimbusTestUtils.enrollWithFeatureConfig(
    {
      featureId: FEATURE_ID,
      value: {
        prefs: {
          "pref.one": { value: "experiment-value", branch: USER },
          "pref.two": { value: 2, branch: USER },
        },
      },
    },
    { manager, slug: "experiment" }
  );

  await NimbusTestUtils.assert.activeEnrollments(["rollout"]);

  const rolloutEnrollment = manager.store.get("rollout");
  const experimentEnrollment = manager.store.get("experiment");

  Assert.ok(rolloutEnrollment.active, "Rollout is active");
  Assert.ok(!experimentEnrollment.active, "Experiment is inactive");
  Assert.equal(experimentEnrollment.unenrollReason, "prefFlips-failed");

  Assert.equal(Services.prefs.getStringPref("pref.one"), "one");
  Assert.equal(Services.prefs.getStringPref("pref.two"), "two");

  await cleanupExperiment();

  Services.prefs.deleteBranch("pref.one");
  Services.prefs.deleteBranch("pref.two");

  await cleanup();
});

add_task(async function test_prefFlips_restore() {
  const PREF_1 = "pref.one";
  const PREF_2 = "pref.two";
  const PREF_3 = "pref.three";
  const PREF_4 = "pref.FOUR";

  const storePath = await NimbusTestUtils.createStoreWith(store => {
    NimbusTestUtils.addEnrollmentForRecipe(
      NimbusTestUtils.factories.recipe.withFeatureConfig(
        "rollout-1",
        {
          featureId: FEATURE_ID,
          value: {
            prefs: {
              [PREF_1]: { branch: USER, value: PREF_1 },
            },
          },
        },
        { isRollout: true }
      ),
      {
        store,
        extra: {
          source: "test",
          prefFlips: {
            originalValues: {
              [PREF_1]: null,
            },
          },
        },
      }
    );

    NimbusTestUtils.addEnrollmentForRecipe(
      NimbusTestUtils.factories.recipe.withFeatureConfig("rollout-2", {
        featureId: FEATURE_ID,
        value: {
          prefs: {
            [PREF_2]: { branch: USER, value: PREF_2 },
          },
        },
      }),
      {
        store,
        extra: {
          source: "test",
          prefFlips: {
            originalValues: {
              [PREF_2]: "original-pref-2-value",
            },
          },
        },
      }
    );

    NimbusTestUtils.addEnrollmentForRecipe(
      NimbusTestUtils.factories.recipe.withFeatureConfig("rollout-3", {
        featureId: FEATURE_ID,
        value: {
          prefs: {
            [PREF_3]: { branch: DEFAULT, value: PREF_3 },
          },
        },
      }),
      {
        store,
        extra: {
          source: "test",
          prefFlips: {
            originalValues: {
              [PREF_3]: null,
            },
          },
        },
      }
    );

    NimbusTestUtils.addEnrollmentForRecipe(
      NimbusTestUtils.factories.recipe.withFeatureConfig("rollout-4", {
        featureId: FEATURE_ID,
        value: {
          prefs: {
            [PREF_4]: { branch: DEFAULT, value: PREF_4 },
          },
        },
      }),
      {
        store,
        extra: {
          source: "test",
          prefFlips: {
            originalValues: {
              [PREF_4]: "original-pref-4-value",
            },
          },
        },
      }
    );
  });

  const { manager, cleanup } = await setupTest({
    storePath,
    migrationState: NimbusTestUtils.migrationState.LATEST,
  });

  Assert.ok(manager.store.get("rollout-1").active, "rollout-1 is active");
  Assert.ok(manager.store.get("rollout-2").active, "rollout-2 is active");
  Assert.ok(manager.store.get("rollout-3").active, "rollout-3 is active");
  Assert.ok(manager.store.get("rollout-4").active, "rollout-4 is active");

  Assert.equal(
    Services.prefs.getStringPref(PREF_1),
    PREF_1,
    `${PREF_1} has the correct value`
  );
  Assert.equal(
    Services.prefs.getStringPref(PREF_2),
    PREF_2,
    `${PREF_2} has the correct value`
  );
  Assert.equal(
    Services.prefs.getStringPref(PREF_3),
    PREF_3,
    `${PREF_3} has the correct value`
  );
  Assert.equal(
    Services.prefs.getStringPref(PREF_4),
    PREF_4,
    `${PREF_4} has the correct value`
  );

  await NimbusTestUtils.cleanupManager(
    ["rollout-1", "rollout-2", "rollout-3", "rollout-4"],
    { manager }
  );

  Assert.equal(
    PrefUtils.getPref(PREF_1),
    null,
    `${PREF_1} has the correct value after unenrollment`
  );
  Assert.equal(
    PrefUtils.getPref(PREF_2),
    "original-pref-2-value",
    `${PREF_2} has the correct value after unenrollment`
  );
  Assert.equal(
    PrefUtils.getPref(PREF_3),
    PREF_3,
    `${PREF_3} has the correct value after unenrollment (can't reset default branch)`
  );
  Assert.equal(
    PrefUtils.getPref(PREF_4),
    "original-pref-4-value",
    `${PREF_4} has the correct value`
  );

  await cleanup();
});

add_task(async function test_prefFlips_restore_failure_conflict() {
  const PREF = "pref.foo.bar";

  const storePath = await NimbusTestUtils.createStoreWith(store => {
    NimbusTestUtils.addEnrollmentForRecipe(
      NimbusTestUtils.factories.recipe.withFeatureConfig("rollout-1", {
        featureId: FEATURE_ID,
        value: {
          prefs: {
            [PREF]: { branch: USER, value: "correct-value" },
          },
        },
      }),
      {
        store,
        extra: {
          source: "test",
          prefFlips: {
            originalValues: {
              [PREF]: null,
            },
          },
        },
      }
    );

    NimbusTestUtils.addEnrollmentForRecipe(
      NimbusTestUtils.factories.recipe.withFeatureConfig(
        "rollout-2",
        {
          featureId: FEATURE_ID,
          value: {
            prefs: {
              [PREF]: { branch: USER, value: "incorrect-value" },
            },
          },
        },
        { isRollout: true }
      ),
      {
        store,
        extra: {
          source: "test",
          prefFlips: {
            originalValues: {
              [PREF]: null,
            },
          },
        },
      }
    );

    NimbusTestUtils.addEnrollmentForRecipe(
      NimbusTestUtils.factories.recipe.withFeatureConfig(
        "rollout-3",
        {
          featureId: FEATURE_ID,
          value: {
            prefs: {
              [PREF]: { branch: DEFAULT, value: "correct-value" },
            },
          },
        },
        { isRollout: true }
      ),
      {
        store,
        extra: {
          source: "test",
          prefFlips: {
            originalValues: {
              [PREF]: null,
            },
          },
        },
      }
    );
  });

  const { manager, cleanup } = await setupTest({
    storePath,
    migrationState: NimbusTestUtils.migrationState.LATEST,
  });

  await NimbusTestUtils.assert.activeEnrollments(["rollout-1"]);
  await NimbusTestUtils.assert.enrollmentExists("rollout-2", { active: false });

  Assert.ok(manager.store.get("rollout-1").active, "rollout-1 is active");
  Assert.ok(!manager.store.get("rollout-2").active, "rollout-2 is not active");
  Assert.equal(
    manager.store.get("rollout-2").unenrollReason,
    "prefFlips-failed"
  );
  Assert.ok(!manager.store.get("rollout-3").active, "rollout-3 is not active");
  Assert.equal(
    manager.store.get("rollout-3").unenrollReason,
    "prefFlips-failed"
  );

  Assert.equal(
    PrefUtils.getPref(PREF),
    "correct-value",
    `${PREF} has the correct value`
  );

  await NimbusTestUtils.cleanupManager(["rollout-1"], { manager });

  Assert.equal(
    PrefUtils.getPref(PREF),
    null,
    `${PREF} has the correct value after unenrollment`
  );

  await cleanup();
});

// Test the case where an experiment sets a default branch pref, but the user
// changed their user.js between restarts.
add_task(async function test_prefFlips_restore_failure_wrong_type() {
  const PREF_1 = "foo.bar.baz";
  const PREF_2 = "qux.quux.corge.grault";

  const recipe = NimbusTestUtils.factories.recipe.withFeatureConfig(
    "prefFlips-test",
    {
      featureId: FEATURE_ID,
      value: {
        prefs: {
          [PREF_1]: {
            branch: DEFAULT,
            value: "recipe-value",
          },
          [PREF_2]: {
            branch: USER,
            value: "recipe-value",
          },
        },
      },
    }
  );

  let storePath;
  {
    const store = NimbusTestUtils.stubs.store();
    await store.init();

    await NimbusTestUtils.addEnrollmentForRecipe(recipe, {
      store,
      extra: {
        source: "test",
        prefFlips: {
          originalValues: {
            [PREF_1]: "original-value",
            [PREF_2]: "original-value",
          },
        },
      },
    });

    storePath = await NimbusTestUtils.saveStore(store);
  }

  Services.prefs.setIntPref(PREF_1, 123);

  const { manager, cleanup } = await setupTest({
    storePath,
    secureExperiments: [recipe],
    migrationState: NimbusTestUtils.migrationState.LATEST,
  });

  await NimbusTestUtils.flushStore(manager.store);
  await NimbusTestUtils.assert.enrollmentExists(recipe.slug, { active: false });

  const enrollment = manager.store.get(recipe.slug);

  Assert.ok(!enrollment.active, "Enrollment should be inactive");
  Assert.equal(enrollment.unenrollReason, "prefFlips-failed");

  Assert.ok(
    !Services.prefs.prefHasDefaultValue(PREF_1),
    `${PREF_1} has no default value`
  );
  Assert.ok(
    !Services.prefs.prefHasDefaultValue(PREF_2),
    `${PREF_2} has no default value`
  );
  Assert.equal(
    Services.prefs.getIntPref(PREF_1),
    123,
    `${PREF_1} value unchanged`
  );
  Assert.ok(!Services.prefs.prefHasUserValue(PREF_2), `${PREF_2} has no value`);

  Services.prefs.deleteBranch(PREF_1);
  Services.prefs.deleteBranch(PREF_2);
  await cleanup();
});

add_task(
  async function test_prefFlips_reenroll_set_default_branch_wrong_type() {
    const PREF = "test.pref.please.ignore";

    const recipe = NimbusTestUtils.factories.recipe.withFeatureConfig(
      "prefFlips-test",
      {
        featureId: FEATURE_ID,
        value: {
          prefs: {
            [PREF]: { value: 123, branch: DEFAULT },
          },
        },
      },
      { isRollout: true }
    );

    const { manager, cleanup } = await setupTest();

    PrefUtils.setPref(PREF, "default-value", { branch: DEFAULT });

    await manager.enroll(recipe, "rs-loader");
    await NimbusTestUtils.assert.enrollmentExists(recipe.slug, {
      active: false,
    });

    let enrollment = manager.store.get(recipe.slug);

    Assert.ok(!enrollment.active, "enrollment should not be active");
    Assert.equal(enrollment.unenrollReason, "prefFlips-failed");

    await manager.enroll(recipe, "rs-loader", { reenroll: true });
    await NimbusTestUtils.assert.enrollmentExists(recipe.slug, {
      active: false,
    });

    enrollment = manager.store.get(recipe.slug);

    Assert.ok(!enrollment.active, "enrollment should not be active");
    Assert.equal(enrollment.unenrollReason, "prefFlips-failed");

    Services.prefs.deleteBranch(PREF);

    await cleanup();
  }
);

add_task(async function testDb() {
  const { manager, cleanup } = await setupTest();

  PrefUtils.setPref("foo.bar.baz", "foo");

  await manager.enroll(
    NimbusTestUtils.factories.recipe.withFeatureConfig("slug", {
      featureId: "prefFlips",
      value: {
        prefs: {
          "foo.bar.baz": {
            value: "bar",
            branch: "user",
          },
        },
      },
    }),
    "test"
  );

  await NimbusTestUtils.flushStore(manager.store);

  const conn = await ProfilesDatastoreService.getConnection();
  const [result] = await conn.execute(
    `
      SELECT
        json(prefFlips) as prefFlips
      FROM NimbusEnrollments
      WHERE
        profileId = :profileId AND
        slug = :slug;
    `,
    {
      slug: "slug",
      profileId: ExperimentAPI.profileId,
    }
  );

  const prefFlips = JSON.parse(result.getResultByName("prefFlips"));
  const enrollment = manager.store.get("slug");

  Assert.deepEqual(
    prefFlips,
    enrollment.prefFlips,
    "prefFlips stored in the database"
  );
  Assert.deepEqual(prefFlips, {
    originalValues: {
      "foo.bar.baz": "foo",
    },
  });

  manager.unenroll("slug");
  await cleanup();

  Services.prefs.deleteBranch("foo.bar.baz");
});

add_task(async function testPrefFlipsDefaultValueChanged() {
  const { manager, cleanup } = await setupTest();

  PrefUtils.setPref(STRING_PREF, "default-value", { branch: DEFAULT });

  await NimbusTestUtils.enrollWithFeatureConfig(
    {
      featureId: FEATURE_ID,
      value: {
        prefs: {
          [STRING_PREF]: { value: "prefFlips-value", branch: USER },
        },
      },
    },
    { slug: "slug" }
  );

  PrefUtils.setPref(STRING_PREF, "changed-default-value", { branch: DEFAULT });

  const enrollment = manager.store.get("slug");
  Assert.ok(enrollment?.active, "Enrollment is active");
  Assert.deepEqual(enrollment.prefFlips, {
    originalValues: {
      [STRING_PREF]: null,
    },
  });

  manager.unenroll("slug", "test");
  Assert.equal(
    PrefUtils.getPref(STRING_PREF, { branch: DEFAULT }),
    "changed-default-value"
  );
  Assert.ok(
    !Services.prefs.prefHasUserValue(STRING_PREF),
    "Pref was reset to changed default branch value"
  );

  await cleanup();
});

Messung V0.5 in Prozent
C=98 H=87 G=92

¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.80Angebot  (Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-10-04) ¤

*Eine klare Vorstellung vom Zielzustand






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.