gen-doc-graphs.spec.ts 4.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. /**
  2. * Tests for the event-relation collector's demand-driven call-site indexing:
  3. * the single-file fast path and the global fallback must recover the same
  4. * helper-parameter event names, including shapes that defeat the locality
  5. * proof (alias escapes and global script files).
  6. */
  7. import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
  8. import { tmpdir } from 'node:os'
  9. import { dirname, join } from 'node:path'
  10. import { afterAll, describe, expect, it } from 'vitest'
  11. import { collectPackageSources, EventRelationCollector } from './gen-doc-graphs.ts'
  12. import { TypeScriptProject } from './ts-project.ts'
  13. const FIXTURE: Record<string, string> = {
  14. 'tsconfig.host.json': JSON.stringify({
  15. compilerOptions: {
  16. target: 'es2022',
  17. module: 'esnext',
  18. moduleResolution: 'bundler',
  19. allowImportingTsExtensions: true,
  20. noEmit: true,
  21. skipLibCheck: true,
  22. types: [],
  23. },
  24. include: ['vendor/**/*.ts', 'packages/**/*.ts'],
  25. }),
  26. 'vendor/cordis/src/context.ts': 'export class Context { private brand!: void }\n',
  27. 'vendor/cordis/src/events.ts': [
  28. 'export class EventsService {',
  29. ' dispatch(type: string, args: unknown[]): unknown[] { return [type, args] }',
  30. '}',
  31. '',
  32. ].join('\n'),
  33. 'packages/core/agent/src/dispatch.ts':
  34. 'export interface AgentEventDispatch { emit(...args: unknown[]): void }\n',
  35. // fireLocal: every same-file reference is a direct callee, so the locality
  36. // proof holds and only this file is indexed. fireAliased: the exported
  37. // const is a value-position reference, so the proof fails and the global
  38. // fallback must find the cross-file call in pkgb.
  39. 'packages/fix/pkga/src/index.ts': [
  40. "import { EventsService } from '../../../../vendor/cordis/src/events.ts'",
  41. 'declare const events: EventsService',
  42. "function fireLocal(args: [string]): void { void events.dispatch('emit', args) }",
  43. "fireLocal(['pkga/local-event'])",
  44. "function fireAliased(args: [string]): void { void events.dispatch('emit', args) }",
  45. 'export const aliased = fireAliased',
  46. '',
  47. ].join('\n'),
  48. 'packages/fix/pkgb/src/index.ts': [
  49. "import { aliased } from '../../pkga/src/index.ts'",
  50. "aliased(['pkgb/aliased-event'])",
  51. '',
  52. ].join('\n'),
  53. // Global script files (no import/export): scriptFire is program-visible, so
  54. // the cross-file call in caller.ts leaves no same-file reference. Only the
  55. // module-ness premise check routes this helper to the global index; without
  56. // it the proof would pass and the event would silently drop.
  57. 'packages/fix/pkgc/src/globals.ts':
  58. "declare var gEvents: import('../../../../vendor/cordis/src/events.ts').EventsService\n",
  59. 'packages/fix/pkgc/src/helper.ts':
  60. "function scriptFire(args: [string]): void { void gEvents.dispatch('emit', args) }\n",
  61. 'packages/fix/pkgc/src/caller.ts': "scriptFire(['pkgc/script-event'])\n",
  62. }
  63. const root = mkdtempSync(join(tmpdir(), 'gen-doc-graphs-'))
  64. for (const [rel, content] of Object.entries(FIXTURE)) {
  65. mkdirSync(dirname(join(root, rel)), { recursive: true })
  66. writeFileSync(join(root, rel), content)
  67. }
  68. const project = new TypeScriptProject(root)
  69. const sources = collectPackageSources(project)
  70. afterAll(() => {
  71. rmSync(root, { recursive: true, force: true })
  72. })
  73. function dispatchersOf(pkgs: readonly string[], event: string): string[] {
  74. const subset = sources.filter(source => pkgs.includes(source.pkg))
  75. const relations = new EventRelationCollector(project, subset).collect()
  76. return [...(relations.get(event)?.dispatchers.keys() ?? [])]
  77. }
  78. describe('event relation call-site indexing', () => {
  79. it('recovers a proven-local helper through the single-file fast path', () => {
  80. expect(dispatchersOf(['pkga', 'pkgb'], 'pkga/local-event')).toEqual(['pkga'])
  81. })
  82. it('recovers an alias-escaped helper through the global fallback', () => {
  83. expect(dispatchersOf(['pkga', 'pkgb'], 'pkgb/aliased-event')).toEqual(['pkga'])
  84. })
  85. it('rejects the locality proof for global script files', () => {
  86. // pkgc alone: the script helper is the first demand, so a wrongly passing
  87. // proof would index helper.ts only and lose the caller.ts call site.
  88. expect(dispatchersOf(['pkgc'], 'pkgc/script-event')).toEqual(['pkgc'])
  89. })
  90. })