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

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  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. invariantSource?: string
  14. invariantExport?: string
  15. runtimeChunk?: string
  16. } = {}): { root: string; loaderUrl: string } {
  17. const root = mkdtempSync(join(tmpdir(), 'dsh-built-package-invariants-'))
  18. roots.push(root)
  19. const packageDir = join(root, 'packages/core/probe')
  20. mkdirSync(join(packageDir, 'lib'), { recursive: true })
  21. writeFileSync(join(packageDir, 'package.json'), `${JSON.stringify({
  22. name: '@deepseek-ai/dsh-probe',
  23. type: 'module',
  24. files: ['lib/invariant.js'],
  25. exports: {
  26. './invariant': {
  27. default: options.invariantExport ?? './lib/invariant.js',
  28. },
  29. },
  30. }, null, 2)}\n`)
  31. writeFileSync(
  32. join(packageDir, 'lib/invariant.js'),
  33. options.invariantSource ?? "export const name = 'probe-invariant'\nexport const inject = ['invariants']\nexport const apply = () => {}\n",
  34. )
  35. if (options.runtimeChunk !== undefined) {
  36. writeFileSync(join(packageDir, 'lib/chunk.js'), options.runtimeChunk)
  37. }
  38. const loaderPath = join(root, 'loader.mjs')
  39. writeFileSync(loaderPath, 'export default class Loader { unwrapExports(value) { return value } }\n')
  40. return { root, loaderUrl: pathToFileURL(loaderPath).href }
  41. }
  42. function verify(root: string, loaderUrl: string) {
  43. return spawnSync(process.execPath, [
  44. verifier,
  45. '--packages-root', root,
  46. '--loader-url', loaderUrl,
  47. ], {
  48. encoding: 'utf8',
  49. timeout: 5_000,
  50. })
  51. }
  52. describe('built package invariant verifier', () => {
  53. it('loads the staged compiled self-reference through plain Node and Loader normalization', () => {
  54. const { root, loaderUrl } = fixture()
  55. const result = verify(root, loaderUrl)
  56. expect(result.status, result.stderr).toBe(0)
  57. expect(result.stdout).toContain('1 compiled companion(s) passed plain-Node Loader checks')
  58. })
  59. it('rejects a default export and a broken invariant export map', () => {
  60. const withDefault = fixture({
  61. invariantSource: "export default {}\nexport const name = 'probe-invariant'\nexport const inject = ['invariants']\nexport const apply = () => {}\n",
  62. })
  63. const defaultResult = verify(withDefault.root, withDefault.loaderUrl)
  64. expect(defaultResult.status).toBe(1)
  65. expect(defaultResult.stderr).toContain('companion has a default export')
  66. const brokenExport = fixture({ invariantExport: './lib/missing.js' })
  67. const exportResult = verify(brokenExport.root, brokenExport.loaderUrl)
  68. expect(exportResult.status).toBe(1)
  69. expect(exportResult.stderr).toContain('@deepseek-ai/dsh-probe')
  70. })
  71. it('rejects an invariant bundle that needs an unstaged runtime chunk', () => {
  72. const { root, loaderUrl } = fixture({
  73. invariantSource: "export * from './chunk.js'\n",
  74. runtimeChunk: "export const name = 'probe-invariant'\nexport const inject = ['invariants']\nexport const apply = () => {}\n",
  75. })
  76. const result = verify(root, loaderUrl)
  77. expect(result.status).toBe(1)
  78. expect(result.stderr).toContain('chunk.js')
  79. })
  80. })