-
Notifications
You must be signed in to change notification settings - Fork 52
Expand file tree
/
Copy patherrors.test.ts
More file actions
262 lines (227 loc) · 10.3 KB
/
Copy patherrors.test.ts
File metadata and controls
262 lines (227 loc) · 10.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
import {
AgentAlreadyExistsError,
getErrorMessage,
isChangesetInProgressError,
isEarlyValidationError,
isExpiredTokenError,
isNoCredentialsError,
isReviewInProgressError,
isStackInProgressError,
} from '../errors.js';
import { describe, expect, it } from 'vitest';
describe('errors', () => {
describe('AgentAlreadyExistsError', () => {
it('has correct message with agent name', () => {
const err = new AgentAlreadyExistsError('my-agent');
expect(err.message).toBe('An agent named "my-agent" already exists in the schema.');
});
it('has correct name', () => {
const err = new AgentAlreadyExistsError('test');
expect(err.name).toBe('AgentAlreadyExistsError');
});
it('is instance of Error', () => {
expect(new AgentAlreadyExistsError('test')).toBeInstanceOf(Error);
});
});
describe('getErrorMessage', () => {
it('returns message from Error instance', () => {
const err = new Error('test error');
expect(getErrorMessage(err)).toBe('test error');
});
it('returns string for non-Error values', () => {
expect(getErrorMessage('raw error')).toBe('raw error');
expect(getErrorMessage(123)).toBe('123');
expect(getErrorMessage(null)).toBe('null');
expect(getErrorMessage(undefined)).toBe('undefined');
});
});
describe('isExpiredTokenError', () => {
// Test ALL error codes in EXPIRED_TOKEN_ERROR_CODES via error.name
const allErrorCodes = [
'ExpiredToken',
'ExpiredTokenException',
'TokenRefreshRequired',
'CredentialsExpired',
'InvalidIdentityToken',
'UnauthorizedAccess',
'InvalidClientTokenId',
'SignatureDoesNotMatch',
'RequestExpired',
];
it('returns true for all SDK v3 error names', () => {
for (const code of allErrorCodes) {
expect(isExpiredTokenError({ name: code }), `Should detect error.name: ${code}`).toBe(true);
}
});
it('returns true for all error Code properties', () => {
for (const code of allErrorCodes) {
expect(isExpiredTokenError({ Code: code }), `Should detect error.Code: ${code}`).toBe(true);
}
});
it('returns true for nested cause with error name', () => {
expect(isExpiredTokenError({ cause: { name: 'ExpiredToken' } })).toBe(true);
});
it('returns true for double-nested cause', () => {
expect(isExpiredTokenError({ cause: { cause: { name: 'ExpiredToken' } } })).toBe(true);
});
it('returns true for nested cause with Code', () => {
expect(isExpiredTokenError({ cause: { Code: 'ExpiredToken' } })).toBe(true);
});
it('returns true for message patterns', () => {
const patterns = [
'expired token',
'token has expired',
'credentials have expired',
'security token included in the request is expired',
'the security token included in the request is invalid',
];
for (const pattern of patterns) {
expect(isExpiredTokenError(new Error(pattern)), `Should detect message: ${pattern}`).toBe(true);
}
});
it('returns false for non-expired errors', () => {
expect(isExpiredTokenError({ name: 'ValidationError' })).toBe(false);
expect(isExpiredTokenError({ Code: 'ResourceNotFound' })).toBe(false);
expect(isExpiredTokenError(new Error('some other error'))).toBe(false);
});
it('returns false for AccessDenied errors (authorization, not authentication)', () => {
// AccessDenied errors indicate authorization failures (wrong account, missing IAM permissions),
// NOT token expiration. Users should see the actual AccessDenied error, not "credentials expired".
expect(isExpiredTokenError({ name: 'AccessDenied' })).toBe(false);
expect(isExpiredTokenError({ name: 'AccessDeniedException' })).toBe(false);
expect(isExpiredTokenError({ Code: 'AccessDenied' })).toBe(false);
expect(isExpiredTokenError({ Code: 'AccessDeniedException' })).toBe(false);
expect(isExpiredTokenError({ cause: { name: 'AccessDenied' } })).toBe(false);
expect(isExpiredTokenError(new Error('AccessDenied: User is not authorized'))).toBe(false);
});
it('returns false for edge cases', () => {
expect(isExpiredTokenError(null)).toBe(false);
expect(isExpiredTokenError(undefined)).toBe(false);
expect(isExpiredTokenError('string')).toBe(false);
expect(isExpiredTokenError(123)).toBe(false);
expect(isExpiredTokenError({})).toBe(false);
expect(isExpiredTokenError({ name: 123 })).toBe(false); // non-string name
expect(isExpiredTokenError({ Code: 123 })).toBe(false); // non-string Code
});
});
describe('isNoCredentialsError', () => {
it('returns true for AwsCredentialsError', () => {
expect(isNoCredentialsError({ name: 'AwsCredentialsError' })).toBe(true);
});
it('returns true for message patterns', () => {
const patterns = ['no aws credentials found', 'could not load credentials', 'credentials not found'];
for (const pattern of patterns) {
expect(isNoCredentialsError(new Error(pattern)), `Should detect message: ${pattern}`).toBe(true);
}
});
it('returns false for other errors', () => {
expect(isNoCredentialsError({ name: 'ExpiredTokenException' })).toBe(false);
expect(isNoCredentialsError(new Error('some other error'))).toBe(false);
});
it('returns false for edge cases', () => {
expect(isNoCredentialsError(null)).toBe(false);
expect(isNoCredentialsError(undefined)).toBe(false);
expect(isNoCredentialsError('string')).toBe(false);
expect(isNoCredentialsError(123)).toBe(false);
expect(isNoCredentialsError({})).toBe(false);
});
});
describe('isStackInProgressError', () => {
it('returns true for in-progress states', () => {
const states = ['UPDATE_IN_PROGRESS', 'CREATE_IN_PROGRESS', 'DELETE_IN_PROGRESS', 'ROLLBACK_IN_PROGRESS'];
for (const state of states) {
expect(isStackInProgressError(new Error(`Stack is in ${state} state`)), `Should detect state: ${state}`).toBe(
true
);
}
});
it('returns true for state and cannot be updated pattern', () => {
expect(
isStackInProgressError(new Error('Stack is in UPDATE_ROLLBACK_IN_PROGRESS state and cannot be updated'))
).toBe(true);
});
it('returns true for currently being updated', () => {
expect(isStackInProgressError(new Error('stack is currently being updated'))).toBe(true);
});
it('returns false for other errors', () => {
expect(isStackInProgressError(new Error('Stack not found'))).toBe(false);
expect(isStackInProgressError(new Error('some other error'))).toBe(false);
});
it('returns false for edge cases', () => {
expect(isStackInProgressError(null)).toBe(false);
expect(isStackInProgressError(undefined)).toBe(false);
expect(isStackInProgressError({})).toBe(false);
});
});
describe('isChangesetInProgressError', () => {
it('returns true for InvalidChangeSetStatus errors', () => {
expect(
isChangesetInProgressError(
new Error('InvalidChangeSetStatusException: An operation on this ChangeSet is currently in progress.')
)
).toBe(true);
});
it('returns true for changeset in progress message patterns', () => {
expect(isChangesetInProgressError(new Error('ChangeSet is currently in progress'))).toBe(true);
expect(isChangesetInProgressError(new Error('An operation on this changeset is currently in progress'))).toBe(
true
);
});
it('returns false for other errors', () => {
expect(isChangesetInProgressError(new Error('Stack not found'))).toBe(false);
expect(isChangesetInProgressError(new Error('some other error'))).toBe(false);
expect(isChangesetInProgressError(new Error('UPDATE_IN_PROGRESS'))).toBe(false);
});
it('returns false for edge cases', () => {
expect(isChangesetInProgressError(null)).toBe(false);
expect(isChangesetInProgressError(undefined)).toBe(false);
expect(isChangesetInProgressError({})).toBe(false);
});
});
describe('isEarlyValidationError', () => {
it('detects AWS::EarlyValidation::PropertyValidation messages', () => {
expect(
isEarlyValidationError(
new Error('The following hook(s)/validation failed: [AWS::EarlyValidation::PropertyValidation]')
)
).toBe(true);
expect(isEarlyValidationError(new Error('EarlyValidation::PropertyValidation rejected runtime'))).toBe(true);
});
it('detects other AWS::EarlyValidation:: hook subtypes', () => {
// Future-proofing: any hook under the EarlyValidation namespace should match.
expect(isEarlyValidationError(new Error('AWS::EarlyValidation::SomeOtherCheck failed for resource'))).toBe(true);
});
it('detects messages that only carry the hook(s)/validation framing', () => {
// When CFN logs the hook name with different separators, we still recognize it.
expect(
isEarlyValidationError(
new Error('The following hook(s)/validation failed: [AWS-EarlyValidation-PropertyValidation]')
)
).toBe(true);
});
it('returns false for unrelated errors', () => {
expect(isEarlyValidationError(new Error('CREATE_FAILED'))).toBe(false);
expect(isEarlyValidationError(new Error('Stack not found'))).toBe(false);
expect(isEarlyValidationError(null)).toBe(false);
});
});
describe('isReviewInProgressError', () => {
it('detects REVIEW_IN_PROGRESS errors', () => {
expect(isReviewInProgressError(new Error('Stack "MyStack" is currently in REVIEW_IN_PROGRESS state.'))).toBe(
true
);
expect(isReviewInProgressError(new Error('Stack "MyStack" is in REVIEW_IN_PROGRESS state'))).toBe(true);
expect(isReviewInProgressError(new Error('REVIEW_IN_PROGRESS state detected'))).toBe(true);
});
it('does not match bare REVIEW_IN_PROGRESS token in passing references', () => {
// e.g. an event-stream history message mentioning prior statuses
expect(
isReviewInProgressError(new Error('History: REVIEW_IN_PROGRESS -> CREATE_IN_PROGRESS -> CREATE_COMPLETE'))
).toBe(false);
});
it('returns false for unrelated errors', () => {
expect(isReviewInProgressError(new Error('UPDATE_IN_PROGRESS'))).toBe(false);
expect(isReviewInProgressError(null)).toBe(false);
});
});
});