@forgehive/hive-sdk 0.1.6 → 0.2.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.
@@ -1,19 +1,47 @@
1
1
  import axios from 'axios'
2
+ import fs from 'fs'
2
3
  import { HiveLogClient, createHiveLogClient, Metadata } from '../index'
3
4
 
4
- // Mock axios
5
+ // Mock axios and fs
5
6
  jest.mock('axios')
7
+ jest.mock('fs')
6
8
  const mockedAxios = axios as jest.Mocked<typeof axios>
9
+ const mockedFs = fs as jest.Mocked<typeof fs>
7
10
 
8
11
  describe('HiveLogClient Metadata', () => {
9
12
  const testConfig = {
10
13
  projectName: 'test-project',
14
+ projectUuid: '550e8400-e29b-41d4-a716-446655440000',
11
15
  apiKey: 'test-api-key',
12
16
  apiSecret: 'test-api-secret',
13
- host: 'https://test-host.com'
17
+ host: 'https://test-host.com',
18
+ forgeConfigPath: './forge.json'
19
+ }
20
+
21
+ const mockForgeConfig = {
22
+ project: {
23
+ name: 'test-project',
24
+ uuid: '550e8400-e29b-41d4-a716-446655440000'
25
+ },
26
+ tasks: {
27
+ 'test-task': {
28
+ path: 'src/tasks/test.ts',
29
+ handler: 'testTask',
30
+ uuid: 'f47ac10b-58cc-4372-a567-0e02b2c3d479'
31
+ },
32
+ 'search-task': {
33
+ path: 'src/tasks/search.ts',
34
+ handler: 'searchTask',
35
+ uuid: 'a45aafe3-8b01-4b58-b15d-9a96274858ee'
36
+ }
37
+ }
14
38
  }
15
39
 
16
40
  beforeEach(() => {
41
+ // Mock fs
42
+ mockedFs.existsSync.mockReturnValue(true)
43
+ mockedFs.readFileSync.mockReturnValue(JSON.stringify(mockForgeConfig))
44
+
17
45
  // Clear all mocks
18
46
  jest.clearAllMocks()
19
47
  })
@@ -72,6 +100,7 @@ describe('HiveLogClient Metadata', () => {
72
100
 
73
101
  beforeEach(() => {
74
102
  client = new HiveLogClient(testConfig)
103
+ jest.clearAllMocks()
75
104
  })
76
105
 
77
106
  it('should send log without metadata parameter', async () => {
@@ -88,27 +117,24 @@ describe('HiveLogClient Metadata', () => {
88
117
  const result = await client.sendLog(executionRecord)
89
118
 
90
119
  expect(result).toBe('success')
91
- expect(mockedAxios.post).toHaveBeenCalledWith(
92
- 'https://test-host.com/api/tasks/log-ingest',
93
- {
94
- projectName: 'test-project',
95
- taskName: 'test-task',
96
- logItem: JSON.stringify({
97
- input: 'test-input',
98
- output: 'test-output',
99
- taskName: 'test-task',
100
- type: 'success',
101
- boundaries: {},
102
- metadata: {}
103
- })
104
- },
105
- {
106
- headers: {
107
- Authorization: 'Bearer test-api-key:test-api-secret',
108
- 'Content-Type': 'application/json'
109
- }
110
- }
111
- )
120
+
121
+ // Verify it uses the UUID endpoint
122
+ const callArgs = mockedAxios.post.mock.calls[0]
123
+ expect(callArgs[0]).toBe('https://test-host.com/api/log-ingest')
124
+
125
+ // Verify request body structure
126
+ const requestBody = callArgs[1] as { projectUuid: string; taskUuid: string; logItem: string }
127
+ expect(requestBody.projectUuid).toBe(testConfig.projectUuid)
128
+ expect(requestBody.taskUuid).toBe('f47ac10b-58cc-4372-a567-0e02b2c3d479')
129
+
130
+ // Parse and verify logItem
131
+ const logItem = JSON.parse(requestBody.logItem)
132
+ expect(logItem.uuid).toBeDefined()
133
+ expect(logItem.input).toBe('test-input')
134
+ expect(logItem.output).toBe('test-output')
135
+ expect(logItem.taskName).toBe('test-task')
136
+ expect(logItem.type).toBe('success')
137
+ expect(logItem.metadata).toEqual({})
112
138
  })
113
139
 
114
140
  it('should send log with metadata parameter', async () => {
@@ -130,30 +156,17 @@ describe('HiveLogClient Metadata', () => {
130
156
  const result = await client.sendLog(executionRecord, metadata)
131
157
 
132
158
  expect(result).toBe('success')
133
- expect(mockedAxios.post).toHaveBeenCalledWith(
134
- 'https://test-host.com/api/tasks/log-ingest',
135
- {
136
- projectName: 'test-project',
137
- taskName: 'test-task',
138
- logItem: JSON.stringify({
139
- input: 'test-input',
140
- output: 'test-output',
141
- taskName: 'test-task',
142
- type: 'success',
143
- boundaries: {},
144
- metadata: {
145
- requestId: 'req-123',
146
- userId: 'user-456'
147
- }
148
- })
149
- },
150
- {
151
- headers: {
152
- Authorization: 'Bearer test-api-key:test-api-secret',
153
- 'Content-Type': 'application/json'
154
- }
155
- }
156
- )
159
+
160
+ const callArgs = mockedAxios.post.mock.calls[0]
161
+ expect(callArgs[0]).toBe('https://test-host.com/api/log-ingest')
162
+
163
+ const requestBody = callArgs[1] as { projectUuid: string; taskUuid: string; logItem: string }
164
+ const logItem = JSON.parse(requestBody.logItem)
165
+
166
+ expect(logItem.metadata).toEqual({
167
+ requestId: 'req-123',
168
+ userId: 'user-456'
169
+ })
157
170
  })
158
171
 
159
172
  it('should handle logItem with only input property', async () => {
@@ -163,21 +176,13 @@ describe('HiveLogClient Metadata', () => {
163
176
  const result = await client.sendLog({ input: 'simple input', taskName: 'test-task', type: 'success' as const, boundaries: {}, metadata: {} }, metadata)
164
177
 
165
178
  expect(result).toBe('success')
166
- expect(mockedAxios.post).toHaveBeenCalledWith(
167
- 'https://test-host.com/api/tasks/log-ingest',
168
- {
169
- projectName: 'test-project',
170
- taskName: 'test-task',
171
- logItem: JSON.stringify({
172
- input: 'simple input',
173
- taskName: 'test-task',
174
- type: 'success',
175
- boundaries: {},
176
- metadata: { type: 'minimal' }
177
- })
178
- },
179
- expect.any(Object)
180
- )
179
+
180
+ const callArgs = mockedAxios.post.mock.calls[0]
181
+ const requestBody = callArgs[1] as { projectUuid: string; taskUuid: string; logItem: string }
182
+ const logItem = JSON.parse(requestBody.logItem)
183
+
184
+ expect(logItem.input).toBe('simple input')
185
+ expect(logItem.metadata).toEqual({ type: 'minimal' })
181
186
  })
182
187
 
183
188
  it('should handle logItem with null input values', async () => {
@@ -187,21 +192,13 @@ describe('HiveLogClient Metadata', () => {
187
192
  const result = await client.sendLog({ input: null, taskName: 'test-task', type: 'success' as const, boundaries: {}, metadata: {} }, metadata)
188
193
 
189
194
  expect(result).toBe('success')
190
- expect(mockedAxios.post).toHaveBeenCalledWith(
191
- 'https://test-host.com/api/tasks/log-ingest',
192
- {
193
- projectName: 'test-project',
194
- taskName: 'test-task',
195
- logItem: JSON.stringify({
196
- input: null,
197
- taskName: 'test-task',
198
- type: 'success',
199
- boundaries: {},
200
- metadata: { type: 'null-test' }
201
- })
202
- },
203
- expect.any(Object)
204
- )
195
+
196
+ const callArgs = mockedAxios.post.mock.calls[0]
197
+ const requestBody = callArgs[1] as { projectUuid: string; taskUuid: string; logItem: string }
198
+ const logItem = JSON.parse(requestBody.logItem)
199
+
200
+ expect(logItem.input).toBeNull()
201
+ expect(logItem.metadata).toEqual({ type: 'null-test' })
205
202
  })
206
203
  })
207
204
 
@@ -215,30 +212,20 @@ describe('HiveLogClient Metadata', () => {
215
212
  }
216
213
 
217
214
  const client = new HiveLogClient({ ...testConfig, metadata: baseMetadata })
215
+ jest.clearAllMocks()
216
+
218
217
  const logItem = { input: 'test' }
219
218
 
220
219
  await client.sendLog({ ...logItem, taskName: 'test-task', type: 'success' as const, boundaries: {}, metadata: {} })
221
220
 
222
- const expectedLogItem = {
223
- input: 'test',
224
- taskName: 'test-task',
225
- type: 'success',
226
- boundaries: {},
227
- metadata: {
228
- environment: 'production',
229
- version: '1.0.0'
230
- }
231
- }
221
+ const callArgs = mockedAxios.post.mock.calls[0]
222
+ const requestBody = callArgs[1] as { projectUuid: string; taskUuid: string; logItem: string }
223
+ const parsedLogItem = JSON.parse(requestBody.logItem)
232
224
 
233
- expect(mockedAxios.post).toHaveBeenCalledWith(
234
- 'https://test-host.com/api/tasks/log-ingest',
235
- {
236
- projectName: 'test-project',
237
- taskName: 'test-task',
238
- logItem: JSON.stringify(expectedLogItem)
239
- },
240
- expect.any(Object)
241
- )
225
+ expect(parsedLogItem.metadata).toEqual({
226
+ environment: 'production',
227
+ version: '1.0.0'
228
+ })
242
229
  })
243
230
 
244
231
  it('should merge logItem metadata with base metadata', async () => {
@@ -250,6 +237,8 @@ describe('HiveLogClient Metadata', () => {
250
237
  }
251
238
 
252
239
  const client = new HiveLogClient({ ...testConfig, metadata: baseMetadata })
240
+ jest.clearAllMocks()
241
+
253
242
  const logItem = {
254
243
  input: 'test',
255
244
  metadata: {
@@ -260,27 +249,15 @@ describe('HiveLogClient Metadata', () => {
260
249
 
261
250
  await client.sendLog({ ...logItem, taskName: 'test-task', type: 'success' as const, boundaries: {}, metadata: logItem.metadata || {} })
262
251
 
263
- const expectedLogItem = {
264
- input: 'test',
265
- metadata: {
266
- environment: 'production', // from base
267
- version: '1.1.0', // from logItem (overrides base)
268
- sessionId: 'session-123' // from logItem
269
- },
270
- taskName: 'test-task',
271
- type: 'success',
272
- boundaries: {}
273
- }
252
+ const callArgs = mockedAxios.post.mock.calls[0]
253
+ const requestBody = callArgs[1] as { projectUuid: string; taskUuid: string; logItem: string }
254
+ const parsedLogItem = JSON.parse(requestBody.logItem)
274
255
 
275
- expect(mockedAxios.post).toHaveBeenCalledWith(
276
- 'https://test-host.com/api/tasks/log-ingest',
277
- {
278
- projectName: 'test-project',
279
- taskName: 'test-task',
280
- logItem: JSON.stringify(expectedLogItem)
281
- },
282
- expect.any(Object)
283
- )
256
+ expect(parsedLogItem.metadata).toEqual({
257
+ environment: 'production', // from base
258
+ version: '1.1.0', // from logItem (overrides base)
259
+ sessionId: 'session-123' // from logItem
260
+ })
284
261
  })
285
262
 
286
263
  it('should give sendLog metadata highest priority', async () => {
@@ -293,6 +270,8 @@ describe('HiveLogClient Metadata', () => {
293
270
  }
294
271
 
295
272
  const client = new HiveLogClient({ ...testConfig, metadata: baseMetadata })
273
+ jest.clearAllMocks()
274
+
296
275
  const logItem = {
297
276
  input: 'test',
298
277
  metadata: {
@@ -310,29 +289,17 @@ describe('HiveLogClient Metadata', () => {
310
289
 
311
290
  await client.sendLog({ ...logItem, taskName: 'test-task', type: 'success' as const, boundaries: {}, metadata: logItem.metadata || {} }, sendLogMetadata)
312
291
 
313
- const expectedLogItem = {
314
- input: 'test',
315
- metadata: {
316
- environment: 'production', // from base
317
- version: '1.2.0', // from sendLog (highest priority)
318
- priority: 'sendLog', // from sendLog (highest priority)
319
- sessionId: 'session-123', // from logItem
320
- requestId: 'req-456' // from sendLog
321
- },
322
- taskName: 'test-task',
323
- type: 'success',
324
- boundaries: {}
325
- }
326
-
327
- expect(mockedAxios.post).toHaveBeenCalledWith(
328
- 'https://test-host.com/api/tasks/log-ingest',
329
- {
330
- projectName: 'test-project',
331
- taskName: 'test-task',
332
- logItem: JSON.stringify(expectedLogItem)
333
- },
334
- expect.any(Object)
335
- )
292
+ const callArgs = mockedAxios.post.mock.calls[0]
293
+ const requestBody = callArgs[1] as { projectUuid: string; taskUuid: string; logItem: string }
294
+ const parsedLogItem = JSON.parse(requestBody.logItem)
295
+
296
+ expect(parsedLogItem.metadata).toEqual({
297
+ environment: 'production', // from base
298
+ version: '1.2.0', // from sendLog (highest priority)
299
+ priority: 'sendLog', // from sendLog (highest priority)
300
+ sessionId: 'session-123', // from logItem
301
+ requestId: 'req-456' // from sendLog
302
+ })
336
303
  })
337
304
 
338
305
  it('should handle all three metadata sources with complex merging', async () => {
@@ -346,6 +313,8 @@ describe('HiveLogClient Metadata', () => {
346
313
  }
347
314
 
348
315
  const client = new HiveLogClient({ ...testConfig, metadata: baseMetadata })
316
+ jest.clearAllMocks()
317
+
349
318
  const logItem = {
350
319
  input: { query: 'search' },
351
320
  output: { results: [] },
@@ -364,33 +333,20 @@ describe('HiveLogClient Metadata', () => {
364
333
 
365
334
  await client.sendLog({ ...logItem, taskName: 'search-task', type: 'success' as const, boundaries: {}, metadata: logItem.metadata || {} }, sendLogMetadata)
366
335
 
367
- const expectedLogItem = {
368
- input: { query: 'search' },
369
- output: { results: [] },
370
- metadata: {
371
- environment: 'production', // from base
372
- service: 'api-gateway', // from base
373
- version: '1.2.0', // from sendLog (highest priority)
374
- datacenter: 'us-west-2', // from base
375
- algorithm: 'fuzzy-search', // from logItem
376
- processingTime: '250', // from logItem
377
- requestId: 'req-789', // from sendLog
378
- userId: 'user-123' // from sendLog
379
- },
380
- taskName: 'search-task',
381
- type: 'success',
382
- boundaries: {}
383
- }
384
-
385
- expect(mockedAxios.post).toHaveBeenCalledWith(
386
- 'https://test-host.com/api/tasks/log-ingest',
387
- {
388
- projectName: 'test-project',
389
- taskName: 'search-task',
390
- logItem: JSON.stringify(expectedLogItem)
391
- },
392
- expect.any(Object)
393
- )
336
+ const callArgs = mockedAxios.post.mock.calls[0]
337
+ const requestBody = callArgs[1] as { projectUuid: string; taskUuid: string; logItem: string }
338
+ const parsedLogItem = JSON.parse(requestBody.logItem)
339
+
340
+ expect(parsedLogItem.metadata).toEqual({
341
+ environment: 'production', // from base
342
+ service: 'api-gateway', // from base
343
+ version: '1.2.0', // from sendLog (highest priority)
344
+ datacenter: 'us-west-2', // from base
345
+ algorithm: 'fuzzy-search', // from logItem
346
+ processingTime: '250', // from logItem
347
+ requestId: 'req-789', // from sendLog
348
+ userId: 'user-123' // from sendLog
349
+ })
394
350
  })
395
351
  })
396
352
 
@@ -400,6 +356,7 @@ describe('HiveLogClient Metadata', () => {
400
356
 
401
357
  const baseMetadata: Metadata = { environment: 'test' }
402
358
  const client = new HiveLogClient({ ...testConfig, metadata: baseMetadata })
359
+ jest.clearAllMocks()
403
360
 
404
361
  const logItem = {
405
362
  input: 'test'
@@ -408,25 +365,13 @@ describe('HiveLogClient Metadata', () => {
408
365
 
409
366
  await client.sendLog({ ...logItem, taskName: 'test-task', type: 'success' as const, boundaries: {}, metadata: {} })
410
367
 
411
- const expectedLogItem = {
412
- input: 'test',
413
- taskName: 'test-task',
414
- type: 'success',
415
- boundaries: {},
416
- metadata: {
417
- environment: 'test' // Only base metadata should be used
418
- }
419
- }
368
+ const callArgs = mockedAxios.post.mock.calls[0]
369
+ const requestBody = callArgs[1] as { projectUuid: string; taskUuid: string; logItem: string }
370
+ const parsedLogItem = JSON.parse(requestBody.logItem)
420
371
 
421
- expect(mockedAxios.post).toHaveBeenCalledWith(
422
- 'https://test-host.com/api/tasks/log-ingest',
423
- {
424
- projectName: 'test-project',
425
- taskName: 'test-task',
426
- logItem: JSON.stringify(expectedLogItem)
427
- },
428
- expect.any(Object)
429
- )
372
+ expect(parsedLogItem.metadata).toEqual({
373
+ environment: 'test' // Only base metadata should be used
374
+ })
430
375
  })
431
376
 
432
377
  it('should handle logItem with undefined metadata', async () => {
@@ -434,6 +379,7 @@ describe('HiveLogClient Metadata', () => {
434
379
 
435
380
  const baseMetadata: Metadata = { environment: 'test' }
436
381
  const client = new HiveLogClient({ ...testConfig, metadata: baseMetadata })
382
+ jest.clearAllMocks()
437
383
 
438
384
  const logItem = {
439
385
  input: 'test',
@@ -442,52 +388,30 @@ describe('HiveLogClient Metadata', () => {
442
388
 
443
389
  await client.sendLog({ ...logItem, taskName: 'test-task', type: 'success' as const, boundaries: {}, metadata: logItem.metadata || {} })
444
390
 
445
- const expectedLogItem = {
446
- input: 'test',
447
- metadata: {
448
- environment: 'test' // Only base metadata should be used
449
- },
450
- taskName: 'test-task',
451
- type: 'success',
452
- boundaries: {}
453
- }
391
+ const callArgs = mockedAxios.post.mock.calls[0]
392
+ const requestBody = callArgs[1] as { projectUuid: string; taskUuid: string; logItem: string }
393
+ const parsedLogItem = JSON.parse(requestBody.logItem)
454
394
 
455
- expect(mockedAxios.post).toHaveBeenCalledWith(
456
- 'https://test-host.com/api/tasks/log-ingest',
457
- {
458
- projectName: 'test-project',
459
- taskName: 'test-task',
460
- logItem: JSON.stringify(expectedLogItem)
461
- },
462
- expect.any(Object)
463
- )
395
+ expect(parsedLogItem.metadata).toEqual({
396
+ environment: 'test' // Only base metadata should be used
397
+ })
464
398
  })
465
399
 
466
400
  it('should handle empty metadata objects', async () => {
467
401
  mockedAxios.post.mockResolvedValueOnce({ data: { success: true } })
468
402
 
469
403
  const client = new HiveLogClient({ ...testConfig, metadata: {} })
404
+ jest.clearAllMocks()
405
+
470
406
  const logItem = { input: 'test', metadata: {} }
471
407
 
472
408
  await client.sendLog({ ...logItem, taskName: 'test-task', type: 'success' as const, boundaries: {}, metadata: logItem.metadata || {} }, {})
473
409
 
474
- const expectedLogItem = {
475
- input: 'test',
476
- metadata: {}, // All empty metadata objects result in empty final metadata
477
- taskName: 'test-task',
478
- type: 'success',
479
- boundaries: {}
480
- }
410
+ const callArgs = mockedAxios.post.mock.calls[0]
411
+ const requestBody = callArgs[1] as { projectUuid: string; taskUuid: string; logItem: string }
412
+ const parsedLogItem = JSON.parse(requestBody.logItem)
481
413
 
482
- expect(mockedAxios.post).toHaveBeenCalledWith(
483
- 'https://test-host.com/api/tasks/log-ingest',
484
- {
485
- projectName: 'test-project',
486
- taskName: 'test-task',
487
- logItem: JSON.stringify(expectedLogItem)
488
- },
489
- expect.any(Object)
490
- )
414
+ expect(parsedLogItem.metadata).toEqual({}) // All empty metadata objects result in empty final metadata
491
415
  })
492
416
 
493
417
  it('should work in silent mode with metadata', async () => {
@@ -505,6 +429,7 @@ describe('HiveLogClient Metadata', () => {
505
429
 
506
430
  const baseMetadata: Metadata = { environment: 'test' }
507
431
  const client = new HiveLogClient({ ...testConfig, metadata: baseMetadata })
432
+ jest.clearAllMocks()
508
433
 
509
434
  const result = await client.sendLog({ input: 'test', taskName: 'test-task', type: 'success' as const, boundaries: {}, metadata: {} }, { requestId: 'req-123' })
510
435