Skip to content

Commit c3b39f9

Browse files
Audience membership core amplitude fbcustomaudience (#3670)
* getting started on audience membership * adding retlMembership * updating payload objects with audience_membership * moving logic * moving membership out of payload * update * updates * update * migrated amplitude cohorts * type fix * updating tests * adding flag * fixing tests for amplitude cohorts * fixing test authentication * fixing amplitude cohort test * adding flags to core * adding google enhanced conversions * adding facebook custom audiences * errors and flags update * braze cohorts audienceMembership migration * braze cohorts core complete apart from tests * adding tests for flags = true * removing snapshot * adding snapshot * upating snapshot * linkedin audiences using audienceMembership from Core * removing core flag * reverting google ec braze cohorts and linkedin audiences * removing unnecessary check * fixing ampitude cohort test * reverting changes to facebook custom audiences
1 parent 64a8fb8 commit c3b39f9

File tree

15 files changed

+583
-326
lines changed

15 files changed

+583
-326
lines changed
Lines changed: 346 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,346 @@
1+
import { createTestIntegration } from '../create-test-integration'
2+
import { engageAudienceMembership, retlAudienceMembership } from '../audience-membership'
3+
import { DestinationDefinition } from '../destination-kit'
4+
import { ExecuteInput } from '../destination-kit/types'
5+
import { JSONObject } from '../json-object'
6+
7+
describe('engageAudienceMembership', () => {
8+
describe('identify events', () => {
9+
it('returns true when type is identify and traits[computation_key] is true', () => {
10+
expect(
11+
engageAudienceMembership({
12+
type: 'identify',
13+
context: { personas: { computation_class: 'audience', computation_key: 'my_audience' } },
14+
traits: { my_audience: true }
15+
})
16+
).toBe(true)
17+
})
18+
19+
it('returns false when type is identify and traits[computation_key] is false', () => {
20+
expect(
21+
engageAudienceMembership({
22+
type: 'identify',
23+
context: { personas: { computation_class: 'audience', computation_key: 'my_audience' } },
24+
traits: { my_audience: false }
25+
})
26+
).toBe(false)
27+
})
28+
29+
it('returns undefined when type is identify but computation_class is missing', () => {
30+
expect(
31+
engageAudienceMembership({
32+
type: 'identify',
33+
context: { personas: { computation_key: 'my_audience' } },
34+
traits: { my_audience: true }
35+
})
36+
).toBeUndefined()
37+
})
38+
39+
it('returns undefined when type is identify but computation_class is not audience or journey_step', () => {
40+
expect(
41+
engageAudienceMembership({
42+
type: 'identify',
43+
context: { personas: { computation_class: 'computed_trait', computation_key: 'my_audience' } },
44+
traits: { my_audience: true }
45+
})
46+
).toBeUndefined()
47+
})
48+
49+
it('returns undefined when type is identify but computation_key is missing', () => {
50+
expect(
51+
engageAudienceMembership({
52+
type: 'identify',
53+
context: { personas: { computation_class: 'audience' } },
54+
traits: { my_audience: true }
55+
})
56+
).toBeUndefined()
57+
})
58+
59+
it('returns undefined when type is identify but traits[computation_key] is absent', () => {
60+
expect(
61+
engageAudienceMembership({
62+
type: 'identify',
63+
context: { personas: { computation_class: 'audience', computation_key: 'my_audience' } },
64+
traits: {}
65+
})
66+
).toBeUndefined()
67+
})
68+
69+
it('returns undefined when type is identify but traits[computation_key] is not a boolean', () => {
70+
expect(
71+
engageAudienceMembership({
72+
type: 'identify',
73+
context: { personas: { computation_class: 'audience', computation_key: 'my_audience' } },
74+
traits: { my_audience: 'true' }
75+
})
76+
).toBeUndefined()
77+
})
78+
})
79+
80+
describe('track events', () => {
81+
it('returns true when type is track and properties[computation_key] is true', () => {
82+
expect(
83+
engageAudienceMembership({
84+
type: 'track',
85+
context: { personas: { computation_class: 'audience', computation_key: 'my_audience' } },
86+
properties: { my_audience: true }
87+
})
88+
).toBe(true)
89+
})
90+
91+
it('returns false when type is track and properties[computation_key] is false', () => {
92+
expect(
93+
engageAudienceMembership({
94+
type: 'track',
95+
context: { personas: { computation_class: 'audience', computation_key: 'my_audience' } },
96+
properties: { my_audience: false }
97+
})
98+
).toBe(false)
99+
})
100+
101+
it('returns undefined when type is track but computation_class is missing', () => {
102+
expect(
103+
engageAudienceMembership({
104+
type: 'track',
105+
context: { personas: { computation_key: 'my_audience' } },
106+
properties: { my_audience: true }
107+
})
108+
).toBeUndefined()
109+
})
110+
111+
it('returns undefined when type is track but computation_class is not audience or journey_step', () => {
112+
expect(
113+
engageAudienceMembership({
114+
type: 'track',
115+
context: { personas: { computation_class: 'computed_trait', computation_key: 'my_audience' } },
116+
properties: { my_audience: true }
117+
})
118+
).toBeUndefined()
119+
})
120+
121+
it('returns undefined when type is track but computation_key is missing', () => {
122+
expect(
123+
engageAudienceMembership({
124+
type: 'track',
125+
context: { personas: { computation_class: 'audience' } },
126+
properties: { my_audience: true }
127+
})
128+
).toBeUndefined()
129+
})
130+
131+
it('returns undefined when type is track but properties[computation_key] is absent', () => {
132+
expect(
133+
engageAudienceMembership({
134+
type: 'track',
135+
context: { personas: { computation_class: 'audience', computation_key: 'my_audience' } },
136+
properties: {}
137+
})
138+
).toBeUndefined()
139+
})
140+
141+
it('returns undefined when type is track but properties[computation_key] is not a boolean', () => {
142+
expect(
143+
engageAudienceMembership({
144+
type: 'track',
145+
context: { personas: { computation_class: 'audience', computation_key: 'my_audience' } },
146+
properties: { my_audience: 'true' }
147+
})
148+
).toBeUndefined()
149+
})
150+
})
151+
152+
describe('other', () => {
153+
it('returns true when computation_class is journey_step (identify)', () => {
154+
expect(
155+
engageAudienceMembership({
156+
type: 'identify',
157+
context: { personas: { computation_class: 'journey_step', computation_key: 'my_step' } },
158+
traits: { my_step: true }
159+
})
160+
).toBe(true)
161+
})
162+
163+
it('returns undefined when rawData is undefined', () => {
164+
expect(engageAudienceMembership(undefined)).toBeUndefined()
165+
})
166+
})
167+
})
168+
169+
describe('retlAudienceMembership', () => {
170+
describe('returns true (added to audience)', () => {
171+
it('returns true when syncMode is add and event is new', () => {
172+
expect(retlAudienceMembership({ type: 'track', event: 'new' }, 'add')).toBe(true)
173+
})
174+
175+
it('returns true when syncMode is update and event is updated', () => {
176+
expect(retlAudienceMembership({ type: 'track', event: 'updated' }, 'update')).toBe(true)
177+
})
178+
179+
it('returns true when syncMode is upsert and event is new', () => {
180+
expect(retlAudienceMembership({ type: 'track', event: 'new' }, 'upsert')).toBe(true)
181+
})
182+
183+
it('returns true when syncMode is upsert and event is updated', () => {
184+
expect(retlAudienceMembership({ type: 'track', event: 'updated' }, 'upsert')).toBe(true)
185+
})
186+
187+
it('returns true when syncMode is mirror and event is new', () => {
188+
expect(retlAudienceMembership({ type: 'track', event: 'new' }, 'mirror')).toBe(true)
189+
})
190+
191+
it('returns true when syncMode is mirror and event is updated', () => {
192+
expect(retlAudienceMembership({ type: 'track', event: 'updated' }, 'mirror')).toBe(true)
193+
})
194+
})
195+
196+
describe('returns false (removed from audience)', () => {
197+
it('returns false when syncMode is delete and event is deleted', () => {
198+
expect(retlAudienceMembership({ type: 'track', event: 'deleted' }, 'delete')).toBe(false)
199+
})
200+
201+
it('returns false when syncMode is mirror and event is deleted', () => {
202+
expect(retlAudienceMembership({ type: 'track', event: 'deleted' }, 'mirror')).toBe(false)
203+
})
204+
})
205+
206+
describe('failure cases', () => {
207+
it('returns undefined when rawData is undefined', () => {
208+
expect(retlAudienceMembership(undefined, 'add')).toBeUndefined()
209+
})
210+
211+
it('returns undefined when syncMode is missing', () => {
212+
expect(retlAudienceMembership({ type: 'track', event: 'new' })).toBeUndefined()
213+
})
214+
215+
it('returns undefined when event type is not track', () => {
216+
expect(retlAudienceMembership({ type: 'identify', event: 'new' }, 'add')).toBeUndefined()
217+
})
218+
219+
it('returns undefined when syncMode is add but event is not new', () => {
220+
expect(retlAudienceMembership({ type: 'track', event: 'updated' }, 'add')).toBeUndefined()
221+
})
222+
223+
it('returns undefined when syncMode is delete but event is not deleted', () => {
224+
expect(retlAudienceMembership({ type: 'track', event: 'new' }, 'delete')).toBeUndefined()
225+
})
226+
})
227+
})
228+
229+
function makeDestination(
230+
captureRef: { data?: ExecuteInput<JSONObject, JSONObject> }
231+
): DestinationDefinition<JSONObject> {
232+
return {
233+
name: 'Test Destination',
234+
mode: 'cloud',
235+
authentication: { scheme: 'custom', fields: {} },
236+
actions: {
237+
testAction: {
238+
title: 'Test Action',
239+
description: 'Test',
240+
fields: {
241+
userId: { label: 'User ID', description: 'The user ID', type: 'string' }
242+
},
243+
syncMode: {
244+
default: 'add',
245+
label: 'Sync Mode',
246+
description: 'The sync mode',
247+
choices: [
248+
{ label: 'Add', value: 'add' },
249+
{ label: 'Update', value: 'update' },
250+
{ label: 'Upsert', value: 'upsert' },
251+
{ label: 'Delete', value: 'delete' },
252+
{ label: 'Mirror', value: 'mirror' }
253+
]
254+
},
255+
perform: (_request, data) => {
256+
captureRef.data = data as ExecuteInput<JSONObject, JSONObject>
257+
}
258+
}
259+
}
260+
}
261+
}
262+
263+
async function runAction(
264+
event: object,
265+
mapping: JSONObject = { userId: { '@path': '$.userId' } },
266+
features: Record<string, boolean> = {}
267+
): Promise<ExecuteInput<JSONObject, JSONObject> | undefined> {
268+
const captureRef: { data?: ExecuteInput<JSONObject, JSONObject> } = {}
269+
const testDestination = createTestIntegration(makeDestination(captureRef))
270+
await testDestination.testAction('testAction', { mapping, event, features })
271+
return captureRef.data
272+
}
273+
274+
describe('audienceMembership on ExecuteInput in perform()', () => {
275+
276+
describe('Engage payloads', () => {
277+
it('is true for identify event with membership in traits', async () => {
278+
const data = await runAction({
279+
type: 'identify',
280+
userId: 'user-1',
281+
context: { personas: { computation_class: 'audience', computation_key: 'my_audience' } },
282+
traits: { my_audience: true }
283+
}, undefined)
284+
expect(data?.audienceMembership).toBe(true)
285+
})
286+
287+
it('is false for identify event with membership in traits', async () => {
288+
const data = await runAction({
289+
type: 'identify',
290+
userId: 'user-1',
291+
context: { personas: { computation_class: 'audience', computation_key: 'my_audience' } },
292+
traits: { my_audience: false }
293+
}, undefined)
294+
expect(data?.audienceMembership).toBe(false)
295+
})
296+
297+
it('is true for track event with membership in properties', async () => {
298+
const data = await runAction({
299+
type: 'track',
300+
userId: 'user-1',
301+
context: { personas: { computation_class: 'audience', computation_key: 'my_audience' } },
302+
properties: { my_audience: true }
303+
}, undefined)
304+
expect(data?.audienceMembership).toBe(true)
305+
})
306+
307+
it('is false for track event with membership in properties', async () => {
308+
const data = await runAction({
309+
type: 'track',
310+
userId: 'user-1',
311+
context: { personas: { computation_class: 'audience', computation_key: 'my_audience' } },
312+
properties: { my_audience: false }
313+
}, undefined)
314+
expect(data?.audienceMembership).toBe(false)
315+
})
316+
})
317+
318+
describe('RETL payloads', () => {
319+
it('is true for track event with syncMode add and event name new', async () => {
320+
const data = await runAction(
321+
{ type: 'track', userId: 'user-1', event: 'new' },
322+
{ userId: { '@path': '$.userId' }, __segment_internal_sync_mode: 'add' }
323+
)
324+
expect(data?.audienceMembership).toBe(true)
325+
})
326+
327+
it('is false for track event with syncMode delete and event name deleted', async () => {
328+
const data = await runAction(
329+
{ type: 'track', userId: 'user-1', event: 'deleted' },
330+
{ userId: { '@path': '$.userId' }, __segment_internal_sync_mode: 'delete' }
331+
)
332+
expect(data?.audienceMembership).toBe(false)
333+
})
334+
})
335+
336+
describe('non-audience events', () => {
337+
it('is undefined for a non-audience event', async () => {
338+
const data = await runAction({
339+
type: 'track',
340+
userId: 'user-1',
341+
properties: { foo: 'bar' }
342+
}, undefined)
343+
expect(data?.audienceMembership).toBeUndefined()
344+
})
345+
})
346+
})

0 commit comments

Comments
 (0)