invariant.spec.ts 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. import { describe, expect, it } from 'vitest'
  2. import { Context } from '@deepseek-ai/cordis'
  3. import { credentialKey } from '@deepseek-ai/dsh-credentials'
  4. import InvariantRegistry from '@deepseek-ai/dsh-invariants'
  5. import AuthorizationService from '@deepseek-ai/dsh-authorization'
  6. import * as AuthorizationInvariant from '../src/invariant.ts'
  7. import { MemoryCredentials } from './memory.ts'
  8. const KEY = credentialKey('llm-pi-ai', 'openai-codex')
  9. describe('authorization invariant companion', () => {
  10. it('accepts an attempt that released its key before settling', async () => {
  11. const ctx = new Context()
  12. await ctx.plugin(InvariantRegistry)
  13. await ctx.plugin(AuthorizationInvariant)
  14. await ctx.plugin(MemoryCredentials)
  15. await ctx.plugin(AuthorizationService)
  16. ctx.authorization.registerFlow({
  17. key: KEY,
  18. label: 'ChatGPT (Codex)',
  19. methods: [{ id: 'oauth', label: 'Sign in' }],
  20. run: () => ctx.credentials
  21. .modifyRecord(KEY, () => Promise.resolve({ kind: 'grant', payload: {} }))
  22. .then(() => undefined),
  23. })
  24. await expect(ctx.authorization.begin({
  25. key: KEY,
  26. interaction: { notify: () => {}, prompt: () => Promise.reject(new Error('unused')) },
  27. })).resolves.toEqual({ status: 'authorized' })
  28. })
  29. it('fails a settlement that left its key in flight', async () => {
  30. const ctx = new Context()
  31. await ctx.plugin(InvariantRegistry)
  32. await ctx.plugin(AuthorizationInvariant)
  33. await ctx.plugin(MemoryCredentials)
  34. await ctx.plugin(AuthorizationService)
  35. const started = Promise.withResolvers<undefined>()
  36. ctx.authorization.registerFlow({
  37. key: KEY,
  38. label: 'ChatGPT (Codex)',
  39. methods: [{ id: 'oauth', label: 'Sign in' }],
  40. run: () => {
  41. started.resolve(undefined)
  42. return new Promise(() => {})
  43. },
  44. })
  45. void ctx.authorization.begin({
  46. key: KEY,
  47. interaction: { notify: () => {}, prompt: () => Promise.reject(new Error('unused')) },
  48. })
  49. await started.promise
  50. expect(() => { ctx.emit('authorization/settled', KEY, 'authorized') })
  51. .toThrow(/left the key in flight/)
  52. })
  53. it('fails a settlement emitted without a live service', async () => {
  54. const ctx = new Context()
  55. await ctx.plugin(InvariantRegistry)
  56. await ctx.plugin(AuthorizationInvariant)
  57. expect(() => { ctx.emit('authorization/settled', KEY, 'cancelled') })
  58. .toThrow(/without a live authorization service/)
  59. })
  60. it('accepts a settlement whose flow left during its own attempt', async () => {
  61. const ctx = new Context()
  62. await ctx.plugin(InvariantRegistry)
  63. await ctx.plugin(AuthorizationInvariant)
  64. await ctx.plugin(MemoryCredentials)
  65. await ctx.plugin(AuthorizationService)
  66. expect(() => { ctx.emit('authorization/settled', KEY, 'cancelled') }).not.toThrow()
  67. })
  68. it('reserves the package name against duplicate registration', async () => {
  69. const ctx = new Context()
  70. await ctx.plugin(InvariantRegistry)
  71. await ctx.plugin(AuthorizationInvariant)
  72. expect(() => {
  73. ctx.invariants.register('@deepseek-ai/dsh-authorization', () => {})
  74. }).toThrow(/already registered/)
  75. })
  76. })