|
| 1 | +import type { ClientOptions, UserContext } from '@growthbook/growthbook'; |
| 2 | +import { GrowthBookClient } from '@growthbook/growthbook'; |
1 | 3 | import { _INTERNAL_FLAG_BUFFER_SIZE as FLAG_BUFFER_SIZE } from '@sentry/core';
|
2 | 4 | import * as Sentry from '@sentry/node';
|
3 | 5 | import { loggingTransport } from '@sentry-internal/node-integration-tests';
|
4 | 6 |
|
5 |
| -// Minimal GrowthBook-like class that matches the real API for testing |
6 |
| -// This is necessary since we don't want to add @growthbook/growthbook as a dependency |
7 |
| -// just for integration tests, but we want to test the actual integration behavior |
8 |
| -class GrowthBookLike { |
9 |
| - private _features: Record<string, { value: unknown }> = {}; |
| 7 | +// Wrapper class to instantiate GrowthBookClient |
| 8 | +class GrowthBookWrapper { |
| 9 | + private _gbClient: GrowthBookClient; |
| 10 | + private _userContext: UserContext = { attributes: { id: 'test-user-123' } }; |
10 | 11 |
|
11 |
| - public isOn(featureKey: string): boolean { |
12 |
| - const feature = this._features[featureKey]; |
13 |
| - return feature ? !!feature.value : false; |
| 12 | + public constructor(..._args: unknown[]) { |
| 13 | + // Create GrowthBookClient with proper configuration |
| 14 | + const clientOptions: ClientOptions = { |
| 15 | + apiHost: 'https://cdn.growthbook.io', |
| 16 | + clientKey: 'sdk-abc123' |
| 17 | + }; |
| 18 | + this._gbClient = new GrowthBookClient(clientOptions); |
| 19 | + |
| 20 | + // Create features for testing |
| 21 | + const features = this._createTestFeatures(); |
| 22 | + |
| 23 | + this._gbClient.initSync({ |
| 24 | + payload: { features } |
| 25 | + }); |
14 | 26 | }
|
15 | 27 |
|
16 |
| - public getFeatureValue(featureKey: string, defaultValue: unknown): unknown { |
17 |
| - const feature = this._features[featureKey]; |
18 |
| - return feature ? feature.value : defaultValue; |
| 28 | + public isOn(featureKey: string, ..._rest: unknown[]): boolean { |
| 29 | + return this._gbClient.isOn(featureKey, this._userContext); |
19 | 30 | }
|
20 | 31 |
|
21 |
| - // Helper method to set feature values for testing |
22 |
| - public setFeature(featureKey: string, value: unknown): void { |
23 |
| - this._features[featureKey] = { value }; |
| 32 | + public getFeatureValue(featureKey: string, defaultValue: unknown, ..._rest: unknown[]): unknown { |
| 33 | + return this._gbClient.getFeatureValue(featureKey, defaultValue as boolean | string | number, this._userContext); |
| 34 | + } |
| 35 | + |
| 36 | + private _createTestFeatures(): Record<string, { defaultValue: unknown }> { |
| 37 | + const features: Record<string, { defaultValue: unknown }> = {}; |
| 38 | + |
| 39 | + // Fill buffer with flags 1-100 (all false by default) |
| 40 | + for (let i = 1; i <= FLAG_BUFFER_SIZE; i++) { |
| 41 | + features[`feat${i}`] = { defaultValue: false }; |
| 42 | + } |
| 43 | + |
| 44 | + // Add feat101 (true), which should evict feat1 |
| 45 | + features[`feat${FLAG_BUFFER_SIZE + 1}`] = { defaultValue: true }; |
| 46 | + |
| 47 | + // Update feat3 to true, which should move it to the end |
| 48 | + features['feat3'] = { defaultValue: true }; |
| 49 | + |
| 50 | + // Test features with boolean values (should be captured) |
| 51 | + features['bool-feat'] = { defaultValue: true }; |
| 52 | + |
| 53 | + // Test features with non-boolean values (should NOT be captured) |
| 54 | + features['string-feat'] = { defaultValue: 'hello' }; |
| 55 | + features['number-feat'] = { defaultValue: 42 }; |
| 56 | + |
| 57 | + return features; |
24 | 58 | }
|
25 | 59 | }
|
26 | 60 |
|
27 | 61 | Sentry.init({
|
28 | 62 | dsn: 'https://[email protected]/1337',
|
29 | 63 | sampleRate: 1.0,
|
30 | 64 | transport: loggingTransport,
|
31 |
| - integrations: [Sentry.growthbookIntegration({ growthbookClass: GrowthBookLike })], |
| 65 | + integrations: [Sentry.growthbookIntegration({ growthbookClass: GrowthBookWrapper })], |
32 | 66 | });
|
33 | 67 |
|
34 |
| -const gb = new GrowthBookLike(); |
| 68 | +// Create GrowthBookWrapper instance |
| 69 | +const gb = new GrowthBookWrapper(); |
35 | 70 |
|
36 | 71 | // Fill buffer with flags 1-100 (all false by default)
|
37 | 72 | for (let i = 1; i <= FLAG_BUFFER_SIZE; i++) {
|
38 | 73 | gb.isOn(`feat${i}`);
|
39 | 74 | }
|
40 | 75 |
|
41 | 76 | // Add feat101 (true), which should evict feat1
|
42 |
| -gb.setFeature(`feat${FLAG_BUFFER_SIZE + 1}`, true); |
43 | 77 | gb.isOn(`feat${FLAG_BUFFER_SIZE + 1}`);
|
44 | 78 |
|
45 | 79 | // Update feat3 to true, which should move it to the end
|
46 |
| -gb.setFeature('feat3', true); |
47 | 80 | gb.isOn('feat3');
|
48 | 81 |
|
49 | 82 | // Test getFeatureValue with boolean values (should be captured)
|
50 |
| -gb.setFeature('bool-feat', true); |
51 | 83 | gb.getFeatureValue('bool-feat', false);
|
52 | 84 |
|
53 | 85 | // Test getFeatureValue with non-boolean values (should NOT be captured)
|
54 |
| -gb.setFeature('string-feat', 'hello'); |
55 | 86 | gb.getFeatureValue('string-feat', 'default');
|
56 |
| -gb.setFeature('number-feat', 42); |
57 | 87 | gb.getFeatureValue('number-feat', 0);
|
58 | 88 |
|
59 | 89 | throw new Error('Test error');
|
0 commit comments