bindings.client.spec.ts 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
  1. // @vitest-environment jsdom
  2. /** Content identity isolates terminal bindings across browser windows and Session scopes. */
  3. import { afterEach, expect, it, vi } from 'vitest'
  4. import type { SessionId } from '@deepseek-ai/dsh-session/types'
  5. import type { WebTerminalId } from '../src/types.ts'
  6. import { TerminalBindings } from '../src/client/bindings.ts'
  7. const session = 'session' as SessionId
  8. const first = 'first' as WebTerminalId
  9. const second = 'second' as WebTerminalId
  10. afterEach(() => { vi.unstubAllGlobals(); vi.restoreAllMocks(); localStorage.clear() })
  11. it('preserves other windows records through interleaved writes, closes and reloads', () => {
  12. const a = new TerminalBindings()
  13. const b = new TerminalBindings()
  14. expect(b.get(session, 'b')).toBeUndefined()
  15. a.set(session, 'a', first)
  16. b.set(session, 'b', second)
  17. a.delete(session, 'a')
  18. const reloaded = new TerminalBindings()
  19. expect(reloaded.get(session, 'a')).toBeUndefined()
  20. expect(reloaded.get(session, 'b')).toBe(second)
  21. expect(localStorage.length).toBe(1)
  22. expect(b.get(session, 'b')).toBe(second)
  23. a.set('session.with.dot' as SessionId, 'b', first)
  24. expect(new TerminalBindings().get(session, 'b')).toBe(second)
  25. expect(new TerminalBindings().get('session.with.dot' as SessionId, 'b')).toBe(first)
  26. })
  27. it.each(['{broken', 'null', '42', '"not/a/terminal"'])('rejects malformed saved identity %s', (value) => {
  28. const bindings = new TerminalBindings()
  29. bindings.set(session, 'content', first)
  30. const key = localStorage.key(0)!
  31. localStorage.setItem(key, value)
  32. expect(new TerminalBindings().get(session, 'content')).toBeUndefined()
  33. })
  34. it('keeps current-window values usable when storage is absent or inaccessible', () => {
  35. const a = new TerminalBindings()
  36. vi.spyOn(Storage.prototype, 'getItem').mockImplementation(() => { throw new Error('Storage blocked') })
  37. expect(a.get(session, 'missing')).toBeUndefined()
  38. vi.spyOn(console, 'error').mockImplementation(() => {})
  39. vi.spyOn(Storage.prototype, 'setItem').mockImplementation(() => { throw new Error('Storage full') })
  40. vi.spyOn(Storage.prototype, 'removeItem').mockImplementation(() => { throw new Error('Storage blocked') })
  41. a.set(session, 'a', first)
  42. expect(a.get(session, 'a')).toBe(first)
  43. a.delete(session, 'a')
  44. expect(a.get(session, 'a')).toBeUndefined()
  45. vi.stubGlobal('localStorage', undefined)
  46. a.set(session, 'b', second)
  47. expect(a.get(session, 'b')).toBe(second)
  48. a.clear()
  49. expect(a.get(session, 'b')).toBeUndefined()
  50. a.delete(session, 'b')
  51. })