@ai-sdk/gateway 3.0.21 → 3.0.22
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.
- package/CHANGELOG.md +9 -0
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +8 -4
- package/src/errors/create-gateway-error.test.ts +0 -590
- package/src/errors/extract-api-call-response.test.ts +0 -270
- package/src/errors/gateway-error-types.test.ts +0 -278
- package/src/errors/parse-auth-method.test.ts +0 -136
- package/src/gateway-embedding-model.test.ts +0 -213
- package/src/gateway-fetch-metadata.test.ts +0 -774
- package/src/gateway-image-model.test.ts +0 -823
- package/src/gateway-language-model.test.ts +0 -1485
- package/src/gateway-provider.test.ts +0 -1210
- package/src/vercel-environment.test.ts +0 -65
|
@@ -1,270 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { APICallError } from '@ai-sdk/provider';
|
|
3
|
-
import { extractApiCallResponse } from './extract-api-call-response';
|
|
4
|
-
|
|
5
|
-
describe('extractResponseFromAPICallError', () => {
|
|
6
|
-
describe('when error.data is available', () => {
|
|
7
|
-
it('should return error.data when successfully parsed by AI SDK', () => {
|
|
8
|
-
const parsedData = {
|
|
9
|
-
error: { message: 'Parsed error', type: 'authentication_error' },
|
|
10
|
-
};
|
|
11
|
-
const apiCallError = new APICallError({
|
|
12
|
-
message: 'Request failed',
|
|
13
|
-
statusCode: 401,
|
|
14
|
-
data: parsedData,
|
|
15
|
-
responseHeaders: {},
|
|
16
|
-
responseBody: JSON.stringify({ different: 'data' }),
|
|
17
|
-
url: 'http://test.url',
|
|
18
|
-
requestBodyValues: {},
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
const result = extractApiCallResponse(apiCallError);
|
|
22
|
-
|
|
23
|
-
expect(result).toBe(parsedData); // Should prefer parsed data over responseBody
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it('should return error.data even when it is null', () => {
|
|
27
|
-
const apiCallError = new APICallError({
|
|
28
|
-
message: 'Request failed',
|
|
29
|
-
statusCode: 500,
|
|
30
|
-
data: null,
|
|
31
|
-
responseHeaders: {},
|
|
32
|
-
responseBody: '{"fallback": "data"}',
|
|
33
|
-
url: 'http://test.url',
|
|
34
|
-
requestBodyValues: {},
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
const result = extractApiCallResponse(apiCallError);
|
|
38
|
-
|
|
39
|
-
expect(result).toBeNull(); // Should return null, not fallback to responseBody
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it('should return error.data even when it is an empty object', () => {
|
|
43
|
-
const emptyData = {};
|
|
44
|
-
const apiCallError = new APICallError({
|
|
45
|
-
message: 'Request failed',
|
|
46
|
-
statusCode: 400,
|
|
47
|
-
data: emptyData,
|
|
48
|
-
responseHeaders: {},
|
|
49
|
-
responseBody: '{"fallback": "data"}',
|
|
50
|
-
url: 'http://test.url',
|
|
51
|
-
requestBodyValues: {},
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
const result = extractApiCallResponse(apiCallError);
|
|
55
|
-
|
|
56
|
-
expect(result).toBe(emptyData); // Should return empty object, not fallback
|
|
57
|
-
});
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
describe('when error.data is undefined', () => {
|
|
61
|
-
it('should parse and return responseBody as JSON when valid', () => {
|
|
62
|
-
const responseData = {
|
|
63
|
-
ferror: { message: 'Malformed error', type: 'model_not_found' },
|
|
64
|
-
};
|
|
65
|
-
const apiCallError = new APICallError({
|
|
66
|
-
message: 'Request failed',
|
|
67
|
-
statusCode: 404,
|
|
68
|
-
data: undefined,
|
|
69
|
-
responseHeaders: {},
|
|
70
|
-
responseBody: JSON.stringify(responseData),
|
|
71
|
-
url: 'http://test.url',
|
|
72
|
-
requestBodyValues: {},
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
const result = extractApiCallResponse(apiCallError);
|
|
76
|
-
|
|
77
|
-
expect(result).toEqual(responseData);
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it('should return raw responseBody when JSON parsing fails', () => {
|
|
81
|
-
const invalidJson = 'This is not valid JSON';
|
|
82
|
-
const apiCallError = new APICallError({
|
|
83
|
-
message: 'Request failed',
|
|
84
|
-
statusCode: 500,
|
|
85
|
-
data: undefined,
|
|
86
|
-
responseHeaders: {},
|
|
87
|
-
responseBody: invalidJson,
|
|
88
|
-
url: 'http://test.url',
|
|
89
|
-
requestBodyValues: {},
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
const result = extractApiCallResponse(apiCallError);
|
|
93
|
-
|
|
94
|
-
expect(result).toBe(invalidJson);
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it('should handle HTML error responses', () => {
|
|
98
|
-
const htmlResponse =
|
|
99
|
-
'<html><body><h1>500 Internal Server Error</h1></body></html>';
|
|
100
|
-
const apiCallError = new APICallError({
|
|
101
|
-
message: 'Request failed',
|
|
102
|
-
statusCode: 500,
|
|
103
|
-
data: undefined,
|
|
104
|
-
responseHeaders: {},
|
|
105
|
-
responseBody: htmlResponse,
|
|
106
|
-
url: 'http://test.url',
|
|
107
|
-
requestBodyValues: {},
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
const result = extractApiCallResponse(apiCallError);
|
|
111
|
-
|
|
112
|
-
expect(result).toBe(htmlResponse);
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
it('should handle empty string responseBody', () => {
|
|
116
|
-
const apiCallError = new APICallError({
|
|
117
|
-
message: 'Request failed',
|
|
118
|
-
statusCode: 502,
|
|
119
|
-
data: undefined,
|
|
120
|
-
responseHeaders: {},
|
|
121
|
-
responseBody: '',
|
|
122
|
-
url: 'http://test.url',
|
|
123
|
-
requestBodyValues: {},
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
const result = extractApiCallResponse(apiCallError);
|
|
127
|
-
|
|
128
|
-
expect(result).toBe('');
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
it('should handle malformed JSON gracefully', () => {
|
|
132
|
-
const malformedJson = '{"incomplete": json';
|
|
133
|
-
const apiCallError = new APICallError({
|
|
134
|
-
message: 'Request failed',
|
|
135
|
-
statusCode: 500,
|
|
136
|
-
data: undefined,
|
|
137
|
-
responseHeaders: {},
|
|
138
|
-
responseBody: malformedJson,
|
|
139
|
-
url: 'http://test.url',
|
|
140
|
-
requestBodyValues: {},
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
const result = extractApiCallResponse(apiCallError);
|
|
144
|
-
|
|
145
|
-
expect(result).toBe(malformedJson); // Should return raw string, not throw
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
it('should parse complex nested JSON structures', () => {
|
|
149
|
-
const complexData = {
|
|
150
|
-
error: {
|
|
151
|
-
message: 'Complex error',
|
|
152
|
-
type: 'validation_error',
|
|
153
|
-
details: {
|
|
154
|
-
field: 'prompt',
|
|
155
|
-
issues: [
|
|
156
|
-
{ code: 'too_long', message: 'Prompt exceeds maximum length' },
|
|
157
|
-
{
|
|
158
|
-
code: 'invalid_format',
|
|
159
|
-
message: 'Contains invalid characters',
|
|
160
|
-
},
|
|
161
|
-
],
|
|
162
|
-
},
|
|
163
|
-
},
|
|
164
|
-
metadata: {
|
|
165
|
-
requestId: '12345',
|
|
166
|
-
timestamp: '2024-01-01T00:00:00Z',
|
|
167
|
-
},
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
const apiCallError = new APICallError({
|
|
171
|
-
message: 'Request failed',
|
|
172
|
-
statusCode: 400,
|
|
173
|
-
data: undefined,
|
|
174
|
-
responseHeaders: {},
|
|
175
|
-
responseBody: JSON.stringify(complexData),
|
|
176
|
-
url: 'http://test.url',
|
|
177
|
-
requestBodyValues: {},
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
const result = extractApiCallResponse(apiCallError);
|
|
181
|
-
|
|
182
|
-
expect(result).toEqual(complexData);
|
|
183
|
-
});
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
describe('when responseBody is not available', () => {
|
|
187
|
-
it('should return empty object when both data and responseBody are undefined', () => {
|
|
188
|
-
const apiCallError = new APICallError({
|
|
189
|
-
message: 'Request failed',
|
|
190
|
-
statusCode: 500,
|
|
191
|
-
data: undefined,
|
|
192
|
-
responseHeaders: {},
|
|
193
|
-
responseBody: undefined as any, // Simulating missing responseBody
|
|
194
|
-
url: 'http://test.url',
|
|
195
|
-
requestBodyValues: {},
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
const result = extractApiCallResponse(apiCallError);
|
|
199
|
-
|
|
200
|
-
expect(result).toEqual({});
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
it('should return empty object when responseBody is null', () => {
|
|
204
|
-
const apiCallError = new APICallError({
|
|
205
|
-
message: 'Request failed',
|
|
206
|
-
statusCode: 500,
|
|
207
|
-
data: undefined,
|
|
208
|
-
responseHeaders: {},
|
|
209
|
-
responseBody: null as any,
|
|
210
|
-
url: 'http://test.url',
|
|
211
|
-
requestBodyValues: {},
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
const result = extractApiCallResponse(apiCallError);
|
|
215
|
-
|
|
216
|
-
expect(result).toEqual({});
|
|
217
|
-
});
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
describe('edge cases', () => {
|
|
221
|
-
it('should handle numeric responseBody', () => {
|
|
222
|
-
const apiCallError = new APICallError({
|
|
223
|
-
message: 'Request failed',
|
|
224
|
-
statusCode: 500,
|
|
225
|
-
data: undefined,
|
|
226
|
-
responseHeaders: {},
|
|
227
|
-
responseBody: '404',
|
|
228
|
-
url: 'http://test.url',
|
|
229
|
-
requestBodyValues: {},
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
const result = extractApiCallResponse(apiCallError);
|
|
233
|
-
|
|
234
|
-
expect(result).toBe(404); // Should parse as number
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
it('should handle boolean responseBody', () => {
|
|
238
|
-
const apiCallError = new APICallError({
|
|
239
|
-
message: 'Request failed',
|
|
240
|
-
statusCode: 500,
|
|
241
|
-
data: undefined,
|
|
242
|
-
responseHeaders: {},
|
|
243
|
-
responseBody: 'true',
|
|
244
|
-
url: 'http://test.url',
|
|
245
|
-
requestBodyValues: {},
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
const result = extractApiCallResponse(apiCallError);
|
|
249
|
-
|
|
250
|
-
expect(result).toBe(true); // Should parse as boolean
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
it('should handle array responseBody', () => {
|
|
254
|
-
const arrayData = ['error1', 'error2', 'error3'];
|
|
255
|
-
const apiCallError = new APICallError({
|
|
256
|
-
message: 'Request failed',
|
|
257
|
-
statusCode: 400,
|
|
258
|
-
data: undefined,
|
|
259
|
-
responseHeaders: {},
|
|
260
|
-
responseBody: JSON.stringify(arrayData),
|
|
261
|
-
url: 'http://test.url',
|
|
262
|
-
requestBodyValues: {},
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
const result = extractApiCallResponse(apiCallError);
|
|
266
|
-
|
|
267
|
-
expect(result).toEqual(arrayData);
|
|
268
|
-
});
|
|
269
|
-
});
|
|
270
|
-
});
|
|
@@ -1,278 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import {
|
|
3
|
-
GatewayError,
|
|
4
|
-
GatewayAuthenticationError,
|
|
5
|
-
GatewayInvalidRequestError,
|
|
6
|
-
GatewayRateLimitError,
|
|
7
|
-
GatewayModelNotFoundError,
|
|
8
|
-
GatewayInternalServerError,
|
|
9
|
-
GatewayResponseError,
|
|
10
|
-
} from './index';
|
|
11
|
-
|
|
12
|
-
describe('GatewayAuthenticationError', () => {
|
|
13
|
-
it('should create error with default values', () => {
|
|
14
|
-
const error = new GatewayAuthenticationError();
|
|
15
|
-
|
|
16
|
-
expect(error).toBeInstanceOf(Error);
|
|
17
|
-
expect(error).toBeInstanceOf(GatewayError);
|
|
18
|
-
expect(error.name).toBe('GatewayAuthenticationError');
|
|
19
|
-
expect(error.type).toBe('authentication_error');
|
|
20
|
-
expect(error.message).toBe('Authentication failed');
|
|
21
|
-
expect(error.statusCode).toBe(401);
|
|
22
|
-
expect(error.cause).toBeUndefined();
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it('should create error with custom values', () => {
|
|
26
|
-
const cause = new Error('Original error');
|
|
27
|
-
const error = new GatewayAuthenticationError({
|
|
28
|
-
message: 'Custom auth failed',
|
|
29
|
-
statusCode: 403,
|
|
30
|
-
cause,
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
expect(error.message).toBe('Custom auth failed');
|
|
34
|
-
expect(error.statusCode).toBe(403);
|
|
35
|
-
expect(error.cause).toBe(cause);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it('should be identifiable via instance check', () => {
|
|
39
|
-
const error = new GatewayAuthenticationError();
|
|
40
|
-
const otherError = new Error('Not a gateway error');
|
|
41
|
-
|
|
42
|
-
expect(GatewayAuthenticationError.isInstance(error)).toBe(true);
|
|
43
|
-
expect(GatewayAuthenticationError.isInstance(otherError)).toBe(false);
|
|
44
|
-
expect(GatewayError.isInstance(error)).toBe(true);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
describe('createContextualError', () => {
|
|
48
|
-
it('should create error for invalid API key only', () => {
|
|
49
|
-
const error = GatewayAuthenticationError.createContextualError({
|
|
50
|
-
apiKeyProvided: true,
|
|
51
|
-
oidcTokenProvided: false,
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
expect(error.message).toContain('Invalid API key');
|
|
55
|
-
expect(error.message).toContain('vercel.com/d?to=');
|
|
56
|
-
expect(error.statusCode).toBe(401);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it('should create error for invalid OIDC token only', () => {
|
|
60
|
-
const error = GatewayAuthenticationError.createContextualError({
|
|
61
|
-
apiKeyProvided: false,
|
|
62
|
-
oidcTokenProvided: true,
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
expect(error.message).toContain('Invalid OIDC token');
|
|
66
|
-
expect(error.message).toContain('npx vercel link');
|
|
67
|
-
expect(error.statusCode).toBe(401);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it('should create error for no authentication provided', () => {
|
|
71
|
-
const error = GatewayAuthenticationError.createContextualError({
|
|
72
|
-
apiKeyProvided: false,
|
|
73
|
-
oidcTokenProvided: false,
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
expect(error.message).toContain('No authentication provided');
|
|
77
|
-
expect(error.message).toContain('Option 1');
|
|
78
|
-
expect(error.message).toContain('Option 2');
|
|
79
|
-
expect(error.statusCode).toBe(401);
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
it('should prioritize API key error when both were provided', () => {
|
|
83
|
-
const error = GatewayAuthenticationError.createContextualError({
|
|
84
|
-
apiKeyProvided: true,
|
|
85
|
-
oidcTokenProvided: true,
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
expect(error.message).toContain('Invalid API key');
|
|
89
|
-
expect(error.message).toContain('vercel.com/d?to=');
|
|
90
|
-
expect(error.statusCode).toBe(401);
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
it('should create error for neither provided (legacy test)', () => {
|
|
94
|
-
const error = GatewayAuthenticationError.createContextualError({
|
|
95
|
-
apiKeyProvided: false,
|
|
96
|
-
oidcTokenProvided: false,
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
expect(error.message).toContain('No authentication provided');
|
|
100
|
-
expect(error.message).toContain('Option 1');
|
|
101
|
-
expect(error.message).toContain('Option 2');
|
|
102
|
-
expect(error.statusCode).toBe(401);
|
|
103
|
-
});
|
|
104
|
-
});
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
describe('GatewayInvalidRequestError', () => {
|
|
108
|
-
it('should create error with default values', () => {
|
|
109
|
-
const error = new GatewayInvalidRequestError();
|
|
110
|
-
|
|
111
|
-
expect(error.name).toBe('GatewayInvalidRequestError');
|
|
112
|
-
expect(error.type).toBe('invalid_request_error');
|
|
113
|
-
expect(error.message).toBe('Invalid request');
|
|
114
|
-
expect(error.statusCode).toBe(400);
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
it('should create error with custom values', () => {
|
|
118
|
-
const error = new GatewayInvalidRequestError({
|
|
119
|
-
message: 'Missing required field',
|
|
120
|
-
statusCode: 422,
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
expect(error.message).toBe('Missing required field');
|
|
124
|
-
expect(error.statusCode).toBe(422);
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
it('should be identifiable via instance check', () => {
|
|
128
|
-
const error = new GatewayInvalidRequestError();
|
|
129
|
-
|
|
130
|
-
expect(GatewayInvalidRequestError.isInstance(error)).toBe(true);
|
|
131
|
-
expect(GatewayAuthenticationError.isInstance(error)).toBe(false);
|
|
132
|
-
expect(GatewayError.isInstance(error)).toBe(true);
|
|
133
|
-
});
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
describe('GatewayRateLimitError', () => {
|
|
137
|
-
it('should create error with default values', () => {
|
|
138
|
-
const error = new GatewayRateLimitError();
|
|
139
|
-
|
|
140
|
-
expect(error.name).toBe('GatewayRateLimitError');
|
|
141
|
-
expect(error.type).toBe('rate_limit_exceeded');
|
|
142
|
-
expect(error.message).toBe('Rate limit exceeded');
|
|
143
|
-
expect(error.statusCode).toBe(429);
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
it('should be identifiable via instance check', () => {
|
|
147
|
-
const error = new GatewayRateLimitError();
|
|
148
|
-
|
|
149
|
-
expect(GatewayRateLimitError.isInstance(error)).toBe(true);
|
|
150
|
-
expect(GatewayError.isInstance(error)).toBe(true);
|
|
151
|
-
});
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
describe('GatewayModelNotFoundError', () => {
|
|
155
|
-
it('should create error with default values', () => {
|
|
156
|
-
const error = new GatewayModelNotFoundError();
|
|
157
|
-
|
|
158
|
-
expect(error.name).toBe('GatewayModelNotFoundError');
|
|
159
|
-
expect(error.type).toBe('model_not_found');
|
|
160
|
-
expect(error.message).toBe('Model not found');
|
|
161
|
-
expect(error.statusCode).toBe(404);
|
|
162
|
-
expect(error.modelId).toBeUndefined();
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
it('should create error with model ID', () => {
|
|
166
|
-
const error = new GatewayModelNotFoundError({
|
|
167
|
-
message: 'Model gpt-4 not found',
|
|
168
|
-
modelId: 'gpt-4',
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
expect(error.message).toBe('Model gpt-4 not found');
|
|
172
|
-
expect(error.modelId).toBe('gpt-4');
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
it('should be identifiable via instance check', () => {
|
|
176
|
-
const error = new GatewayModelNotFoundError();
|
|
177
|
-
|
|
178
|
-
expect(GatewayModelNotFoundError.isInstance(error)).toBe(true);
|
|
179
|
-
expect(GatewayError.isInstance(error)).toBe(true);
|
|
180
|
-
});
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
describe('GatewayInternalServerError', () => {
|
|
184
|
-
it('should create error with default values', () => {
|
|
185
|
-
const error = new GatewayInternalServerError();
|
|
186
|
-
|
|
187
|
-
expect(error.name).toBe('GatewayInternalServerError');
|
|
188
|
-
expect(error.type).toBe('internal_server_error');
|
|
189
|
-
expect(error.message).toBe('Internal server error');
|
|
190
|
-
expect(error.statusCode).toBe(500);
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
it('should be identifiable via instance check', () => {
|
|
194
|
-
const error = new GatewayInternalServerError();
|
|
195
|
-
|
|
196
|
-
expect(GatewayInternalServerError.isInstance(error)).toBe(true);
|
|
197
|
-
expect(GatewayError.isInstance(error)).toBe(true);
|
|
198
|
-
});
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
describe('GatewayResponseError', () => {
|
|
202
|
-
it('should create error with default values', () => {
|
|
203
|
-
const error = new GatewayResponseError();
|
|
204
|
-
|
|
205
|
-
expect(error.name).toBe('GatewayResponseError');
|
|
206
|
-
expect(error.type).toBe('response_error');
|
|
207
|
-
expect(error.message).toBe('Invalid response from Gateway');
|
|
208
|
-
expect(error.statusCode).toBe(502);
|
|
209
|
-
expect(error.response).toBeUndefined();
|
|
210
|
-
expect(error.validationError).toBeUndefined();
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
it('should create error with response and validation error details', () => {
|
|
214
|
-
const response = { invalidField: 'value' };
|
|
215
|
-
const validationError = {
|
|
216
|
-
issues: [{ path: ['error'], message: 'Required' }],
|
|
217
|
-
} as any; // Mock ZodError structure
|
|
218
|
-
|
|
219
|
-
const error = new GatewayResponseError({
|
|
220
|
-
message: 'Custom parsing error',
|
|
221
|
-
statusCode: 422,
|
|
222
|
-
response,
|
|
223
|
-
validationError,
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
expect(error.message).toBe('Custom parsing error');
|
|
227
|
-
expect(error.statusCode).toBe(422);
|
|
228
|
-
expect(error.response).toBe(response);
|
|
229
|
-
expect(error.validationError).toBe(validationError);
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
it('should be identifiable via instance check', () => {
|
|
233
|
-
const error = new GatewayResponseError();
|
|
234
|
-
|
|
235
|
-
expect(GatewayResponseError.isInstance(error)).toBe(true);
|
|
236
|
-
expect(GatewayError.isInstance(error)).toBe(true);
|
|
237
|
-
});
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
describe('Cross-realm instance checking', () => {
|
|
241
|
-
it('should work with symbol-based type checking', () => {
|
|
242
|
-
const error = new GatewayAuthenticationError();
|
|
243
|
-
|
|
244
|
-
// Simulate different realm by creating a new instance in different context
|
|
245
|
-
const gatewayErrorMarker = Symbol.for('vercel.ai.gateway.error');
|
|
246
|
-
const authErrorMarker = Symbol.for(
|
|
247
|
-
'vercel.ai.gateway.error.GatewayAuthenticationError',
|
|
248
|
-
);
|
|
249
|
-
|
|
250
|
-
// Verify the symbols are present
|
|
251
|
-
expect((error as any)[gatewayErrorMarker]).toBe(true);
|
|
252
|
-
expect((error as any)[authErrorMarker]).toBe(true);
|
|
253
|
-
|
|
254
|
-
// Test cross-realm safety
|
|
255
|
-
expect(GatewayError.hasMarker(error)).toBe(true);
|
|
256
|
-
expect(GatewayAuthenticationError.isInstance(error)).toBe(true);
|
|
257
|
-
});
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
describe('Error inheritance chain', () => {
|
|
261
|
-
it('should maintain proper inheritance', () => {
|
|
262
|
-
const error = new GatewayAuthenticationError();
|
|
263
|
-
|
|
264
|
-
expect(error instanceof Error).toBe(true);
|
|
265
|
-
expect(error instanceof GatewayError).toBe(true);
|
|
266
|
-
expect(error instanceof GatewayAuthenticationError).toBe(true);
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
it('should have proper stack traces', () => {
|
|
270
|
-
const error = new GatewayAuthenticationError({
|
|
271
|
-
message: 'Test error',
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
expect(error.stack).toBeDefined();
|
|
275
|
-
expect(error.stack).toContain('GatewayAuthenticationError');
|
|
276
|
-
expect(error.stack).toContain('Test error');
|
|
277
|
-
});
|
|
278
|
-
});
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import {
|
|
3
|
-
GATEWAY_AUTH_METHOD_HEADER,
|
|
4
|
-
parseAuthMethod,
|
|
5
|
-
} from './parse-auth-method';
|
|
6
|
-
|
|
7
|
-
describe('GATEWAY_AUTH_METHOD_HEADER', () => {
|
|
8
|
-
it('should export the correct header name', () => {
|
|
9
|
-
expect(GATEWAY_AUTH_METHOD_HEADER).toBe('ai-gateway-auth-method');
|
|
10
|
-
});
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
describe('parseAuthMethod', () => {
|
|
14
|
-
describe('valid authentication methods', () => {
|
|
15
|
-
it('should parse "api-key" auth method', async () => {
|
|
16
|
-
const headers = {
|
|
17
|
-
[GATEWAY_AUTH_METHOD_HEADER]: 'api-key',
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
const result = await parseAuthMethod(headers);
|
|
21
|
-
|
|
22
|
-
expect(result).toBe('api-key');
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it('should parse "oidc" auth method', async () => {
|
|
26
|
-
const headers = {
|
|
27
|
-
[GATEWAY_AUTH_METHOD_HEADER]: 'oidc',
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
const result = await parseAuthMethod(headers);
|
|
31
|
-
|
|
32
|
-
expect(result).toBe('oidc');
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('should handle headers with other fields present', async () => {
|
|
36
|
-
const headers = {
|
|
37
|
-
authorization: 'Bearer token',
|
|
38
|
-
'content-type': 'application/json',
|
|
39
|
-
[GATEWAY_AUTH_METHOD_HEADER]: 'api-key',
|
|
40
|
-
'user-agent': 'test-agent',
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
const result = await parseAuthMethod(headers);
|
|
44
|
-
|
|
45
|
-
expect(result).toBe('api-key');
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
describe('invalid authentication methods', () => {
|
|
50
|
-
it('should return undefined for invalid auth method string', async () => {
|
|
51
|
-
const headers = {
|
|
52
|
-
[GATEWAY_AUTH_METHOD_HEADER]: 'invalid-method',
|
|
53
|
-
};
|
|
54
|
-
expect(await parseAuthMethod(headers)).toBeUndefined();
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('should return undefined for empty string', async () => {
|
|
58
|
-
const headers = {
|
|
59
|
-
[GATEWAY_AUTH_METHOD_HEADER]: '',
|
|
60
|
-
};
|
|
61
|
-
expect(await parseAuthMethod(headers)).toBeUndefined();
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it('should return undefined for numeric value', async () => {
|
|
65
|
-
const headers = {
|
|
66
|
-
[GATEWAY_AUTH_METHOD_HEADER]: '123',
|
|
67
|
-
};
|
|
68
|
-
expect(await parseAuthMethod(headers)).toBeUndefined();
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it('should return undefined for boolean-like strings', async () => {
|
|
72
|
-
const headers = {
|
|
73
|
-
[GATEWAY_AUTH_METHOD_HEADER]: 'true',
|
|
74
|
-
};
|
|
75
|
-
expect(await parseAuthMethod(headers)).toBeUndefined();
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('should return undefined for case-sensitive variations', async () => {
|
|
79
|
-
const headers = {
|
|
80
|
-
[GATEWAY_AUTH_METHOD_HEADER]: 'API-KEY',
|
|
81
|
-
};
|
|
82
|
-
expect(await parseAuthMethod(headers)).toBeUndefined();
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it('should return undefined for OIDC case variations', async () => {
|
|
86
|
-
const headers = {
|
|
87
|
-
[GATEWAY_AUTH_METHOD_HEADER]: 'OIDC',
|
|
88
|
-
};
|
|
89
|
-
expect(await parseAuthMethod(headers)).toBeUndefined();
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
describe('missing or undefined headers', () => {
|
|
94
|
-
it('should return undefined when header is missing', async () => {
|
|
95
|
-
const headers = {
|
|
96
|
-
authorization: 'Bearer token',
|
|
97
|
-
};
|
|
98
|
-
expect(await parseAuthMethod(headers)).toBeUndefined();
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it('should return undefined when header is undefined', async () => {
|
|
102
|
-
const headers = {
|
|
103
|
-
[GATEWAY_AUTH_METHOD_HEADER]: undefined,
|
|
104
|
-
};
|
|
105
|
-
expect(await parseAuthMethod(headers)).toBeUndefined();
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it('should return undefined when headers object is empty', async () => {
|
|
109
|
-
const headers = {};
|
|
110
|
-
expect(await parseAuthMethod(headers)).toBeUndefined();
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
describe('edge cases', () => {
|
|
115
|
-
it('should return undefined for whitespace-only strings', async () => {
|
|
116
|
-
const headers = {
|
|
117
|
-
[GATEWAY_AUTH_METHOD_HEADER]: ' ',
|
|
118
|
-
};
|
|
119
|
-
expect(await parseAuthMethod(headers)).toBeUndefined();
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
it('should return undefined for auth methods with extra whitespace', async () => {
|
|
123
|
-
const headers = {
|
|
124
|
-
[GATEWAY_AUTH_METHOD_HEADER]: ' api-key ',
|
|
125
|
-
};
|
|
126
|
-
expect(await parseAuthMethod(headers)).toBeUndefined();
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
it('should handle null values', async () => {
|
|
130
|
-
const headers = {
|
|
131
|
-
[GATEWAY_AUTH_METHOD_HEADER]: null as any,
|
|
132
|
-
};
|
|
133
|
-
expect(await parseAuthMethod(headers)).toBeUndefined();
|
|
134
|
-
});
|
|
135
|
-
});
|
|
136
|
-
});
|