Skip to content

Commit 3ff5363

Browse files
DVC-6570 run all bucketing tests against variableForUser() and variableForUserPB() (#434)
* run bucketing tests against variableForUser() and variableForUserPB() * cleanup
1 parent 3f0d889 commit 3ff5363

File tree

4 files changed

+194
-31
lines changed

4 files changed

+194
-31
lines changed

lib/shared/bucketing-assembly-script/__tests__/bucketing/bucketing.test.ts

Lines changed: 64 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,12 @@ const { config, barrenConfig } = testData
1313

1414
import moment from 'moment'
1515
import * as uuid from 'uuid'
16-
import { BucketedUserConfig, SDKVariable } from '../../assembly/types'
16+
import {
17+
BucketedUserConfig,
18+
SDKVariable,
19+
} from '@devcycle/types'
1720
import { cleanupSDK, initSDK } from '../setPlatformData'
21+
import { variableForUserPB, VariableForUserArgs } from '../protobufVariableHelper'
1822

1923
type BoundedHash = { rolloutHash: number, bucketingHash: number }
2024

@@ -54,10 +58,25 @@ const generateBucketedConfig = (user: unknown): BucketedUserConfig => {
5458
return JSON.parse(bucketedConfig) as BucketedUserConfig
5559
}
5660

57-
const variableForUser = (
58-
{ user, variableKey, variableType }:
59-
{ user: unknown, variableKey: string, variableType: VariableType }
60-
): SDKVariable | null => {
61+
const expectVariableForUser = (
62+
args: { user: any, variableKey: string, variableType: VariableType },
63+
expectedValue: unknown
64+
) => {
65+
const variable = variableForUser({ ...args, sdkKey })
66+
const pbVariable = variableForUserPB({ ...args, sdkKey })
67+
68+
if (expectedValue === null) {
69+
expect(variable).toBeNull()
70+
expect(pbVariable).toBeNull()
71+
} else {
72+
expect(variable).not.toBeNull()
73+
expect(variable).toEqual(expectedValue)
74+
expect(pbVariable).not.toBeNull()
75+
expect(pbVariable).toEqual(expectedValue)
76+
}
77+
}
78+
79+
const variableForUser = ({ user, variableKey, variableType }: VariableForUserArgs): SDKVariable | null => {
6180
const variableJSON = variableForUser_AS(
6281
sdkKey, JSON.stringify(user), variableKey, variableType, true
6382
)
@@ -222,8 +241,10 @@ describe('Config Parsing and Generating', () => {
222241
const c = generateBucketedConfig(user)
223242
expect(c).toEqual(expected)
224243

225-
expect(variableForUser({ user, variableKey: 'swagTest', variableType: VariableType.String }))
226-
.toEqual(expected.variables.swagTest)
244+
expectVariableForUser(
245+
{ user, variableKey: 'swagTest', variableType: VariableType.String },
246+
expected.variables.swagTest
247+
)
227248
})
228249

229250
it('puts the user in the target for the first audience they match', () => {
@@ -372,16 +393,26 @@ describe('Config Parsing and Generating', () => {
372393
const c = generateBucketedConfig(user)
373394
expect(c).toEqual(expected)
374395

375-
expect(variableForUser({ user, variableKey: 'audience-match', variableType: VariableType.String }))
376-
.toEqual(expected.variables['audience-match'])
377-
expect(variableForUser({ user, variableKey: 'feature2.cool', variableType: VariableType.String }))
378-
.toEqual(expected.variables['feature2.cool'])
379-
expect(variableForUser({ user, variableKey: 'feature2.hello', variableType: VariableType.String }))
380-
.toEqual(expected.variables['feature2.hello'])
381-
expect(variableForUser({ user, variableKey: 'swagTest', variableType: VariableType.String }))
382-
.toEqual(expected.variables['swagTest'])
383-
expect(variableForUser({ user, variableKey: 'test', variableType: VariableType.String }))
384-
.toEqual(expected.variables['test'])
396+
expectVariableForUser(
397+
{ user, variableKey: 'audience-match', variableType: VariableType.String },
398+
expected.variables['audience-match']
399+
)
400+
expectVariableForUser(
401+
{ user, variableKey: 'feature2.cool', variableType: VariableType.String },
402+
expected.variables['feature2.cool']
403+
)
404+
expectVariableForUser(
405+
{ user, variableKey: 'feature2.hello', variableType: VariableType.String },
406+
expected.variables['feature2.hello']
407+
)
408+
expectVariableForUser(
409+
{ user, variableKey: 'swagTest', variableType: VariableType.String },
410+
expected.variables['swagTest']
411+
)
412+
expectVariableForUser(
413+
{ user, variableKey: 'test', variableType: VariableType.String },
414+
expected.variables['test']
415+
)
385416
})
386417

387418
it('holds user back if not in rollout', () => {
@@ -439,11 +470,14 @@ describe('Config Parsing and Generating', () => {
439470
}
440471
}
441472
initSDK(sdkKey, config)
473+
442474
const c = generateBucketedConfig(user)
443475
expect(c).toEqual(expected)
444476

445-
expect(variableForUser({ user, variableKey: 'feature2Var', variableType: VariableType.String }))
446-
.toEqual(expected.variables['feature2Var'])
477+
expectVariableForUser(
478+
{ user, variableKey: 'feature2Var', variableType: VariableType.String },
479+
expected.variables['feature2Var']
480+
)
447481
})
448482

449483
it('puts user through if in rollout', () => {
@@ -551,10 +585,14 @@ describe('Config Parsing and Generating', () => {
551585
const c = generateBucketedConfig(user)
552586
expect(c).toEqual(expected)
553587

554-
expect(variableForUser({ user, variableKey: 'swagTest', variableType: VariableType.String }))
555-
.toEqual(expected.variables['swagTest'])
556-
expect(variableForUser({ user, variableKey: 'feature2Var', variableType: VariableType.String }))
557-
.toEqual(expected.variables['feature2Var'])
588+
expectVariableForUser(
589+
{ user, variableKey: 'swagTest', variableType: VariableType.String },
590+
expected.variables['swagTest']
591+
)
592+
expectVariableForUser(
593+
{ user, variableKey: 'feature2Var', variableType: VariableType.String },
594+
expected.variables['feature2Var']
595+
)
558596
})
559597

560598
it('errors when feature missing distribution', () => {
@@ -567,8 +605,7 @@ describe('Config Parsing and Generating', () => {
567605
expect(() => generateBucketedConfig(user))
568606
.toThrow('Failed to decide target variation: 61536f3bc838a705c105eb62')
569607

570-
expect(variableForUser({ user, variableKey: 'feature2Var', variableType: VariableType.String }))
571-
.toBeNull()
608+
expectVariableForUser({ user, variableKey: 'feature2Var', variableType: VariableType.String }, null)
572609
})
573610

574611
it('errors when config missing variations', () => {
@@ -591,8 +628,7 @@ describe('Config Parsing and Generating', () => {
591628
expect(() => generateBucketedConfig(user))
592629
.toThrow('Config missing variation: 615382338424cb11646d7667')
593630

594-
expect(variableForUser({ user, variableKey: 'feature2Var', variableType: VariableType.String }))
595-
.toBeNull()
631+
expectVariableForUser({ user, variableKey: 'feature2Var', variableType: VariableType.String }, null)
596632
})
597633

598634
it('errors when config missing variables', () => {
@@ -605,8 +641,7 @@ describe('Config Parsing and Generating', () => {
605641
expect(() => generateBucketedConfig(user))
606642
.toThrow('Config missing variable: 61538237b0a70b58ae6af71g')
607643

608-
expect(variableForUser({ user, variableKey: 'feature2.cool', variableType: VariableType.String }))
609-
.toBeNull()
644+
expectVariableForUser({ user, variableKey: 'feature2.cool', variableType: VariableType.String }, null)
610645
})
611646
})
612647

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
import path from 'path'
2+
import protobuf from 'protobufjs'
3+
import { SDKVariable, VariableType as VariableTypeStr } from '@devcycle/types'
4+
import {
5+
variableForUser_PB, VariableType
6+
} from './bucketingImportHelper'
7+
8+
// TODO replace all this PB importing + types once we import the pb file directly into static JS Classes
9+
const protoFile = '../protobuf/variableForUserParams.proto'
10+
const filePath = path.resolve(__dirname, protoFile)
11+
const root = protobuf.loadSync(filePath)
12+
13+
const VariableForUserParams_PB = root.lookupType('VariableForUserParams_PB')
14+
if (!VariableForUserParams_PB) throw new Error('VariableForUserParams_PB not found')
15+
16+
const SDKVariable_PB = root.lookupType('SDKVariable_PB')
17+
if (!SDKVariable_PB) throw new Error('SDKVariable_PB not found')
18+
19+
type SDKVariable_PB_Type = {
20+
_id: string
21+
type: number
22+
key: string
23+
boolValue: boolean
24+
doubleValue: number
25+
stringValue: string
26+
}
27+
28+
const pbSDKVariableToJS = (pbSDKVariable: SDKVariable_PB_Type): SDKVariable => {
29+
if (pbSDKVariable.type === 0) {
30+
return {
31+
_id: pbSDKVariable._id,
32+
key: pbSDKVariable.key,
33+
value: pbSDKVariable.boolValue,
34+
type: VariableTypeStr.boolean
35+
}
36+
} else if (pbSDKVariable.type === 1) {
37+
return {
38+
_id: pbSDKVariable._id,
39+
key: pbSDKVariable.key,
40+
value: pbSDKVariable.doubleValue,
41+
type: VariableTypeStr.number
42+
}
43+
} else if (pbSDKVariable.type === 2) {
44+
return {
45+
_id: pbSDKVariable._id,
46+
key: pbSDKVariable.key,
47+
value: pbSDKVariable.stringValue,
48+
type: VariableTypeStr.string
49+
}
50+
} else if (pbSDKVariable.type === 3) {
51+
return {
52+
_id: pbSDKVariable._id,
53+
key: pbSDKVariable.key,
54+
value: JSON.parse(pbSDKVariable.stringValue),
55+
type: VariableTypeStr.json
56+
}
57+
}
58+
throw new Error(`Unknown variable type: ${pbSDKVariable.type}`)
59+
}
60+
61+
enum CustomDataTypePB {
62+
Bool,
63+
Num,
64+
Str,
65+
Null
66+
}
67+
68+
type CustomDataValuePB = {
69+
type: CustomDataTypePB,
70+
boolValue?: boolean,
71+
doubleValue?: number,
72+
stringValue?: string
73+
}
74+
75+
const customDataToPB = (customData: any): Record<string, CustomDataValuePB> | undefined => {
76+
if (!customData) return undefined
77+
78+
const customDataPB: Record<string, CustomDataValuePB> = {}
79+
for (const [key, value] of Object.entries(customData)) {
80+
if (typeof value === 'boolean') {
81+
customDataPB[key] = { type: CustomDataTypePB.Bool, boolValue: value }
82+
} else if (typeof value === 'number') {
83+
customDataPB[key] = { type: CustomDataTypePB.Num, doubleValue: value }
84+
} else if (typeof value === 'string') {
85+
customDataPB[key] = { type: CustomDataTypePB.Str, stringValue: value }
86+
} else if (value === null) {
87+
customDataPB[key] = { type: CustomDataTypePB.Null }
88+
} else {
89+
throw new Error(`Unknown custom data type: ${typeof value}`)
90+
}
91+
}
92+
return customDataPB
93+
}
94+
95+
export type VariableForUserArgs = { sdkKey: string, user: any, variableKey: string, variableType: VariableType }
96+
97+
export const variableForUserPB = (
98+
{ sdkKey, user, variableKey, variableType }: VariableForUserArgs
99+
): SDKVariable | null => {
100+
const customData = customDataToPB(user.customData)
101+
const privateCustomData = customDataToPB(user.privateCustomData)
102+
const params = {
103+
sdkKey,
104+
variableKey,
105+
variableType,
106+
user: {
107+
userId: user.user_id,
108+
email: user.email ? { value: user.email, isNull: false } : undefined,
109+
name: user.name ? { value: user.name, isNull: false } : undefined,
110+
language: user.language ? { value: user.language, isNull: false } : undefined,
111+
country: user.country ? { value: user.country, isNull: false } : undefined,
112+
appBuild: user.appBuild ? { value: user.appBuild, isNull: false } : undefined,
113+
appVersion: user.appVersion ? { value: user.appVersion, isNull: false } : undefined,
114+
deviceModel: user.deviceModel ? { value: user.deviceModel, isNull: false } : undefined,
115+
customData: customData ? { value: customData, isNull: false } : undefined,
116+
privateCustomData: privateCustomData ? { value: privateCustomData, isNull: false } : undefined
117+
},
118+
shouldTrackEvent: true
119+
}
120+
const err = VariableForUserParams_PB.verify(params)
121+
if (err) throw new Error(err)
122+
123+
const pbMsg = VariableForUserParams_PB.create(params)
124+
const buffer = VariableForUserParams_PB.encode(pbMsg).finish()
125+
const resultBuffer = variableForUser_PB(buffer)
126+
if (!resultBuffer) return null
127+
const pbSDKVariable = SDKVariable_PB.decode(resultBuffer!) as unknown as SDKVariable_PB_Type
128+
return pbSDKVariableToJS(pbSDKVariable)
129+
}

lib/shared/bucketing-assembly-script/__tests__/types/protobuf.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ describe('protobuf variable tests', () => {
8282
})
8383

8484
const varForUserParams = {
85-
sdkKey: sdkKey,
85+
sdkKey,
8686
variableKey: 'swagTest',
8787
variableType: 2,
8888
shouldTrackEvent: true,

lib/shared/bucketing-assembly-script/assembly/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ export function variableForUser_PB(protobuf: Uint8Array): Uint8Array | null {
7070
variableTypeFromPB(params.variableType),
7171
params.shouldTrackEvent
7272
)
73-
7473
return variable ? variable.toProtobuf() : null
7574
}
7675

0 commit comments

Comments
 (0)