Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions packages/node/src/exposure/exposure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,20 @@ export class Exposure {
}

public canonicalize(): string {
let canonical = `${this.user.user_id?.trim()} ${this.user.device_id?.trim()} `;
const userId =
this.user.user_id != null
? String(this.user.user_id).trim()
: this.user.user_id;
const deviceId =
this.user.device_id != null
? String(this.user.device_id).trim()
: this.user.device_id;
let canonical = `${userId} ${deviceId} `;
for (const key of Object.keys(this.results).sort()) {
const variant = this.results[key];
if (variant?.key) {
canonical += key.trim() + ' ' + variant?.key?.trim() + ' ';
const variantKey = String(variant.key).trim();
canonical += key.trim() + ' ' + variantKey + ' ';
}
}
return canonical;
Expand Down
26 changes: 26 additions & 0 deletions packages/node/test/local/exposure/exposure-filter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,29 @@ test('filter - ttl-based eviction', async () => {
await sleep(950);
expect(filter.shouldTrack(exposure2)).toEqual(false);
});

test('filter - non-string user_id should not throw', async () => {
// Simulate runtime scenario where user_id is a number (e.g., from untyped JS or JSON)
const user = { user_id: 12345 } as unknown as ExperimentUser;
const results = {
'flag-key-1': { key: 'on', value: 'on' },
};
const filter = new InMemoryExposureFilter(100);
const exposure = new Exposure(user, results);
// Should not throw TypeError: trim is not a function
expect(() => filter.shouldTrack(exposure)).not.toThrow();
expect(filter.shouldTrack(exposure)).toEqual(false); // Already tracked
});

test('filter - non-string device_id should not throw', async () => {
// Simulate runtime scenario where device_id is a number
const user = { device_id: 67890 } as unknown as ExperimentUser;
const results = {
'flag-key-1': { key: 'on', value: 'on' },
};
const filter = new InMemoryExposureFilter(100);
const exposure = new Exposure(user, results);
// Should not throw TypeError: trim is not a function
expect(() => filter.shouldTrack(exposure)).not.toThrow();
expect(filter.shouldTrack(exposure)).toEqual(false); // Already tracked
});