Skip to content

Commit 41a1147

Browse files
committed
forced variation test
1 parent 4a54701 commit 41a1147

File tree

2 files changed

+319
-1
lines changed

2 files changed

+319
-1
lines changed

lib/core/decision_service/index.spec.ts

Lines changed: 318 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1419,4 +1419,322 @@ describe('DecisionService', () => {
14191419
});
14201420
});
14211421
});
1422+
1423+
1424+
describe('forced variation management', () => {
1425+
it('should return true for a valid forcedVariation in setForcedVariation', function() {
1426+
const config = createProjectConfig(cloneDeep(testData));
1427+
const { decisionService } = getDecisionService();
1428+
1429+
const didSetVariation = decisionService.setForcedVariation(
1430+
config,
1431+
'testExperiment',
1432+
'user1',
1433+
'control'
1434+
);
1435+
expect(didSetVariation).toBe(true);
1436+
});
1437+
1438+
it('should return the same variation from getVariation as was set in setVariation', function() {
1439+
const config = createProjectConfig(cloneDeep(testData));
1440+
const { decisionService } = getDecisionService();
1441+
decisionService.setForcedVariation(
1442+
config,
1443+
'testExperiment',
1444+
'user1',
1445+
'control'
1446+
);
1447+
1448+
const variation = decisionService.getForcedVariation(config, 'testExperiment', 'user1').result;
1449+
expect(variation).toBe('control');
1450+
});
1451+
1452+
it('should return null from getVariation if no forced variation was set for a valid experimentKey', function() {
1453+
const config = createProjectConfig(cloneDeep(testData));
1454+
const { decisionService } = getDecisionService();
1455+
1456+
expect(config.experimentKeyMap['testExperiment']).toBeDefined();
1457+
const variation = decisionService.getForcedVariation(config, 'testExperiment', 'user1').result;
1458+
1459+
expect(variation).toBe(null);
1460+
});
1461+
1462+
it('should return null from getVariation for an invalid experimentKey', function() {
1463+
const config = createProjectConfig(cloneDeep(testData));
1464+
const { decisionService } = getDecisionService();
1465+
1466+
expect(config.experimentKeyMap['definitely_not_valid_exp_key']).not.toBeDefined();
1467+
const variation = decisionService.getForcedVariation(config, 'definitely_not_valid_exp_key', 'user1').result;
1468+
1469+
expect(variation).toBe(null);
1470+
});
1471+
1472+
it('should return null when a forced decision is set on another experiment key', function() {
1473+
const config = createProjectConfig(cloneDeep(testData));
1474+
const { decisionService } = getDecisionService();
1475+
1476+
decisionService.setForcedVariation(config, 'testExperiment', 'user1', 'control');
1477+
var variation = decisionService.getForcedVariation(config, 'testExperimentLaunched', 'user1').result;
1478+
expect(variation).toBe(null);
1479+
});
1480+
1481+
it('should not set forced variation for an invalid variation key and return false', function() {
1482+
const config = createProjectConfig(cloneDeep(testData));
1483+
const { decisionService } = getDecisionService();
1484+
1485+
const wasSet = decisionService.setForcedVariation(
1486+
config,
1487+
'testExperiment',
1488+
'user1',
1489+
'definitely_not_valid_variation_key'
1490+
);
1491+
1492+
expect(wasSet).toBe(false);
1493+
const variation = decisionService.getForcedVariation(config, 'testExperiment', 'user1').result;
1494+
expect(variation).toBe(null);
1495+
});
1496+
1497+
it('should reset the forcedVariation if null is passed to setForcedVariation', function() {
1498+
const config = createProjectConfig(cloneDeep(testData));
1499+
const { decisionService } = getDecisionService();
1500+
1501+
const didSetVariation = decisionService.setForcedVariation(
1502+
config,
1503+
'testExperiment',
1504+
'user1',
1505+
'control'
1506+
);
1507+
1508+
expect(didSetVariation).toBe(true);
1509+
1510+
let variation = decisionService.getForcedVariation(config, 'testExperiment', 'user1').result;
1511+
expect(variation).toBe('control');
1512+
1513+
const didSetVariationAgain = decisionService.setForcedVariation(
1514+
config,
1515+
'testExperiment',
1516+
'user1',
1517+
null
1518+
);
1519+
1520+
expect(didSetVariationAgain).toBe(true);
1521+
1522+
variation = decisionService.getForcedVariation(config, 'testExperiment', 'user1').result;
1523+
expect(variation).toBe(null);
1524+
});
1525+
1526+
it('should be able to add variations for multiple experiments for one user', function() {
1527+
const config = createProjectConfig(cloneDeep(testData));
1528+
const { decisionService } = getDecisionService();
1529+
1530+
const didSetVariation1 = decisionService.setForcedVariation(
1531+
config,
1532+
'testExperiment',
1533+
'user1',
1534+
'control'
1535+
);
1536+
expect(didSetVariation1).toBe(true);
1537+
1538+
const didSetVariation2 = decisionService.setForcedVariation(
1539+
config,
1540+
'testExperimentLaunched',
1541+
'user1',
1542+
'controlLaunched'
1543+
);
1544+
expect(didSetVariation2).toBe(true);
1545+
1546+
const variation = decisionService.getForcedVariation(config, 'testExperiment', 'user1').result;
1547+
const variation2 = decisionService.getForcedVariation(config, 'testExperimentLaunched', 'user1').result;
1548+
expect(variation).toBe('control');
1549+
expect(variation2).toBe('controlLaunched');
1550+
});
1551+
1552+
it('should be able to forced variation to same experiment for multiple users', function() {
1553+
const config = createProjectConfig(cloneDeep(testData));
1554+
const { decisionService } = getDecisionService();
1555+
1556+
const didSetVariation1 = decisionService.setForcedVariation(
1557+
config,
1558+
'testExperiment',
1559+
'user1',
1560+
'control'
1561+
);
1562+
expect(didSetVariation1).toBe(true);
1563+
1564+
const didSetVariation2 = decisionService.setForcedVariation(
1565+
config,
1566+
'testExperiment',
1567+
'user2',
1568+
'variation'
1569+
);
1570+
expect(didSetVariation2).toBe(true);
1571+
1572+
const variationControl = decisionService.getForcedVariation(config, 'testExperiment', 'user1').result;
1573+
const variationVariation = decisionService.getForcedVariation(config, 'testExperiment', 'user2').result;
1574+
1575+
expect(variationControl).toBe('control');
1576+
expect(variationVariation).toBe('variation');
1577+
});
1578+
1579+
it('should be able to reset a variation for a user with multiple experiments', function() {
1580+
const config = createProjectConfig(cloneDeep(testData));
1581+
const { decisionService } = getDecisionService();
1582+
1583+
// Set the first time
1584+
const didSetVariation1 = decisionService.setForcedVariation(
1585+
config,
1586+
'testExperiment',
1587+
'user1',
1588+
'control'
1589+
);
1590+
expect(didSetVariation1).toBe(true);
1591+
1592+
const didSetVariation2 = decisionService.setForcedVariation(
1593+
config,
1594+
'testExperimentLaunched',
1595+
'user1',
1596+
'controlLaunched'
1597+
);
1598+
expect(didSetVariation2).toBe(true);
1599+
1600+
let variation1 = decisionService.getForcedVariation(config, 'testExperiment', 'user1').result;
1601+
let variation2 = decisionService.getForcedVariation(config, 'testExperimentLaunched', 'user1').result;
1602+
1603+
expect(variation1).toBe('control');
1604+
expect(variation2).toBe('controlLaunched');
1605+
1606+
// Reset for one of the experiments
1607+
const didSetVariationAgain = decisionService.setForcedVariation(
1608+
config,
1609+
'testExperiment',
1610+
'user1',
1611+
'variation'
1612+
);
1613+
expect(didSetVariationAgain).toBe(true);
1614+
1615+
variation1 = decisionService.getForcedVariation(config, 'testExperiment', 'user1').result;
1616+
variation2 = decisionService.getForcedVariation(config, 'testExperimentLaunched', 'user1').result;
1617+
1618+
expect(variation1).toBe('variation');
1619+
expect(variation2).toBe('controlLaunched');
1620+
});
1621+
1622+
it('should be able to unset a variation for a user with multiple experiments', function() {
1623+
const config = createProjectConfig(cloneDeep(testData));
1624+
const { decisionService } = getDecisionService();
1625+
1626+
// Set the first time
1627+
const didSetVariation1 = decisionService.setForcedVariation(
1628+
config,
1629+
'testExperiment',
1630+
'user1',
1631+
'control'
1632+
);
1633+
expect(didSetVariation1).toBe(true);
1634+
1635+
const didSetVariation2 = decisionService.setForcedVariation(
1636+
config,
1637+
'testExperimentLaunched',
1638+
'user1',
1639+
'controlLaunched'
1640+
);
1641+
expect(didSetVariation2).toBe(true);
1642+
1643+
let variation1 = decisionService.getForcedVariation(config, 'testExperiment', 'user1').result;
1644+
let variation2 = decisionService.getForcedVariation(config, 'testExperimentLaunched', 'user1').result;
1645+
1646+
expect(variation1).toBe('control');
1647+
expect(variation2).toBe('controlLaunched');
1648+
1649+
// Unset for one of the experiments
1650+
decisionService.setForcedVariation(config, 'testExperiment', 'user1', null);
1651+
1652+
variation1 = decisionService.getForcedVariation(config, 'testExperiment', 'user1').result;
1653+
variation2 = decisionService.getForcedVariation(config, 'testExperimentLaunched', 'user1').result;
1654+
1655+
expect(variation1).toBe(null);
1656+
expect(variation2).toBe('controlLaunched');
1657+
});
1658+
1659+
it('should return false for an empty variation key', function() {
1660+
const config = createProjectConfig(cloneDeep(testData));
1661+
const { decisionService } = getDecisionService();
1662+
1663+
const didSetVariation = decisionService.setForcedVariation(config, 'testExperiment', 'user1', '');
1664+
expect(didSetVariation).toBe(false);
1665+
});
1666+
1667+
it('should return null when a variation was previously set, and that variation no longer exists on the config object', function() {
1668+
const config = createProjectConfig(cloneDeep(testData));
1669+
const { decisionService } = getDecisionService();
1670+
1671+
const didSetVariation = decisionService.setForcedVariation(
1672+
config,
1673+
'testExperiment',
1674+
'user1',
1675+
'control'
1676+
);
1677+
expect(didSetVariation).toBe(true);
1678+
1679+
const newDatafile = cloneDeep(testData);
1680+
// Remove 'control' variation from variations, traffic allocation, and datafile forcedVariations.
1681+
newDatafile.experiments[0].variations = [
1682+
{
1683+
key: 'variation',
1684+
id: '111129',
1685+
},
1686+
];
1687+
newDatafile.experiments[0].trafficAllocation = [
1688+
{
1689+
entityId: '111129',
1690+
endOfRange: 9000,
1691+
},
1692+
];
1693+
newDatafile.experiments[0].forcedVariations = {
1694+
user1: 'variation',
1695+
user2: 'variation',
1696+
};
1697+
// Now the only variation in testExperiment is 'variation'
1698+
const newConfigObj = createProjectConfig(newDatafile);
1699+
const forcedVar = decisionService.getForcedVariation(newConfigObj, 'testExperiment', 'user1').result;
1700+
expect(forcedVar).toBe(null);
1701+
});
1702+
1703+
it("should return null when a variation was previously set, and that variation's experiment no longer exists on the config object", function() {
1704+
const config = createProjectConfig(cloneDeep(testData));
1705+
const { decisionService } = getDecisionService();
1706+
1707+
const didSetVariation = decisionService.setForcedVariation(
1708+
config,
1709+
'testExperiment',
1710+
'user1',
1711+
'control'
1712+
);
1713+
expect(didSetVariation).toBe(true);
1714+
1715+
const newConfigObj = createProjectConfig(cloneDeep(testDataWithFeatures));
1716+
const forcedVar = decisionService.getForcedVariation(newConfigObj, 'testExperiment', 'user1').result;
1717+
expect(forcedVar).toBe(null);
1718+
});
1719+
1720+
it('should return false from setForcedVariation and not set for invalid experiment key', function() {
1721+
const config = createProjectConfig(cloneDeep(testData));
1722+
const { decisionService } = getDecisionService();
1723+
1724+
const didSetVariation = decisionService.setForcedVariation(
1725+
config,
1726+
'definitelyNotAValidExperimentKey',
1727+
'user1',
1728+
'control'
1729+
);
1730+
expect(didSetVariation).toBe(false);
1731+
1732+
const variation = decisionService.getForcedVariation(
1733+
config,
1734+
'definitelyNotAValidExperimentKey',
1735+
'user1'
1736+
).result;
1737+
expect(variation).toBe(null);
1738+
});
1739+
});
14221740
});

lib/core/decision_service/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -962,7 +962,7 @@ export class DecisionService {
962962
* @param {string} experimentKey Key representing the experiment id
963963
* @throws If the user id is not valid or not in the forced variation map
964964
*/
965-
removeForcedVariation(userId: string, experimentId: string, experimentKey: string): void {
965+
private removeForcedVariation(userId: string, experimentId: string, experimentKey: string): void {
966966
if (!userId) {
967967
throw new OptimizelyError(INVALID_USER_ID);
968968
}

0 commit comments

Comments
 (0)