Skip to content

Commit f2ac5d6

Browse files
Fix tests - add type validations
1 parent d3c8665 commit f2ac5d6

File tree

5 files changed

+65
-110
lines changed

5 files changed

+65
-110
lines changed

src/evaluator/fallbackTreatmentsCalculator/__tests__/fallback-calculator.spec.ts

Lines changed: 3 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -6,94 +6,13 @@ import { CONTROL } from '../../../utils/constants';
66
describe('FallbackTreatmentsCalculator' , () => {
77
const longName = 'a'.repeat(101);
88

9-
test('logs an error if flag name is invalid - by Flag', () => {
10-
let config: FallbackTreatmentConfiguration = {
11-
byFlag: {
12-
'feature A': { treatment: 'TREATMENT_A', config: '{ value: 1 }' },
13-
},
14-
};
15-
new FallbackTreatmentsCalculator(loggerMock, config);
16-
expect(loggerMock.error.mock.calls[0][0]).toBe(
17-
'Fallback treatments - Discarded flag \'feature A\': Invalid flag name (max 100 chars, no spaces)'
18-
);
19-
config = {
20-
byFlag: {
21-
[longName]: { treatment: 'TREATMENT_A', config: '{ value: 1 }' },
22-
},
23-
};
24-
new FallbackTreatmentsCalculator(loggerMock, config);
25-
expect(loggerMock.error.mock.calls[1][0]).toBe(
26-
`Fallback treatments - Discarded flag '${longName}': Invalid flag name (max 100 chars, no spaces)`
27-
);
28-
29-
config = {
30-
byFlag: {
31-
'featureB': { treatment: longName, config: '{ value: 1 }' },
32-
},
33-
};
34-
new FallbackTreatmentsCalculator(loggerMock, config);
35-
expect(loggerMock.error.mock.calls[2][0]).toBe(
36-
'Fallback treatments - Discarded treatment for flag \'featureB\': Invalid treatment (max 100 chars and must match pattern)'
37-
);
38-
39-
config = {
40-
byFlag: {
41-
// @ts-ignore
42-
'featureC': { config: '{ global: true }' },
43-
},
44-
};
45-
new FallbackTreatmentsCalculator(loggerMock, config);
46-
expect(loggerMock.error.mock.calls[3][0]).toBe(
47-
'Fallback treatments - Discarded treatment for flag \'featureC\': Invalid treatment (max 100 chars and must match pattern)'
48-
);
49-
50-
config = {
51-
byFlag: {
52-
// @ts-ignore
53-
'featureC': { treatment: 'invalid treatment!', config: '{ global: true }' },
54-
},
55-
};
56-
new FallbackTreatmentsCalculator(loggerMock, config);
57-
expect(loggerMock.error.mock.calls[4][0]).toBe(
58-
'Fallback treatments - Discarded treatment for flag \'featureC\': Invalid treatment (max 100 chars and must match pattern)'
59-
);
60-
});
61-
62-
test('logs an error if flag name is invalid - global', () => {
63-
let config: FallbackTreatmentConfiguration = {
64-
global: { treatment: longName, config: '{ value: 1 }' },
65-
};
66-
new FallbackTreatmentsCalculator(loggerMock, config);
67-
expect(loggerMock.error.mock.calls[2][0]).toBe(
68-
'Fallback treatments - Discarded treatment for flag \'featureB\': Invalid treatment (max 100 chars and must match pattern)'
69-
);
70-
71-
config = {
72-
// @ts-ignore
73-
global: { config: '{ global: true }' },
74-
};
75-
new FallbackTreatmentsCalculator(loggerMock, config);
76-
expect(loggerMock.error.mock.calls[3][0]).toBe(
77-
'Fallback treatments - Discarded treatment for flag \'featureC\': Invalid treatment (max 100 chars and must match pattern)'
78-
);
79-
80-
config = {
81-
// @ts-ignore
82-
global: { treatment: 'invalid treatment!', config: '{ global: true }' },
83-
};
84-
new FallbackTreatmentsCalculator(loggerMock, config);
85-
expect(loggerMock.error.mock.calls[4][0]).toBe(
86-
'Fallback treatments - Discarded treatment for flag \'featureC\': Invalid treatment (max 100 chars and must match pattern)'
87-
);
88-
});
89-
909
test('returns specific fallback if flag exists', () => {
9110
const config: FallbackTreatmentConfiguration = {
9211
byFlag: {
9312
'featureA': { treatment: 'TREATMENT_A', config: '{ value: 1 }' },
9413
},
9514
};
96-
const calculator = new FallbackTreatmentsCalculator(loggerMock, config);
15+
const calculator = new FallbackTreatmentsCalculator(config);
9716
const result = calculator.resolve('featureA', 'label by flag');
9817

9918
expect(result).toEqual({
@@ -108,7 +27,7 @@ describe('FallbackTreatmentsCalculator' , () => {
10827
byFlag: {},
10928
global: { treatment: 'GLOBAL_TREATMENT', config: '{ global: true }' },
11029
};
111-
const calculator = new FallbackTreatmentsCalculator(loggerMock, config);
30+
const calculator = new FallbackTreatmentsCalculator(config);
11231
const result = calculator.resolve('missingFlag', 'label by global');
11332

11433
expect(result).toEqual({
@@ -122,7 +41,7 @@ describe('FallbackTreatmentsCalculator' , () => {
12241
const config: FallbackTreatmentConfiguration = {
12342
byFlag: {},
12443
};
125-
const calculator = new FallbackTreatmentsCalculator(loggerMock, config);
44+
const calculator = new FallbackTreatmentsCalculator(config);
12645
const result = calculator.resolve('missingFlag', 'label by noFallback');
12746

12847
expect(result).toEqual({
Lines changed: 55 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1-
import { FallbacksSanitizer } from '../fallbackSanitizer';
1+
import { isValidFlagName, isValidTreatment, sanitizeFallbacks } from '../fallbackSanitizer';
22
import { TreatmentWithConfig } from '../../../../types/splitio';
33
import { loggerMock } from '../../../logger/__tests__/sdkLogger.mock';
44

55
describe('FallbacksSanitizer', () => {
66
const validTreatment: TreatmentWithConfig = { treatment: 'on', config: '{"color":"blue"}' };
77
const invalidTreatment: TreatmentWithConfig = { treatment: ' ', config: null };
8+
const fallbackMock = {
9+
global: undefined,
10+
byFlag: {}
11+
};
812

913
beforeEach(() => {
1014
jest.spyOn(console, 'error').mockImplementation(() => {});
@@ -17,49 +21,54 @@ describe('FallbacksSanitizer', () => {
1721
describe('isValidFlagName', () => {
1822
test('returns true for a valid flag name', () => {
1923
// @ts-expect-private-access
20-
expect((FallbacksSanitizer as any).isValidFlagName('my_flag')).toBe(true);
24+
expect(isValidFlagName('my_flag')).toBe(true);
2125
});
2226

2327
test('returns false for a name longer than 100 chars', () => {
2428
const longName = 'a'.repeat(101);
25-
expect((FallbacksSanitizer as any).isValidFlagName(longName)).toBe(false);
29+
expect(isValidFlagName(longName)).toBe(false);
2630
});
2731

2832
test('returns false if the name contains spaces', () => {
29-
expect((FallbacksSanitizer as any).isValidFlagName('invalid flag')).toBe(false);
33+
expect(isValidFlagName('invalid flag')).toBe(false);
34+
});
35+
36+
test('returns false if the name contains spaces', () => {
37+
// @ts-ignore
38+
expect(isValidFlagName(true)).toBe(false);
3039
});
3140
});
3241

3342
describe('isValidTreatment', () => {
3443
test('returns true for a valid treatment string', () => {
35-
expect((FallbacksSanitizer as any).isValidTreatment(validTreatment)).toBe(true);
44+
expect(isValidTreatment(validTreatment)).toBe(true);
3645
});
3746

3847
test('returns false for null or undefined', () => {
39-
expect((FallbacksSanitizer as any).isValidTreatment(null)).toBe(false);
40-
expect((FallbacksSanitizer as any).isValidTreatment(undefined)).toBe(false);
48+
expect(isValidTreatment()).toBe(false);
49+
expect(isValidTreatment(undefined)).toBe(false);
4150
});
4251

4352
test('returns false for a treatment longer than 100 chars', () => {
44-
const long = { treatment: 'a'.repeat(101) };
45-
expect((FallbacksSanitizer as any).isValidTreatment(long)).toBe(false);
53+
const long = { treatment: 'a'.repeat(101), config: null };
54+
expect(isValidTreatment(long)).toBe(false);
4655
});
4756

4857
test('returns false if treatment does not match regex pattern', () => {
49-
const invalid = { treatment: 'invalid treatment!' };
50-
expect((FallbacksSanitizer as any).isValidTreatment(invalid)).toBe(false);
58+
const invalid = { treatment: 'invalid treatment!', config: null };
59+
expect(isValidTreatment(invalid)).toBe(false);
5160
});
5261
});
5362

5463
describe('sanitizeGlobal', () => {
5564
test('returns the treatment if valid', () => {
56-
expect(FallbacksSanitizer.sanitizeGlobal(loggerMock, validTreatment)).toEqual(validTreatment);
65+
expect(sanitizeFallbacks(loggerMock, { ...fallbackMock, global: validTreatment })).toEqual({ ...fallbackMock, global: validTreatment });
5766
expect(loggerMock.error).not.toHaveBeenCalled();
5867
});
5968

6069
test('returns undefined and logs error if invalid', () => {
61-
const result = FallbacksSanitizer.sanitizeGlobal(loggerMock, invalidTreatment);
62-
expect(result).toBeUndefined();
70+
const result = sanitizeFallbacks(loggerMock, { ...fallbackMock, global: invalidTreatment });
71+
expect(result).toEqual(fallbackMock);
6372
expect(loggerMock.error).toHaveBeenCalledWith(
6473
expect.stringContaining('Fallback treatments - Discarded fallback')
6574
);
@@ -74,9 +83,9 @@ describe('FallbacksSanitizer', () => {
7483
bad_treatment: invalidTreatment,
7584
};
7685

77-
const result = FallbacksSanitizer.sanitizeByFlag(loggerMock, input);
86+
const result = sanitizeFallbacks(loggerMock, {...fallbackMock, byFlag: input});
7887

79-
expect(result).toEqual({ valid_flag: validTreatment });
88+
expect(result).toEqual({ ...fallbackMock, byFlag: { valid_flag: validTreatment } });
8089
expect(loggerMock.error).toHaveBeenCalledTimes(2); // invalid flag + bad_treatment
8190
});
8291

@@ -85,20 +94,45 @@ describe('FallbacksSanitizer', () => {
8594
'invalid flag': invalidTreatment,
8695
};
8796

88-
const result = FallbacksSanitizer.sanitizeByFlag(loggerMock, input);
89-
expect(result).toEqual({});
97+
const result = sanitizeFallbacks(loggerMock, {...fallbackMock, byFlag: input});
98+
expect(result).toEqual(fallbackMock);
9099
expect(loggerMock.error).toHaveBeenCalled();
91100
});
92101

93102
test('returns same object if all valid', () => {
94103
const input = {
95-
flag_one: validTreatment,
96-
flag_two: { treatment: 'valid_2', config: null },
104+
...fallbackMock,
105+
byFlag:{
106+
flag_one: validTreatment,
107+
flag_two: { treatment: 'valid_2', config: null },
108+
}
97109
};
98110

99-
const result = FallbacksSanitizer.sanitizeByFlag(loggerMock, input);
111+
const result = sanitizeFallbacks(loggerMock, input);
100112
expect(result).toEqual(input);
101113
expect(loggerMock.error).not.toHaveBeenCalled();
102114
});
103115
});
116+
describe('sanitizeFallbacks', () => {
117+
test('returns undefined and logs error if fallbacks is not an object', () => {
118+
const result = sanitizeFallbacks(loggerMock, 'invalid_fallbacks');
119+
expect(result).toBeUndefined();
120+
expect(loggerMock.error).toHaveBeenCalledWith(
121+
'Fallback treatments - Discarded configuration: it must be an object with optional `global` and `byFlag` properties'
122+
);
123+
});
124+
125+
test('returns undefined and logs error if fallbacks is not an object', () => {
126+
const result = sanitizeFallbacks(loggerMock, true);
127+
expect(result).toBeUndefined();
128+
expect(loggerMock.error).toHaveBeenCalledWith(
129+
'Fallback treatments - Discarded configuration: it must be an object with optional `global` and `byFlag` properties'
130+
);
131+
});
132+
133+
test('sanitizes both global and byFlag fallbacks for empty object', () => {
134+
const result = sanitizeFallbacks(loggerMock, { global: {} });
135+
expect(result).toEqual({ global: undefined, byFlag: {} });
136+
});
137+
});
104138
});

src/evaluator/fallbackTreatmentsCalculator/fallbackSanitizer/index.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ enum FallbackDiscardReason {
99

1010
const TREATMENT_PATTERN = /^[0-9]+[.a-zA-Z0-9_-]*$|^[a-zA-Z]+[a-zA-Z0-9_-]*$/;
1111

12-
function isValidFlagName(name: string): boolean {
12+
export function isValidFlagName(name: string): boolean {
1313
return name.length <= 100 && !name.includes(' ');
1414
}
1515

16-
function isValidTreatment(t?: Treatment | TreatmentWithConfig): boolean {
16+
export function isValidTreatment(t?: Treatment | TreatmentWithConfig): boolean {
1717
const treatment = isObject(t) ? (t as TreatmentWithConfig).treatment : t;
1818

1919
if (!isString(treatment) || treatment.length > 100) {
@@ -23,6 +23,7 @@ function isValidTreatment(t?: Treatment | TreatmentWithConfig): boolean {
2323
}
2424

2525
function sanitizeGlobal(logger: ILogger, treatment?: Treatment | TreatmentWithConfig): Treatment | TreatmentWithConfig | undefined {
26+
if (treatment === undefined) return undefined;
2627
if (!isValidTreatment(treatment)) {
2728
logger.error(`Fallback treatments - Discarded fallback: ${FallbackDiscardReason.Treatment}`);
2829
return undefined;
@@ -39,6 +40,7 @@ function sanitizeByFlag(
3940
const entries = Object.keys(byFlagFallbacks);
4041
entries.forEach((flag) => {
4142
const t = byFlagFallbacks[flag];
43+
if (!t) return;
4244
if (!isValidFlagName(flag)) {
4345
logger.error(`Fallback treatments - Discarded flag '${flag}': ${FallbackDiscardReason.FlagName}`);
4446
return;

src/sdkClient/__tests__/sdkClientMethod.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const paramMocks = [
1919
telemetryTracker: telemetryTrackerFactory(),
2020
clients: {},
2121
uniqueKeysTracker: { start: jest.fn(), stop: jest.fn() },
22-
fallbackTreatmentsCalculator: new FallbackTreatmentsCalculator(loggerMock, {})
22+
fallbackTreatmentsCalculator: new FallbackTreatmentsCalculator({})
2323
},
2424
// SyncManager (i.e., Sync SDK) and Signal listener
2525
{
@@ -31,7 +31,7 @@ const paramMocks = [
3131
telemetryTracker: telemetryTrackerFactory(),
3232
clients: {},
3333
uniqueKeysTracker: { start: jest.fn(), stop: jest.fn() },
34-
fallbackTreatmentsCalculator: new FallbackTreatmentsCalculator(loggerMock, {})
34+
fallbackTreatmentsCalculator: new FallbackTreatmentsCalculator({})
3535
}
3636
];
3737

src/sdkFactory/__tests__/index.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ const paramsForAsyncSDK = {
3737
platform: {
3838
EventEmitter
3939
},
40-
fallbackTreatmentsCalculator: new FallbackTreatmentsCalculator(fullSettings.log)
40+
fallbackTreatmentsCalculator: new FallbackTreatmentsCalculator()
4141
};
4242

4343
const SignalListenerInstanceMock = { start: jest.fn() };

0 commit comments

Comments
 (0)