Skip to content

Commit ae90453

Browse files
authored
Use new SDK test data (#8)
* Use new SDK test data * fix directory path * fix linter error * use separate directory for test data
1 parent 43677f9 commit ae90453

File tree

9 files changed

+88
-430
lines changed

9 files changed

+88
-430
lines changed

.github/workflows/lint-test-sdk.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ jobs:
1717
with:
1818
path: './node_modules'
1919
key: ${{ runner.os }}-root-node-modules-${{ hashFiles('./yarn.lock') }}
20+
- name: 'Set up GCP SDK for downloading test data'
21+
uses: 'google-github-actions/setup-gcloud@v0'
2022
- name: Install SDK dependencies
2123
run: yarn --frozen-lockfile
2224
working-directory: ./

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ temp
77
.env
88
yarn-error.log
99

10-
test/assignmentTestData
10+
test/data

Makefile

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Make settings - @see https://tech.davis-hansson.com/p/make/
2+
SHELL := bash
3+
.ONESHELL:
4+
.SHELLFLAGS := -eu -o pipefail -c
5+
.DELETE_ON_ERROR:
6+
MAKEFLAGS += --warn-undefined-variables
7+
MAKEFLAGS += --no-builtin-rules
8+
9+
# Log levels
10+
DEBUG := $(shell printf "\e[2D\e[35m")
11+
INFO := $(shell printf "\e[2D\e[36m🔵 ")
12+
OK := $(shell printf "\e[2D\e[32m🟢 ")
13+
WARN := $(shell printf "\e[2D\e[33m🟡 ")
14+
ERROR := $(shell printf "\e[2D\e[31m🔴 ")
15+
END := $(shell printf "\e[0m")
16+
17+
.PHONY: default
18+
default: help
19+
20+
## help - Print help message.
21+
.PHONY: help
22+
help: Makefile
23+
@echo "usage: make <target>"
24+
@sed -n 's/^##//p' $<
25+
26+
## test-data
27+
testDataDir := test/data/
28+
.PHONY: test-data
29+
test-data:
30+
rm -rf $(testDataDir)
31+
mkdir -p $(testDataDir)
32+
gsutil cp gs://sdk-test-data/rac-experiments.json $(testDataDir)
33+
gsutil cp -r gs://sdk-test-data/assignment-v2 $(testDataDir)
34+
35+
## prepare
36+
.PHONY: prepare
37+
prepare: test-data
38+
husky install
39+
rm -rf dist/
40+
tsc
41+
webpack
42+
api-extractor run --local

jest.config.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ module.exports = {
99
transform: {
1010
'^.+\\.(t|j)s$': 'ts-jest',
1111
},
12-
globalSetup: './test/globalSetup.ts',
1312
collectCoverageFrom: ['**/*.(t|j)s'],
1413
coverageDirectory: 'coverage/',
1514
testEnvironment: 'node',

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"lint": "eslint '**/*.{ts,tsx}' '**/*.d.{ts,tsx}' --cache",
1212
"lint:fix": "eslint --fix '**/*.{ts,tsx}' --cache",
1313
"lint:fix-pre-commit": "eslint -c .eslintrc.pre-commit.js --fix '**/*.{ts,tsx}' --no-eslintrc --cache",
14-
"prepare": "husky install && rm -rf dist/ && tsc && webpack && api-extractor run --local",
14+
"prepare": "make prepare",
1515
"pre-commit": "lint-staged && tsc && yarn docs",
1616
"typecheck": "tsc",
1717
"test": "yarn test:unit",
@@ -30,7 +30,6 @@
3030
},
3131
"homepage": "https://github.com/Eppo-exp/js-client-sdk#readme",
3232
"devDependencies": {
33-
"@google-cloud/storage": "^6.1.0",
3433
"@microsoft/api-documenter": "^7.17.17",
3534
"@microsoft/api-extractor": "^7.25.0",
3635
"@types/jest": "^28.1.1",

src/eppo-client.spec.ts

Lines changed: 25 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@
44
import * as td from 'testdouble';
55
import mock from 'xhr-mock';
66

7-
import { IAssignmentTestCase, readAssignmentTestData } from '../test/testHelpers';
7+
import {
8+
IAssignmentTestCase,
9+
readAssignmentTestData,
10+
readMockRacResponse,
11+
} from '../test/testHelpers';
812

913
import { IAssignmentLogger } from './assignment-logger';
1014
import { MAX_EVENT_QUEUE_SIZE } from './constants';
1115
import EppoClient from './eppo-client';
12-
import { IExperimentConfiguration } from './experiment/experiment-configuration';
13-
import { IVariation } from './experiment/variation';
1416
import { EppoLocalStorage } from './local-storage';
1517
import { OperatorType } from './rule';
1618
import { EppoSessionStorage } from './session-storage';
@@ -26,20 +28,8 @@ describe('EppoClient E2E test', () => {
2628
window.sessionStorage.clear();
2729
mock.setup();
2830
mock.get(/randomized_assignment\/config*/, (_req, res) => {
29-
const testCases: IAssignmentTestCase[] = readAssignmentTestData();
30-
const assignmentConfig: Record<string, IExperimentConfiguration> = {};
31-
testCases.forEach(({ experiment, percentExposure, variations }) => {
32-
assignmentConfig[experiment] = {
33-
name: experiment,
34-
percentExposure,
35-
enabled: true,
36-
subjectShards: 10000,
37-
variations,
38-
overrides: {},
39-
rules: [],
40-
};
41-
});
42-
return res.status(200).body(JSON.stringify({ experiments: assignmentConfig }));
31+
const rac = readMockRacResponse();
32+
return res.status(200).body(JSON.stringify(rac));
4333
});
4434
const preloadedConfig = {
4535
name: preloadedConfigExperiment,
@@ -142,22 +132,16 @@ describe('EppoClient E2E test', () => {
142132
it.each(readAssignmentTestData())(
143133
'test variation assignment splits',
144134
async ({
145-
variations,
146135
experiment,
147-
percentExposure,
148136
subjects,
137+
subjectsWithAttributes,
149138
expectedAssignments,
150139
}: IAssignmentTestCase) => {
151140
console.log(`---- Test Case for ${experiment} Experiment ----`);
152-
const assignments = getAssignments(subjects, experiment);
153-
// verify the assingments don't change across test runs (deterministic)
141+
const assignments = subjectsWithAttributes
142+
? getAssignmentsWithSubjectAttributes(subjectsWithAttributes, experiment)
143+
: getAssignments(subjects, experiment);
154144
expect(assignments).toEqual(expectedAssignments);
155-
const expectedVariationSplitPercentage = percentExposure / variations.length;
156-
const unassignedCount = assignments.filter((assignment) => assignment == null).length;
157-
expectToBeCloseToPercentage(unassignedCount / assignments.length, 1 - percentExposure);
158-
variations.forEach((variation) => {
159-
validateAssignmentCounts(assignments, expectedVariationSplitPercentage, variation);
160-
});
161145
},
162146
);
163147
});
@@ -286,30 +270,24 @@ describe('EppoClient E2E test', () => {
286270
expect(assignment).toEqual('control');
287271
});
288272

289-
function validateAssignmentCounts(
290-
assignments: string[],
291-
expectedPercentage: number,
292-
variation: IVariation,
293-
) {
294-
const assignedCount = assignments.filter((assignment) => assignment === variation.name).length;
295-
console.log(
296-
`Expect variation ${variation.name} percentage of ${
297-
assignedCount / assignments.length
298-
} to be close to ${expectedPercentage}`,
299-
);
300-
expectToBeCloseToPercentage(assignedCount / assignments.length, expectedPercentage);
301-
}
302-
303-
// expect assignment count to be within 5 percentage points of the expected count (because the hash output is random)
304-
function expectToBeCloseToPercentage(percentage: number, expectedPercentage: number) {
305-
expect(percentage).toBeGreaterThanOrEqual(expectedPercentage - 0.05);
306-
expect(percentage).toBeLessThanOrEqual(expectedPercentage + 0.05);
307-
}
308-
309273
function getAssignments(subjects: string[], experiment: string): string[] {
310274
const client = getInstance();
311275
return subjects.map((subjectKey) => {
312276
return client.getAssignment(subjectKey, experiment);
313277
});
314278
}
279+
280+
function getAssignmentsWithSubjectAttributes(
281+
subjectsWithAttributes: {
282+
subjectKey: string;
283+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
284+
subjectAttributes: Record<string, any>;
285+
}[],
286+
experiment: string,
287+
): string[] {
288+
const client = getInstance();
289+
return subjectsWithAttributes.map((subject) => {
290+
return client.getAssignment(subject.subjectKey, experiment, subject.subjectAttributes);
291+
});
292+
}
315293
});

test/globalSetup.ts

Lines changed: 0 additions & 25 deletions
This file was deleted.

test/testHelpers.ts

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

3+
import { IExperimentConfiguration } from '../src/experiment/experiment-configuration';
34
import { IVariation } from '../src/experiment/variation';
45

5-
export const TEST_DATA_DIR = './test/assignmentTestData/';
6+
export const TEST_DATA_DIR = './test/data/';
7+
export const ASSIGNMENT_TEST_DATA_DIR = TEST_DATA_DIR + 'assignment-v2/';
8+
export const MOCK_RAC_RESPONSE_FILE = 'rac-experiments.json';
69

710
export interface IAssignmentTestCase {
811
experiment: string;
912
percentExposure: number;
1013
variations: IVariation[];
1114
subjects: string[];
15+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
16+
subjectsWithAttributes: { subjectKey: string; subjectAttributes: Record<string, any> }[];
1217
expectedAssignments: string[];
1318
}
1419

20+
export function readMockRacResponse(): Record<string, IExperimentConfiguration> {
21+
return JSON.parse(fs.readFileSync(TEST_DATA_DIR + MOCK_RAC_RESPONSE_FILE, 'utf-8'));
22+
}
23+
1524
export function readAssignmentTestData(): IAssignmentTestCase[] {
16-
const testDataDir = './test/assignmentTestData/';
1725
const testCaseData: IAssignmentTestCase[] = [];
18-
const testCaseFiles = fs.readdirSync(testDataDir);
26+
const testCaseFiles = fs.readdirSync(ASSIGNMENT_TEST_DATA_DIR);
1927
testCaseFiles.forEach((file) => {
20-
const testCase = JSON.parse(fs.readFileSync(testDataDir + file, 'utf8'));
28+
const testCase = JSON.parse(fs.readFileSync(ASSIGNMENT_TEST_DATA_DIR + file, 'utf8'));
2129
testCaseData.push(testCase);
2230
});
2331
return testCaseData;

0 commit comments

Comments
 (0)