Skip to content

Commit d0d27ca

Browse files
authored
Update eppo js commons dependency to v1.4.1 to include allocation key in logged event's experiment attribute (FF-858) (#24)
* Update version of common dependency * Update test files * Fix type issues in tests * Add tests to confirm allocation logging
1 parent 8b85397 commit d0d27ca

File tree

5 files changed

+189
-20
lines changed

5 files changed

+189
-20
lines changed

Makefile

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,18 @@ help: Makefile
2525

2626
## test-data
2727
testDataDir := test/data/
28+
tempDir := ${testDataDir}temp/
29+
gitDataDir := ${tempDir}sdk-test-data/
30+
branchName := main
31+
githubRepoLink := https://github.com/Eppo-exp/sdk-test-data.git
2832
.PHONY: test-data
29-
test-data:
33+
test-data:
3034
rm -rf $(testDataDir)
31-
mkdir -p $(testDataDir)
32-
gsutil cp gs://sdk-test-data/rac-experiments-v2.json $(testDataDir)
33-
gsutil cp -r gs://sdk-test-data/assignment-v2 $(testDataDir)
35+
mkdir -p $(tempDir)
36+
git clone -b ${branchName} --depth 1 --single-branch ${githubRepoLink} ${gitDataDir}
37+
cp ${gitDataDir}rac-experiments-v3.json ${testDataDir}
38+
cp -r ${gitDataDir}assignment-v2 ${testDataDir}
39+
rm -rf ${tempDir}
3440

3541
## prepare
3642
.PHONY: prepare

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@eppo/js-client-sdk",
3-
"version": "1.2.0",
3+
"version": "1.2.1",
44
"description": "Eppo SDK for client-side JavaScript applications",
55
"main": "dist/index.js",
66
"files": [
@@ -56,7 +56,7 @@
5656
"xhr-mock": "^2.5.1"
5757
},
5858
"dependencies": {
59-
"@eppo/js-client-sdk-common": "^1.3.1",
59+
"@eppo/js-client-sdk-common": "^1.4.1",
6060
"axios": "^0.27.2",
6161
"md5": "^2.3.0"
6262
}

src/index.spec.ts

Lines changed: 170 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
* @jest-environment jsdom
33
*/
44

5+
import * as td from 'testdouble';
56
import mock from 'xhr-mock';
67

78
import {
@@ -10,18 +11,70 @@ import {
1011
readMockRacResponse,
1112
} from '../test/testHelpers';
1213

13-
import { EppoJSClient, init } from './index';
14+
import { EppoLocalStorage } from './local-storage';
15+
16+
import { EppoJSClient, IAssignmentLogger, IEppoClient, init } from './index';
1417

1518
describe('EppoJSClient E2E test', () => {
16-
let client: EppoJSClient;
19+
let globalClient: IEppoClient;
20+
21+
const flagKey = 'mock-experiment';
22+
23+
const mockExperimentConfig = {
24+
name: flagKey,
25+
enabled: true,
26+
subjectShards: 100,
27+
overrides: {},
28+
typedOverrides: {},
29+
rules: [
30+
{
31+
allocationKey: 'allocation1',
32+
conditions: [],
33+
},
34+
],
35+
allocations: {
36+
allocation1: {
37+
percentExposure: 1,
38+
variations: [
39+
{
40+
name: 'control',
41+
value: 'control',
42+
typedValue: 'control',
43+
shardRange: {
44+
start: 0,
45+
end: 34,
46+
},
47+
},
48+
{
49+
name: 'variant-1',
50+
value: 'variant-1',
51+
typedValue: 'variant-1',
52+
shardRange: {
53+
start: 34,
54+
end: 67,
55+
},
56+
},
57+
{
58+
name: 'variant-2',
59+
value: 'variant-2',
60+
typedValue: 'variant-2',
61+
shardRange: {
62+
start: 67,
63+
end: 100,
64+
},
65+
},
66+
],
67+
},
68+
},
69+
};
1770

1871
beforeAll(async () => {
1972
mock.setup();
2073
mock.get(/randomized_assignment\/v3\/config*/, (_req, res) => {
2174
const rac = readMockRacResponse();
2275
return res.status(200).body(JSON.stringify(rac));
2376
});
24-
client = await init({
77+
globalClient = await init({
2578
apiKey: 'dummy',
2679
baseUrl: 'http://127.0.0.1:4000',
2780
assignmentLogger: {
@@ -37,6 +90,116 @@ describe('EppoJSClient E2E test', () => {
3790
mock.teardown();
3891
});
3992

93+
it('assigns subject from overrides when experiment is enabled', () => {
94+
const mockConfigStore = td.object<EppoLocalStorage>();
95+
const mockLogger = td.object<IAssignmentLogger>();
96+
td.when(mockConfigStore.get(flagKey)).thenReturn({
97+
...mockExperimentConfig,
98+
overrides: {
99+
'1b50f33aef8f681a13f623963da967ed': 'variant-2',
100+
},
101+
typedOverrides: {
102+
'1b50f33aef8f681a13f623963da967ed': 'variant-2',
103+
},
104+
});
105+
const client = new EppoJSClient(mockConfigStore);
106+
client.setLogger(mockLogger);
107+
const assignment = client.getAssignment('subject-10', flagKey);
108+
expect(assignment).toEqual('variant-2');
109+
});
110+
111+
it('assigns subject from overrides when experiment is not enabled', () => {
112+
const mockConfigStore = td.object<EppoLocalStorage>();
113+
const mockLogger = td.object<IAssignmentLogger>();
114+
td.when(mockConfigStore.get(flagKey)).thenReturn({
115+
...mockExperimentConfig,
116+
overrides: {
117+
'1b50f33aef8f681a13f623963da967ed': 'variant-2',
118+
},
119+
typedOverrides: {
120+
'1b50f33aef8f681a13f623963da967ed': 'variant-2',
121+
},
122+
});
123+
const client = new EppoJSClient(mockConfigStore);
124+
client.setLogger(mockLogger);
125+
const assignment = client.getAssignment('subject-10', flagKey);
126+
expect(assignment).toEqual('variant-2');
127+
});
128+
129+
it('returns null when experiment config is absent', () => {
130+
const mockConfigStore = td.object<EppoLocalStorage>();
131+
const mockLogger = td.object<IAssignmentLogger>();
132+
td.when(mockConfigStore.get(flagKey)).thenReturn(null);
133+
const client = new EppoJSClient(mockConfigStore);
134+
client.setLogger(mockLogger);
135+
const assignment = client.getAssignment('subject-10', flagKey);
136+
expect(assignment).toEqual(null);
137+
});
138+
139+
it('logs variation assignment and experiment key', () => {
140+
const mockConfigStore = td.object<EppoLocalStorage>();
141+
const mockLogger = td.object<IAssignmentLogger>();
142+
td.when(mockConfigStore.get(flagKey)).thenReturn(mockExperimentConfig);
143+
const subjectAttributes = { foo: 3 };
144+
const client = new EppoJSClient(mockConfigStore);
145+
client.setLogger(mockLogger);
146+
const assignment = client.getAssignment('subject-10', flagKey, subjectAttributes);
147+
expect(assignment).toEqual('control');
148+
expect(td.explain(mockLogger.logAssignment).callCount).toEqual(1);
149+
expect(td.explain(mockLogger?.logAssignment).calls[0]?.args[0].subject).toEqual('subject-10');
150+
expect(td.explain(mockLogger?.logAssignment).calls[0]?.args[0].featureFlag).toEqual(flagKey);
151+
expect(td.explain(mockLogger?.logAssignment).calls[0]?.args[0].experiment).toEqual(
152+
`${flagKey}-${mockExperimentConfig?.rules[0]?.allocationKey}`,
153+
);
154+
expect(td.explain(mockLogger?.logAssignment).calls[0]?.args[0].allocation).toEqual(
155+
`${mockExperimentConfig?.rules[0]?.allocationKey}`,
156+
);
157+
});
158+
159+
it('handles logging exception', () => {
160+
const mockConfigStore = td.object<EppoLocalStorage>();
161+
const mockLogger = td.object<IAssignmentLogger>();
162+
td.when(mockLogger.logAssignment(td.matchers.anything())).thenThrow(new Error('logging error'));
163+
td.when(mockConfigStore.get(flagKey)).thenReturn(mockExperimentConfig);
164+
const subjectAttributes = { foo: 3 };
165+
const client = new EppoJSClient(mockConfigStore);
166+
client.setLogger(mockLogger);
167+
const assignment = client.getAssignment('subject-10', flagKey, subjectAttributes);
168+
expect(assignment).toEqual('control');
169+
});
170+
171+
it('only returns variation if subject matches rules', () => {
172+
const mockConfigStore = td.object<EppoLocalStorage>();
173+
const mockLogger = td.object<IAssignmentLogger>();
174+
td.when(mockConfigStore.get(flagKey)).thenReturn({
175+
...mockExperimentConfig,
176+
rules: [
177+
{
178+
allocationKey: 'allocation1',
179+
conditions: [
180+
{
181+
operator: 'GT',
182+
attribute: 'appVersion',
183+
value: 10,
184+
},
185+
],
186+
},
187+
],
188+
});
189+
const client = new EppoJSClient(mockConfigStore);
190+
client.setLogger(mockLogger);
191+
let assignment = client.getAssignment('subject-10', flagKey, {
192+
appVersion: 9,
193+
});
194+
expect(assignment).toEqual(null);
195+
assignment = client.getAssignment('subject-10', flagKey);
196+
expect(assignment).toEqual(null);
197+
assignment = client.getAssignment('subject-10', flagKey, {
198+
appVersion: 11,
199+
});
200+
expect(assignment).toEqual('control');
201+
});
202+
40203
describe('getAssignment', () => {
41204
const testData = readAssignmentTestData();
42205

@@ -69,9 +232,9 @@ describe('EppoJSClient E2E test', () => {
69232
});
70233
});
71234

72-
function getAssignments(subjects: string[], experiment: string): string[] {
235+
function getAssignments(subjects: string[], experiment: string): (string | null)[] {
73236
return subjects.map((subjectKey) => {
74-
return client.getAssignment(subjectKey, experiment);
237+
return globalClient.getAssignment(subjectKey, experiment);
75238
});
76239
}
77240

@@ -82,9 +245,9 @@ describe('EppoJSClient E2E test', () => {
82245
subjectAttributes: Record<string, any>;
83246
}[],
84247
experiment: string,
85-
): string[] {
248+
): (string | null)[] {
86249
return subjectsWithAttributes.map((subject) => {
87-
return client.getAssignment(subject.subjectKey, experiment, subject.subjectAttributes);
250+
return globalClient.getAssignment(subject.subjectKey, experiment, subject.subjectAttributes);
88251
});
89252
}
90253
});

test/testHelpers.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import * as fs from 'fs';
22

3-
import { IExperimentConfiguration } from '../src/dto/experiment-configuration-dto';
4-
import { IVariation } from '../src/experiment/variation-dto';
3+
import { IExperimentConfiguration } from '@eppo/js-client-sdk-common/dist/dto/experiment-configuration-dto';
4+
import { IVariation } from '@eppo/js-client-sdk-common/dist/dto/variation-dto';
55

66
export const TEST_DATA_DIR = './test/data/';
77
export const ASSIGNMENT_TEST_DATA_DIR = TEST_DATA_DIR + 'assignment-v2/';
8-
export const MOCK_RAC_RESPONSE_FILE = 'rac-experiments-v2.json';
8+
export const MOCK_RAC_RESPONSE_FILE = 'rac-experiments-v3.json';
99

1010
export interface IAssignmentTestCase {
1111
experiment: string;

yarn.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -289,10 +289,10 @@
289289
resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70"
290290
integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==
291291

292-
"@eppo/js-client-sdk-common@^1.3.1":
293-
version "1.3.1"
294-
resolved "https://registry.yarnpkg.com/@eppo/js-client-sdk-common/-/js-client-sdk-common-1.3.1.tgz#5688eb6a2bc96339446c12f2576016c9cddd52e3"
295-
integrity sha512-hxvGSqr+55ZALMe7wv76gFXuFgOAaApI1EepLGWX80xLb2r1gNke9+IEYQWe8kXLB+EUCaQJ1v4/c3OBqk9vQQ==
292+
"@eppo/js-client-sdk-common@^1.4.1":
293+
version "1.4.1"
294+
resolved "https://registry.yarnpkg.com/@eppo/js-client-sdk-common/-/js-client-sdk-common-1.4.1.tgz#b75bb34c9004ea34070be38250532e04d2683e83"
295+
integrity sha512-1dPursZSgtJb7ufLbePI1qIryydFTP6Y+EJYiV+I1njhBEdIUjc/E1+Qe3kBDSXoyowrk8VjUZGV4RD4YnPhcA==
296296
dependencies:
297297
axios "^0.27.2"
298298
md5 "^2.3.0"

0 commit comments

Comments
 (0)