Skip to content
Merged
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
12 changes: 9 additions & 3 deletions sdk/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@ import { FlagsmithAPIError, FlagsmithClientError } from './errors.js';

import { DefaultFlag, Flags } from './models.js';
import { EnvironmentDataPollingManager } from './polling_manager.js';
import { Deferred, generateIdentitiesData, getUserAgent, retryFetch } from './utils.js';
import {
Deferred,
generateIdentitiesData,
getUserAgent,
isTraitConfig,
retryFetch
} from './utils.js';
import {
SegmentModel,
EnvironmentModel,
Expand Down Expand Up @@ -275,7 +281,7 @@ export class Flagsmith {
identifier,
Object.keys(traits || {}).map(key => ({
key,
value: traits?.[key]
value: isTraitConfig(traits?.[key]) ? traits![key].value : traits?.[key]
}))
);

Expand Down Expand Up @@ -474,7 +480,7 @@ export class Flagsmith {
identifier,
Object.keys(traits).map(key => ({
key,
value: traits[key]
value: isTraitConfig(traits[key]) ? traits[key].value : traits[key]
}))
);

Expand Down
70 changes: 70 additions & 0 deletions tests/sdk/flagsmith-identity-flags.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,76 @@ test('test_identity_with_transient_traits', async () => {
expect(identityFlags[0].featureName).toBe('some_feature');
});

test('getIdentityFlags local evaluation with plain traits matches segment', async () => {
const identifier = 'identifier';
// Plain trait format: age=30 should match segment rule "age LESS_THAN 40"
const traits = { age: 30 };

const flg = flagsmith({
environmentKey: 'ser.key',
enableLocalEvaluation: true
});

const flags = await flg.getIdentityFlags(identifier, traits);

// Should get segment override value, not the default
expect(flags.getFeatureValue('some_feature')).toBe('segment_override');
expect(flags.isFeatureEnabled('some_feature')).toBe(false);
});

test('getIdentityFlags local evaluation with TraitConfig format matches segment', async () => {
const identifier = 'identifier';
// TraitConfig format: same trait value wrapped with transient metadata
const traits = { age: { value: 30, transient: true } };

const flg = flagsmith({
environmentKey: 'ser.key',
enableLocalEvaluation: true
});

const flags = await flg.getIdentityFlags(identifier, traits);

// Should get segment override value — same result as plain trait format
expect(flags.getFeatureValue('some_feature')).toBe('segment_override');
expect(flags.isFeatureEnabled('some_feature')).toBe(false);
});

test('getIdentityFlags local evaluation with mixed trait formats matches segment', async () => {
const identifier = 'identifier';
// Mix of plain and TraitConfig formats
const traits = {
age: { value: 30, transient: true },
some_other_trait: 'plain_value'
};

const flg = flagsmith({
environmentKey: 'ser.key',
enableLocalEvaluation: true
});

const flags = await flg.getIdentityFlags(identifier, traits);

// Should get segment override value
expect(flags.getFeatureValue('some_feature')).toBe('segment_override');
expect(flags.isFeatureEnabled('some_feature')).toBe(false);
});

test('getIdentitySegments with TraitConfig format matches segment', async () => {
const identifier = 'identifier';
// TraitConfig format should work for getIdentitySegments too
const traits = { age: { value: 30, transient: true } };

const flg = flagsmith({
environmentKey: 'ser.key',
enableLocalEvaluation: true
});

const segments = await flg.getIdentitySegments(identifier, traits);

expect(segments).toHaveLength(1);
expect(segments[0].name).toBe('regular_segment');
});

test('getIdentityFlags fails if API call failed and no default flag handler was provided', async () => {
const flg = flagsmith({
fetch: badFetch
Expand Down
Loading