Skip to content

Commit bff5800

Browse files
authored
Merge pull request #1260 from manodyaSenevirathne/feature/admin-defined-constraints-application-properties
Add e2e and unit tests for Key Manager Configuration Constraints feature
2 parents ce57ec5 + 317a667 commit bff5800

File tree

2 files changed

+403
-0
lines changed

2 files changed

+403
-0
lines changed
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
/*
2+
* Copyright (c) 2026, WSO2 LLC. (http://www.wso2.org) All Rights Reserved.
3+
*
4+
* WSO2 LLC. licenses this file to you under the Apache License,
5+
* Version 2.0 (the "License"); you may not use this file except
6+
* in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing,
12+
* software distributed under the License is distributed on an
13+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
* KIND, either express or implied. See the License for the
15+
* specific language governing permissions and limitations
16+
* under the License.
17+
*/
18+
19+
import validateConstraint, { VALIDATOR_TYPES, getConstraintHint } from 'AppComponents/Shared/AppsAndKeys/constraintValidator';
20+
21+
const mockIntl = {
22+
formatMessage: (msg, values) => {
23+
let { defaultMessage } = msg;
24+
if (values) {
25+
Object.keys(values).forEach((key) => {
26+
defaultMessage = defaultMessage.replace(`{${key}}`, values[key]);
27+
});
28+
}
29+
return defaultMessage;
30+
},
31+
};
32+
const mockMessages = {
33+
rangeMin: { defaultMessage: 'Value must be at least {min}' },
34+
rangeMax: { defaultMessage: 'Value must be at most {max}' },
35+
rangeInvalid: { defaultMessage: 'Value must be a number between {min} and {max}' },
36+
regexInvalid: { defaultMessage: 'Value must match the required pattern: {pattern}' },
37+
};
38+
39+
describe('constraintValidator', () => {
40+
describe('validateConstraint', () => {
41+
describe('MIN validation', () => {
42+
const constraint = {
43+
type: VALIDATOR_TYPES.MIN,
44+
value: { min: 10 },
45+
};
46+
47+
it('should return valid for value greater than min', () => {
48+
const result = validateConstraint('15', constraint, mockIntl, mockMessages);
49+
expect(result.valid).toBe(true);
50+
});
51+
52+
it('should return valid for value equal to min', () => {
53+
const result = validateConstraint('10', constraint, mockIntl, mockMessages);
54+
expect(result.valid).toBe(true);
55+
});
56+
57+
it('should return invalid for value less than min', () => {
58+
const result = validateConstraint('5', constraint, mockIntl, mockMessages);
59+
expect(result.valid).toBe(false);
60+
expect(result.message).toBe('Value must be at least 10');
61+
});
62+
63+
it('should return invalid for non-numeric value', () => {
64+
const result = validateConstraint('abc', constraint, mockIntl, mockMessages);
65+
expect(result.valid).toBe(false);
66+
});
67+
68+
it('should return invalid for empty value', () => {
69+
const result = validateConstraint('', constraint, mockIntl, mockMessages);
70+
expect(result.valid).toBe(false);
71+
});
72+
73+
it('should handle negative numbers correctly', () => {
74+
const negConstraint = { type: VALIDATOR_TYPES.MIN, value: { min: -5 } };
75+
expect(validateConstraint('-3', negConstraint, mockIntl, mockMessages).valid).toBe(true);
76+
expect(validateConstraint('-10', negConstraint, mockIntl, mockMessages).valid).toBe(false);
77+
});
78+
79+
it('should handle decimal numbers correctly', () => {
80+
const decConstraint = { type: VALIDATOR_TYPES.MIN, value: { min: 5.5 } };
81+
expect(validateConstraint('5.6', decConstraint, mockIntl, mockMessages).valid).toBe(true);
82+
expect(validateConstraint('5.4', decConstraint, mockIntl, mockMessages).valid).toBe(false);
83+
});
84+
});
85+
86+
describe('MAX validation', () => {
87+
const constraint = {
88+
type: VALIDATOR_TYPES.MAX,
89+
value: { max: 100 },
90+
};
91+
92+
it('should return valid for value less than max', () => {
93+
const result = validateConstraint('50', constraint, mockIntl, mockMessages);
94+
expect(result.valid).toBe(true);
95+
});
96+
97+
it('should return valid for value equal to max', () => {
98+
const result = validateConstraint('100', constraint, mockIntl, mockMessages);
99+
expect(result.valid).toBe(true);
100+
});
101+
102+
it('should return invalid for value greater than max', () => {
103+
const result = validateConstraint('150', constraint, mockIntl, mockMessages);
104+
expect(result.valid).toBe(false);
105+
expect(result.message).toBe('Value must be at most 100');
106+
});
107+
108+
it('should handle zero correctly', () => {
109+
const zeroConstraint = { type: VALIDATOR_TYPES.MAX, value: { max: 0 } };
110+
expect(validateConstraint('-1', zeroConstraint, mockIntl, mockMessages).valid).toBe(true);
111+
expect(validateConstraint('0', zeroConstraint, mockIntl, mockMessages).valid).toBe(true);
112+
expect(validateConstraint('1', zeroConstraint, mockIntl, mockMessages).valid).toBe(false);
113+
});
114+
});
115+
116+
describe('RANGE validation', () => {
117+
const constraint = {
118+
type: VALIDATOR_TYPES.RANGE,
119+
value: { min: 10, max: 20 },
120+
};
121+
122+
it('should return valid for value within range', () => {
123+
const result = validateConstraint('15', constraint, mockIntl, mockMessages);
124+
expect(result.valid).toBe(true);
125+
});
126+
127+
it('should return valid for value at min bound', () => {
128+
const result = validateConstraint('10', constraint, mockIntl, mockMessages);
129+
expect(result.valid).toBe(true);
130+
});
131+
132+
it('should return valid for value at max bound', () => {
133+
const result = validateConstraint('20', constraint, mockIntl, mockMessages);
134+
expect(result.valid).toBe(true);
135+
});
136+
137+
it('should return invalid for value below min', () => {
138+
const result = validateConstraint('5', constraint, mockIntl, mockMessages);
139+
expect(result.valid).toBe(false);
140+
expect(result.message).toBe('Value must be a number between 10 and 20');
141+
});
142+
143+
it('should return invalid for value above max', () => {
144+
const result = validateConstraint('25', constraint, mockIntl, mockMessages);
145+
expect(result.valid).toBe(false);
146+
expect(result.message).toBe('Value must be a number between 10 and 20');
147+
});
148+
149+
it('should return invalid for non-numeric value', () => {
150+
const result = validateConstraint('abc', constraint, mockIntl, mockMessages);
151+
expect(result.valid).toBe(false);
152+
});
153+
});
154+
155+
describe('REGEX validation', () => {
156+
const constraint = {
157+
type: VALIDATOR_TYPES.REGEX,
158+
value: { pattern: '^[0-9]+$' },
159+
};
160+
161+
it('should return valid for matching value', () => {
162+
const result = validateConstraint('123', constraint, mockIntl, mockMessages);
163+
expect(result.valid).toBe(true);
164+
});
165+
166+
it('should return invalid for non-matching value', () => {
167+
const result = validateConstraint('abc', constraint, mockIntl, mockMessages);
168+
expect(result.valid).toBe(false);
169+
expect(result.message).toBe('Value must match the required pattern: ^[0-9]+$');
170+
});
171+
172+
it('should return valid if regex pattern is invalid (to prevent app crash)', () => {
173+
const badConstraint = { type: VALIDATOR_TYPES.REGEX, value: { pattern: '[' } };
174+
const result = validateConstraint('any', badConstraint, mockIntl, mockMessages);
175+
expect(result.valid).toBe(true);
176+
});
177+
});
178+
179+
describe('Edge Cases', () => {
180+
it('should return valid if no constraint is provided', () => {
181+
const result = validateConstraint('val', null, mockIntl, mockMessages);
182+
expect(result.valid).toBe(true);
183+
});
184+
185+
it('should return valid if constraint type is unknown', () => {
186+
const result = validateConstraint('val', { type: 'UNKNOWN' }, mockIntl, mockMessages);
187+
expect(result.valid).toBe(true);
188+
});
189+
});
190+
});
191+
192+
describe('getConstraintHint', () => {
193+
it('should return empty string for missing arguments', () => {
194+
expect(getConstraintHint(null, mockIntl, mockMessages)).toBe('');
195+
expect(getConstraintHint({}, mockIntl, mockMessages)).toBe('');
196+
});
197+
198+
it('should return correct hint for MIN', () => {
199+
const constraint = { type: VALIDATOR_TYPES.MIN, value: { min: 5 } };
200+
expect(getConstraintHint(constraint, mockIntl, mockMessages)).toBe('Value must be at least 5');
201+
});
202+
203+
it('should return correct hint for MAX', () => {
204+
const constraint = { type: VALIDATOR_TYPES.MAX, value: { max: 50 } };
205+
expect(getConstraintHint(constraint, mockIntl, mockMessages)).toBe('Value must be at most 50');
206+
});
207+
208+
it('should return correct hint for RANGE', () => {
209+
const constraint = { type: VALIDATOR_TYPES.RANGE, value: { min: 5, max: 10 } };
210+
expect(getConstraintHint(constraint, mockIntl, mockMessages)).toBe('Value must be a number between 5 and 10');
211+
});
212+
213+
it('should return correct hint for REGEX', () => {
214+
const constraint = { type: VALIDATOR_TYPES.REGEX, value: { pattern: '.*' } };
215+
expect(getConstraintHint(constraint, mockIntl, mockMessages)).toBe('Value must match the required pattern: .*');
216+
});
217+
218+
it('should return empty string for unknown type', () => {
219+
const constraint = { type: 'UNKNOWN', value: { some: 'val' } };
220+
expect(getConstraintHint(constraint, mockIntl, mockMessages)).toBe('');
221+
});
222+
});
223+
});

0 commit comments

Comments
 (0)