verify-application-entrypoints.spec.ts 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. /** Application-entrypoint classification and dsh-launch enforcement. */
  2. import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
  3. import { tmpdir } from 'node:os'
  4. import { dirname, join, resolve } from 'node:path'
  5. import { afterEach, describe, expect, it } from 'vitest'
  6. import { applicationEntrypointViolations } from './verify-application-entrypoints.ts'
  7. const cleanups: string[] = []
  8. afterEach(() => {
  9. for (const path of cleanups.splice(0)) rmSync(path, { recursive: true, force: true })
  10. })
  11. function fixture(): string {
  12. const root = mkdtempSync(join(tmpdir(), 'dsh-application-entrypoints-'))
  13. cleanups.push(root)
  14. return root
  15. }
  16. function write(root: string, path: string, content: string): void {
  17. const target = resolve(root, path)
  18. mkdirSync(dirname(target), { recursive: true })
  19. writeFileSync(target, content)
  20. }
  21. describe('application entrypoints', () => {
  22. it('accepts the repository launcher inventory', () => {
  23. expect(applicationEntrypointViolations(resolve(import.meta.dirname, '..'))).toEqual([])
  24. })
  25. it('rejects a package-level application bin', () => {
  26. const root = fixture()
  27. write(root, 'packages/example/app/package.json', JSON.stringify({ bin: { app: 'lib/bin.js' } }))
  28. expect(applicationEntrypointViolations(root)).toEqual([
  29. 'packages/example/app/package.json: package bin bypasses the dsh launcher; applications use apps/cli profiles',
  30. ])
  31. })
  32. it('rejects an unclassified executable source', () => {
  33. const root = fixture()
  34. write(root, 'packages/example/app/src/bin.ts', '#!/usr/bin/env node\n')
  35. expect(applicationEntrypointViolations(root)).toEqual([
  36. 'packages/example/app/src/bin.ts: executable source has no application/build/test classification',
  37. ])
  38. })
  39. it('rejects an executable at an application package root', () => {
  40. const root = fixture()
  41. write(root, 'apps/example/rogue.mjs', '#!/usr/bin/env node\n')
  42. expect(applicationEntrypointViolations(root)).toEqual([
  43. 'apps/example/rogue.mjs: executable source has no application/build/test classification',
  44. ])
  45. })
  46. it('rejects an executable at the repository root', () => {
  47. const root = fixture()
  48. write(root, 'rogue.mjs', '#!/usr/bin/env node\n')
  49. expect(applicationEntrypointViolations(root)).toEqual([
  50. 'rogue.mjs: executable source has no application/build/test classification',
  51. ])
  52. })
  53. it('rejects an unclassified executable in an app workspace', () => {
  54. const root = fixture()
  55. write(root, 'apps/rogue/src/bin.ts', '#!/usr/bin/env node\n')
  56. expect(applicationEntrypointViolations(root)).toEqual([
  57. 'apps/rogue/src/bin.ts: executable source has no application/build/test classification',
  58. ])
  59. })
  60. it('rejects a private Python application carrier outside dsh', () => {
  61. const root = fixture()
  62. write(root, 'packages/sdk/rogue-python-runtime/package.json', JSON.stringify({ private: true }))
  63. write(root, 'packages/sdk/rogue-python-runtime/src/bin.ts', '#!/usr/bin/env node\n')
  64. expect(applicationEntrypointViolations(root)).toEqual([
  65. 'packages/sdk/rogue-python-runtime/src/bin.ts: executable source has no application/build/test classification',
  66. ])
  67. })
  68. it('rejects a classified demo wrapper that launches a package entry', () => {
  69. const root = fixture()
  70. write(root, 'package.json', JSON.stringify({ scripts: { 'demo:ptc': 'node scripts/demo-ptc.mjs' } }))
  71. write(root, 'scripts/demo-ptc.mjs', "spawn('node', ['packages/example/app/src/bin.ts'])\n")
  72. expect(applicationEntrypointViolations(root)).toEqual([
  73. 'scripts/demo-ptc.mjs: application demo wrapper must launch apps/cli/src/bin.ts',
  74. 'scripts/demo-ptc.mjs: application demo wrapper must not launch a package entry directly',
  75. ])
  76. })
  77. it('rejects a new root demo until its launch role is classified', () => {
  78. const root = fixture()
  79. write(root, 'package.json', JSON.stringify({ scripts: { 'demo:new-app': 'dsh --profile new-app' } }))
  80. expect(applicationEntrypointViolations(root)).toEqual([
  81. 'package.json scripts.demo:new-app: demo launcher has no explicit dsh or in-process classification',
  82. ])
  83. })
  84. })