development-project.spec.ts 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. import { mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync, writeFileSync } from 'node:fs'
  2. import { tmpdir } from 'node:os'
  3. import { join } from 'node:path'
  4. import { afterEach, describe, expect, it } from 'vitest'
  5. import { prepareDevelopmentProject } from '../scripts/development-project.ts'
  6. import { DESKTOP_HOST_PROTOCOL_VERSION } from '../src/host-protocol.ts'
  7. import { DesktopProjectManager } from '../src/project-manager.ts'
  8. import { resolveDesktopPaths } from '../src/paths.ts'
  9. import type { DesktopRelease } from '../src/release.ts'
  10. const roots: string[] = []
  11. function temporaryRoot(): string {
  12. const root = mkdtempSync(join(tmpdir(), 'dsh-desktop-development-test-'))
  13. roots.push(root)
  14. return root
  15. }
  16. function release(version = '1.2.3'): DesktopRelease {
  17. return {
  18. schemaVersion: 1,
  19. version,
  20. hostProtocolVersion: DESKTOP_HOST_PROTOCOL_VERSION,
  21. nodeVersion: '24.17.0',
  22. pnpmVersion: '11.7.0',
  23. }
  24. }
  25. afterEach(() => {
  26. for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true })
  27. })
  28. describe('desktop development project', () => {
  29. it('manages development plugins without modifying the linked workspace packages', async () => {
  30. const root = temporaryRoot()
  31. const cli = join(root, 'apps', 'cli')
  32. const host = join(root, 'apps', 'desktop-host')
  33. const dependencies = join(root, 'workspace-dependencies')
  34. mkdirSync(join(cli, 'lib'), { recursive: true })
  35. mkdirSync(join(host, 'lib'), { recursive: true })
  36. mkdirSync(join(dependencies, '@scope'), { recursive: true })
  37. mkdirSync(join(dependencies, '@deepseek-ai', 'dsh'), { recursive: true })
  38. writeFileSync(join(cli, 'package.json'), '{"name":"@deepseek-ai/dsh","version":"1.2.3"}\n')
  39. writeFileSync(join(host, 'package.json'), '{"name":"@deepseek-ai/dsh-desktop-host","version":"1.2.3"}\n')
  40. writeFileSync(join(host, 'lib', 'index.js'), '')
  41. writeFileSync(join(dependencies, '@deepseek-ai', 'dsh', 'package.json'), '{}\n')
  42. mkdirSync(join(dependencies, 'plain-dependency'))
  43. writeFileSync(join(dependencies, 'plain-dependency', 'package.json'), '{}\n')
  44. mkdirSync(join(dependencies, '@scope', 'dependency'))
  45. writeFileSync(join(dependencies, '@scope', 'dependency', 'package.json'), '{}\n')
  46. const project = prepareDevelopmentProject({
  47. projectDir: join(root, 'development'),
  48. cliDir: cli,
  49. hostDir: host,
  50. dependencyDir: dependencies,
  51. release: release(),
  52. })
  53. expect(realpathSync(join(project, 'node_modules', '@deepseek-ai', 'dsh'))).toBe(realpathSync(cli))
  54. expect(realpathSync(join(project, 'node_modules', '@deepseek-ai', 'dsh-desktop-host'))).toBe(realpathSync(host))
  55. expect(realpathSync(join(project, 'node_modules', 'plain-dependency')))
  56. .toBe(realpathSync(join(dependencies, 'plain-dependency')))
  57. expect(realpathSync(join(project, 'node_modules', '@scope', 'dependency')))
  58. .toBe(realpathSync(join(dependencies, '@scope', 'dependency')))
  59. const manifest = JSON.parse(readFileSync(join(project, 'package.json'), 'utf8')) as {
  60. dependencies: Record<string, string>
  61. }
  62. expect(manifest.dependencies['@deepseek-ai/dsh']).toBe('1.2.3')
  63. expect(manifest.dependencies['@deepseek-ai/dsh-desktop-host']).toBe('1.2.3')
  64. const manager = new DesktopProjectManager(resolveDesktopPaths(join(root, 'home')), {
  65. dsh: project,
  66. })
  67. await manager.applyRelease()
  68. await manager.disableAllPlugins()
  69. expect(readFileSync(join(cli, 'package.json'), 'utf8')).toBe('{"name":"@deepseek-ai/dsh","version":"1.2.3"}\n')
  70. expect(readFileSync(join(host, 'lib', 'index.js'), 'utf8')).toBe('')
  71. })
  72. it('rejects a CLI package from another release', () => {
  73. const root = temporaryRoot()
  74. const cli = join(root, 'apps', 'cli')
  75. const host = join(root, 'apps', 'desktop-host')
  76. const dependencies = join(root, 'workspace-dependencies')
  77. mkdirSync(join(cli, 'lib'), { recursive: true })
  78. mkdirSync(join(host, 'lib'), { recursive: true })
  79. mkdirSync(dependencies, { recursive: true })
  80. writeFileSync(join(cli, 'package.json'), '{"name":"@deepseek-ai/dsh","version":"2.0.0"}\n')
  81. writeFileSync(join(host, 'package.json'), '{"name":"@deepseek-ai/dsh-desktop-host","version":"1.2.3"}\n')
  82. writeFileSync(join(host, 'lib', 'index.js'), '')
  83. expect(() => prepareDevelopmentProject({
  84. projectDir: join(root, 'development'),
  85. cliDir: cli,
  86. hostDir: host,
  87. dependencyDir: dependencies,
  88. release: release(),
  89. })).toThrow(/must be @deepseek-ai\/dsh@1\.2\.3/u)
  90. })
  91. })