pushRuntime.test.ts 3.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. import { mkdtempSync, rmSync } from 'node:fs'
  2. import { tmpdir } from 'node:os'
  3. import { join } from 'node:path'
  4. import { afterEach, describe, expect, it, vi } from 'vitest'
  5. import { createNodeServer, type ApplicationRuntime } from '../../src/server/app.js'
  6. import type { ServerConfig } from '../../src/server/config.js'
  7. import { PushCoordinator } from '../../src/server/pushCoordinator.js'
  8. import {
  9. GroupPushEventWatcher,
  10. type GroupEventConnection,
  11. type GroupEventSource,
  12. type PushEventCoordinator,
  13. } from '../../src/server/pushEvents.js'
  14. import { createAuthenticatedApplication } from './authenticatedApplication.js'
  15. const roots: string[] = []
  16. const runtimes: ApplicationRuntime[] = []
  17. afterEach(() => {
  18. for (const runtime of runtimes.splice(0)) runtime.close()
  19. for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true })
  20. })
  21. describe('dynamic push runtime', () => {
  22. it('closes a group connection that finishes opening after the watcher was stopped', async () => {
  23. const epoch = '11111111-1111-4111-8111-111111111111'
  24. let resolveConnection!: (connection: GroupEventConnection) => void
  25. const connection = { close: vi.fn() }
  26. const source: GroupEventSource = {
  27. currentAnchor: async () => ({ epoch, cursor: 0 }),
  28. connect: () => new Promise(resolve => { resolveConnection = resolve }),
  29. }
  30. const coordinator: PushEventCoordinator = {
  31. saveChatJob: () => undefined,
  32. completeChatJob: () => undefined,
  33. pendingChatJobs: () => [],
  34. enqueueNotification: () => 'ignored',
  35. groupWatchAnchor: () => ({ epoch, cursor: 0 }),
  36. groupSubscribers: () => [],
  37. advanceGroupCursor: () => undefined,
  38. resetGroupCursor: reset => ({ epoch: reset.epoch, cursor: reset.cursor }),
  39. }
  40. const watcher = new GroupPushEventWatcher(source, coordinator)
  41. watcher.start()
  42. await vi.waitFor(() => expect(resolveConnection).toBeTypeOf('function'))
  43. watcher.stop()
  44. resolveConnection(connection)
  45. await vi.waitFor(() => expect(connection.close).toHaveBeenCalledTimes(1))
  46. })
  47. it('starts and stops background observers when Web configuration changes', async () => {
  48. const home = mkdtempSync(join(tmpdir(), 'yaoyao-push-runtime-'))
  49. roots.push(home)
  50. const config: ServerConfig = {
  51. host: '127.0.0.1', port: 8800, upstream: new URL('http://127.0.0.1:9119'),
  52. allowedHosts: new Set(), home, mediaRoot: home, attachmentsRoot: home, imagesRoot: home,
  53. mediaOwner: 'tester', allowInsecureLan: false, insecureLan: false, production: false,
  54. }
  55. const push = new PushCoordinator({
  56. home,
  57. autoFlush: false,
  58. providerFactory: () => ({ send: async () => ({ disposition: 'success', status: 200 }) }),
  59. })
  60. const runtime = createAuthenticatedApplication({ config, push })
  61. const chatStart = vi.spyOn(runtime.chatPushJobs, 'start').mockImplementation(() => undefined)
  62. const chatStop = vi.spyOn(runtime.chatPushJobs, 'stop').mockImplementation(() => undefined)
  63. const groupStart = vi.spyOn(runtime.groupPushEvents, 'start').mockImplementation(() => undefined)
  64. const groupStop = vi.spyOn(runtime.groupPushEvents, 'stop').mockImplementation(() => undefined)
  65. const node = createNodeServer(runtime)
  66. expect(chatStop).toHaveBeenCalledTimes(1)
  67. expect(groupStop).toHaveBeenCalledTimes(1)
  68. await push.configureAPNs({
  69. keyFile: '/private/unused.p8', keyId: 'KEY1234567', teamId: 'TEAM123456',
  70. topic: 'cn.samien.yaoyao.hermes', environments: ['development'],
  71. })
  72. expect(chatStart).toHaveBeenCalledTimes(1)
  73. expect(groupStart).toHaveBeenCalledTimes(1)
  74. await push.configureAPNs()
  75. expect(chatStop).toHaveBeenCalledTimes(2)
  76. expect(groupStop).toHaveBeenCalledTimes(2)
  77. await node.close()
  78. })
  79. })