Files
deepseek-harness/apps/desktop/tests/development-project.spec.ts
T
mektpoy 80e4da0e3d fix(desktop): take the development project target as an explicit input
`prepareDevelopmentProject()` resolved the build target from the host
platform and architecture, so a Linux host threw `unsupported target
linux-x64` while writing the runtime descriptor. Pass the target in
instead: the launcher supplies the target it prepared, and isolated tests
supply a fixed supported target.

Extract `desktopTargetPlatform()` into desktop-build-paths.mjs so the
target-to-payload mapping has one home, and use it for the qualification
runner, which previously derived both its target directory and its
descriptor from the host architecture.

Correct the README limitation to name the module that now performs the
architecture check.
2026-09-23 16:03:17 +08:00

126 lines
6.3 KiB
TypeScript

import { mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync, symlinkSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, describe, expect, it } from 'vitest'
import { prepareDevelopmentProject } from '../scripts/development-project.ts'
import { DESKTOP_HOST_PROTOCOL_VERSION } from '../src/host-protocol.ts'
import { DesktopProjectManager } from '../src/project-manager.ts'
import { resolveDesktopPaths } from '../src/paths.ts'
import type { DesktopRelease } from '../src/release.ts'
const roots: string[] = []
function temporaryRoot(): string {
const root = mkdtempSync(join(tmpdir(), 'dsh-desktop-development-test-'))
roots.push(root)
return root
}
function release(version = '1.2.3'): DesktopRelease {
return {
schemaVersion: 1,
version,
hostProtocolVersion: DESKTOP_HOST_PROTOCOL_VERSION,
nodeVersion: '24.17.0',
pnpmVersion: '11.7.0',
}
}
afterEach(() => {
for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true })
})
describe('desktop development project', () => {
it('includes declared workspace packages missing from the hoist directory in the runtime inventory', () => {
const root = temporaryRoot()
const cli = join(root, 'cli')
const host = join(root, 'host')
const dependency = join(root, 'unhoisted')
const hoisted = join(root, 'hoisted')
mkdirSync(join(cli, 'node_modules'), { recursive: true })
mkdirSync(join(host, 'lib'), { recursive: true })
mkdirSync(dependency)
mkdirSync(hoisted)
writeFileSync(join(cli, 'package.json'), JSON.stringify({ name: '@deepseek-ai/dsh', version: '1.2.3', dependencies: { unhoisted: 'workspace:^' } }))
writeFileSync(join(host, 'package.json'), JSON.stringify({ name: '@deepseek-ai/dsh-desktop-host', version: '1.2.3' }))
writeFileSync(join(host, 'lib/index.js'), '')
writeFileSync(join(dependency, 'package.json'), JSON.stringify({ name: 'unhoisted', version: '1.2.3' }))
symlinkSync(dependency, join(cli, 'node_modules/unhoisted'), process.platform === 'win32' ? 'junction' : 'dir')
const project = prepareDevelopmentProject({ projectDir: join(root, 'runtime'), cliDir: cli, hostDir: host, dependencyDir: hoisted, release: release(), target: 'mac-arm64' })
expect(realpathSync(join(project, 'node_modules/unhoisted'))).toBe(realpathSync(dependency))
const descriptor = JSON.parse(readFileSync(join(project, 'desktop-runtime.json'), 'utf8')) as { platform: string; arch: string; sharedPackages: unknown[] }
expect(descriptor).toMatchObject({ platform: 'darwin', arch: 'arm64' })
expect(descriptor.sharedPackages).toContainEqual({ name: 'unhoisted', version: '1.2.3', path: 'node_modules/unhoisted' })
})
it('manages development plugins without modifying the linked workspace packages', async () => {
const root = temporaryRoot()
const cli = join(root, 'apps', 'cli')
const host = join(root, 'apps', 'desktop-host')
const dependencies = join(root, 'workspace-dependencies')
mkdirSync(join(cli, 'lib'), { recursive: true })
mkdirSync(join(host, 'lib'), { recursive: true })
mkdirSync(join(dependencies, '@scope'), { recursive: true })
mkdirSync(join(dependencies, '@deepseek-ai', 'dsh'), { recursive: true })
writeFileSync(join(cli, 'package.json'), '{"name":"@deepseek-ai/dsh","version":"1.2.3"}\n')
writeFileSync(join(host, 'package.json'), '{"name":"@deepseek-ai/dsh-desktop-host","version":"1.2.3"}\n')
writeFileSync(join(host, 'lib', 'index.js'), '')
writeFileSync(join(dependencies, '@deepseek-ai', 'dsh', 'package.json'), '{}\n')
mkdirSync(join(dependencies, 'plain-dependency'))
writeFileSync(join(dependencies, 'plain-dependency', 'package.json'), '{}\n')
mkdirSync(join(dependencies, '@scope', 'dependency'))
writeFileSync(join(dependencies, '@scope', 'dependency', 'package.json'), '{}\n')
const project = prepareDevelopmentProject({
projectDir: join(root, 'development'),
cliDir: cli,
hostDir: host,
dependencyDir: dependencies,
release: release(),
target: 'win-x64',
})
expect(realpathSync(join(project, 'node_modules', '@deepseek-ai', 'dsh'))).toBe(realpathSync(cli))
expect(realpathSync(join(project, 'node_modules', '@deepseek-ai', 'dsh-desktop-host'))).toBe(realpathSync(host))
expect(realpathSync(join(project, 'node_modules', 'plain-dependency')))
.toBe(realpathSync(join(dependencies, 'plain-dependency')))
expect(realpathSync(join(project, 'node_modules', '@scope', 'dependency')))
.toBe(realpathSync(join(dependencies, '@scope', 'dependency')))
const manifest = JSON.parse(readFileSync(join(project, 'package.json'), 'utf8')) as {
dependencies: Record<string, string>
}
expect(manifest.dependencies['@deepseek-ai/dsh']).toBe('1.2.3')
expect(manifest.dependencies['@deepseek-ai/dsh-desktop-host']).toBe('1.2.3')
const descriptor = JSON.parse(readFileSync(join(project, 'desktop-runtime.json'), 'utf8')) as { platform: string; arch: string }
expect(descriptor).toMatchObject({ platform: 'win32', arch: 'x64' })
const manager = new DesktopProjectManager(resolveDesktopPaths(join(root, 'home')), {
dsh: project,
})
await manager.applyRelease()
await manager.disableAllPlugins()
expect(readFileSync(join(cli, 'package.json'), 'utf8')).toBe('{"name":"@deepseek-ai/dsh","version":"1.2.3"}\n')
expect(readFileSync(join(host, 'lib', 'index.js'), 'utf8')).toBe('')
})
it('rejects a CLI package from another release', () => {
const root = temporaryRoot()
const cli = join(root, 'apps', 'cli')
const host = join(root, 'apps', 'desktop-host')
const dependencies = join(root, 'workspace-dependencies')
mkdirSync(join(cli, 'lib'), { recursive: true })
mkdirSync(join(host, 'lib'), { recursive: true })
mkdirSync(dependencies, { recursive: true })
writeFileSync(join(cli, 'package.json'), '{"name":"@deepseek-ai/dsh","version":"2.0.0"}\n')
writeFileSync(join(host, 'package.json'), '{"name":"@deepseek-ai/dsh-desktop-host","version":"1.2.3"}\n')
writeFileSync(join(host, 'lib', 'index.js'), '')
expect(() => prepareDevelopmentProject({
projectDir: join(root, 'development'),
cliDir: cli,
hostDir: host,
dependencyDir: dependencies,
release: release(),
target: 'mac-x64',
})).toThrow(/must be @deepseek-ai\/dsh@1\.2\.3/u)
})
})