@friggframework/devtools 2.0.0--canary.474.27d9425.0 → 2.0.0--canary.474.97bfcf0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,267 @@
1
+ /**
2
+ * Tests for HealthScore Value Object
3
+ */
4
+
5
+ const HealthScore = require('./health-score');
6
+
7
+ describe('HealthScore', () => {
8
+ describe('constructor', () => {
9
+ it('should create health score with valid value', () => {
10
+ const score = new HealthScore(75);
11
+
12
+ expect(score.value).toBe(75);
13
+ });
14
+
15
+ it('should accept 0 as minimum score', () => {
16
+ const score = new HealthScore(0);
17
+
18
+ expect(score.value).toBe(0);
19
+ });
20
+
21
+ it('should accept 100 as maximum score', () => {
22
+ const score = new HealthScore(100);
23
+
24
+ expect(score.value).toBe(100);
25
+ });
26
+
27
+ it('should reject negative scores', () => {
28
+ expect(() => {
29
+ new HealthScore(-1);
30
+ }).toThrow('Health score must be between 0 and 100');
31
+ });
32
+
33
+ it('should reject scores above 100', () => {
34
+ expect(() => {
35
+ new HealthScore(101);
36
+ }).toThrow('Health score must be between 0 and 100');
37
+ });
38
+
39
+ it('should reject non-numeric scores', () => {
40
+ expect(() => {
41
+ new HealthScore('75');
42
+ }).toThrow('Health score must be a number');
43
+ });
44
+
45
+ it('should reject NaN', () => {
46
+ expect(() => {
47
+ new HealthScore(NaN);
48
+ }).toThrow('Health score must be a number');
49
+ });
50
+
51
+ it('should reject Infinity', () => {
52
+ expect(() => {
53
+ new HealthScore(Infinity);
54
+ }).toThrow('Health score must be a number');
55
+ });
56
+ });
57
+
58
+ describe('qualitativeAssessment', () => {
59
+ it('should return "healthy" for score 100', () => {
60
+ const score = new HealthScore(100);
61
+
62
+ expect(score.qualitativeAssessment()).toBe('healthy');
63
+ });
64
+
65
+ it('should return "healthy" for score 90', () => {
66
+ const score = new HealthScore(90);
67
+
68
+ expect(score.qualitativeAssessment()).toBe('healthy');
69
+ });
70
+
71
+ it('should return "healthy" for score 80', () => {
72
+ const score = new HealthScore(80);
73
+
74
+ expect(score.qualitativeAssessment()).toBe('healthy');
75
+ });
76
+
77
+ it('should return "degraded" for score 79', () => {
78
+ const score = new HealthScore(79);
79
+
80
+ expect(score.qualitativeAssessment()).toBe('degraded');
81
+ });
82
+
83
+ it('should return "degraded" for score 50', () => {
84
+ const score = new HealthScore(50);
85
+
86
+ expect(score.qualitativeAssessment()).toBe('degraded');
87
+ });
88
+
89
+ it('should return "degraded" for score 40', () => {
90
+ const score = new HealthScore(40);
91
+
92
+ expect(score.qualitativeAssessment()).toBe('degraded');
93
+ });
94
+
95
+ it('should return "unhealthy" for score 39', () => {
96
+ const score = new HealthScore(39);
97
+
98
+ expect(score.qualitativeAssessment()).toBe('unhealthy');
99
+ });
100
+
101
+ it('should return "unhealthy" for score 0', () => {
102
+ const score = new HealthScore(0);
103
+
104
+ expect(score.qualitativeAssessment()).toBe('unhealthy');
105
+ });
106
+ });
107
+
108
+ describe('isHealthy', () => {
109
+ it('should return true for score 80', () => {
110
+ const score = new HealthScore(80);
111
+
112
+ expect(score.isHealthy()).toBe(true);
113
+ });
114
+
115
+ it('should return true for score 100', () => {
116
+ const score = new HealthScore(100);
117
+
118
+ expect(score.isHealthy()).toBe(true);
119
+ });
120
+
121
+ it('should return false for score 79', () => {
122
+ const score = new HealthScore(79);
123
+
124
+ expect(score.isHealthy()).toBe(false);
125
+ });
126
+
127
+ it('should return false for score 0', () => {
128
+ const score = new HealthScore(0);
129
+
130
+ expect(score.isHealthy()).toBe(false);
131
+ });
132
+ });
133
+
134
+ describe('isDegraded', () => {
135
+ it('should return true for score 79', () => {
136
+ const score = new HealthScore(79);
137
+
138
+ expect(score.isDegraded()).toBe(true);
139
+ });
140
+
141
+ it('should return true for score 40', () => {
142
+ const score = new HealthScore(40);
143
+
144
+ expect(score.isDegraded()).toBe(true);
145
+ });
146
+
147
+ it('should return false for score 80', () => {
148
+ const score = new HealthScore(80);
149
+
150
+ expect(score.isDegraded()).toBe(false);
151
+ });
152
+
153
+ it('should return false for score 39', () => {
154
+ const score = new HealthScore(39);
155
+
156
+ expect(score.isDegraded()).toBe(false);
157
+ });
158
+ });
159
+
160
+ describe('isUnhealthy', () => {
161
+ it('should return true for score 39', () => {
162
+ const score = new HealthScore(39);
163
+
164
+ expect(score.isUnhealthy()).toBe(true);
165
+ });
166
+
167
+ it('should return true for score 0', () => {
168
+ const score = new HealthScore(0);
169
+
170
+ expect(score.isUnhealthy()).toBe(true);
171
+ });
172
+
173
+ it('should return false for score 40', () => {
174
+ const score = new HealthScore(40);
175
+
176
+ expect(score.isUnhealthy()).toBe(false);
177
+ });
178
+
179
+ it('should return false for score 80', () => {
180
+ const score = new HealthScore(80);
181
+
182
+ expect(score.isUnhealthy()).toBe(false);
183
+ });
184
+ });
185
+
186
+ describe('toString', () => {
187
+ it('should return string representation for healthy score', () => {
188
+ const score = new HealthScore(100);
189
+
190
+ expect(score.toString()).toBe('100 (healthy)');
191
+ });
192
+
193
+ it('should return string representation for degraded score', () => {
194
+ const score = new HealthScore(50);
195
+
196
+ expect(score.toString()).toBe('50 (degraded)');
197
+ });
198
+
199
+ it('should return string representation for unhealthy score', () => {
200
+ const score = new HealthScore(25);
201
+
202
+ expect(score.toString()).toBe('25 (unhealthy)');
203
+ });
204
+ });
205
+
206
+ describe('static factory methods', () => {
207
+ it('should create perfect score', () => {
208
+ const score = HealthScore.perfect();
209
+
210
+ expect(score.value).toBe(100);
211
+ expect(score.isHealthy()).toBe(true);
212
+ });
213
+
214
+ it('should create failed score', () => {
215
+ const score = HealthScore.failed();
216
+
217
+ expect(score.value).toBe(0);
218
+ expect(score.isUnhealthy()).toBe(true);
219
+ });
220
+
221
+ it('should create from percentage (1.0 = 100)', () => {
222
+ const score = HealthScore.fromPercentage(0.75);
223
+
224
+ expect(score.value).toBe(75);
225
+ });
226
+
227
+ it('should create from percentage (0.0 = 0)', () => {
228
+ const score = HealthScore.fromPercentage(0);
229
+
230
+ expect(score.value).toBe(0);
231
+ });
232
+
233
+ it('should create from percentage (1.0 = 100)', () => {
234
+ const score = HealthScore.fromPercentage(1.0);
235
+
236
+ expect(score.value).toBe(100);
237
+ });
238
+
239
+ it('should reject percentage below 0', () => {
240
+ expect(() => {
241
+ HealthScore.fromPercentage(-0.1);
242
+ }).toThrow('Percentage must be between 0 and 1');
243
+ });
244
+
245
+ it('should reject percentage above 1', () => {
246
+ expect(() => {
247
+ HealthScore.fromPercentage(1.1);
248
+ }).toThrow('Percentage must be between 0 and 1');
249
+ });
250
+ });
251
+
252
+ describe('immutability', () => {
253
+ it('should not allow modification of value', () => {
254
+ const score = new HealthScore(75);
255
+
256
+ expect(() => {
257
+ score.value = 50;
258
+ }).toThrow();
259
+ });
260
+
261
+ it('should be frozen', () => {
262
+ const score = new HealthScore(75);
263
+
264
+ expect(Object.isFrozen(score)).toBe(true);
265
+ });
266
+ });
267
+ });
@@ -0,0 +1,161 @@
1
+ /**
2
+ * PropertyMutability Value Object
3
+ *
4
+ * Enum-like immutable mutability classification for CloudFormation resource properties
5
+ *
6
+ * Types:
7
+ * - MUTABLE: Property can be changed without replacing the resource (Update requires: No interruption)
8
+ * - IMMUTABLE: Property cannot be changed - requires resource replacement (Update requires: Replacement)
9
+ * - CONDITIONAL: Property mutability depends on other properties or conditions (Update requires: Some interruptions)
10
+ */
11
+
12
+ class PropertyMutability {
13
+ /**
14
+ * Valid mutability types
15
+ * @type {string[]}
16
+ */
17
+ static VALID_TYPES = [
18
+ 'MUTABLE',
19
+ 'IMMUTABLE',
20
+ 'CONDITIONAL',
21
+ ];
22
+
23
+ /**
24
+ * Create a new PropertyMutability
25
+ *
26
+ * @param {string} value - Mutability type
27
+ */
28
+ constructor(value) {
29
+ if (value === undefined || value === null) {
30
+ throw new Error('Property mutability is required');
31
+ }
32
+
33
+ if (!PropertyMutability.VALID_TYPES.includes(value)) {
34
+ throw new Error(`Invalid property mutability: ${value}`);
35
+ }
36
+
37
+ this._value = value;
38
+
39
+ // Make immutable
40
+ Object.freeze(this);
41
+ }
42
+
43
+ /**
44
+ * Get mutability value
45
+ * @returns {string}
46
+ */
47
+ get value() {
48
+ return this._value;
49
+ }
50
+
51
+ /**
52
+ * Prevent modification of value
53
+ * @throws {TypeError}
54
+ */
55
+ set value(newValue) {
56
+ throw new TypeError('Cannot modify immutable property value');
57
+ }
58
+
59
+ /**
60
+ * Check if property is mutable
61
+ * @returns {boolean}
62
+ */
63
+ isMutable() {
64
+ return this._value === 'MUTABLE';
65
+ }
66
+
67
+ /**
68
+ * Check if property is immutable
69
+ * @returns {boolean}
70
+ */
71
+ isImmutable() {
72
+ return this._value === 'IMMUTABLE';
73
+ }
74
+
75
+ /**
76
+ * Check if property mutability is conditional
77
+ * @returns {boolean}
78
+ */
79
+ isConditional() {
80
+ return this._value === 'CONDITIONAL';
81
+ }
82
+
83
+ /**
84
+ * Check if property can be changed
85
+ * @returns {boolean}
86
+ */
87
+ canChange() {
88
+ return this._value === 'MUTABLE';
89
+ }
90
+
91
+ /**
92
+ * Check if changing property requires resource replacement
93
+ * @returns {boolean}
94
+ */
95
+ requiresReplacement() {
96
+ return this._value === 'IMMUTABLE';
97
+ }
98
+
99
+ /**
100
+ * Get description of mutability type
101
+ * @returns {string}
102
+ */
103
+ getDescription() {
104
+ const descriptions = {
105
+ MUTABLE: 'Property can be changed without replacing the resource',
106
+ IMMUTABLE: 'Property cannot be changed - requires resource replacement',
107
+ CONDITIONAL: 'Property mutability depends on other property values or conditions',
108
+ };
109
+
110
+ return descriptions[this._value];
111
+ }
112
+
113
+ /**
114
+ * Check equality with another PropertyMutability
115
+ *
116
+ * @param {PropertyMutability} other
117
+ * @returns {boolean}
118
+ */
119
+ equals(other) {
120
+ if (!(other instanceof PropertyMutability)) {
121
+ return false;
122
+ }
123
+
124
+ return this._value === other._value;
125
+ }
126
+
127
+ /**
128
+ * Get string representation
129
+ *
130
+ * @returns {string}
131
+ */
132
+ toString() {
133
+ return this._value;
134
+ }
135
+
136
+ /**
137
+ * Predefined mutability: MUTABLE
138
+ * @type {PropertyMutability}
139
+ */
140
+ static get MUTABLE() {
141
+ return new PropertyMutability('MUTABLE');
142
+ }
143
+
144
+ /**
145
+ * Predefined mutability: IMMUTABLE
146
+ * @type {PropertyMutability}
147
+ */
148
+ static get IMMUTABLE() {
149
+ return new PropertyMutability('IMMUTABLE');
150
+ }
151
+
152
+ /**
153
+ * Predefined mutability: CONDITIONAL
154
+ * @type {PropertyMutability}
155
+ */
156
+ static get CONDITIONAL() {
157
+ return new PropertyMutability('CONDITIONAL');
158
+ }
159
+ }
160
+
161
+ module.exports = PropertyMutability;
@@ -0,0 +1,198 @@
1
+ /**
2
+ * Tests for PropertyMutability Value Object
3
+ */
4
+
5
+ const PropertyMutability = require('./property-mutability');
6
+
7
+ describe('PropertyMutability', () => {
8
+ describe('valid mutability types', () => {
9
+ it('should accept MUTABLE', () => {
10
+ const mutability = new PropertyMutability('MUTABLE');
11
+
12
+ expect(mutability.value).toBe('MUTABLE');
13
+ });
14
+
15
+ it('should accept IMMUTABLE', () => {
16
+ const mutability = new PropertyMutability('IMMUTABLE');
17
+
18
+ expect(mutability.value).toBe('IMMUTABLE');
19
+ });
20
+
21
+ it('should accept CONDITIONAL', () => {
22
+ const mutability = new PropertyMutability('CONDITIONAL');
23
+
24
+ expect(mutability.value).toBe('CONDITIONAL');
25
+ });
26
+ });
27
+
28
+ describe('invalid mutability types', () => {
29
+ it('should reject invalid mutability', () => {
30
+ expect(() => {
31
+ new PropertyMutability('INVALID');
32
+ }).toThrow('Invalid property mutability: INVALID');
33
+ });
34
+
35
+ it('should reject lowercase mutability', () => {
36
+ expect(() => {
37
+ new PropertyMutability('mutable');
38
+ }).toThrow('Invalid property mutability: mutable');
39
+ });
40
+
41
+ it('should reject null', () => {
42
+ expect(() => {
43
+ new PropertyMutability(null);
44
+ }).toThrow('Property mutability is required');
45
+ });
46
+
47
+ it('should reject undefined', () => {
48
+ expect(() => {
49
+ new PropertyMutability(undefined);
50
+ }).toThrow('Property mutability is required');
51
+ });
52
+ });
53
+
54
+ describe('mutability checks', () => {
55
+ it('should check if property is mutable', () => {
56
+ const mutability = new PropertyMutability('MUTABLE');
57
+
58
+ expect(mutability.isMutable()).toBe(true);
59
+ expect(mutability.isImmutable()).toBe(false);
60
+ expect(mutability.isConditional()).toBe(false);
61
+ });
62
+
63
+ it('should check if property is immutable', () => {
64
+ const mutability = new PropertyMutability('IMMUTABLE');
65
+
66
+ expect(mutability.isMutable()).toBe(false);
67
+ expect(mutability.isImmutable()).toBe(true);
68
+ expect(mutability.isConditional()).toBe(false);
69
+ });
70
+
71
+ it('should check if property is conditionally mutable', () => {
72
+ const mutability = new PropertyMutability('CONDITIONAL');
73
+
74
+ expect(mutability.isMutable()).toBe(false);
75
+ expect(mutability.isImmutable()).toBe(false);
76
+ expect(mutability.isConditional()).toBe(true);
77
+ });
78
+ });
79
+
80
+ describe('changeability checks', () => {
81
+ it('should allow changes for mutable properties', () => {
82
+ const mutability = new PropertyMutability('MUTABLE');
83
+
84
+ expect(mutability.canChange()).toBe(true);
85
+ expect(mutability.requiresReplacement()).toBe(false);
86
+ });
87
+
88
+ it('should not allow changes for immutable properties', () => {
89
+ const mutability = new PropertyMutability('IMMUTABLE');
90
+
91
+ expect(mutability.canChange()).toBe(false);
92
+ expect(mutability.requiresReplacement()).toBe(true);
93
+ });
94
+
95
+ it('should conditionally allow changes', () => {
96
+ const mutability = new PropertyMutability('CONDITIONAL');
97
+
98
+ // Conditional means it depends on other factors
99
+ expect(mutability.canChange()).toBe(false); // Can't change without conditions met
100
+ expect(mutability.requiresReplacement()).toBe(false); // Doesn't always require replacement
101
+ });
102
+ });
103
+
104
+ describe('equality', () => {
105
+ it('should be equal to same mutability', () => {
106
+ const m1 = new PropertyMutability('MUTABLE');
107
+ const m2 = new PropertyMutability('MUTABLE');
108
+
109
+ expect(m1.equals(m2)).toBe(true);
110
+ });
111
+
112
+ it('should not be equal to different mutability', () => {
113
+ const m1 = new PropertyMutability('MUTABLE');
114
+ const m2 = new PropertyMutability('IMMUTABLE');
115
+
116
+ expect(m1.equals(m2)).toBe(false);
117
+ });
118
+
119
+ it('should not be equal to non-PropertyMutability', () => {
120
+ const mutability = new PropertyMutability('MUTABLE');
121
+
122
+ expect(mutability.equals('MUTABLE')).toBe(false);
123
+ expect(mutability.equals(null)).toBe(false);
124
+ });
125
+ });
126
+
127
+ describe('toString', () => {
128
+ it('should return string representation', () => {
129
+ const mutability = new PropertyMutability('MUTABLE');
130
+
131
+ expect(mutability.toString()).toBe('MUTABLE');
132
+ });
133
+ });
134
+
135
+ describe('static constants', () => {
136
+ it('should provide MUTABLE constant', () => {
137
+ expect(PropertyMutability.MUTABLE.value).toBe('MUTABLE');
138
+ });
139
+
140
+ it('should provide IMMUTABLE constant', () => {
141
+ expect(PropertyMutability.IMMUTABLE.value).toBe('IMMUTABLE');
142
+ });
143
+
144
+ it('should provide CONDITIONAL constant', () => {
145
+ expect(PropertyMutability.CONDITIONAL.value).toBe('CONDITIONAL');
146
+ });
147
+
148
+ it('should provide VALID_TYPES array', () => {
149
+ expect(PropertyMutability.VALID_TYPES).toEqual([
150
+ 'MUTABLE',
151
+ 'IMMUTABLE',
152
+ 'CONDITIONAL',
153
+ ]);
154
+ });
155
+ });
156
+
157
+ describe('immutability', () => {
158
+ it('should not allow modification of value', () => {
159
+ const mutability = new PropertyMutability('MUTABLE');
160
+
161
+ expect(() => {
162
+ mutability.value = 'IMMUTABLE';
163
+ }).toThrow();
164
+ });
165
+
166
+ it('should be frozen', () => {
167
+ const mutability = new PropertyMutability('MUTABLE');
168
+
169
+ expect(Object.isFrozen(mutability)).toBe(true);
170
+ });
171
+ });
172
+
173
+ describe('description', () => {
174
+ it('should provide description for MUTABLE', () => {
175
+ const mutability = new PropertyMutability('MUTABLE');
176
+
177
+ expect(mutability.getDescription()).toBe(
178
+ 'Property can be changed without replacing the resource'
179
+ );
180
+ });
181
+
182
+ it('should provide description for IMMUTABLE', () => {
183
+ const mutability = new PropertyMutability('IMMUTABLE');
184
+
185
+ expect(mutability.getDescription()).toBe(
186
+ 'Property cannot be changed - requires resource replacement'
187
+ );
188
+ });
189
+
190
+ it('should provide description for CONDITIONAL', () => {
191
+ const mutability = new PropertyMutability('CONDITIONAL');
192
+
193
+ expect(mutability.getDescription()).toBe(
194
+ 'Property mutability depends on other property values or conditions'
195
+ );
196
+ });
197
+ });
198
+ });