valenceai 0.5.0 → 1.0.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.
- package/CHANGELOG.md +16 -0
- package/README.dev.md +1 -1
- package/README.md +358 -178
- package/package.json +15 -6
- package/src/config.js +25 -14
- package/src/rateLimit.js +77 -0
- package/src/streaming.js +193 -0
- package/src/utils/logger.js +3 -3
- package/src/valenceClient.js +173 -68
- package/tests/asyncAudio.test.js +128 -71
- package/tests/client.test.js +10 -25
- package/tests/config.test.js +21 -21
- package/tests/e2e.asyncWorkflow.test.js +343 -0
- package/tests/e2e.streaming.test.js +420 -0
- package/tests/logger.test.js +3 -0
- package/tests/rateLimit.test.js +137 -0
- package/tests/setup.js +5 -4
- package/tests/streaming.test.js +187 -0
- package/tests/valenceClient.test.js +50 -5
package/tests/asyncAudio.test.js
CHANGED
|
@@ -10,11 +10,11 @@ describe('AsyncAudio', () => {
|
|
|
10
10
|
beforeEach(() => {
|
|
11
11
|
process.env = { ...originalEnv };
|
|
12
12
|
process.env.VALENCE_API_KEY = 'test-api-key';
|
|
13
|
-
process.env.
|
|
14
|
-
|
|
13
|
+
process.env.VALENCE_API_BASE_URL = 'https://test-api.com';
|
|
14
|
+
|
|
15
15
|
fsMock = jest.spyOn(fs, 'existsSync');
|
|
16
16
|
statMock = jest.spyOn(fs, 'statSync');
|
|
17
|
-
|
|
17
|
+
|
|
18
18
|
// Mock createReadStream to simulate file chunks
|
|
19
19
|
jest.spyOn(fs, 'createReadStream').mockReturnValue({
|
|
20
20
|
[Symbol.asyncIterator]: async function* () {
|
|
@@ -44,9 +44,9 @@ describe('AsyncAudio', () => {
|
|
|
44
44
|
]
|
|
45
45
|
};
|
|
46
46
|
|
|
47
|
-
// Mock initiate request
|
|
47
|
+
// Mock initiate request (v1 endpoint)
|
|
48
48
|
nock('https://test-api.com')
|
|
49
|
-
.get('/upload/initiate')
|
|
49
|
+
.get('/v1/asynch/emotion/upload/initiate')
|
|
50
50
|
.query({ file_name: 'test.wav', part_count: 2 })
|
|
51
51
|
.reply(200, mockInitiateResponse);
|
|
52
52
|
|
|
@@ -59,9 +59,9 @@ describe('AsyncAudio', () => {
|
|
|
59
59
|
.put('/part2')
|
|
60
60
|
.reply(200, '', { etag: '"etag2"' });
|
|
61
61
|
|
|
62
|
-
// Mock complete request
|
|
62
|
+
// Mock complete request (v1 endpoint)
|
|
63
63
|
nock('https://test-api.com')
|
|
64
|
-
.post('/upload/complete', {
|
|
64
|
+
.post('/v1/asynch/emotion/upload/complete', {
|
|
65
65
|
request_id: 'req-123',
|
|
66
66
|
upload_id: 'upload-123',
|
|
67
67
|
parts: [
|
|
@@ -71,21 +71,21 @@ describe('AsyncAudio', () => {
|
|
|
71
71
|
})
|
|
72
72
|
.reply(200);
|
|
73
73
|
|
|
74
|
-
const client = new ValenceClient();
|
|
74
|
+
const client = new ValenceClient({ apiKey: 'test-api-key' });
|
|
75
75
|
const result = await client.asynch.upload('test.wav');
|
|
76
|
-
|
|
76
|
+
|
|
77
77
|
expect(result).toBe('req-123');
|
|
78
78
|
});
|
|
79
79
|
|
|
80
80
|
test('should throw error when filePath is missing', async () => {
|
|
81
|
-
const client = new ValenceClient();
|
|
81
|
+
const client = new ValenceClient({ apiKey: 'test-key' });
|
|
82
82
|
await expect(client.asynch.upload()).rejects.toThrow(
|
|
83
83
|
'filePath is required and must be a string'
|
|
84
84
|
);
|
|
85
85
|
});
|
|
86
86
|
|
|
87
87
|
test('should throw error when filePath is not a string', async () => {
|
|
88
|
-
const client = new ValenceClient();
|
|
88
|
+
const client = new ValenceClient({ apiKey: 'test-key' });
|
|
89
89
|
await expect(client.asynch.upload(123)).rejects.toThrow(
|
|
90
90
|
'filePath is required and must be a string'
|
|
91
91
|
);
|
|
@@ -93,11 +93,10 @@ describe('AsyncAudio', () => {
|
|
|
93
93
|
|
|
94
94
|
test('should throw error when API key is missing', async () => {
|
|
95
95
|
delete process.env.VALENCE_API_KEY;
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
);
|
|
96
|
+
|
|
97
|
+
expect(() => {
|
|
98
|
+
new ValenceClient();
|
|
99
|
+
}).toThrow('API key not provided');
|
|
101
100
|
});
|
|
102
101
|
|
|
103
102
|
test('should throw error when file does not exist', async () => {
|
|
@@ -180,83 +179,88 @@ describe('AsyncAudio', () => {
|
|
|
180
179
|
});
|
|
181
180
|
|
|
182
181
|
describe('getEmotions', () => {
|
|
183
|
-
test('should successfully get emotions', async () => {
|
|
182
|
+
test('should successfully get emotions with timeline data', async () => {
|
|
184
183
|
const mockResponse = {
|
|
185
|
-
emotions:
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
184
|
+
emotions: [
|
|
185
|
+
{
|
|
186
|
+
timestamp: 0.5,
|
|
187
|
+
start_time: 0.0,
|
|
188
|
+
end_time: 1.0,
|
|
189
|
+
emotion: 'happy',
|
|
190
|
+
confidence: 0.9,
|
|
191
|
+
all_predictions: { happy: 0.9, sad: 0.1 }
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
timestamp: 1.5,
|
|
195
|
+
start_time: 1.0,
|
|
196
|
+
end_time: 2.0,
|
|
197
|
+
emotion: 'sad',
|
|
198
|
+
confidence: 0.8,
|
|
199
|
+
all_predictions: { happy: 0.2, sad: 0.8 }
|
|
200
|
+
}
|
|
201
|
+
],
|
|
202
|
+
status: 'completed'
|
|
190
203
|
};
|
|
191
204
|
|
|
205
|
+
// v1 endpoint with request_id in path
|
|
192
206
|
nock('https://test-api.com')
|
|
193
|
-
.get('/
|
|
194
|
-
.query({ request_id: 'req-123' })
|
|
207
|
+
.get('/v1/asynch/emotion/status/req-123')
|
|
195
208
|
.reply(200, mockResponse);
|
|
196
209
|
|
|
197
|
-
const client = new ValenceClient();
|
|
210
|
+
const client = new ValenceClient({ apiKey: 'test-api-key' });
|
|
198
211
|
const result = await client.asynch.emotions('req-123');
|
|
199
|
-
|
|
212
|
+
|
|
200
213
|
expect(result).toEqual(mockResponse);
|
|
214
|
+
expect(result.emotions.length).toBe(2);
|
|
215
|
+
expect(result.emotions[0].emotion).toBe('happy');
|
|
201
216
|
});
|
|
202
217
|
|
|
203
218
|
test('should poll until success', async () => {
|
|
204
|
-
const mockResponse = { emotions:
|
|
219
|
+
const mockResponse = { emotions: [], status: 'completed' };
|
|
205
220
|
|
|
206
221
|
nock('https://test-api.com')
|
|
207
|
-
.get('/
|
|
208
|
-
.query({ request_id: 'req-123' })
|
|
222
|
+
.get('/v1/asynch/emotion/status/req-123')
|
|
209
223
|
.reply(202) // Processing
|
|
210
|
-
.get('/
|
|
211
|
-
.query({ request_id: 'req-123' })
|
|
224
|
+
.get('/v1/asynch/emotion/status/req-123')
|
|
212
225
|
.reply(200, mockResponse); // Success
|
|
213
226
|
|
|
214
|
-
const client = new ValenceClient();
|
|
227
|
+
const client = new ValenceClient({ apiKey: 'test-api-key' });
|
|
215
228
|
const result = await client.asynch.emotions('req-123', 5, 100); // Short interval for testing
|
|
216
|
-
|
|
229
|
+
|
|
217
230
|
expect(result).toEqual(mockResponse);
|
|
218
231
|
});
|
|
219
232
|
|
|
220
233
|
test('should throw error when requestId is missing', async () => {
|
|
221
|
-
const client = new ValenceClient();
|
|
234
|
+
const client = new ValenceClient({ apiKey: 'test-key' });
|
|
222
235
|
await expect(client.asynch.emotions()).rejects.toThrow(
|
|
223
236
|
'requestId is required and must be a string'
|
|
224
237
|
);
|
|
225
238
|
});
|
|
226
239
|
|
|
227
240
|
test('should throw error when requestId is not a string', async () => {
|
|
228
|
-
const client = new ValenceClient();
|
|
241
|
+
const client = new ValenceClient({ apiKey: 'test-key' });
|
|
229
242
|
await expect(client.asynch.emotions(123)).rejects.toThrow(
|
|
230
243
|
'requestId is required and must be a string'
|
|
231
244
|
);
|
|
232
245
|
});
|
|
233
246
|
|
|
234
|
-
test('should throw error when API key is missing', async () => {
|
|
235
|
-
delete process.env.VALENCE_API_KEY;
|
|
236
|
-
|
|
237
|
-
const client = new ValenceClient();
|
|
238
|
-
await expect(client.asynch.emotions('req-123')).rejects.toThrow(
|
|
239
|
-
'VALENCE_API_KEY is required'
|
|
240
|
-
);
|
|
241
|
-
});
|
|
242
|
-
|
|
243
247
|
test('should throw error with invalid maxTries', async () => {
|
|
244
|
-
const client = new ValenceClient();
|
|
248
|
+
const client = new ValenceClient({ apiKey: 'test-key' });
|
|
245
249
|
await expect(client.asynch.emotions('req-123', 0)).rejects.toThrow(
|
|
246
250
|
'maxTries must be between 1 and 100'
|
|
247
251
|
);
|
|
248
|
-
|
|
252
|
+
|
|
249
253
|
await expect(client.asynch.emotions('req-123', 101)).rejects.toThrow(
|
|
250
254
|
'maxTries must be between 1 and 100'
|
|
251
255
|
);
|
|
252
256
|
});
|
|
253
257
|
|
|
254
258
|
test('should throw error with invalid intervalMs', async () => {
|
|
255
|
-
const client = new ValenceClient();
|
|
259
|
+
const client = new ValenceClient({ apiKey: 'test-key' });
|
|
256
260
|
await expect(client.asynch.emotions('req-123', 5, 500)).rejects.toThrow(
|
|
257
261
|
'intervalMs must be between 1000 and 60000'
|
|
258
262
|
);
|
|
259
|
-
|
|
263
|
+
|
|
260
264
|
await expect(client.asynch.emotions('req-123', 5, 70000)).rejects.toThrow(
|
|
261
265
|
'intervalMs must be between 1000 and 60000'
|
|
262
266
|
);
|
|
@@ -264,11 +268,10 @@ describe('AsyncAudio', () => {
|
|
|
264
268
|
|
|
265
269
|
test('should handle 404 errors', async () => {
|
|
266
270
|
nock('https://test-api.com')
|
|
267
|
-
.get('/
|
|
268
|
-
.query({ request_id: 'invalid-id' })
|
|
271
|
+
.get('/v1/asynch/emotion/status/invalid-id')
|
|
269
272
|
.reply(404);
|
|
270
273
|
|
|
271
|
-
const client = new ValenceClient();
|
|
274
|
+
const client = new ValenceClient({ apiKey: 'test-api-key' });
|
|
272
275
|
await expect(client.asynch.emotions('invalid-id')).rejects.toThrow(
|
|
273
276
|
'Request ID not found: invalid-id'
|
|
274
277
|
);
|
|
@@ -276,11 +279,10 @@ describe('AsyncAudio', () => {
|
|
|
276
279
|
|
|
277
280
|
test('should handle client errors', async () => {
|
|
278
281
|
nock('https://test-api.com')
|
|
279
|
-
.get('/
|
|
280
|
-
.query({ request_id: 'req-123' })
|
|
282
|
+
.get('/v1/asynch/emotion/status/req-123')
|
|
281
283
|
.reply(400, { message: 'Bad request' });
|
|
282
284
|
|
|
283
|
-
const client = new ValenceClient();
|
|
285
|
+
const client = new ValenceClient({ apiKey: 'test-api-key' });
|
|
284
286
|
await expect(client.asynch.emotions('req-123')).rejects.toThrow(
|
|
285
287
|
'Client error (400): Bad request'
|
|
286
288
|
);
|
|
@@ -288,12 +290,11 @@ describe('AsyncAudio', () => {
|
|
|
288
290
|
|
|
289
291
|
test('should timeout after max attempts', async () => {
|
|
290
292
|
nock('https://test-api.com')
|
|
291
|
-
.get('/
|
|
292
|
-
.query({ request_id: 'req-123' })
|
|
293
|
+
.get('/v1/asynch/emotion/status/req-123')
|
|
293
294
|
.times(3)
|
|
294
295
|
.reply(202); // Always processing
|
|
295
296
|
|
|
296
|
-
const client = new ValenceClient();
|
|
297
|
+
const client = new ValenceClient({ apiKey: 'test-api-key' });
|
|
297
298
|
await expect(client.asynch.emotions('req-123', 3, 100)).rejects.toThrow(
|
|
298
299
|
'Prediction not available after 3 attempts. The request may still be processing.'
|
|
299
300
|
);
|
|
@@ -301,31 +302,87 @@ describe('AsyncAudio', () => {
|
|
|
301
302
|
|
|
302
303
|
test('should handle server errors gracefully', async () => {
|
|
303
304
|
nock('https://test-api.com')
|
|
304
|
-
.get('/
|
|
305
|
-
.query({ request_id: 'req-123' })
|
|
305
|
+
.get('/v1/asynch/emotion/status/req-123')
|
|
306
306
|
.reply(500)
|
|
307
|
-
.get('/
|
|
308
|
-
.
|
|
309
|
-
.reply(200, { emotions: { happy: 1.0 } });
|
|
307
|
+
.get('/v1/asynch/emotion/status/req-123')
|
|
308
|
+
.reply(200, { emotions: [], status: 'completed' });
|
|
310
309
|
|
|
311
|
-
const client = new ValenceClient();
|
|
310
|
+
const client = new ValenceClient({ apiKey: 'test-api-key' });
|
|
312
311
|
const result = await client.asynch.emotions('req-123', 5, 100);
|
|
313
|
-
|
|
314
|
-
expect(result).toEqual({ emotions:
|
|
312
|
+
|
|
313
|
+
expect(result).toEqual({ emotions: [], status: 'completed' });
|
|
315
314
|
});
|
|
316
315
|
|
|
317
316
|
test('should include correct headers', async () => {
|
|
318
317
|
const scope = nock('https://test-api.com')
|
|
319
|
-
.get('/
|
|
318
|
+
.get('/v1/asynch/emotion/status/req-123')
|
|
320
319
|
.matchHeader('x-api-key', 'test-api-key')
|
|
321
|
-
.matchHeader('User-Agent', 'valenceai/0.
|
|
322
|
-
.
|
|
323
|
-
.reply(200, { emotions: {} });
|
|
320
|
+
.matchHeader('User-Agent', 'valenceai/1.0.0')
|
|
321
|
+
.reply(200, { emotions: [], status: 'completed' });
|
|
324
322
|
|
|
325
|
-
const client = new ValenceClient();
|
|
323
|
+
const client = new ValenceClient({ apiKey: 'test-api-key' });
|
|
326
324
|
await client.asynch.emotions('req-123');
|
|
327
|
-
|
|
325
|
+
|
|
328
326
|
expect(scope.isDone()).toBe(true);
|
|
329
327
|
});
|
|
328
|
+
|
|
329
|
+
test('should get timeline data', async () => {
|
|
330
|
+
const mockResponse = {
|
|
331
|
+
emotions: [
|
|
332
|
+
{ timestamp: 0.5, emotion: 'happy' },
|
|
333
|
+
{ timestamp: 1.5, emotion: 'sad' }
|
|
334
|
+
],
|
|
335
|
+
status: 'completed'
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
nock('https://test-api.com')
|
|
339
|
+
.get('/v1/asynch/emotion/status/req-123')
|
|
340
|
+
.reply(200, mockResponse);
|
|
341
|
+
|
|
342
|
+
const client = new ValenceClient({ apiKey: 'test-api-key' });
|
|
343
|
+
const timeline = await client.asynch.getTimeline('req-123');
|
|
344
|
+
|
|
345
|
+
expect(timeline.length).toBe(2);
|
|
346
|
+
expect(timeline[0].emotion).toBe('happy');
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
test('should get emotion at specific time', async () => {
|
|
350
|
+
const mockResponse = {
|
|
351
|
+
emotions: [
|
|
352
|
+
{ timestamp: 0.5, start_time: 0.0, end_time: 1.0, emotion: 'happy' },
|
|
353
|
+
{ timestamp: 1.5, start_time: 1.0, end_time: 2.0, emotion: 'sad' }
|
|
354
|
+
],
|
|
355
|
+
status: 'completed'
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
nock('https://test-api.com')
|
|
359
|
+
.get('/v1/asynch/emotion/status/req-123')
|
|
360
|
+
.reply(200, mockResponse);
|
|
361
|
+
|
|
362
|
+
const client = new ValenceClient({ apiKey: 'test-api-key' });
|
|
363
|
+
const emotion = await client.asynch.getEmotionAtTime('req-123', 0.7);
|
|
364
|
+
|
|
365
|
+
expect(emotion.emotion).toBe('happy');
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
test('should get dominant emotion', async () => {
|
|
369
|
+
const mockResponse = {
|
|
370
|
+
emotions: [
|
|
371
|
+
{ timestamp: 0.5, emotion: 'happy' },
|
|
372
|
+
{ timestamp: 1.5, emotion: 'happy' },
|
|
373
|
+
{ timestamp: 2.5, emotion: 'sad' }
|
|
374
|
+
],
|
|
375
|
+
status: 'completed'
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
nock('https://test-api.com')
|
|
379
|
+
.get('/v1/asynch/emotion/status/req-123')
|
|
380
|
+
.reply(200, mockResponse);
|
|
381
|
+
|
|
382
|
+
const client = new ValenceClient({ apiKey: 'test-api-key' });
|
|
383
|
+
const dominant = await client.asynch.getDominantEmotion('req-123');
|
|
384
|
+
|
|
385
|
+
expect(dominant).toBe('happy');
|
|
386
|
+
});
|
|
330
387
|
});
|
|
331
388
|
});
|
package/tests/client.test.js
CHANGED
|
@@ -1,42 +1,27 @@
|
|
|
1
|
-
import { describe, test, expect
|
|
2
|
-
import { getHeaders } from '../src/client.js';
|
|
1
|
+
import { describe, test, expect } from '@jest/globals';
|
|
3
2
|
|
|
4
3
|
describe('Client', () => {
|
|
5
|
-
const originalEnv = process.env;
|
|
6
|
-
|
|
7
|
-
beforeEach(() => {
|
|
8
|
-
process.env = { ...originalEnv };
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
afterEach(() => {
|
|
12
|
-
process.env = originalEnv;
|
|
13
|
-
});
|
|
14
|
-
|
|
15
4
|
describe('getHeaders', () => {
|
|
16
5
|
test('should return headers with API key', async () => {
|
|
6
|
+
// Test with a specific API key set in environment
|
|
7
|
+
const originalKey = process.env.VALENCE_API_KEY;
|
|
17
8
|
process.env.VALENCE_API_KEY = 'test-api-key';
|
|
18
|
-
|
|
9
|
+
|
|
19
10
|
// Re-import to get fresh config
|
|
20
11
|
const { getHeaders: freshGetHeaders } = await import('../src/client.js?' + Math.random());
|
|
21
|
-
|
|
12
|
+
|
|
22
13
|
const headers = freshGetHeaders();
|
|
23
|
-
|
|
14
|
+
|
|
24
15
|
expect(headers).toEqual({
|
|
25
16
|
'x-api-key': 'test-api-key',
|
|
26
17
|
'User-Agent': 'valenceai/0.4.1'
|
|
27
18
|
});
|
|
28
|
-
});
|
|
29
19
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
expect(() => getHeaders()).toThrow('VALENCE_API_KEY is required but not configured');
|
|
20
|
+
// Restore original
|
|
21
|
+
process.env.VALENCE_API_KEY = originalKey;
|
|
34
22
|
});
|
|
35
23
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
expect(() => getHeaders()).toThrow('VALENCE_API_KEY is required but not configured');
|
|
40
|
-
});
|
|
24
|
+
// Note: API key validation is thoroughly tested in ValenceClient tests
|
|
25
|
+
// getHeaders() is an internal utility function that's always called with a valid config
|
|
41
26
|
});
|
|
42
27
|
});
|
package/tests/config.test.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, test, expect, beforeEach, afterEach } from '@jest/globals';
|
|
2
|
-
import {
|
|
2
|
+
import { validateConfig } from '../src/config.js';
|
|
3
3
|
|
|
4
4
|
describe('Config', () => {
|
|
5
5
|
const originalEnv = process.env;
|
|
@@ -15,37 +15,37 @@ describe('Config', () => {
|
|
|
15
15
|
describe('config object', () => {
|
|
16
16
|
test('should load from environment variables', async () => {
|
|
17
17
|
process.env.VALENCE_API_KEY = 'test-key';
|
|
18
|
-
process.env.
|
|
19
|
-
process.env.
|
|
18
|
+
process.env.VALENCE_API_BASE_URL = 'https://test-base.com';
|
|
19
|
+
process.env.VALENCE_WEBSOCKET_URL = 'wss://test-websocket.com';
|
|
20
20
|
process.env.VALENCE_LOG_LEVEL = 'debug';
|
|
21
21
|
|
|
22
22
|
// Re-import to get fresh config
|
|
23
23
|
const { config: freshConfig } = await import('../src/config.js?' + Math.random());
|
|
24
24
|
|
|
25
25
|
expect(freshConfig.apiKey).toBe('test-key');
|
|
26
|
-
expect(freshConfig.
|
|
27
|
-
expect(freshConfig.
|
|
26
|
+
expect(freshConfig.baseUrl).toBe('https://test-base.com');
|
|
27
|
+
expect(freshConfig.websocketUrl).toBe('wss://test-websocket.com');
|
|
28
28
|
expect(freshConfig.logLevel).toBe('debug');
|
|
29
29
|
});
|
|
30
30
|
|
|
31
31
|
test('should use default URLs when not provided', async () => {
|
|
32
32
|
process.env.VALENCE_API_KEY = 'test-key';
|
|
33
|
-
delete process.env.
|
|
34
|
-
delete process.env.
|
|
33
|
+
delete process.env.VALENCE_API_BASE_URL;
|
|
34
|
+
delete process.env.VALENCE_WEBSOCKET_URL;
|
|
35
35
|
|
|
36
36
|
// Re-import to get fresh config
|
|
37
37
|
const { config: freshConfig } = await import('../src/config.js?' + Math.random());
|
|
38
38
|
|
|
39
|
-
expect(freshConfig.
|
|
40
|
-
expect(freshConfig.
|
|
39
|
+
expect(freshConfig.baseUrl).toBe('https://demo.getvalenceai.com');
|
|
40
|
+
expect(freshConfig.websocketUrl).toBe('wss://demo.getvalenceai.com');
|
|
41
41
|
});
|
|
42
42
|
|
|
43
43
|
test('should use default log level when not provided', async () => {
|
|
44
44
|
delete process.env.VALENCE_LOG_LEVEL;
|
|
45
|
-
|
|
45
|
+
|
|
46
46
|
// Re-import to get fresh config
|
|
47
47
|
const { config: freshConfig } = await import('../src/config.js?' + Math.random());
|
|
48
|
-
|
|
48
|
+
|
|
49
49
|
expect(freshConfig.logLevel).toBe('info');
|
|
50
50
|
});
|
|
51
51
|
});
|
|
@@ -53,8 +53,8 @@ describe('Config', () => {
|
|
|
53
53
|
describe('validateConfig', () => {
|
|
54
54
|
test('should pass with valid configuration', () => {
|
|
55
55
|
process.env.VALENCE_API_KEY = 'test-key';
|
|
56
|
-
process.env.
|
|
57
|
-
process.env.
|
|
56
|
+
process.env.VALENCE_API_BASE_URL = 'https://test-base.com';
|
|
57
|
+
process.env.VALENCE_WEBSOCKET_URL = 'wss://test-websocket.com';
|
|
58
58
|
process.env.VALENCE_LOG_LEVEL = 'info';
|
|
59
59
|
|
|
60
60
|
expect(() => validateConfig()).not.toThrow();
|
|
@@ -63,28 +63,28 @@ describe('Config', () => {
|
|
|
63
63
|
test('should throw when API key is missing', async () => {
|
|
64
64
|
delete process.env.VALENCE_API_KEY;
|
|
65
65
|
const { validateConfig: freshValidateConfig } = await import('../src/config.js?' + Math.random());
|
|
66
|
-
expect(() => freshValidateConfig()).toThrow('VALENCE_API_KEY
|
|
66
|
+
expect(() => freshValidateConfig()).toThrow('VALENCE_API_KEY is required');
|
|
67
67
|
});
|
|
68
68
|
|
|
69
|
-
test('should throw when
|
|
69
|
+
test('should throw when base URL is not HTTPS', async () => {
|
|
70
70
|
process.env.VALENCE_API_KEY = 'test-key';
|
|
71
|
-
process.env.
|
|
71
|
+
process.env.VALENCE_API_BASE_URL = 'http://test-base.com';
|
|
72
72
|
const { validateConfig: freshValidateConfig } = await import('../src/config.js?' + Math.random());
|
|
73
|
-
expect(() => freshValidateConfig()).toThrow('
|
|
73
|
+
expect(() => freshValidateConfig()).toThrow('baseUrl must be a valid HTTPS URL');
|
|
74
74
|
});
|
|
75
75
|
|
|
76
|
-
test('should throw when
|
|
76
|
+
test('should throw when websocket URL is not WSS', async () => {
|
|
77
77
|
process.env.VALENCE_API_KEY = 'test-key';
|
|
78
|
-
process.env.
|
|
78
|
+
process.env.VALENCE_WEBSOCKET_URL = 'ws://test-websocket.com';
|
|
79
79
|
const { validateConfig: freshValidateConfig } = await import('../src/config.js?' + Math.random());
|
|
80
|
-
expect(() => freshValidateConfig()).toThrow('
|
|
80
|
+
expect(() => freshValidateConfig()).toThrow('websocketUrl must be a valid WSS URL');
|
|
81
81
|
});
|
|
82
82
|
|
|
83
83
|
test('should throw with invalid log level', async () => {
|
|
84
84
|
process.env.VALENCE_API_KEY = 'test-key';
|
|
85
85
|
process.env.VALENCE_LOG_LEVEL = 'invalid';
|
|
86
86
|
const { validateConfig: freshValidateConfig } = await import('../src/config.js?' + Math.random());
|
|
87
|
-
expect(() => freshValidateConfig()).toThrow('
|
|
87
|
+
expect(() => freshValidateConfig()).toThrow('logLevel must be one of: debug, info, warn, error');
|
|
88
88
|
});
|
|
89
89
|
});
|
|
90
90
|
});
|