diff --git a/packages/flag-evaluation/src/index.ts b/packages/flag-evaluation/src/index.ts index 4d57de9e9..a7374a3e7 100644 --- a/packages/flag-evaluation/src/index.ts +++ b/packages/flag-evaluation/src/index.ts @@ -131,7 +131,7 @@ export interface ContextFilter { * - `type` - Indicates the type of the filter. For this filter type, it will always be "rolloutPercentage". * - `key` - A unique key or identifier that distinguishes this rollout filter. * - `partialRolloutAttribute` - Specifies the attribute used to evaluate eligibility for the rollout. - * - `partialRolloutThreshold` - A numeric value representing the upper-bound threshold (0-100) for the percentage-based rollout. + * - `partialRolloutThreshold` - A numeric value representing the upper-bound threshold (0-100000) for the percentage-based rollout. */ export type PercentageRolloutFilter = { type: "rolloutPercentage"; diff --git a/packages/flag-evaluation/test/index.test.ts b/packages/flag-evaluation/test/index.test.ts index 4e179d32b..7f34835c7 100644 --- a/packages/flag-evaluation/test/index.test.ts +++ b/packages/flag-evaluation/test/index.test.ts @@ -198,6 +198,36 @@ describe("evaluate flag targeting integration ", () => { }); }); + it("evaluates percentage rollouts using user.id", () => { + const userRollout = { + flagKey: "test-flag", + rules: [ + { + value: true, + filter: { + type: "rolloutPercentage" as const, + key: "test-flag", + partialRolloutAttribute: "user.id", + partialRolloutThreshold: 50000, + }, + }, + ], + }; + + expect( + evaluateFlagRules({ + ...userRollout, + context: { user: { id: "user-1" } }, + }).value, + ).toBe(true); + expect( + evaluateFlagRules({ + ...userRollout, + context: { user: { id: "user-2" } }, + }).value, + ).toBeUndefined(); + }); + it("returns list of missing context keys ", async () => { const res = evaluateFlagRules({ ...flag, diff --git a/packages/node-sdk/test/client.test.ts b/packages/node-sdk/test/client.test.ts index a0ea0edbe..0f592eba4 100644 --- a/packages/node-sdk/test/client.test.ts +++ b/packages/node-sdk/test/client.test.ts @@ -1568,6 +1568,59 @@ describe("ReflagClient", () => { }); }); + it("evaluates percentage rollouts using user.id", async () => { + const userRolloutDefinitions: FlagsAPIResponse = { + flagStateVersion: 2, + features: [ + { + key: "test-flag", + description: "User rollout", + targeting: { + version: 1, + rules: [ + { + filter: { + type: "rolloutPercentage", + key: "test-flag", + partialRolloutAttribute: "user.id", + partialRolloutThreshold: 50000, + }, + }, + ], + }, + }, + ], + }; + httpClient.get.mockResolvedValue({ + ok: true, + status: 200, + body: { success: true, ...userRolloutDefinitions }, + }); + + await client.initialize(); + + expect( + client.getFlag( + { + user: { id: "user-1" }, + company, + enableTracking: false, + }, + "test-flag", + ).isEnabled, + ).toBe(true); + expect( + client.getFlag( + { + user: { id: "user-2" }, + company, + enableTracking: false, + }, + "test-flag", + ).isEnabled, + ).toBe(false); + }); + it("`track` sends all expected events when `enableTracking` is `true`", async () => { const context = { company,