Skip to content

Commit 4ecb265

Browse files
committed
fix unit tests
1 parent cd659ab commit 4ecb265

File tree

1 file changed

+171
-75
lines changed

1 file changed

+171
-75
lines changed

common/src/util/__tests__/partial-json-delta.test.ts

Lines changed: 171 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import {
55
parsePartialJsonObjectSingle,
66
} from '../partial-json-delta'
77

8-
describe('parsePartialJsonObject', () => {
9-
describe('valid JSON', () => {
8+
describe('parsePartialJsonObjectSingle', () => {
9+
describe('complete valid JSON', () => {
1010
it('should parse complete valid JSON', () => {
1111
const input = '{"name": "test", "value": 42}'
1212
const result = parsePartialJsonObjectSingle(input)
@@ -196,37 +196,59 @@ describe('parsePartialJsonObject', () => {
196196
})
197197

198198
describe('getPartialJsonDelta', () => {
199-
describe('basic delta detection', () => {
200-
it('should detect new properties', () => {
199+
describe('input validation', () => {
200+
it('should throw error when content does not start with previous', () => {
201+
const content = '{"name": "test"}'
202+
const previous = '{"other": "value"}'
203+
204+
expect(() => getPartialJsonDelta(content, previous)).toThrow(
205+
'Content must be previous content plus new content'
206+
)
207+
})
208+
209+
it('should work when content starts with previous', () => {
201210
const content = '{"name": "test", "value": 42}'
202-
const previous = {}
211+
const previous = '{"name": "test"'
212+
213+
expect(() => getPartialJsonDelta(content, previous)).not.toThrow()
214+
})
215+
})
216+
217+
describe('basic delta detection from streaming JSON', () => {
218+
it('should detect new properties added to empty object', () => {
219+
const content = '{"name": "test", "value": 42}'
220+
const previous = '{'
203221
const result = getPartialJsonDelta(content, previous)
204222

205223
expect(result.delta).toEqual({ name: 'test', value: 42 })
206224
expect(result.result).toEqual({ name: 'test', value: 42 })
225+
expect(result.lastParam.key).toBe('value')
226+
expect(result.lastParam.complete).toBe(false)
207227
})
208228

209-
it('should detect changed properties', () => {
210-
const content = '{"value":100,"name":"updated"}'
211-
const previous = { value: 100, name: 'upda' }
229+
it('should detect completion of partial string value', () => {
230+
const content = '{"name": "updated"}'
231+
const previous = '{"name": "upda'
212232
const result = getPartialJsonDelta(content, previous)
213233

214234
expect(result.delta).toEqual({ name: 'ted' })
215-
expect(result.result).toEqual({ value: 100, name: 'updated' })
235+
expect(result.result).toEqual({ name: 'updated' })
236+
expect(result.lastParam.key).toBe('name')
237+
expect(result.lastParam.complete).toBe(true)
216238
})
217239

218-
it('should return empty delta for unchanged properties', () => {
240+
it('should return empty delta when no changes detected', () => {
219241
const content = '{"name": "test", "value": 42}'
220-
const previous = { name: 'test', value: 42 }
242+
const previous = '{"name": "test", "value": 42'
221243
const result = getPartialJsonDelta(content, previous)
222244

223245
expect(result.delta).toEqual({})
224246
expect(result.result).toEqual({ name: 'test', value: 42 })
225247
})
226248

227-
it('should detect partial changes', () => {
249+
it('should detect new property being added', () => {
228250
const content = '{"name": "test", "value": 100}'
229-
const previous = { name: 'test', value: 42 }
251+
const previous = '{"name": "test", "value": '
230252
const result = getPartialJsonDelta(content, previous)
231253

232254
expect(result.delta).toEqual({ value: 100 })
@@ -235,139 +257,213 @@ describe('getPartialJsonDelta', () => {
235257
})
236258

237259
describe('string delta handling', () => {
238-
it('should handle string changes', () => {
260+
it('should return string slice for partial string updates', () => {
239261
const content = '{"message": "Hello World"}'
240-
const previous = { message: 'Hello' }
262+
const previous = '{"message": "Hello'
241263
const result = getPartialJsonDelta(content, previous)
242264

243-
// Note: Current implementation has a bug - it doesn't return the sliced string
244-
// This test documents the current behavior
245265
expect(result.delta).toEqual({ message: ' World' })
246266
expect(result.result).toEqual({ message: 'Hello World' })
247267
})
248268

249269
it('should handle empty string to non-empty string', () => {
250270
const content = '{"message": "Hello"}'
251-
const previous = { message: '' }
271+
const previous = '{"message": "'
252272
const result = getPartialJsonDelta(content, previous)
253273

254274
expect(result.delta).toEqual({ message: 'Hello' })
255275
expect(result.result).toEqual({ message: 'Hello' })
256276
})
257277

258-
it('should handle undefined to string', () => {
278+
it('should handle new string property', () => {
259279
const content = '{"message": "Hello"}'
260-
const previous = {}
280+
const previous = '{'
261281
const result = getPartialJsonDelta(content, previous)
262282

263283
expect(result.delta).toEqual({ message: 'Hello' })
264284
expect(result.result).toEqual({ message: 'Hello' })
265285
})
286+
287+
it('should handle multi-line string streaming', () => {
288+
const content = '{"text": "Line 1\\nLine 2\\nLine 3"}'
289+
const previous = '{"text": "Line 1\\nLine'
290+
const result = getPartialJsonDelta(content, previous)
291+
292+
expect(result.delta).toEqual({ text: ' 2\nLine 3' })
293+
expect(result.result).toEqual({ text: 'Line 1\nLine 2\nLine 3' })
294+
})
266295
})
267296

268-
describe('complex object changes', () => {
269-
it('should handle nested object changes', () => {
270-
const content = '{"user": {"name": "John", "age": 31}}'
271-
const previous = { user: { name: 'John', age: 30 } }
297+
describe('non-string value changes', () => {
298+
it('should return full value for non-string changes', () => {
299+
const content = '{"count": 42}'
300+
const previous = '{"count": '
301+
const result = getPartialJsonDelta(content, previous)
302+
303+
expect(result.delta).toEqual({ count: 42 })
304+
expect(result.result).toEqual({ count: 42 })
305+
})
306+
307+
it('should handle boolean values', () => {
308+
const content = '{"active": true}'
309+
const previous = '{"active": '
310+
const result = getPartialJsonDelta(content, previous)
311+
312+
expect(result.delta).toEqual({ active: true })
313+
expect(result.result).toEqual({ active: true })
314+
})
315+
316+
it('should handle null values', () => {
317+
const content = '{"data": null}'
318+
const previous = '{"data": '
272319
const result = getPartialJsonDelta(content, previous)
273320

274-
expect(result.delta).toEqual({ user: { name: 'John', age: 31 } })
275-
expect(result.result).toEqual({ user: { name: 'John', age: 31 } })
321+
expect(result.delta).toEqual({ data: null })
322+
expect(result.result).toEqual({ data: null })
276323
})
277324

278-
it('should handle array changes', () => {
279-
const content = '{"items": [1, 2, 3, 4]}'
280-
const previous = { items: [1, 2, 3] }
325+
it('should handle array values', () => {
326+
const content = '{"items": [1, 2, 3]}'
327+
const previous = '{"items": '
281328
const result = getPartialJsonDelta(content, previous)
282329

283-
expect(result.delta).toEqual({ items: [1, 2, 3, 4] })
284-
expect(result.result).toEqual({ items: [1, 2, 3, 4] })
330+
expect(result.delta).toEqual({ items: [1, 2, 3] })
331+
expect(result.result).toEqual({ items: [1, 2, 3] })
285332
})
286333

287-
it('should handle mixed type changes', () => {
288-
const content = '{"value": "string"}'
289-
const previous = { value: 42 }
334+
it('should handle nested object values', () => {
335+
const content = '{"user": {"name": "John", "age": 30}}'
336+
const previous = '{"user": '
290337
const result = getPartialJsonDelta(content, previous)
291338

292-
expect(result.delta).toEqual({ value: 'string' })
293-
expect(result.result).toEqual({ value: 'string' })
339+
expect(result.delta).toEqual({ user: { name: 'John', age: 30 } })
340+
expect(result.result).toEqual({ user: { name: 'John', age: 30 } })
294341
})
295342
})
296343

297-
describe('streaming scenarios', () => {
344+
describe('realistic streaming scenarios', () => {
298345
it('should handle progressive JSON building', () => {
299-
let previous = {}
300-
301-
// First chunk
346+
// Simulate streaming JSON construction
347+
let previous = '{'
302348
let content = '{"status": "processing"'
303349
let result = getPartialJsonDelta(content, previous)
350+
304351
expect(result.delta).toEqual({ status: 'processing' })
305-
previous = result.result
352+
expect(result.result).toEqual({ status: 'processing' })
306353

307-
// Second chunk
308-
content += ', "progress": 1.0'
354+
// Add more content
355+
previous = content
356+
content = '{"status": "processing", "progress": 0.5'
309357
result = getPartialJsonDelta(content, previous)
310-
expect(result.delta).toEqual({ progress: 1.0 })
311-
previous = result.result
312358

313-
// Final chunk
314-
content += ', "result": "success"}'
359+
expect(result.delta).toEqual({ progress: 0.5 })
360+
expect(result.result).toEqual({ status: 'processing', progress: 0.5 })
361+
362+
// Complete the JSON
363+
previous = content
364+
content =
365+
'{"status": "processing", "progress": 0.5, "message": "Almost done"}'
315366
result = getPartialJsonDelta(content, previous)
316-
expect(result.delta).toEqual({
317-
result: 'success',
367+
368+
expect(result.delta).toEqual({ message: 'Almost done' })
369+
expect(result.result).toEqual({
370+
status: 'processing',
371+
progress: 0.5,
372+
message: 'Almost done',
318373
})
319374
})
320375

321-
it('should handle incomplete JSON in streaming', () => {
376+
it('should handle streaming text completion', () => {
377+
const previous = '{"response": "The quick brown'
322378
const content =
323-
'{"message": "This is a long message that is being streamed'
324-
const previous = { message: 'This is a long' }
379+
'{"response": "The quick brown fox jumps over the lazy dog"}'
325380
const result = getPartialJsonDelta(content, previous)
326381

327-
expect(result.delta).toEqual({
328-
message: ' message that is being streamed',
382+
expect(result.delta).toEqual({ response: ' fox jumps over the lazy dog' })
383+
expect(result.result).toEqual({
384+
response: 'The quick brown fox jumps over the lazy dog',
329385
})
386+
})
387+
388+
it('should handle incomplete JSON with trailing comma', () => {
389+
const previous = '{"name": "test", "value": 42,'
390+
const content = '{"name": "test", "value": 42, "status": "complete"}'
391+
const result = getPartialJsonDelta(content, previous)
392+
393+
expect(result.delta).toEqual({ status: 'complete' })
330394
expect(result.result).toEqual({
331-
message: 'This is a long message that is being streamed',
395+
name: 'test',
396+
value: 42,
397+
status: 'complete',
332398
})
333399
})
334400
})
335401

336-
describe('edge cases', () => {
337-
it('should handle empty content', () => {
338-
const content = ''
339-
const previous = { name: 'test' }
402+
describe('lastParam tracking', () => {
403+
it('should track last parameter key and completion status', () => {
404+
const content = '{"name": "test", "value": 42}'
405+
const previous = '{'
340406
const result = getPartialJsonDelta(content, previous)
341407

342-
expect(result.delta).toEqual({})
343-
expect(result.result).toEqual({})
408+
expect(result.lastParam.key).toBe('value')
409+
expect(result.lastParam.complete).toBe(false)
410+
})
411+
412+
it('should indicate incomplete last parameter', () => {
413+
const content = '{"name": "test", "message": "partial'
414+
const previous = '{'
415+
const result = getPartialJsonDelta(content, previous)
416+
417+
expect(result.lastParam.key).toBe('message')
418+
expect(result.lastParam.complete).toBe(false)
344419
})
345420

346-
it('should handle invalid JSON content', () => {
347-
const content = 'not json'
348-
const previous = { name: 'test' }
421+
it('should handle undefined key for empty object', () => {
422+
const content = '{}'
423+
const previous = '{'
424+
const result = getPartialJsonDelta(content, previous)
425+
426+
expect(result.lastParam.key).toBeUndefined()
427+
expect(result.lastParam.complete).toBe(false)
428+
})
429+
})
430+
431+
describe('edge cases', () => {
432+
it('should handle empty previous string', () => {
433+
const content = '{"name": "test"}'
434+
const previous = ''
435+
436+
expect(() => getPartialJsonDelta(content, previous)).not.toThrow()
437+
const result = getPartialJsonDelta(content, previous)
438+
expect(result.delta).toEqual({ name: 'test' })
439+
})
440+
441+
it('should handle identical content and previous', () => {
442+
const content = '{"name": "test"}'
443+
const previous = '{"name": "test"}'
349444
const result = getPartialJsonDelta(content, previous)
350445

351446
expect(result.delta).toEqual({})
352-
expect(result.result).toEqual({})
447+
expect(result.result).toEqual({ name: 'test' })
353448
})
354449

355-
it('should handle null and undefined values', () => {
356-
const content = '{"value": null}'
357-
const previous = { value: undefined }
450+
it('should handle malformed JSON gracefully', () => {
451+
const content = '{"name": test}' // Invalid JSON
452+
const previous = '{'
358453
const result = getPartialJsonDelta(content, previous)
359454

360-
expect(result.delta).toEqual({ value: null })
361-
expect(result.result).toEqual({ value: null })
455+
// Should return empty objects when JSON parsing fails
456+
expect(result.delta).toEqual({})
457+
expect(result.result).toEqual({})
362458
})
363459

364-
it('should handle boolean changes', () => {
365-
const content = '{"active": false}'
366-
const previous = { active: true }
460+
it('should handle escaped quotes in strings', () => {
461+
const content = '{"message": "He said \\"Hello\\" to me"}'
462+
const previous = '{"message": "He said \\"Hello'
367463
const result = getPartialJsonDelta(content, previous)
368464

369-
expect(result.delta).toEqual({ active: false })
370-
expect(result.result).toEqual({ active: false })
465+
expect(result.delta).toEqual({ message: '" to me' })
466+
expect(result.result).toEqual({ message: 'He said "Hello" to me' })
371467
})
372468
})
373469
})

0 commit comments

Comments
 (0)