verify-runtime-closure.spec.ts 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
  2. import { tmpdir } from 'node:os'
  3. import { dirname, join } from 'node:path'
  4. import { afterEach, describe, expect, it } from 'vitest'
  5. import { verifyRuntimeClosure } from './verify-runtime-closure.ts'
  6. const roots: string[] = []
  7. function fixture(files: Record<string, string | Record<string, unknown>>): string {
  8. const root = mkdtempSync(join(tmpdir(), 'dsh-runtime-closure-'))
  9. roots.push(root)
  10. for (const [relative, value] of Object.entries(files)) {
  11. const path = join(root, relative)
  12. mkdirSync(dirname(path), { recursive: true })
  13. writeFileSync(path, typeof value === 'string' ? value : `${JSON.stringify(value, null, 2)}\n`)
  14. }
  15. return root
  16. }
  17. const platforms = {
  18. 'linux-x64': { tag: 'manylinux_2_28_x86_64', executable: 'runtime-linux-x64' },
  19. 'linux-arm64': { tag: 'manylinux_2_28_aarch64', executable: 'runtime-linux-arm64' },
  20. 'macos-arm64': { tag: 'macosx_14_0_arm64', executable: 'runtime-macos-arm64' },
  21. }
  22. function workspace(root: string, name: string, manifest: Record<string, unknown>): void {
  23. const packageName = name.replace('@scope/', '')
  24. const path = join(root, 'packages/core', packageName, 'package.json')
  25. mkdirSync(dirname(path), { recursive: true })
  26. writeFileSync(path, `${JSON.stringify({ name, ...manifest }, null, 2)}\n`)
  27. }
  28. afterEach(() => {
  29. for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true })
  30. })
  31. describe('verifyRuntimeClosure', () => {
  32. it('requires only plugins active for a Linux or macOS target', async () => {
  33. const root = fixture({
  34. 'python/sdk-runtime/package.json': { name: 'runtime', dependencies: { '@scope/shared': 'workspace:^' } },
  35. 'python/sdk-runtime/platforms.json': platforms,
  36. 'apps/cli/config/agent-presets/standard/agent.cordis.yml': `
  37. - id: tools
  38. name: cordis:group
  39. group: true
  40. config:
  41. - id: shared
  42. name: '@scope/shared'
  43. - id: linux
  44. name: '@scope/linux'
  45. disabled: !!js process.platform !== 'linux'
  46. - id: macos
  47. name: '@scope/macos'
  48. disabled: !!js process.platform !== 'darwin'
  49. `,
  50. })
  51. const result = await verifyRuntimeClosure(root)
  52. expect(result.presetCount).toBe(1)
  53. expect(result.failures).toEqual([
  54. 'standard preset -> @scope/linux (linux-arm64, linux-x64)',
  55. 'standard preset -> @scope/macos (macos-arm64)',
  56. ])
  57. })
  58. it('treats an unsupported disabled expression as active on every target', async () => {
  59. const root = fixture({
  60. 'python/sdk-runtime/package.json': { name: 'runtime', dependencies: {} },
  61. 'python/sdk-runtime/platforms.json': platforms,
  62. 'apps/cli/config/agent-presets/standard/agent.cordis.yml': `
  63. - id: conditional
  64. name: '@scope/conditional'
  65. disabled: !!js process.env.DSH_DISABLE_CONDITIONAL === '1'
  66. `,
  67. })
  68. const result = await verifyRuntimeClosure(root)
  69. expect(result.failures).toEqual([
  70. 'standard preset -> @scope/conditional (linux-arm64, linux-x64, macos-arm64)',
  71. ])
  72. })
  73. it('does not interpret an ordinary plugin array config as nested Loader entries', async () => {
  74. const root = fixture({
  75. 'python/sdk-runtime/package.json': { name: 'runtime', dependencies: { '@scope/plugin': 'workspace:^' } },
  76. 'python/sdk-runtime/platforms.json': platforms,
  77. 'apps/cli/config/agent-presets/standard/agent.cordis.yml': `
  78. - id: plugin
  79. name: '@scope/plugin'
  80. config:
  81. - name: '@scope/config-value'
  82. `,
  83. })
  84. const result = await verifyRuntimeClosure(root)
  85. expect(result.failures).toEqual([])
  86. })
  87. it('requires preset plugins to be linked from the workspace', async () => {
  88. const root = fixture({
  89. 'python/sdk-runtime/package.json': { name: 'runtime', dependencies: { '@scope/plugin': '1.2.3' } },
  90. 'python/sdk-runtime/platforms.json': platforms,
  91. 'apps/cli/config/agent-presets/standard/agent.cordis.yml': `
  92. - id: plugin
  93. name: '@scope/plugin'
  94. `,
  95. })
  96. const result = await verifyRuntimeClosure(root)
  97. expect(result.failures).toEqual([
  98. 'standard preset -> @scope/plugin [runtime dependency is "1.2.3"; expected workspace:] (linux-arm64, linux-x64, macos-arm64)',
  99. ])
  100. })
  101. it('fails when no shipped preset is discovered', async () => {
  102. const root = fixture({
  103. 'python/sdk-runtime/package.json': { name: 'runtime', dependencies: {} },
  104. 'python/sdk-runtime/platforms.json': platforms,
  105. })
  106. const result = await verifyRuntimeClosure(root)
  107. expect(result.presetCount).toBe(0)
  108. expect(result.failures).toEqual([
  109. 'no agent presets matched apps/cli/config/agent-presets/*/agent.cordis.yml',
  110. ])
  111. })
  112. it('fails when the runtime platform manifest has no targets', async () => {
  113. const root = fixture({
  114. 'python/sdk-runtime/package.json': { name: 'runtime', dependencies: {} },
  115. 'python/sdk-runtime/platforms.json': {},
  116. 'apps/cli/config/agent-presets/standard/agent.cordis.yml': '[]\n',
  117. })
  118. const result = await verifyRuntimeClosure(root)
  119. expect(result.failures).toEqual([
  120. 'python/sdk-runtime/platforms.json defines no runtime targets',
  121. ])
  122. })
  123. it('retains the required workspace-peer closure check', async () => {
  124. const root = fixture({
  125. 'python/sdk-runtime/package.json': { name: 'runtime', dependencies: { '@scope/root': 'workspace:^' } },
  126. 'python/sdk-runtime/platforms.json': platforms,
  127. 'apps/cli/config/agent-presets/minimal/agent.cordis.yml': '[]\n',
  128. })
  129. workspace(root, '@scope/root', {
  130. peerDependencies: { '@scope/required': 'workspace:^', '@scope/optional': 'workspace:^' },
  131. peerDependenciesMeta: { '@scope/optional': { optional: true } },
  132. })
  133. workspace(root, '@scope/required', {})
  134. workspace(root, '@scope/optional', {})
  135. const result = await verifyRuntimeClosure(root)
  136. expect(result.workspacePackageCount).toBe(1)
  137. expect(result.failures).toEqual(['runtime -> @scope/root -> @scope/required'])
  138. })
  139. })