@forgehive/hive-sdk 0.0.4 → 0.1.1

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.
@@ -77,8 +77,15 @@ describe('HiveLogClient Metadata', () => {
77
77
  it('should send log without metadata parameter', async () => {
78
78
  mockedAxios.post.mockResolvedValueOnce({ data: { success: true } })
79
79
 
80
- const logItem = { input: 'test-input', output: 'test-output' }
81
- const result = await client.sendLog('test-task', logItem)
80
+ const executionRecord = {
81
+ input: 'test-input',
82
+ output: 'test-output',
83
+ taskName: 'test-task',
84
+ type: 'success' as const,
85
+ boundaries: {},
86
+ metadata: {}
87
+ }
88
+ const result = await client.sendLog(executionRecord)
82
89
 
83
90
  expect(result).toBe('success')
84
91
  expect(mockedAxios.post).toHaveBeenCalledWith(
@@ -89,6 +96,9 @@ describe('HiveLogClient Metadata', () => {
89
96
  logItem: JSON.stringify({
90
97
  input: 'test-input',
91
98
  output: 'test-output',
99
+ taskName: 'test-task',
100
+ type: 'success',
101
+ boundaries: {},
92
102
  metadata: {}
93
103
  })
94
104
  },
@@ -104,13 +114,20 @@ describe('HiveLogClient Metadata', () => {
104
114
  it('should send log with metadata parameter', async () => {
105
115
  mockedAxios.post.mockResolvedValueOnce({ data: { success: true } })
106
116
 
107
- const logItem = { input: 'test-input', output: 'test-output' }
117
+ const executionRecord = {
118
+ input: 'test-input',
119
+ output: 'test-output',
120
+ taskName: 'test-task',
121
+ type: 'success' as const,
122
+ boundaries: {},
123
+ metadata: {}
124
+ }
108
125
  const metadata: Metadata = {
109
126
  requestId: 'req-123',
110
127
  userId: 'user-456'
111
128
  }
112
129
 
113
- const result = await client.sendLog('test-task', logItem, metadata)
130
+ const result = await client.sendLog(executionRecord, metadata)
114
131
 
115
132
  expect(result).toBe('success')
116
133
  expect(mockedAxios.post).toHaveBeenCalledWith(
@@ -121,6 +138,9 @@ describe('HiveLogClient Metadata', () => {
121
138
  logItem: JSON.stringify({
122
139
  input: 'test-input',
123
140
  output: 'test-output',
141
+ taskName: 'test-task',
142
+ type: 'success',
143
+ boundaries: {},
124
144
  metadata: {
125
145
  requestId: 'req-123',
126
146
  userId: 'user-456'
@@ -140,7 +160,7 @@ describe('HiveLogClient Metadata', () => {
140
160
  mockedAxios.post.mockResolvedValueOnce({ data: { success: true } })
141
161
 
142
162
  const metadata: Metadata = { type: 'minimal' }
143
- const result = await client.sendLog('test-task', { input: 'simple input' }, metadata)
163
+ const result = await client.sendLog({ input: 'simple input', taskName: 'test-task', type: 'success' as const, boundaries: {}, metadata: {} }, metadata)
144
164
 
145
165
  expect(result).toBe('success')
146
166
  expect(mockedAxios.post).toHaveBeenCalledWith(
@@ -150,6 +170,9 @@ describe('HiveLogClient Metadata', () => {
150
170
  taskName: 'test-task',
151
171
  logItem: JSON.stringify({
152
172
  input: 'simple input',
173
+ taskName: 'test-task',
174
+ type: 'success',
175
+ boundaries: {},
153
176
  metadata: { type: 'minimal' }
154
177
  })
155
178
  },
@@ -161,7 +184,7 @@ describe('HiveLogClient Metadata', () => {
161
184
  mockedAxios.post.mockResolvedValueOnce({ data: { success: true } })
162
185
 
163
186
  const metadata: Metadata = { type: 'null-test' }
164
- const result = await client.sendLog('test-task', { input: null }, metadata)
187
+ const result = await client.sendLog({ input: null, taskName: 'test-task', type: 'success' as const, boundaries: {}, metadata: {} }, metadata)
165
188
 
166
189
  expect(result).toBe('success')
167
190
  expect(mockedAxios.post).toHaveBeenCalledWith(
@@ -171,6 +194,9 @@ describe('HiveLogClient Metadata', () => {
171
194
  taskName: 'test-task',
172
195
  logItem: JSON.stringify({
173
196
  input: null,
197
+ taskName: 'test-task',
198
+ type: 'success',
199
+ boundaries: {},
174
200
  metadata: { type: 'null-test' }
175
201
  })
176
202
  },
@@ -191,10 +217,13 @@ describe('HiveLogClient Metadata', () => {
191
217
  const client = new HiveLogClient({ ...testConfig, metadata: baseMetadata })
192
218
  const logItem = { input: 'test' }
193
219
 
194
- await client.sendLog('test-task', logItem)
220
+ await client.sendLog({ ...logItem, taskName: 'test-task', type: 'success' as const, boundaries: {}, metadata: {} })
195
221
 
196
222
  const expectedLogItem = {
197
223
  input: 'test',
224
+ taskName: 'test-task',
225
+ type: 'success',
226
+ boundaries: {},
198
227
  metadata: {
199
228
  environment: 'production',
200
229
  version: '1.0.0'
@@ -229,7 +258,7 @@ describe('HiveLogClient Metadata', () => {
229
258
  }
230
259
  }
231
260
 
232
- await client.sendLog('test-task', logItem)
261
+ await client.sendLog({ ...logItem, taskName: 'test-task', type: 'success' as const, boundaries: {}, metadata: logItem.metadata || {} })
233
262
 
234
263
  const expectedLogItem = {
235
264
  input: 'test',
@@ -237,7 +266,10 @@ describe('HiveLogClient Metadata', () => {
237
266
  environment: 'production', // from base
238
267
  version: '1.1.0', // from logItem (overrides base)
239
268
  sessionId: 'session-123' // from logItem
240
- }
269
+ },
270
+ taskName: 'test-task',
271
+ type: 'success',
272
+ boundaries: {}
241
273
  }
242
274
 
243
275
  expect(mockedAxios.post).toHaveBeenCalledWith(
@@ -276,7 +308,7 @@ describe('HiveLogClient Metadata', () => {
276
308
  priority: 'sendLog'
277
309
  }
278
310
 
279
- await client.sendLog('test-task', logItem, sendLogMetadata)
311
+ await client.sendLog({ ...logItem, taskName: 'test-task', type: 'success' as const, boundaries: {}, metadata: logItem.metadata || {} }, sendLogMetadata)
280
312
 
281
313
  const expectedLogItem = {
282
314
  input: 'test',
@@ -286,7 +318,10 @@ describe('HiveLogClient Metadata', () => {
286
318
  priority: 'sendLog', // from sendLog (highest priority)
287
319
  sessionId: 'session-123', // from logItem
288
320
  requestId: 'req-456' // from sendLog
289
- }
321
+ },
322
+ taskName: 'test-task',
323
+ type: 'success',
324
+ boundaries: {}
290
325
  }
291
326
 
292
327
  expect(mockedAxios.post).toHaveBeenCalledWith(
@@ -327,7 +362,7 @@ describe('HiveLogClient Metadata', () => {
327
362
  version: '1.2.0' // overrides both base and logItem
328
363
  }
329
364
 
330
- await client.sendLog('search-task', logItem, sendLogMetadata)
365
+ await client.sendLog({ ...logItem, taskName: 'search-task', type: 'success' as const, boundaries: {}, metadata: logItem.metadata || {} }, sendLogMetadata)
331
366
 
332
367
  const expectedLogItem = {
333
368
  input: { query: 'search' },
@@ -341,7 +376,10 @@ describe('HiveLogClient Metadata', () => {
341
376
  processingTime: '250', // from logItem
342
377
  requestId: 'req-789', // from sendLog
343
378
  userId: 'user-123' // from sendLog
344
- }
379
+ },
380
+ taskName: 'search-task',
381
+ type: 'success',
382
+ boundaries: {}
345
383
  }
346
384
 
347
385
  expect(mockedAxios.post).toHaveBeenCalledWith(
@@ -368,10 +406,13 @@ describe('HiveLogClient Metadata', () => {
368
406
  // No metadata property
369
407
  }
370
408
 
371
- await client.sendLog('test-task', logItem)
409
+ await client.sendLog({ ...logItem, taskName: 'test-task', type: 'success' as const, boundaries: {}, metadata: {} })
372
410
 
373
411
  const expectedLogItem = {
374
412
  input: 'test',
413
+ taskName: 'test-task',
414
+ type: 'success',
415
+ boundaries: {},
375
416
  metadata: {
376
417
  environment: 'test' // Only base metadata should be used
377
418
  }
@@ -399,13 +440,16 @@ describe('HiveLogClient Metadata', () => {
399
440
  metadata: undefined
400
441
  }
401
442
 
402
- await client.sendLog('test-task', logItem)
443
+ await client.sendLog({ ...logItem, taskName: 'test-task', type: 'success' as const, boundaries: {}, metadata: logItem.metadata || {} })
403
444
 
404
445
  const expectedLogItem = {
405
446
  input: 'test',
406
447
  metadata: {
407
448
  environment: 'test' // Only base metadata should be used
408
- }
449
+ },
450
+ taskName: 'test-task',
451
+ type: 'success',
452
+ boundaries: {}
409
453
  }
410
454
 
411
455
  expect(mockedAxios.post).toHaveBeenCalledWith(
@@ -425,11 +469,14 @@ describe('HiveLogClient Metadata', () => {
425
469
  const client = new HiveLogClient({ ...testConfig, metadata: {} })
426
470
  const logItem = { input: 'test', metadata: {} }
427
471
 
428
- await client.sendLog('test-task', logItem, {})
472
+ await client.sendLog({ ...logItem, taskName: 'test-task', type: 'success' as const, boundaries: {}, metadata: logItem.metadata || {} }, {})
429
473
 
430
474
  const expectedLogItem = {
431
475
  input: 'test',
432
- metadata: {} // All empty metadata objects result in empty final metadata
476
+ metadata: {}, // All empty metadata objects result in empty final metadata
477
+ taskName: 'test-task',
478
+ type: 'success',
479
+ boundaries: {}
433
480
  }
434
481
 
435
482
  expect(mockedAxios.post).toHaveBeenCalledWith(
@@ -447,7 +494,7 @@ describe('HiveLogClient Metadata', () => {
447
494
  const baseMetadata: Metadata = { environment: 'test' }
448
495
  const silentClient = new HiveLogClient({ projectName: 'silent-project', metadata: baseMetadata })
449
496
 
450
- const result = await silentClient.sendLog('test-task', { input: 'test' }, { requestId: 'req-123' })
497
+ const result = await silentClient.sendLog({ input: 'test', taskName: 'test-task', type: 'success' as const, boundaries: {}, metadata: {} }, { requestId: 'req-123' })
451
498
 
452
499
  expect(result).toBe('silent')
453
500
  expect(mockedAxios.post).not.toHaveBeenCalled()
@@ -459,7 +506,7 @@ describe('HiveLogClient Metadata', () => {
459
506
  const baseMetadata: Metadata = { environment: 'test' }
460
507
  const client = new HiveLogClient({ ...testConfig, metadata: baseMetadata })
461
508
 
462
- const result = await client.sendLog('test-task', { input: 'test' }, { requestId: 'req-123' })
509
+ const result = await client.sendLog({ input: 'test', taskName: 'test-task', type: 'success' as const, boundaries: {}, metadata: {} }, { requestId: 'req-123' })
463
510
 
464
511
  expect(result).toBe('error')
465
512
  })
@@ -5,7 +5,7 @@ import { HiveLogClient } from '../index'
5
5
  jest.mock('axios')
6
6
  const mockedAxios = axios as jest.Mocked<typeof axios>
7
7
 
8
- describe('HiveLogClient sendLog', () => {
8
+ describe('HiveLogClient sendLog with ExecutionRecord', () => {
9
9
  let client: HiveLogClient
10
10
 
11
11
  const testConfig = {
@@ -23,13 +23,21 @@ describe('HiveLogClient sendLog', () => {
23
23
  jest.clearAllMocks()
24
24
  })
25
25
 
26
- describe('successful sendLog', () => {
27
- it('should send log successfully and return true', async () => {
26
+ describe('successful sendLog with ExecutionRecord', () => {
27
+ it('should send log successfully with ExecutionRecord and return success', async () => {
28
28
  // Mock successful axios response
29
29
  mockedAxios.post.mockResolvedValueOnce({ data: { success: true } })
30
30
 
31
- const logItem = { input: 'test-input', output: 'test-output' }
32
- const result = await client.sendLog('test-task', logItem)
31
+ const executionRecord = {
32
+ input: { value: 'test-input' },
33
+ output: { result: 'test-output' },
34
+ taskName: 'test-task',
35
+ type: 'success' as const,
36
+ boundaries: {},
37
+ metadata: {}
38
+ }
39
+
40
+ const result = await client.sendLog(executionRecord)
33
41
 
34
42
  expect(result).toBe('success')
35
43
  expect(mockedAxios.post).toHaveBeenCalledTimes(1)
@@ -38,7 +46,14 @@ describe('HiveLogClient sendLog', () => {
38
46
  {
39
47
  projectName: 'test-project',
40
48
  taskName: 'test-task',
41
- logItem: JSON.stringify({ ...logItem, metadata: {} })
49
+ logItem: JSON.stringify({
50
+ input: { value: 'test-input' },
51
+ output: { result: 'test-output' },
52
+ taskName: 'test-task',
53
+ type: 'success',
54
+ boundaries: {},
55
+ metadata: {}
56
+ })
42
57
  },
43
58
  {
44
59
  headers: {
@@ -49,19 +64,30 @@ describe('HiveLogClient sendLog', () => {
49
64
  )
50
65
  })
51
66
 
52
- it('should handle complex log items', async () => {
67
+ it('should handle ExecutionRecord with complex boundaries', async () => {
53
68
  mockedAxios.post.mockResolvedValueOnce({ data: { success: true } })
54
69
 
55
- const complexLogItem = {
70
+ const executionRecord = {
56
71
  input: { userId: 123, action: 'login' },
57
72
  output: { success: true, sessionId: 'abc123' },
58
- error: null,
73
+ taskName: 'complex-task',
74
+ type: 'success' as const,
59
75
  boundaries: {
60
- database: [{ input: 'SELECT * FROM users', output: [{ id: 123 }], error: null }]
61
- }
76
+ database: [{
77
+ input: ['SELECT * FROM users'],
78
+ output: [{ id: 123 }],
79
+ timing: { startTime: 1000, endTime: 1100, duration: 100 }
80
+ }],
81
+ api: [{
82
+ input: [{ endpoint: '/auth' }],
83
+ output: { token: 'jwt123' },
84
+ timing: { startTime: 1200, endTime: 1250, duration: 50 }
85
+ }]
86
+ },
87
+ metadata: { environment: 'test' }
62
88
  }
63
89
 
64
- const result = await client.sendLog('complex-task', complexLogItem)
90
+ const result = await client.sendLog(executionRecord)
65
91
 
66
92
  expect(result).toBe('success')
67
93
  expect(mockedAxios.post).toHaveBeenCalledWith(
@@ -69,7 +95,25 @@ describe('HiveLogClient sendLog', () => {
69
95
  {
70
96
  projectName: 'test-project',
71
97
  taskName: 'complex-task',
72
- logItem: JSON.stringify({ ...complexLogItem, metadata: {} })
98
+ logItem: JSON.stringify({
99
+ input: { userId: 123, action: 'login' },
100
+ output: { success: true, sessionId: 'abc123' },
101
+ taskName: 'complex-task',
102
+ type: 'success',
103
+ boundaries: {
104
+ database: [{
105
+ input: ['SELECT * FROM users'],
106
+ output: [{ id: 123 }],
107
+ timing: { startTime: 1000, endTime: 1100, duration: 100 }
108
+ }],
109
+ api: [{
110
+ input: [{ endpoint: '/auth' }],
111
+ output: { token: 'jwt123' },
112
+ timing: { startTime: 1200, endTime: 1250, duration: 50 }
113
+ }]
114
+ },
115
+ metadata: { environment: 'test' }
116
+ })
73
117
  },
74
118
  {
75
119
  headers: {
@@ -79,64 +123,288 @@ describe('HiveLogClient sendLog', () => {
79
123
  }
80
124
  )
81
125
  })
126
+
127
+ it('should handle ExecutionRecord with error', async () => {
128
+ mockedAxios.post.mockResolvedValueOnce({ data: { success: true } })
129
+
130
+ const executionRecord = {
131
+ input: { value: 'test-input' },
132
+ output: undefined,
133
+ error: 'Task execution failed',
134
+ taskName: 'error-task',
135
+ type: 'error' as const,
136
+ boundaries: {},
137
+ metadata: {}
138
+ }
139
+
140
+ const result = await client.sendLog(executionRecord)
141
+
142
+ expect(result).toBe('success')
143
+ expect(mockedAxios.post).toHaveBeenCalledWith(
144
+ 'https://test-host.com/api/tasks/log-ingest',
145
+ {
146
+ projectName: 'test-project',
147
+ taskName: 'error-task',
148
+ logItem: JSON.stringify({
149
+ input: { value: 'test-input' },
150
+ output: undefined,
151
+ error: 'Task execution failed',
152
+ taskName: 'error-task',
153
+ type: 'error',
154
+ boundaries: {},
155
+ metadata: {}
156
+ })
157
+ },
158
+ expect.any(Object)
159
+ )
160
+ })
161
+
162
+ it('should use "unknown-task" when taskName is missing', async () => {
163
+ mockedAxios.post.mockResolvedValueOnce({ data: { success: true } })
164
+
165
+ const executionRecord = {
166
+ input: { value: 'test-input' },
167
+ output: { result: 'test-output' },
168
+ // taskName is missing
169
+ type: 'success' as const,
170
+ boundaries: {},
171
+ metadata: {}
172
+ }
173
+
174
+ const result = await client.sendLog(executionRecord)
175
+
176
+ expect(result).toBe('success')
177
+ expect(mockedAxios.post).toHaveBeenCalledWith(
178
+ 'https://test-host.com/api/tasks/log-ingest',
179
+ {
180
+ projectName: 'test-project',
181
+ taskName: 'unknown-task',
182
+ logItem: JSON.stringify({
183
+ input: { value: 'test-input' },
184
+ output: { result: 'test-output' },
185
+ type: 'success',
186
+ boundaries: {},
187
+ metadata: {},
188
+ taskName: 'unknown-task'
189
+ })
190
+ },
191
+ expect.any(Object)
192
+ )
193
+ })
194
+ })
195
+
196
+ describe('sendLog with additional metadata', () => {
197
+ it('should merge metadata from ExecutionRecord and sendLog parameter', async () => {
198
+ mockedAxios.post.mockResolvedValueOnce({ data: { success: true } })
199
+
200
+ const executionRecord = {
201
+ input: { value: 'test-input' },
202
+ output: { result: 'test-output' },
203
+ taskName: 'metadata-task',
204
+ type: 'success' as const,
205
+ boundaries: {},
206
+ metadata: {
207
+ recordMeta: 'from-record',
208
+ sharedKey: 'record-value'
209
+ }
210
+ }
211
+
212
+ const sendLogMetadata = {
213
+ sendLogMeta: 'from-sendlog',
214
+ sharedKey: 'sendlog-value' // This should override record value
215
+ }
216
+
217
+ const result = await client.sendLog(executionRecord, sendLogMetadata)
218
+
219
+ expect(result).toBe('success')
220
+ expect(mockedAxios.post).toHaveBeenCalledWith(
221
+ 'https://test-host.com/api/tasks/log-ingest',
222
+ {
223
+ projectName: 'test-project',
224
+ taskName: 'metadata-task',
225
+ logItem: JSON.stringify({
226
+ input: { value: 'test-input' },
227
+ output: { result: 'test-output' },
228
+ taskName: 'metadata-task',
229
+ type: 'success',
230
+ boundaries: {},
231
+ metadata: {
232
+ recordMeta: 'from-record',
233
+ sharedKey: 'sendlog-value', // sendLog metadata takes priority
234
+ sendLogMeta: 'from-sendlog'
235
+ }
236
+ })
237
+ },
238
+ expect.any(Object)
239
+ )
240
+ })
82
241
  })
83
242
 
84
243
  describe('failed sendLog', () => {
85
- it('should return false when axios throws an error', async () => {
244
+ it('should return error when axios throws an error', async () => {
86
245
  // Mock axios to throw an error
87
246
  mockedAxios.post.mockRejectedValueOnce(new Error('Network error'))
88
247
 
89
- const logItem = { input: 'test-input' }
90
- const result = await client.sendLog('test-task', logItem)
248
+ const executionRecord = {
249
+ input: { value: 'test-input' },
250
+ taskName: 'test-task',
251
+ type: 'success' as const,
252
+ boundaries: {},
253
+ metadata: {}
254
+ }
255
+
256
+ const result = await client.sendLog(executionRecord)
91
257
 
92
258
  expect(result).toBe('error')
93
259
  })
94
260
 
95
- it('should return false when server returns 500', async () => {
261
+ it('should return error when server returns 500', async () => {
96
262
  // Mock axios to throw a server error
97
263
  const serverError = new Error('Server Error')
98
264
  mockedAxios.post.mockRejectedValueOnce(serverError)
99
265
 
100
- const result = await client.sendLog('test-task', { input: 'test' })
266
+ const executionRecord = {
267
+ input: { value: 'test' },
268
+ taskName: 'test-task',
269
+ type: 'success' as const,
270
+ boundaries: {},
271
+ metadata: {}
272
+ }
273
+
274
+ const result = await client.sendLog(executionRecord)
101
275
 
102
276
  expect(result).toBe('error')
103
277
  })
104
278
  })
105
279
 
106
- describe('sendLog parameters', () => {
107
- it('should handle log items with minimal input', async () => {
280
+ describe('sendLog in silent mode', () => {
281
+ it('should return silent when client is not initialized', async () => {
282
+ const uninitializedClient = new HiveLogClient({
283
+ projectName: 'test-project'
284
+ // No API credentials
285
+ })
286
+
287
+ const executionRecord = {
288
+ input: { value: 'test-input' },
289
+ taskName: 'test-task',
290
+ type: 'success' as const,
291
+ boundaries: {},
292
+ metadata: {}
293
+ }
294
+
295
+ const result = await uninitializedClient.sendLog(executionRecord)
296
+
297
+ expect(result).toBe('silent')
298
+ expect(mockedAxios.post).not.toHaveBeenCalled()
299
+ })
300
+ })
301
+ })
302
+
303
+
304
+ describe('HiveLogClient getListener', () => {
305
+ let client: HiveLogClient
306
+
307
+ const testConfig = {
308
+ projectName: 'test-project',
309
+ apiKey: 'test-api-key',
310
+ apiSecret: 'test-api-secret',
311
+ host: 'https://test-host.com'
312
+ }
313
+
314
+ beforeEach(() => {
315
+ client = new HiveLogClient(testConfig)
316
+ jest.clearAllMocks()
317
+ })
318
+
319
+ describe('getListener method', () => {
320
+ it('should return a function that calls sendLog', async () => {
108
321
  mockedAxios.post.mockResolvedValueOnce({ data: { success: true } })
109
322
 
110
- const result = await client.sendLog('minimal-task', { input: 'minimal input' })
323
+ const listener = client.getListener()
111
324
 
112
- expect(result).toBe('success')
325
+ expect(typeof listener).toBe('function')
326
+
327
+ const executionRecord = {
328
+ input: { value: 'test-input' },
329
+ output: { result: 'test-output' },
330
+ taskName: 'test-task',
331
+ type: 'success' as const,
332
+ boundaries: {},
333
+ metadata: {}
334
+ }
335
+
336
+ await listener(executionRecord)
337
+
338
+ expect(mockedAxios.post).toHaveBeenCalledTimes(1)
113
339
  expect(mockedAxios.post).toHaveBeenCalledWith(
114
340
  'https://test-host.com/api/tasks/log-ingest',
115
341
  {
116
342
  projectName: 'test-project',
117
- taskName: 'minimal-task',
118
- logItem: JSON.stringify({ input: 'minimal input', metadata: {} })
343
+ taskName: 'test-task',
344
+ logItem: JSON.stringify({
345
+ input: { value: 'test-input' },
346
+ output: { result: 'test-output' },
347
+ taskName: 'test-task',
348
+ type: 'success',
349
+ boundaries: {},
350
+ metadata: {}
351
+ })
119
352
  },
120
353
  expect.any(Object)
121
354
  )
122
355
  })
123
356
 
124
- it('should handle null/undefined values in log items', async () => {
357
+ it('should return a function that calls sendLog with provided metadata', async () => {
125
358
  mockedAxios.post.mockResolvedValueOnce({ data: { success: true } })
126
359
 
127
- const logItem = { input: null, output: undefined, error: 'some error' }
128
- const result = await client.sendLog('null-task', logItem)
360
+ const listener = client.getListener()
361
+
362
+ const executionRecord = {
363
+ input: { value: 'test-input' },
364
+ output: { result: 'test-output' },
365
+ taskName: 'test-task',
366
+ type: 'success' as const,
367
+ boundaries: {},
368
+ metadata: { recordMeta: 'from-record' }
369
+ }
370
+
371
+ await listener(executionRecord)
129
372
 
130
- expect(result).toBe('success')
131
373
  expect(mockedAxios.post).toHaveBeenCalledWith(
132
374
  'https://test-host.com/api/tasks/log-ingest',
133
375
  {
134
376
  projectName: 'test-project',
135
- taskName: 'null-task',
136
- logItem: JSON.stringify({ ...logItem, metadata: {} })
377
+ taskName: 'test-task',
378
+ logItem: JSON.stringify({
379
+ input: { value: 'test-input' },
380
+ output: { result: 'test-output' },
381
+ taskName: 'test-task',
382
+ type: 'success',
383
+ boundaries: {},
384
+ metadata: {
385
+ recordMeta: 'from-record'
386
+ }
387
+ })
137
388
  },
138
389
  expect.any(Object)
139
390
  )
140
391
  })
392
+
393
+ it('should handle listener errors gracefully', async () => {
394
+ mockedAxios.post.mockRejectedValueOnce(new Error('Network error'))
395
+
396
+ const listener = client.getListener()
397
+
398
+ const executionRecord = {
399
+ input: { value: 'test-input' },
400
+ taskName: 'test-task',
401
+ type: 'success' as const,
402
+ boundaries: {},
403
+ metadata: {}
404
+ }
405
+
406
+ // Should not throw, even if sendLog fails
407
+ await expect(listener(executionRecord)).resolves.toBeUndefined()
408
+ })
141
409
  })
142
410
  })