verify-built-package-invariants.spec.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
  2. import { tmpdir } from 'node:os'
  3. import { join } from 'node:path'
  4. import { fileURLToPath, pathToFileURL } from 'node:url'
  5. import { spawnSync } from 'node:child_process'
  6. import { afterEach, describe, expect, it } from 'vitest'
  7. const verifier = fileURLToPath(new URL('./verify-built-package-invariants.mjs', import.meta.url))
  8. const roots: string[] = []
  9. afterEach(() => {
  10. for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true })
  11. })
  12. function fixture(options: {
  13. companion?: boolean
  14. invariantSource?: string
  15. invariantExport?: string
  16. runtimeChunk?: string
  17. } = {}): { root: string; loaderUrl: string } {
  18. const root = mkdtempSync(join(tmpdir(), 'dsh-built-package-invariants-'))
  19. roots.push(root)
  20. const packageDir = join(root, 'packages/core/probe')
  21. mkdirSync(join(packageDir, 'lib'), { recursive: true })
  22. const companion = options.companion ?? true
  23. writeFileSync(join(packageDir, 'package.json'), `${JSON.stringify({
  24. name: '@deepseek-ai/dsh-probe',
  25. type: 'module',
  26. files: companion ? ['lib/invariant.js'] : [],
  27. exports: companion ? {
  28. './invariant': {
  29. default: options.invariantExport ?? './lib/invariant.js',
  30. },
  31. } : {},
  32. }, null, 2)}\n`)
  33. if (companion) {
  34. writeFileSync(
  35. join(packageDir, 'lib/invariant.js'),
  36. options.invariantSource ?? "export const name = 'probe-invariant'\nexport const inject = ['invariants']\nexport const apply = () => {}\n",
  37. )
  38. }
  39. if (options.runtimeChunk !== undefined) {
  40. writeFileSync(join(packageDir, 'lib/chunk.js'), options.runtimeChunk)
  41. }
  42. const loaderPath = join(root, 'loader.mjs')
  43. writeFileSync(loaderPath, 'export default class Loader { unwrapExports(value) { return value } }\n')
  44. return { root, loaderUrl: pathToFileURL(loaderPath).href }
  45. }
  46. function verify(root: string, loaderUrl: string, timeout: number) {
  47. const result = spawnSync(process.execPath, [
  48. verifier,
  49. '--packages-root', root,
  50. '--loader-url', loaderUrl,
  51. ], {
  52. encoding: 'utf8',
  53. // Plain-Node startup and staged imports share the runner's platform budget.
  54. timeout,
  55. })
  56. expect(result.error).toBeUndefined()
  57. expect(result.signal, result.stderr).toBeNull()
  58. return result
  59. }
  60. describe('built package invariant verifier', () => {
  61. it('loads the staged compiled self-reference through plain Node and Loader normalization', ({ task }) => {
  62. const { root, loaderUrl } = fixture()
  63. const result = verify(root, loaderUrl, task.timeout)
  64. expect(result.status, result.stderr).toBe(0)
  65. expect(result.stdout).toContain('1 compiled companion(s) passed plain-Node Loader checks')
  66. })
  67. it('accepts packages that do not publish a companion', ({ task }) => {
  68. const { root, loaderUrl } = fixture({ companion: false })
  69. const result = verify(root, loaderUrl, task.timeout)
  70. expect(result.status, result.stderr).toBe(0)
  71. expect(result.stdout).toContain('0 compiled companion(s) passed plain-Node Loader checks')
  72. })
  73. it('rejects a default export and a broken invariant export map', ({ task }) => {
  74. const withDefault = fixture({
  75. invariantSource: "export default {}\nexport const name = 'probe-invariant'\nexport const inject = ['invariants']\nexport const apply = () => {}\n",
  76. })
  77. const defaultResult = verify(withDefault.root, withDefault.loaderUrl, task.timeout)
  78. expect(defaultResult.status).toBe(1)
  79. expect(defaultResult.stderr).toContain('companion has a default export')
  80. const brokenExport = fixture({ invariantExport: './lib/missing.js' })
  81. const exportResult = verify(brokenExport.root, brokenExport.loaderUrl, task.timeout)
  82. expect(exportResult.status).toBe(1)
  83. expect(exportResult.stderr).toContain('@deepseek-ai/dsh-probe')
  84. })
  85. it('rejects an invariant bundle that needs an unstaged runtime chunk', ({ task }) => {
  86. const { root, loaderUrl } = fixture({
  87. invariantSource: "export * from './chunk.js'\n",
  88. runtimeChunk: "export const name = 'probe-invariant'\nexport const inject = ['invariants']\nexport const apply = () => {}\n",
  89. })
  90. const result = verify(root, loaderUrl, task.timeout)
  91. expect(result.status).toBe(1)
  92. expect(result.stderr).toContain('chunk.js')
  93. })
  94. })