Skip to content

Commit d2799c8

Browse files
committed
Add test coverage for override functionality
1 parent 3b56a74 commit d2799c8

File tree

1 file changed

+143
-1
lines changed

1 file changed

+143
-1
lines changed

src/client/eppo-client.spec.ts

Lines changed: 143 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import { IConfigurationStore } from '../configuration-store/configuration-store'
2323
import { MemoryOnlyConfigurationStore } from '../configuration-store/memory.store';
2424
import { MAX_EVENT_QUEUE_SIZE, DEFAULT_POLL_INTERVAL_MS, POLL_JITTER_PCT } from '../constants';
2525
import { decodePrecomputedFlag } from '../decoding';
26-
import { Flag, ObfuscatedFlag, VariationType } from '../interfaces';
26+
import { Flag, ObfuscatedFlag, Variation, VariationType } from '../interfaces';
2727
import { getMD5Hash } from '../obfuscation';
2828
import { AttributeType } from '../types';
2929

@@ -945,4 +945,146 @@ describe('EppoClient E2E test', () => {
945945
);
946946
});
947947
});
948+
949+
describe('flag overrides', () => {
950+
let client: EppoClient;
951+
let mockLogger: IAssignmentLogger;
952+
let overrideStore: IConfigurationStore<Variation>;
953+
954+
beforeEach(() => {
955+
storage.setEntries({ [flagKey]: mockFlag });
956+
mockLogger = td.object<IAssignmentLogger>();
957+
overrideStore = new MemoryOnlyConfigurationStore<Variation>();
958+
client = new EppoClient({
959+
flagConfigurationStore: storage,
960+
overridesStore: overrideStore,
961+
});
962+
client.setAssignmentLogger(mockLogger);
963+
client.useNonExpiringInMemoryAssignmentCache();
964+
});
965+
966+
it('returns override values for all supported types', () => {
967+
overrideStore.setEntries({
968+
'string-flag': {
969+
key: 'override-variation',
970+
value: 'override-string',
971+
},
972+
'boolean-flag': {
973+
key: 'override-variation',
974+
value: true,
975+
},
976+
'numeric-flag': {
977+
key: 'override-variation',
978+
value: 42.5,
979+
},
980+
'json-flag': {
981+
key: 'override-variation',
982+
value: '{"foo": "bar"}',
983+
},
984+
});
985+
986+
expect(client.getStringAssignment('string-flag', 'subject-10', {}, 'default')).toBe(
987+
'override-string',
988+
);
989+
expect(client.getBooleanAssignment('boolean-flag', 'subject-10', {}, false)).toBe(true);
990+
expect(client.getNumericAssignment('numeric-flag', 'subject-10', {}, 0)).toBe(42.5);
991+
expect(client.getJSONAssignment('json-flag', 'subject-10', {}, {})).toEqual({ foo: 'bar' });
992+
});
993+
994+
it('does not log assignments when override is applied', () => {
995+
overrideStore.setEntries({
996+
[flagKey]: {
997+
key: 'override-variation',
998+
value: 'override-value',
999+
},
1000+
});
1001+
1002+
client.getStringAssignment(flagKey, 'subject-10', {}, 'default');
1003+
1004+
expect(td.explain(mockLogger.logAssignment).callCount).toBe(0);
1005+
});
1006+
1007+
it('includes override details in assignment details', () => {
1008+
overrideStore.setEntries({
1009+
[flagKey]: {
1010+
key: 'override-variation',
1011+
value: 'override-value',
1012+
},
1013+
});
1014+
1015+
const result = client.getStringAssignmentDetails(
1016+
flagKey,
1017+
'subject-10',
1018+
{ foo: 3 },
1019+
'default',
1020+
);
1021+
1022+
expect(result).toMatchObject({
1023+
variation: 'override-value',
1024+
evaluationDetails: {
1025+
flagEvaluationCode: 'MATCH',
1026+
flagEvaluationDescription: 'Flag override applied',
1027+
},
1028+
});
1029+
});
1030+
1031+
it('does not update assignment cache when override is applied', () => {
1032+
overrideStore.setEntries({
1033+
[flagKey]: {
1034+
key: 'override-variation',
1035+
value: 'override-value',
1036+
},
1037+
});
1038+
1039+
// First call with override
1040+
client.getStringAssignment(flagKey, 'subject-10', {}, 'default');
1041+
1042+
// Remove override
1043+
overrideStore.setEntries({});
1044+
1045+
// Second call without override should trigger logging since cache wasn't updated
1046+
client.getStringAssignment(flagKey, 'subject-10', {}, 'default');
1047+
1048+
expect(td.explain(mockLogger.logAssignment).callCount).toBe(1);
1049+
expect(td.explain(mockLogger.logAssignment).calls[0].args[0]).toMatchObject({
1050+
featureFlag: flagKey,
1051+
subject: 'subject-10',
1052+
});
1053+
});
1054+
1055+
it('uses normal assignment when no override exists for flag', () => {
1056+
// Set override for a different flag
1057+
overrideStore.setEntries({
1058+
'other-flag': {
1059+
key: 'override-variation',
1060+
value: 'override-value',
1061+
},
1062+
});
1063+
1064+
const result = client.getStringAssignment(flagKey, 'subject-10', {}, 'default');
1065+
1066+
// Should get the normal assignment value from mockFlag
1067+
expect(result).toBe(variationA.value);
1068+
expect(td.explain(mockLogger.logAssignment).callCount).toBe(1);
1069+
});
1070+
1071+
it('uses normal assignment when no overrides store is configured', () => {
1072+
// Create client without overrides store
1073+
const clientWithoutOverrides = new EppoClient({
1074+
flagConfigurationStore: storage,
1075+
});
1076+
clientWithoutOverrides.setAssignmentLogger(mockLogger);
1077+
1078+
const result = clientWithoutOverrides.getStringAssignment(
1079+
flagKey,
1080+
'subject-10',
1081+
{},
1082+
'default',
1083+
);
1084+
1085+
// Should get the normal assignment value from mockFlag
1086+
expect(result).toBe(variationA.value);
1087+
expect(td.explain(mockLogger.logAssignment).callCount).toBe(1);
1088+
});
1089+
});
9481090
});

0 commit comments

Comments
 (0)