@ai-sdk/google-vertex 4.0.27 → 4.0.28

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.
@@ -1,308 +0,0 @@
1
- import {
2
- generateAuthToken,
3
- GoogleCredentials,
4
- } from './google-vertex-auth-edge';
5
- import { beforeEach, afterEach, describe, expect, it, vi } from 'vitest';
6
-
7
- // Mock provider-utils to control runtime environment detection
8
- vi.mock('@ai-sdk/provider-utils', async () => {
9
- const actual = await vi.importActual('@ai-sdk/provider-utils');
10
- return {
11
- ...actual,
12
- getRuntimeEnvironmentUserAgent: vi.fn(() => 'runtime/testenv'),
13
- withUserAgentSuffix: actual.withUserAgentSuffix,
14
- };
15
- });
16
-
17
- vi.mock('../version', () => ({
18
- VERSION: '0.0.0-test',
19
- }));
20
-
21
- describe('Google Vertex Edge Auth', () => {
22
- const mockCredentials: GoogleCredentials = {
23
- clientEmail: 'test@test.iam.gserviceaccount.com',
24
- privateKey: 'mock-private-key',
25
- privateKeyId: 'test-key-id',
26
- };
27
-
28
- const setupAtobStub = (credentials: typeof mockCredentials) => {
29
- vi.stubGlobal(
30
- 'atob',
31
- vi.fn().mockImplementation(str => {
32
- const payload = {
33
- alg: 'RS256',
34
- typ: 'JWT',
35
- iss: credentials.clientEmail,
36
- scope: 'https://www.googleapis.com/auth/cloud-platform',
37
- aud: 'https://oauth2.googleapis.com/token',
38
- iat: 1616161616,
39
- exp: 1616165216,
40
- };
41
-
42
- if (credentials.privateKeyId) {
43
- Object.assign(payload, { kid: credentials.privateKeyId });
44
- }
45
-
46
- return JSON.stringify(payload);
47
- }),
48
- );
49
- };
50
-
51
- beforeEach(() => {
52
- // Mock WebCrypto
53
- const mockSubtleCrypto = {
54
- importKey: vi.fn().mockResolvedValue('mock-crypto-key'),
55
- sign: vi.fn().mockResolvedValue(new Uint8Array([1, 2, 3])),
56
- };
57
-
58
- const mockCrypto = {
59
- subtle: mockSubtleCrypto,
60
- };
61
-
62
- // Use vi.stubGlobal instead of direct assignment
63
- vi.stubGlobal('crypto', mockCrypto);
64
-
65
- // Mock fetch
66
- global.fetch = vi.fn().mockResolvedValue({
67
- ok: true,
68
- json: () => Promise.resolve({ access_token: 'mock.jwt.token' }),
69
- });
70
-
71
- setupAtobStub(mockCredentials);
72
- });
73
-
74
- afterEach(() => {
75
- vi.restoreAllMocks();
76
- vi.unstubAllGlobals();
77
- });
78
-
79
- it('should generate a valid JWT token', async () => {
80
- // Mock successful token exchange
81
- global.fetch = vi.fn().mockResolvedValue({
82
- ok: true,
83
- json: () =>
84
- Promise.resolve({
85
- access_token:
86
- 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InRlc3Qta2V5LWlkIn0.eyJpc3MiOiJ0ZXN0QHRlc3QuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJzY29wZSI6Imh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2F1dGgvY2xvdWQtcGxhdGZvcm0iLCJhdWQiOiJodHRwczovL29hdXRoMi5nb29nbGVhcGlzLmNvbS90b2tlbiIsImlhdCI6MTYxNjE2MTYxNiwiZXhwIjoxNjE2MTY1MjE2fQ.signature',
87
- }),
88
- });
89
-
90
- const token = await generateAuthToken(mockCredentials);
91
-
92
- // JWT structure validation
93
- const parts = token.split('.');
94
- expect(parts).toHaveLength(3);
95
-
96
- // Header validation
97
- const header = JSON.parse(atob(parts[0]));
98
- expect(header).toMatchObject({
99
- alg: 'RS256',
100
- typ: 'JWT',
101
- kid: mockCredentials.privateKeyId,
102
- iss: mockCredentials.clientEmail,
103
- });
104
-
105
- // Payload validation
106
- const payload = JSON.parse(atob(parts[1]));
107
- expect(payload).toHaveProperty('iss', mockCredentials.clientEmail);
108
- expect(payload).toHaveProperty(
109
- 'scope',
110
- 'https://www.googleapis.com/auth/cloud-platform',
111
- );
112
- expect(payload).toHaveProperty(
113
- 'aud',
114
- 'https://oauth2.googleapis.com/token',
115
- );
116
- expect(payload).toHaveProperty('iat');
117
- expect(payload).toHaveProperty('exp');
118
-
119
- // Verify exp is ~1 hour after iat
120
- expect(payload.exp - payload.iat).toBe(3600);
121
- });
122
-
123
- it('should throw error with invalid credentials', async () => {
124
- // Mock failed token exchange
125
- global.fetch = vi.fn().mockResolvedValue({
126
- ok: false,
127
- status: 400,
128
- statusText: 'Bad Request',
129
- json: () => Promise.resolve({ error: 'invalid_grant' }),
130
- });
131
-
132
- const invalidCredentials = {
133
- ...mockCredentials,
134
- private_key: 'invalid-key',
135
- };
136
-
137
- await expect(generateAuthToken(invalidCredentials)).rejects.toThrow(
138
- 'Token request failed: Bad Request',
139
- );
140
- });
141
-
142
- it('should load credentials from environment variables', async () => {
143
- process.env.GOOGLE_CLIENT_EMAIL = mockCredentials.clientEmail;
144
- process.env.GOOGLE_PRIVATE_KEY = mockCredentials.privateKey;
145
- process.env.GOOGLE_PRIVATE_KEY_ID = mockCredentials.privateKeyId;
146
-
147
- // Mock successful token exchange
148
- global.fetch = vi.fn().mockResolvedValue({
149
- ok: true,
150
- json: () => Promise.resolve({ access_token: 'mock.jwt.token' }),
151
- });
152
-
153
- const token = await generateAuthToken();
154
- expect(token).toBeTruthy();
155
- expect(token.split('.')).toHaveLength(3);
156
-
157
- // Clean up
158
- delete process.env.GOOGLE_CLIENT_EMAIL;
159
- delete process.env.GOOGLE_PRIVATE_KEY;
160
- delete process.env.GOOGLE_PRIVATE_KEY_ID;
161
- });
162
-
163
- it('should throw error when client email is missing', async () => {
164
- delete process.env.GOOGLE_CLIENT_EMAIL;
165
- process.env.GOOGLE_PRIVATE_KEY = mockCredentials.privateKey;
166
- process.env.GOOGLE_PRIVATE_KEY_ID = mockCredentials.privateKeyId;
167
-
168
- await expect(generateAuthToken()).rejects.toThrow(
169
- "Google client email setting is missing. Pass it using the 'clientEmail' parameter or the GOOGLE_CLIENT_EMAIL environment variable.",
170
- );
171
-
172
- // Clean up
173
- delete process.env.GOOGLE_PRIVATE_KEY;
174
- delete process.env.GOOGLE_PRIVATE_KEY_ID;
175
- });
176
-
177
- it('should throw error when private key is missing', async () => {
178
- process.env.GOOGLE_CLIENT_EMAIL = mockCredentials.clientEmail;
179
- delete process.env.GOOGLE_PRIVATE_KEY;
180
- process.env.GOOGLE_PRIVATE_KEY_ID = mockCredentials.privateKeyId;
181
-
182
- await expect(generateAuthToken()).rejects.toThrow(
183
- "Google private key setting is missing. Pass it using the 'privateKey' parameter or the GOOGLE_PRIVATE_KEY environment variable.",
184
- );
185
-
186
- // Clean up
187
- delete process.env.GOOGLE_CLIENT_EMAIL;
188
- delete process.env.GOOGLE_PRIVATE_KEY_ID;
189
- });
190
-
191
- it('should work with or without private key ID', async () => {
192
- // Test with private key ID
193
- process.env.GOOGLE_CLIENT_EMAIL = mockCredentials.clientEmail;
194
- process.env.GOOGLE_PRIVATE_KEY = mockCredentials.privateKey;
195
- process.env.GOOGLE_PRIVATE_KEY_ID = mockCredentials.privateKeyId;
196
-
197
- // Mock successful token exchange
198
- global.fetch = vi.fn().mockResolvedValue({
199
- ok: true,
200
- json: () => Promise.resolve({ access_token: 'mock.jwt.token' }),
201
- });
202
-
203
- const tokenWithKeyId = await generateAuthToken();
204
- expect(tokenWithKeyId).toBeTruthy();
205
-
206
- // Test without private key ID
207
- delete process.env.GOOGLE_PRIVATE_KEY_ID;
208
-
209
- const tokenWithoutKeyId = await generateAuthToken();
210
- expect(tokenWithoutKeyId).toBeTruthy();
211
-
212
- // Clean up
213
- delete process.env.GOOGLE_CLIENT_EMAIL;
214
- delete process.env.GOOGLE_PRIVATE_KEY;
215
- });
216
-
217
- it('should handle newlines in private key from env vars', async () => {
218
- process.env.GOOGLE_CLIENT_EMAIL = mockCredentials.clientEmail;
219
- process.env.GOOGLE_PRIVATE_KEY = mockCredentials.privateKey.replace(
220
- /\n/g,
221
- '\\n',
222
- );
223
- process.env.GOOGLE_PRIVATE_KEY_ID = mockCredentials.privateKeyId;
224
-
225
- // Mock successful token exchange
226
- global.fetch = vi.fn().mockResolvedValue({
227
- ok: true,
228
- json: () => Promise.resolve({ access_token: 'mock.jwt.token' }),
229
- });
230
-
231
- const token = await generateAuthToken();
232
- expect(token).toBeTruthy();
233
-
234
- // Clean up
235
- delete process.env.GOOGLE_CLIENT_EMAIL;
236
- delete process.env.GOOGLE_PRIVATE_KEY;
237
- delete process.env.GOOGLE_PRIVATE_KEY_ID;
238
- });
239
-
240
- it('should throw error on fetch failure', async () => {
241
- const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
242
- global.fetch = vi.fn().mockRejectedValue(new Error('Network error'));
243
-
244
- await expect(generateAuthToken(mockCredentials)).rejects.toThrow(
245
- 'Network error',
246
- );
247
-
248
- consoleSpy.mockRestore();
249
- });
250
-
251
- it('should throw error when token request fails', async () => {
252
- // Mock a failed response from the token endpoint
253
- global.fetch = vi.fn().mockResolvedValue({
254
- ok: false,
255
- statusText: 'Unauthorized',
256
- status: 401,
257
- json: () => Promise.resolve({ error: 'unauthorized' }),
258
- });
259
-
260
- await expect(generateAuthToken(mockCredentials)).rejects.toThrow(
261
- 'Token request failed: Unauthorized',
262
- );
263
- });
264
-
265
- it('should work without privateKeyId', async () => {
266
- const credentialsWithoutKeyId = {
267
- clientEmail: mockCredentials.clientEmail,
268
- privateKey: mockCredentials.privateKey,
269
- };
270
- setupAtobStub(credentialsWithoutKeyId);
271
-
272
- // Mock successful token exchange
273
- global.fetch = vi.fn().mockResolvedValue({
274
- ok: true,
275
- json: () => Promise.resolve({ access_token: 'mock.jwt.token' }),
276
- });
277
-
278
- const token = await generateAuthToken(credentialsWithoutKeyId);
279
- expect(token).toBeTruthy();
280
-
281
- // Verify the JWT structure
282
- const parts = token.split('.');
283
- expect(parts).toHaveLength(3);
284
-
285
- // Verify header doesn't include kid when privateKeyId is not provided
286
- const header = JSON.parse(atob(parts[0]));
287
- expect(header).not.toHaveProperty('kid');
288
- });
289
-
290
- it('should include correct user-agent header', async () => {
291
- const mockFetch = vi.fn().mockResolvedValue({
292
- ok: true,
293
- json: () => Promise.resolve({ access_token: 'mock.jwt.token' }),
294
- });
295
- global.fetch = mockFetch;
296
-
297
- await generateAuthToken(mockCredentials);
298
-
299
- expect(mockFetch).toHaveBeenCalledWith(
300
- 'https://oauth2.googleapis.com/token',
301
- expect.objectContaining({
302
- headers: expect.objectContaining({
303
- 'user-agent': 'ai-sdk/google-vertex/0.0.0-test runtime/testenv',
304
- }),
305
- }),
306
- );
307
- });
308
- });
@@ -1,105 +0,0 @@
1
- import { resolve } from '@ai-sdk/provider-utils';
2
- import { createVertex as createVertexEdge } from './google-vertex-provider-edge';
3
- import { createVertex as createVertexOriginal } from '../google-vertex-provider';
4
- import * as edgeAuth from './google-vertex-auth-edge';
5
- import { describe, beforeEach, afterEach, expect, it, vi } from 'vitest';
6
-
7
- // Mock the imported modules
8
- vi.mock('./google-vertex-auth-edge', () => ({
9
- generateAuthToken: vi.fn().mockResolvedValue('mock-auth-token'),
10
- }));
11
-
12
- vi.mock('../google-vertex-provider', () => ({
13
- createVertex: vi.fn().mockImplementation(options => ({
14
- ...options,
15
- })),
16
- }));
17
-
18
- describe('google-vertex-provider-edge', () => {
19
- beforeEach(() => {
20
- vi.clearAllMocks();
21
- delete process.env.GOOGLE_VERTEX_API_KEY;
22
- });
23
-
24
- afterEach(() => {
25
- delete process.env.GOOGLE_VERTEX_API_KEY;
26
- });
27
-
28
- it('default headers function should return auth token', async () => {
29
- createVertexEdge({ project: 'test-project' });
30
-
31
- const mockCreateVertex = vi.mocked(createVertexOriginal);
32
- const passedOptions = mockCreateVertex.mock.calls[0][0];
33
-
34
- expect(mockCreateVertex).toHaveBeenCalledTimes(1);
35
- expect(typeof passedOptions?.headers).toBe('function');
36
-
37
- expect(await resolve(passedOptions?.headers)).toStrictEqual({
38
- Authorization: 'Bearer mock-auth-token',
39
- });
40
- });
41
-
42
- it('should use custom headers in addition to auth token when provided', async () => {
43
- createVertexEdge({
44
- project: 'test-project',
45
- headers: async () => ({
46
- 'Custom-Header': 'custom-value',
47
- }),
48
- });
49
-
50
- const mockCreateVertex = vi.mocked(createVertexOriginal);
51
- const passedOptions = mockCreateVertex.mock.calls[0][0];
52
-
53
- expect(mockCreateVertex).toHaveBeenCalledTimes(1);
54
- expect(typeof passedOptions?.headers).toBe('function');
55
- expect(await resolve(passedOptions?.headers)).toEqual({
56
- Authorization: 'Bearer mock-auth-token',
57
- 'Custom-Header': 'custom-value',
58
- });
59
- });
60
-
61
- it('should use edge auth token generator', async () => {
62
- createVertexEdge({ project: 'test-project' });
63
-
64
- const mockCreateVertex = vi.mocked(createVertexOriginal);
65
- const passedOptions = mockCreateVertex.mock.calls[0][0];
66
-
67
- // Verify the headers function actually calls generateAuthToken by checking its result
68
- expect(passedOptions?.headers).toBeDefined();
69
- await resolve(passedOptions?.headers);
70
- expect(edgeAuth.generateAuthToken).toHaveBeenCalled();
71
- });
72
-
73
- it('passes googleCredentials to generateAuthToken', async () => {
74
- createVertexEdge({
75
- project: 'test-project',
76
- googleCredentials: {
77
- clientEmail: 'test@example.com',
78
- privateKey: 'test-key',
79
- },
80
- });
81
-
82
- const mockCreateVertex = vi.mocked(createVertexOriginal);
83
- const passedOptions = mockCreateVertex.mock.calls[0][0];
84
-
85
- await resolve(passedOptions?.headers); // call the headers function
86
-
87
- expect(edgeAuth.generateAuthToken).toHaveBeenCalledWith({
88
- clientEmail: 'test@example.com',
89
- privateKey: 'test-key',
90
- });
91
- });
92
-
93
- it('should pass options through to base provider when apiKey is provided', async () => {
94
- createVertexEdge({
95
- apiKey: 'test-api-key',
96
- });
97
-
98
- const mockCreateVertex = vi.mocked(createVertexOriginal);
99
- const passedOptions = mockCreateVertex.mock.calls[0][0];
100
-
101
- expect(passedOptions?.apiKey).toBe('test-api-key');
102
- expect(passedOptions?.headers).toBeUndefined();
103
- expect(edgeAuth.generateAuthToken).not.toHaveBeenCalled();
104
- });
105
- });
@@ -1,59 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach } from 'vitest';
2
- import {
3
- generateAuthToken,
4
- _resetAuthInstance,
5
- } from './google-vertex-auth-google-auth-library';
6
- import { GoogleAuth } from 'google-auth-library';
7
-
8
- vi.mock('google-auth-library', () => {
9
- return {
10
- GoogleAuth: vi.fn().mockImplementation(() => {
11
- return {
12
- getClient: vi.fn().mockResolvedValue({
13
- getAccessToken: vi.fn().mockResolvedValue({ token: 'mocked-token' }),
14
- }),
15
- };
16
- }),
17
- };
18
- });
19
-
20
- describe('generateAuthToken', () => {
21
- beforeEach(() => {
22
- vi.clearAllMocks();
23
- _resetAuthInstance();
24
- });
25
-
26
- it('should generate a valid auth token', async () => {
27
- const token = await generateAuthToken();
28
- expect(token).toBe('mocked-token');
29
- });
30
-
31
- it('should return null if no token is received', async () => {
32
- // Reset the mock completely
33
- vi.mocked(GoogleAuth).mockReset();
34
-
35
- // Create a new mock implementation
36
- vi.mocked(GoogleAuth).mockImplementation(
37
- () =>
38
- ({
39
- getClient: vi.fn().mockResolvedValue({
40
- getAccessToken: vi.fn().mockResolvedValue({ token: null }),
41
- }),
42
- isGCE: vi.fn(),
43
- }) as unknown as GoogleAuth,
44
- );
45
-
46
- const token = await generateAuthToken();
47
- expect(token).toBeNull();
48
- });
49
-
50
- it('should create new auth instance with provided options', async () => {
51
- const options = { keyFile: 'test-key.json' };
52
- await generateAuthToken(options);
53
-
54
- expect(GoogleAuth).toHaveBeenCalledWith({
55
- scopes: ['https://www.googleapis.com/auth/cloud-platform'],
56
- keyFile: 'test-key.json',
57
- });
58
- });
59
- });