@tdk-landscape/tdk-cli-core 1.3.45 → 1.3.49

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (431) hide show
  1. package/.github/workflows/release-binaries.yml +54 -0
  2. package/.pre-commit-config.yaml +57 -0
  3. package/Makefile +92 -0
  4. package/bun.lock +538 -0
  5. package/cli/AGENTS.md +390 -0
  6. package/cli/DESIGN.md +735 -0
  7. package/cli/biome.json +44 -0
  8. package/cli/dependency-graph.svg +1135 -0
  9. package/cli/knip.json +6 -0
  10. package/cli/package.json +77 -0
  11. package/cli/pnpm-lock.yaml +2305 -0
  12. package/cli/pnpm-workspace.yaml +2 -0
  13. package/cli/shared-platform-engineering/docker-templates/bun-hoisted-symlink-fix.sh +23 -0
  14. package/cli/shared-platform-engineering/docker-templates/infisical-entrypoint.sh +464 -0
  15. package/cli/shared-platform-engineering/docker-templates/install-deps.sh +152 -0
  16. package/cli/shared-platform-engineering/docker-templates/prisma-bun-client-link-fix.sh +12 -0
  17. package/cli/shared-platform-engineering/docker-templates/prisma-normalize-client.sh +56 -0
  18. package/{dist/cli.js → cli/src/cli.ts} +21 -13
  19. package/cli/src/commands/__tests__/always-enabled-infra.test.ts +115 -0
  20. package/cli/src/commands/__tests__/config.test.ts +146 -0
  21. package/cli/src/commands/__tests__/doctor.test.ts +557 -0
  22. package/cli/src/commands/__tests__/error-handling.test.ts +78 -0
  23. package/cli/src/commands/__tests__/phases-model.test.ts +163 -0
  24. package/cli/src/commands/__tests__/project-resource-features-e2e.test.ts +207 -0
  25. package/cli/src/commands/__tests__/project.test.ts +68 -0
  26. package/cli/src/commands/__tests__/resource.test.ts +226 -0
  27. package/cli/src/commands/__tests__/saas-starter-integration.test.ts +196 -0
  28. package/cli/src/commands/__tests__/stack-features-e2e.test.ts +129 -0
  29. package/cli/src/commands/__tests__/upgrade.test.ts +101 -0
  30. package/{dist/commands/completion.js → cli/src/commands/completion.ts} +78 -72
  31. package/cli/src/commands/config.ts +299 -0
  32. package/cli/src/commands/doctor.ts +790 -0
  33. package/cli/src/commands/down.ts +37 -0
  34. package/cli/src/commands/help.ts +150 -0
  35. package/cli/src/commands/networks-design.md +80 -0
  36. package/cli/src/commands/networks.ts +332 -0
  37. package/cli/src/commands/project.ts +439 -0
  38. package/cli/src/commands/projects.ts +82 -0
  39. package/cli/src/commands/resource.ts +652 -0
  40. package/cli/src/commands/resources.ts +95 -0
  41. package/cli/src/commands/stack.ts +128 -0
  42. package/cli/src/commands/stacks.ts +52 -0
  43. package/cli/src/commands/status.ts +88 -0
  44. package/cli/src/commands/ui.tsx +843 -0
  45. package/cli/src/commands/up.ts +150 -0
  46. package/cli/src/commands/upgrade.ts +570 -0
  47. package/{dist/commands/version.js → cli/src/commands/version.ts} +5 -5
  48. package/cli/src/components/Accessible.tsx +21 -0
  49. package/cli/src/components/BaseTooltip.tsx +66 -0
  50. package/cli/src/components/DetailPanel.tsx +146 -0
  51. package/cli/src/components/FileTree.tsx +89 -0
  52. package/cli/src/components/ResourceSelectInput.tsx +30 -0
  53. package/cli/src/components/ResourceTable.tsx +78 -0
  54. package/cli/src/components/TabBar.tsx +53 -0
  55. package/cli/src/components/Tooltip.tsx +11 -0
  56. package/{dist/components/index.js → cli/src/components/index.ts} +0 -1
  57. package/cli/src/config/platform-standards.test.ts +11 -0
  58. package/cli/src/config/platform-standards.ts +196 -0
  59. package/cli/src/generator/__tests__/extension-fetch.test.ts +220 -0
  60. package/cli/src/generator/__tests__/template-engine.test.ts +70 -0
  61. package/cli/src/generator/extension-fetch.ts +279 -0
  62. package/cli/src/generator/template-engine.ts +754 -0
  63. package/cli/src/index.ts +86 -0
  64. package/cli/src/templates.d.ts +4 -0
  65. package/cli/src/types/index.ts +351 -0
  66. package/cli/src/utils/__tests__/cache.test.ts +98 -0
  67. package/cli/src/utils/__tests__/formatting.test.ts +170 -0
  68. package/cli/src/utils/__tests__/paths.test.ts +180 -0
  69. package/cli/src/utils/__tests__/port-assignment.test.ts +121 -0
  70. package/cli/src/utils/__tests__/service-urls.test.ts +174 -0
  71. package/cli/src/utils/__tests__/services.test.ts +531 -0
  72. package/cli/src/utils/__tests__/tilt.test.ts +143 -0
  73. package/cli/src/utils/__tests__/validation.test.ts +148 -0
  74. package/cli/src/utils/cache.ts +76 -0
  75. package/cli/src/utils/command-helpers.ts +54 -0
  76. package/cli/src/utils/constants.ts +126 -0
  77. package/cli/src/utils/discovery-context.ts +49 -0
  78. package/cli/src/utils/docker.ts +20 -0
  79. package/cli/src/utils/doctor-runtime.ts +604 -0
  80. package/cli/src/utils/env-validator.ts +139 -0
  81. package/cli/src/utils/errors.ts +168 -0
  82. package/cli/src/utils/file-helpers.ts +62 -0
  83. package/cli/src/utils/formatting.ts +215 -0
  84. package/cli/src/utils/paths.ts +53 -0
  85. package/cli/src/utils/port-assignment.ts +106 -0
  86. package/cli/src/utils/project-features.ts +118 -0
  87. package/cli/src/utils/project-templates.ts +28 -0
  88. package/cli/src/utils/prompt.ts +123 -0
  89. package/cli/src/utils/resource-features.ts +209 -0
  90. package/cli/src/utils/service-urls.ts +124 -0
  91. package/cli/src/utils/services.ts +363 -0
  92. package/cli/src/utils/stack-features.ts +30 -0
  93. package/cli/src/utils/tar.ts +24 -0
  94. package/cli/src/utils/tilt.ts +113 -0
  95. package/cli/src/utils/validation.ts +67 -0
  96. package/{templates → cli/templates}/Tiltfile.hbs +1 -1
  97. package/cli/tsconfig.json +25 -0
  98. package/cli/tsconfig.typecheck.json +8 -0
  99. package/docs/FEATURES.md +279 -0
  100. package/engine/topologies/platform/docker/dockerfile/dockerfile.star +3 -3
  101. package/engine/topologies/platform/docker/generators/golden_docker_generator_v2.star +1 -1
  102. package/engine/topologies/platform/docker/layers/l2_dependency_layers.star +0 -3
  103. package/engine/topologies/platform/docker/layers/l3_builder_layers.star +1 -1
  104. package/engine/topologies/platform/docker/layers/l4_runtime_layers.star +1 -1
  105. package/engine/topologies/platform/docker/secrets/infisical.star +19 -47
  106. package/engine/topologies/tilt/resources/orchestrator/apply_migrator_orchestration.star +1 -1
  107. package/install.sh +242 -0
  108. package/knip.json +10 -0
  109. package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/.openspec.yaml +2 -0
  110. package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/design.md +102 -0
  111. package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/proposal.md +31 -0
  112. package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/specs/vscode-tdk-notifications/spec.md +48 -0
  113. package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/specs/vscode-tdk-orchestration/spec.md +63 -0
  114. package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/specs/vscode-tdk-troubleshooting/spec.md +56 -0
  115. package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/tasks.md +61 -0
  116. package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/.openspec.yaml +2 -0
  117. package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/design.md +114 -0
  118. package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/proposal.md +32 -0
  119. package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/specs/manifest-field-migration/spec.md +20 -0
  120. package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/specs/resource-terminology-standard/spec.md +20 -0
  121. package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/specs/stack-terminology-standard/spec.md +20 -0
  122. package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/tasks.md +194 -0
  123. package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/.openspec.yaml +2 -0
  124. package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/design.md +111 -0
  125. package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/proposal.md +33 -0
  126. package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/specs/function-naming-alignment/spec.md +12 -0
  127. package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/specs/optional-parameter-patterns/spec.md +18 -0
  128. package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/specs/parameter-documentation/spec.md +16 -0
  129. package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/specs/parameter-naming-consistency/spec.md +12 -0
  130. package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/specs/parameter-ordering-standard/spec.md +12 -0
  131. package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/tasks.md +194 -0
  132. package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/.openspec.yaml +2 -0
  133. package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/codebase-audit-report.md +62 -0
  134. package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/design.md +84 -0
  135. package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/proposal.md +31 -0
  136. package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/specs/codebase-audit/spec.md +27 -0
  137. package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/specs/dead-code-cleanup/spec.md +27 -0
  138. package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/specs/test-gap-fill/spec.md +23 -0
  139. package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/specs/todo-resolution/spec.md +27 -0
  140. package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/tasks.md +39 -0
  141. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/.openspec.yaml +5 -0
  142. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/design.md +94 -0
  143. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/proposal.md +46 -0
  144. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/ai-slop-cleanup/CLEANUP_REPORT_8.md +174 -0
  145. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/ai-slop-cleanup/CRITICAL_ASSESSMENT_8.md +160 -0
  146. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/circular-deps/CRITICAL_ASSESSMENT_4.md +381 -0
  147. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/dead-code/CRITICAL_ASSESSMENT_3.md +316 -0
  148. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/deduplication/CRITICAL_ASSESSMENT_1.md +290 -0
  149. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/deduplication/IMPLEMENTATION_SUMMARY_1.md +233 -0
  150. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/defensive-cleanup/CRITICAL_ASSESSMENT_6.md +835 -0
  151. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/legacy-cleanup/CRITICAL_ASSESSMENT_7.md +254 -0
  152. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/legacy-cleanup/IMPLEMENTATION_SUMMARY_7.md +187 -0
  153. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/strong-typing/CRITICAL_ASSESSMENT_5.md +269 -0
  154. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/type-consolidation/CRITICAL_ASSESSMENT_2.md +312 -0
  155. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/type-consolidation/IMPLEMENTATION_SUMMARY.md +170 -0
  156. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/tasks.md +137 -0
  157. package/openspec/changes/archive/2026-06-25-landscape-consolidation/.openspec.yaml +2 -0
  158. package/openspec/changes/archive/2026-06-25-landscape-consolidation/design.md +80 -0
  159. package/openspec/changes/archive/2026-06-25-landscape-consolidation/proposal.md +40 -0
  160. package/openspec/changes/archive/2026-06-25-landscape-consolidation/spec.md +52 -0
  161. package/openspec/changes/archive/2026-06-25-landscape-consolidation/tasks.md +51 -0
  162. package/openspec/changes/archive/2026-09-17-fix-generated-stack-boot/.openspec.yaml +2 -0
  163. package/openspec/changes/archive/2026-09-17-fix-generated-stack-boot/design.md +43 -0
  164. package/openspec/changes/archive/2026-09-17-fix-generated-stack-boot/proposal.md +29 -0
  165. package/openspec/changes/archive/2026-09-17-fix-generated-stack-boot/specs/self-contained-generated-projects/spec.md +67 -0
  166. package/openspec/changes/archive/2026-09-17-fix-generated-stack-boot/tasks.md +27 -0
  167. package/openspec/specs/manifest-field-migration/spec.md +24 -0
  168. package/openspec/specs/resource-terminology-standard/spec.md +24 -0
  169. package/openspec/specs/self-contained-generated-projects/spec.md +67 -0
  170. package/openspec/specs/stack-terminology-standard/spec.md +24 -0
  171. package/package.json +27 -64
  172. package/platform/services/platform/proxy/service.yaml +14 -0
  173. package/scripts/audit-codebase.sh +213 -0
  174. package/scripts/install-git-hooks.sh +14 -0
  175. package/scripts/release-binaries.sh +94 -0
  176. package/scripts/setup-pre-commit.sh +33 -0
  177. package/scripts/stage-npm-engine.sh +36 -0
  178. package/shared-platform-engineering/docker-templates/bun-hoisted-symlink-fix.sh +23 -0
  179. package/shared-platform-engineering/docker-templates/infisical-entrypoint.sh +464 -0
  180. package/shared-platform-engineering/docker-templates/install-deps.sh +152 -0
  181. package/shared-platform-engineering/docker-templates/prisma-bun-client-link-fix.sh +12 -0
  182. package/shared-platform-engineering/docker-templates/prisma-normalize-client.sh +56 -0
  183. package/tests/tilt-engine/__init__.py +5 -0
  184. package/tests/tilt-engine/conftest.py +182 -0
  185. package/tests/tilt-engine/fixtures/appointment-frontend-service.json +11 -0
  186. package/tests/tilt-engine/fixtures/identity-backend-service.json +12 -0
  187. package/tests/tilt-engine/fixtures/invalid-service.json +4 -0
  188. package/tests/tilt-engine/test_agents_md_generator.py +341 -0
  189. package/tests/tilt-engine/test_agents_md_integration.py +378 -0
  190. package/tests/tilt-engine/test_cache_operations.py +181 -0
  191. package/tests/tilt-engine/test_cache_persistence.py +142 -0
  192. package/tests/tilt-engine/test_code_executor.py +152 -0
  193. package/tests/tilt-engine/test_code_viewer.py +127 -0
  194. package/tests/tilt-engine/test_compose_port_anchors.py +310 -0
  195. package/tests/tilt-engine/test_config_inspector.py +162 -0
  196. package/tests/tilt-engine/test_daemon_focus_mode.py +89 -0
  197. package/tests/tilt-engine/test_daemon_polling.py +122 -0
  198. package/tests/tilt-engine/test_daemon_registration.py +115 -0
  199. package/tests/tilt-engine/test_daemon_validation.py +107 -0
  200. package/tests/tilt-engine/test_docker_generator_bugs.py +493 -0
  201. package/tests/tilt-engine/test_docker_prune.py +119 -0
  202. package/tests/tilt-engine/test_dockerignore_path_bug.py +82 -0
  203. package/tests/tilt-engine/test_file_browser.py +193 -0
  204. package/tests/tilt-engine/test_frontend_basepath_manifest.py +27 -0
  205. package/tests/tilt-engine/test_health_dependencies.py +130 -0
  206. package/tests/tilt-engine/test_health_endpoints.py +118 -0
  207. package/tests/tilt-engine/test_health_tilt_ui.py +85 -0
  208. package/tests/tilt-engine/test_safety_aliases.py +84 -0
  209. package/tests/tilt-engine/test_snapshot_diff.py +136 -0
  210. package/tests/tilt-engine/test_snapshot_persistence.py +163 -0
  211. package/tests/tilt-engine/test_snapshot_scanning.py +123 -0
  212. package/tests/tilt-engine/test_standalone_project_boot.py +61 -0
  213. package/tests/tilt-engine/test_standalone_traefik.py +108 -0
  214. package/dist/cli.d.ts +0 -3
  215. package/dist/cli.d.ts.map +0 -1
  216. package/dist/cli.js.map +0 -1
  217. package/dist/commands/completion.d.ts +0 -3
  218. package/dist/commands/completion.d.ts.map +0 -1
  219. package/dist/commands/completion.js.map +0 -1
  220. package/dist/commands/config.d.ts +0 -3
  221. package/dist/commands/config.d.ts.map +0 -1
  222. package/dist/commands/config.js +0 -231
  223. package/dist/commands/config.js.map +0 -1
  224. package/dist/commands/doctor.d.ts +0 -7
  225. package/dist/commands/doctor.d.ts.map +0 -1
  226. package/dist/commands/doctor.js +0 -597
  227. package/dist/commands/doctor.js.map +0 -1
  228. package/dist/commands/down.d.ts +0 -3
  229. package/dist/commands/down.d.ts.map +0 -1
  230. package/dist/commands/down.js +0 -32
  231. package/dist/commands/down.js.map +0 -1
  232. package/dist/commands/help.d.ts +0 -2
  233. package/dist/commands/help.d.ts.map +0 -1
  234. package/dist/commands/help.js +0 -130
  235. package/dist/commands/help.js.map +0 -1
  236. package/dist/commands/networks.d.ts +0 -3
  237. package/dist/commands/networks.d.ts.map +0 -1
  238. package/dist/commands/networks.js +0 -260
  239. package/dist/commands/networks.js.map +0 -1
  240. package/dist/commands/project.d.ts +0 -3
  241. package/dist/commands/project.d.ts.map +0 -1
  242. package/dist/commands/project.js +0 -373
  243. package/dist/commands/project.js.map +0 -1
  244. package/dist/commands/projects.d.ts +0 -3
  245. package/dist/commands/projects.d.ts.map +0 -1
  246. package/dist/commands/projects.js +0 -71
  247. package/dist/commands/projects.js.map +0 -1
  248. package/dist/commands/resource.d.ts +0 -69
  249. package/dist/commands/resource.d.ts.map +0 -1
  250. package/dist/commands/resource.js +0 -579
  251. package/dist/commands/resource.js.map +0 -1
  252. package/dist/commands/resources.d.ts +0 -3
  253. package/dist/commands/resources.d.ts.map +0 -1
  254. package/dist/commands/resources.js +0 -73
  255. package/dist/commands/resources.js.map +0 -1
  256. package/dist/commands/stack.d.ts +0 -3
  257. package/dist/commands/stack.d.ts.map +0 -1
  258. package/dist/commands/stack.js +0 -97
  259. package/dist/commands/stack.js.map +0 -1
  260. package/dist/commands/stacks.d.ts +0 -3
  261. package/dist/commands/stacks.d.ts.map +0 -1
  262. package/dist/commands/stacks.js +0 -43
  263. package/dist/commands/stacks.js.map +0 -1
  264. package/dist/commands/status.d.ts +0 -3
  265. package/dist/commands/status.d.ts.map +0 -1
  266. package/dist/commands/status.js +0 -71
  267. package/dist/commands/status.js.map +0 -1
  268. package/dist/commands/ui.d.ts +0 -3
  269. package/dist/commands/ui.d.ts.map +0 -1
  270. package/dist/commands/ui.js +0 -445
  271. package/dist/commands/ui.js.map +0 -1
  272. package/dist/commands/up.d.ts +0 -3
  273. package/dist/commands/up.d.ts.map +0 -1
  274. package/dist/commands/up.js +0 -110
  275. package/dist/commands/up.js.map +0 -1
  276. package/dist/commands/upgrade.d.ts +0 -11
  277. package/dist/commands/upgrade.d.ts.map +0 -1
  278. package/dist/commands/upgrade.js +0 -496
  279. package/dist/commands/upgrade.js.map +0 -1
  280. package/dist/commands/version.d.ts +0 -3
  281. package/dist/commands/version.d.ts.map +0 -1
  282. package/dist/commands/version.js.map +0 -1
  283. package/dist/components/Accessible.d.ts +0 -8
  284. package/dist/components/Accessible.d.ts.map +0 -1
  285. package/dist/components/Accessible.js +0 -10
  286. package/dist/components/Accessible.js.map +0 -1
  287. package/dist/components/BaseTooltip.d.ts +0 -4
  288. package/dist/components/BaseTooltip.d.ts.map +0 -1
  289. package/dist/components/BaseTooltip.js +0 -28
  290. package/dist/components/BaseTooltip.js.map +0 -1
  291. package/dist/components/DetailPanel.d.ts +0 -4
  292. package/dist/components/DetailPanel.d.ts.map +0 -1
  293. package/dist/components/DetailPanel.js +0 -18
  294. package/dist/components/DetailPanel.js.map +0 -1
  295. package/dist/components/FileTree.d.ts +0 -4
  296. package/dist/components/FileTree.d.ts.map +0 -1
  297. package/dist/components/FileTree.js +0 -47
  298. package/dist/components/FileTree.js.map +0 -1
  299. package/dist/components/ResourceSelectInput.d.ts +0 -4
  300. package/dist/components/ResourceSelectInput.d.ts.map +0 -1
  301. package/dist/components/ResourceSelectInput.js +0 -7
  302. package/dist/components/ResourceSelectInput.js.map +0 -1
  303. package/dist/components/ResourceTable.d.ts +0 -4
  304. package/dist/components/ResourceTable.d.ts.map +0 -1
  305. package/dist/components/ResourceTable.js +0 -18
  306. package/dist/components/ResourceTable.js.map +0 -1
  307. package/dist/components/TabBar.d.ts +0 -4
  308. package/dist/components/TabBar.d.ts.map +0 -1
  309. package/dist/components/TabBar.js +0 -17
  310. package/dist/components/TabBar.js.map +0 -1
  311. package/dist/components/Tooltip.d.ts +0 -12
  312. package/dist/components/Tooltip.d.ts.map +0 -1
  313. package/dist/components/Tooltip.js +0 -12
  314. package/dist/components/Tooltip.js.map +0 -1
  315. package/dist/components/index.d.ts +0 -8
  316. package/dist/components/index.d.ts.map +0 -1
  317. package/dist/components/index.js.map +0 -1
  318. package/dist/config/platform-standards.d.ts +0 -159
  319. package/dist/config/platform-standards.d.ts.map +0 -1
  320. package/dist/config/platform-standards.js +0 -181
  321. package/dist/config/platform-standards.js.map +0 -1
  322. package/dist/generator/extension-fetch.d.ts +0 -25
  323. package/dist/generator/extension-fetch.d.ts.map +0 -1
  324. package/dist/generator/extension-fetch.js +0 -252
  325. package/dist/generator/extension-fetch.js.map +0 -1
  326. package/dist/generator/template-engine.d.ts +0 -61
  327. package/dist/generator/template-engine.d.ts.map +0 -1
  328. package/dist/generator/template-engine.js +0 -557
  329. package/dist/generator/template-engine.js.map +0 -1
  330. package/dist/index.d.ts +0 -11
  331. package/dist/index.d.ts.map +0 -1
  332. package/dist/index.js +0 -11
  333. package/dist/index.js.map +0 -1
  334. package/dist/types/index.d.ts +0 -255
  335. package/dist/types/index.d.ts.map +0 -1
  336. package/dist/types/index.js +0 -17
  337. package/dist/types/index.js.map +0 -1
  338. package/dist/utils/cache.d.ts +0 -19
  339. package/dist/utils/cache.d.ts.map +0 -1
  340. package/dist/utils/cache.js +0 -64
  341. package/dist/utils/cache.js.map +0 -1
  342. package/dist/utils/command-helpers.d.ts +0 -17
  343. package/dist/utils/command-helpers.d.ts.map +0 -1
  344. package/dist/utils/command-helpers.js +0 -41
  345. package/dist/utils/command-helpers.js.map +0 -1
  346. package/dist/utils/constants.d.ts +0 -52
  347. package/dist/utils/constants.d.ts.map +0 -1
  348. package/dist/utils/constants.js +0 -112
  349. package/dist/utils/constants.js.map +0 -1
  350. package/dist/utils/discovery-context.d.ts +0 -4
  351. package/dist/utils/discovery-context.d.ts.map +0 -1
  352. package/dist/utils/discovery-context.js +0 -39
  353. package/dist/utils/discovery-context.js.map +0 -1
  354. package/dist/utils/docker.d.ts +0 -4
  355. package/dist/utils/docker.d.ts.map +0 -1
  356. package/dist/utils/docker.js +0 -19
  357. package/dist/utils/docker.js.map +0 -1
  358. package/dist/utils/doctor-runtime.d.ts +0 -66
  359. package/dist/utils/doctor-runtime.d.ts.map +0 -1
  360. package/dist/utils/doctor-runtime.js +0 -469
  361. package/dist/utils/doctor-runtime.js.map +0 -1
  362. package/dist/utils/env-validator.d.ts +0 -15
  363. package/dist/utils/env-validator.d.ts.map +0 -1
  364. package/dist/utils/env-validator.js +0 -109
  365. package/dist/utils/env-validator.js.map +0 -1
  366. package/dist/utils/errors.d.ts +0 -31
  367. package/dist/utils/errors.d.ts.map +0 -1
  368. package/dist/utils/errors.js +0 -140
  369. package/dist/utils/errors.js.map +0 -1
  370. package/dist/utils/file-helpers.d.ts +0 -14
  371. package/dist/utils/file-helpers.d.ts.map +0 -1
  372. package/dist/utils/file-helpers.js +0 -45
  373. package/dist/utils/file-helpers.js.map +0 -1
  374. package/dist/utils/formatting.d.ts +0 -30
  375. package/dist/utils/formatting.d.ts.map +0 -1
  376. package/dist/utils/formatting.js +0 -164
  377. package/dist/utils/formatting.js.map +0 -1
  378. package/dist/utils/paths.d.ts +0 -3
  379. package/dist/utils/paths.d.ts.map +0 -1
  380. package/dist/utils/paths.js +0 -41
  381. package/dist/utils/paths.js.map +0 -1
  382. package/dist/utils/port-assignment.d.ts +0 -6
  383. package/dist/utils/port-assignment.d.ts.map +0 -1
  384. package/dist/utils/port-assignment.js +0 -81
  385. package/dist/utils/port-assignment.js.map +0 -1
  386. package/dist/utils/project-features.d.ts +0 -23
  387. package/dist/utils/project-features.d.ts.map +0 -1
  388. package/dist/utils/project-features.js +0 -98
  389. package/dist/utils/project-features.js.map +0 -1
  390. package/dist/utils/project-templates.d.ts +0 -7
  391. package/dist/utils/project-templates.d.ts.map +0 -1
  392. package/dist/utils/project-templates.js +0 -24
  393. package/dist/utils/project-templates.js.map +0 -1
  394. package/dist/utils/prompt.d.ts +0 -28
  395. package/dist/utils/prompt.d.ts.map +0 -1
  396. package/dist/utils/prompt.js +0 -79
  397. package/dist/utils/prompt.js.map +0 -1
  398. package/dist/utils/resource-features.d.ts +0 -29
  399. package/dist/utils/resource-features.d.ts.map +0 -1
  400. package/dist/utils/resource-features.js +0 -171
  401. package/dist/utils/resource-features.js.map +0 -1
  402. package/dist/utils/service-urls.d.ts +0 -30
  403. package/dist/utils/service-urls.d.ts.map +0 -1
  404. package/dist/utils/service-urls.js +0 -93
  405. package/dist/utils/service-urls.js.map +0 -1
  406. package/dist/utils/services.d.ts +0 -18
  407. package/dist/utils/services.d.ts.map +0 -1
  408. package/dist/utils/services.js +0 -291
  409. package/dist/utils/services.js.map +0 -1
  410. package/dist/utils/stack-features.d.ts +0 -22
  411. package/dist/utils/stack-features.d.ts.map +0 -1
  412. package/dist/utils/stack-features.js +0 -13
  413. package/dist/utils/stack-features.js.map +0 -1
  414. package/dist/utils/tar.d.ts +0 -7
  415. package/dist/utils/tar.d.ts.map +0 -1
  416. package/dist/utils/tar.js +0 -21
  417. package/dist/utils/tar.js.map +0 -1
  418. package/dist/utils/tilt.d.ts +0 -19
  419. package/dist/utils/tilt.d.ts.map +0 -1
  420. package/dist/utils/tilt.js +0 -78
  421. package/dist/utils/tilt.js.map +0 -1
  422. package/dist/utils/validation.d.ts +0 -16
  423. package/dist/utils/validation.d.ts.map +0 -1
  424. package/dist/utils/validation.js +0 -58
  425. package/dist/utils/validation.js.map +0 -1
  426. /package/{README.md → cli/README.md} +0 -0
  427. /package/{bin → cli/bin}/tdk.js +0 -0
  428. /package/{templates → cli/templates}/.tiltignore.hbs +0 -0
  429. /package/{templates → cli/templates}/TILT_RESOURCE_DEFAULTS.star.hbs +0 -0
  430. /package/{templates → cli/templates}/TILT_TECH_STACK.star.hbs +0 -0
  431. /package/{templates → cli/templates}/spec.master.hbs +0 -0
@@ -0,0 +1,378 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Integration test for AGENTS.md generator.
4
+
5
+ This test verifies the generator works by:
6
+ 1. Mocking the Starlark environment (Utils, write_fn)
7
+ 2. Calling the generator logic with test manifests
8
+ 3. Verifying the generated AGENTS.md content
9
+
10
+ Usage:
11
+ python3 tests/tilt-engine/test_agents_md_integration.py
12
+ """
13
+
14
+ import re
15
+ import sys
16
+ from pathlib import Path
17
+ from typing import Dict, Any, List
18
+
19
+ # Add project root to path
20
+ PROJECT_ROOT = Path(__file__).parent.parent.parent
21
+ sys.path.insert(0, str(PROJECT_ROOT))
22
+
23
+
24
+ class MockUtils:
25
+ """Mock Utils struct for testing."""
26
+
27
+ @staticmethod
28
+ def get_template_header(template_type: str, generator_name: str, resource_name: str, output_hint: str) -> str:
29
+ return f"# GENERATED BY TDK - {template_type} for {resource_name}\n# Generator: {generator_name}\n# {output_hint}"
30
+
31
+
32
+ class MockFileWriter:
33
+ """Mock write_fn that captures written files."""
34
+
35
+ def __init__(self):
36
+ self.files: Dict[str, str] = {}
37
+
38
+ def write(self, path: str, content: str) -> None:
39
+ """Mock write_file function."""
40
+ self.files[path] = content
41
+ print(f" 📝 Mock write: {path}")
42
+
43
+
44
+ def extract_template_from_generator() -> str:
45
+ """Extract TEMPLATE_AGENTS_MD from the generator source."""
46
+ generator_path = PROJECT_ROOT / "engine" / "topologies" / "tilt" / "generators" / "agents_md.star"
47
+ content = generator_path.read_text()
48
+
49
+ match = re.search(r'TEMPLATE_AGENTS_MD = """(.*?)"""', content, re.DOTALL)
50
+ if not match:
51
+ raise ValueError("Could not find TEMPLATE_AGENTS_MD in generator")
52
+
53
+ return match.group(1)
54
+
55
+
56
+ def simulate_generate_agents_md(manifest: Dict[str, Any], write_fn=None) -> str:
57
+ """
58
+ Simulate the generate_agents_md function in Python.
59
+
60
+ This mirrors the logic in agents_md.star but in Python for testing.
61
+ """
62
+ resource_path = manifest.get('_servicePath', '')
63
+ app_name = manifest.get('appName', 'unknown-service')
64
+ app_type = manifest.get('appType', 'backend')
65
+
66
+ # Get template
67
+ template = extract_template_from_generator()
68
+
69
+ # Generate header
70
+ header = MockUtils.get_template_header(
71
+ 'agents_md',
72
+ 'AgentsMd.generate()',
73
+ app_name,
74
+ 'Output: .autogenerated/AGENTS.md'
75
+ )
76
+
77
+ # Format template
78
+ timestamp = "2026-04-28 12:00:00" # Fixed for testing
79
+ content = template.format(
80
+ header=header,
81
+ resource_name=app_name,
82
+ app_type=app_type,
83
+ timestamp=timestamp,
84
+ )
85
+
86
+ if write_fn:
87
+ output_path = f"{resource_path}/.autogenerated/AGENTS.md"
88
+ write_fn(output_path, content)
89
+
90
+ return content
91
+
92
+
93
+ def test_backend_service():
94
+ """Test AGENTS.md generation for a backend service."""
95
+ print("\n" + "=" * 70)
96
+ print("🧪 TEST 1: Backend Service (test-api-backend)")
97
+ print("=" * 70)
98
+
99
+ manifest = {
100
+ "appName": "test-api-backend",
101
+ "appType": "backend",
102
+ "stack": "test",
103
+ "port": 4001,
104
+ "_servicePath": "services/product/test/test-api-backend",
105
+ }
106
+
107
+ writer = MockFileWriter()
108
+ content = simulate_generate_agents_md(manifest, writer.write)
109
+
110
+ # Assertions
111
+ assert "test-api-backend" in content, "Service name should appear"
112
+ assert "backend" in content, "App type should appear"
113
+ assert ".autogenerated/" in content, "Should reference .autogenerated folder"
114
+
115
+ output_path = "services/product/test/test-api-backend/.autogenerated/AGENTS.md"
116
+ assert output_path in writer.files, f"Should write to correct path: {output_path}"
117
+ assert output_path.endswith("AGENTS.md"), "File should be named AGENTS.md"
118
+
119
+ print(f"\n✅ Backend test passed!")
120
+ print(f" Output path: {output_path}")
121
+ print(f" Content length: {len(content)} chars")
122
+
123
+ return True
124
+
125
+
126
+ def test_frontend_service():
127
+ """Test AGENTS.md generation for a frontend service."""
128
+ print("\n" + "=" * 70)
129
+ print("🧪 TEST 2: Frontend Service (test-app-frontend)")
130
+ print("=" * 70)
131
+
132
+ manifest = {
133
+ "appName": "test-app-frontend",
134
+ "appType": "frontend",
135
+ "stack": "test",
136
+ "port": 3001,
137
+ "backendName": "test-api-backend",
138
+ "_servicePath": "services/product/test/test-app-frontend",
139
+ }
140
+
141
+ writer = MockFileWriter()
142
+ content = simulate_generate_agents_md(manifest, writer.write)
143
+
144
+ # Assertions
145
+ assert "test-app-frontend" in content, "Service name should appear"
146
+ assert "frontend" in content, "App type should appear"
147
+ assert "vite.config" in content, "Should mention vite config (relevant for frontend)"
148
+ assert "nginx" in content, "Should mention nginx (relevant for frontend)"
149
+
150
+ output_path = "services/product/test/test-app-frontend/.autogenerated/AGENTS.md"
151
+ assert output_path in writer.files, f"Should write to correct path: {output_path}"
152
+
153
+ print(f"\n✅ Frontend test passed!")
154
+ print(f" Content includes frontend-specific references")
155
+
156
+ return True
157
+
158
+
159
+ def test_migrator_service():
160
+ """Test AGENTS.md generation for a migrator service."""
161
+ print("\n" + "=" * 70)
162
+ print("🧪 TEST 3: Migrator Service (test-db-migrator)")
163
+ print("=" * 70)
164
+
165
+ manifest = {
166
+ "appName": "test-db-migrator",
167
+ "appType": "migrator",
168
+ "stack": "test",
169
+ "port": 7001,
170
+ "_servicePath": "services/product/test/test-db-migrator",
171
+ }
172
+
173
+ writer = MockFileWriter()
174
+ content = simulate_generate_agents_md(manifest, writer.write)
175
+
176
+ assert "test-db-migrator" in content, "Service name should appear"
177
+ assert "migrator" in content or "backend" in content, "App type should appear"
178
+
179
+ output_path = "services/product/test/test-db-migrator/.autogenerated/AGENTS.md"
180
+ assert output_path in writer.files, f"Should write to correct path: {output_path}"
181
+
182
+ print(f"\n✅ Migrator test passed!")
183
+
184
+ return True
185
+
186
+
187
+ def test_content_structure():
188
+ """Test that generated content has all required sections."""
189
+ print("\n" + "=" * 70)
190
+ print("🧪 TEST 4: Content Structure Validation")
191
+ print("=" * 70)
192
+
193
+ manifest = {
194
+ "appName": "structure-test-service",
195
+ "appType": "backend",
196
+ "_servicePath": "services/test",
197
+ }
198
+
199
+ content = simulate_generate_agents_md(manifest)
200
+
201
+ required_sections = [
202
+ ("---", "YAML frontmatter start"),
203
+ ("title:", "Title field"),
204
+ ("status: autogenerated", "Autogenerated status"),
205
+ ("# ⚠️ AUTOGENERATED FILES", "Warning header"),
206
+ ("## Why This Folder Exists", "Why section"),
207
+ ("## What Happens If You Modify Files Here", "Consequences section"),
208
+ ("## Where to Make Changes Instead", "Alternatives section"),
209
+ ("## How Regeneration Works", "Architecture section"),
210
+ ("## For AI Agents", "AI agent section"),
211
+ ("## Emergency Override", "Emergency section"),
212
+ ("service.json", "References service.json"),
213
+ ("package.json", "References package.json"),
214
+ ("NEVER", "Critical NEVER instruction"),
215
+ ("ALWAYS", "Best practice ALWAYS instruction"),
216
+ ("Generator Metadata", "Metadata section"),
217
+ ]
218
+
219
+ all_passed = True
220
+ for pattern, description in required_sections:
221
+ if pattern in content:
222
+ print(f" ✅ {description}")
223
+ else:
224
+ print(f" ❌ {description} - missing: '{pattern}'")
225
+ all_passed = False
226
+
227
+ # Verify list of generated files
228
+ generated_files = [
229
+ "vite.config", "tsconfig", "Dockerfile", "nginx",
230
+ ".env", "bunfig", ".npmrc", "docker-compose",
231
+ "playwright", "prisma"
232
+ ]
233
+
234
+ for file_type in generated_files:
235
+ if file_type in content:
236
+ print(f" ✅ Mentions {file_type}")
237
+ else:
238
+ print(f" ⚠️ Missing mention of {file_type}")
239
+
240
+ return all_passed
241
+
242
+
243
+ def test_ai_agent_instructions():
244
+ """Test that AI agent instructions are clear and present."""
245
+ print("\n" + "=" * 70)
246
+ print("🧪 TEST 5: AI Agent Instructions")
247
+ print("=" * 70)
248
+
249
+ manifest = {
250
+ "appName": "ai-test-service",
251
+ "appType": "backend",
252
+ "_servicePath": "services/ai/test",
253
+ }
254
+
255
+ content = simulate_generate_agents_md(manifest)
256
+
257
+ # Check for AI-specific instructions
258
+ ai_checks = [
259
+ ("write()", "References write() function"),
260
+ ("edit()", "References edit() function"),
261
+ ("skill(", "References skill() function"),
262
+ ("CRITICAL INSTRUCTIONS", "Has critical instructions section"),
263
+ ("❌", "Has X emoji for wrong way"),
264
+ ("✅", "Has checkmark emoji for right way"),
265
+ ]
266
+
267
+ all_passed = True
268
+ for pattern, description in ai_checks:
269
+ if pattern in content:
270
+ print(f" ✅ {description}")
271
+ else:
272
+ print(f" ❌ {description} - missing: '{pattern}'")
273
+ all_passed = False
274
+
275
+ return all_passed
276
+
277
+
278
+ def test_no_write_fn():
279
+ """Test that generator works without write_fn (returns content only)."""
280
+ print("\n" + "=" * 70)
281
+ print("🧪 TEST 6: Generator Without write_fn")
282
+ print("=" * 70)
283
+
284
+ manifest = {
285
+ "appName": "no-write-service",
286
+ "appType": "backend",
287
+ "_servicePath": "services/test",
288
+ }
289
+
290
+ # Call without write_fn
291
+ content = simulate_generate_agents_md(manifest, write_fn=None)
292
+
293
+ assert content is not None, "Should return content"
294
+ assert len(content) > 1000, "Content should be substantial"
295
+ assert "no-write-service" in content, "Should include service name"
296
+
297
+ print(" ✅ Returns content without writing when write_fn=None")
298
+
299
+ return True
300
+
301
+
302
+ def test_all_services():
303
+ """Test generation for all service types."""
304
+ print("\n" + "=" * 70)
305
+ print("🧪 TEST 7: All Service Types")
306
+ print("=" * 70)
307
+
308
+ resource_types = ["backend", "frontend", "migrator", "library", "sdk"]
309
+
310
+ for svc_type in resource_types:
311
+ manifest = {
312
+ "appName": f"test-{svc_type}",
313
+ "appType": svc_type,
314
+ "_servicePath": f"services/test/{svc_type}",
315
+ }
316
+
317
+ writer = MockFileWriter()
318
+ content = simulate_generate_agents_md(manifest, writer.write)
319
+
320
+ assert svc_type in content, f"Should include appType: {svc_type}"
321
+ assert f"test-{svc_type}" in content, f"Should include service name"
322
+ print(f" ✅ {svc_type} service works")
323
+
324
+ return True
325
+
326
+
327
+ def run_all_tests():
328
+ """Run all integration tests."""
329
+ print("\n" + "=" * 70)
330
+ print("🚀 AGENTS.MD GENERATOR INTEGRATION TESTS")
331
+ print("=" * 70)
332
+
333
+ tests = [
334
+ ("Backend Service", test_backend_service),
335
+ ("Frontend Service", test_frontend_service),
336
+ ("Migrator Service", test_migrator_service),
337
+ ("Content Structure", test_content_structure),
338
+ ("AI Agent Instructions", test_ai_agent_instructions),
339
+ ("No Write Function", test_no_write_fn),
340
+ ("All Service Types", test_all_services),
341
+ ]
342
+
343
+ results = []
344
+ for name, test_func in tests:
345
+ try:
346
+ passed = test_func()
347
+ results.append((name, passed))
348
+ except Exception as e:
349
+ print(f"\n💥 ERROR in {name}: {e}")
350
+ import traceback
351
+ traceback.print_exc()
352
+ results.append((name, False))
353
+
354
+ # Summary
355
+ print("\n" + "=" * 70)
356
+ print("📊 TEST SUMMARY")
357
+ print("=" * 70)
358
+
359
+ passed_count = sum(1 for _, p in results if p)
360
+ total_count = len(results)
361
+
362
+ for name, passed in results:
363
+ status = "✅ PASS" if passed else "❌ FAIL"
364
+ print(f" {status}: {name}")
365
+
366
+ print(f"\n Total: {passed_count}/{total_count} tests passed")
367
+
368
+ if passed_count == total_count:
369
+ print("\n🎉 All tests passed!")
370
+ return 0
371
+ else:
372
+ print(f"\n⚠️ {total_count - passed_count} test(s) failed")
373
+ return 1
374
+
375
+
376
+ if __name__ == "__main__":
377
+ exit_code = run_all_tests()
378
+ sys.exit(exit_code)
@@ -0,0 +1,181 @@
1
+ """
2
+ Tests for registry cache operations.
3
+ Tests the CRUD operations for the service registry cache.
4
+ """
5
+
6
+ import pytest
7
+
8
+
9
+ @pytest.mark.cache
10
+ class TestCacheOperations:
11
+ """Tests for registry cache CRUD operations."""
12
+
13
+ def test_add_new_resource_to_cache(self, resource_registry_cache):
14
+ """
15
+ Scenario: Add new service to cache
16
+ WHEN `CacheOps.add_resource_to_cache()` is called with a valid service object
17
+ THEN the service SHALL be stored in `_DISCOVERY_CACHE`
18
+ AND it SHALL be retrievable by path via `CacheOps.get_resource_by_path_from_cache()`
19
+ """
20
+ cache = resource_registry_cache
21
+ service = {
22
+ "name": "test-service",
23
+ "path": "services/product/test/test-service"
24
+ }
25
+
26
+ # Add to cache
27
+ self._add_to_cache(cache, service["path"], service)
28
+
29
+ # Verify retrievable
30
+ retrieved = self._get_from_cache(cache, service["path"])
31
+ assert retrieved is not None
32
+ assert retrieved["name"] == "test-service"
33
+
34
+ def test_add_resource_with_metadata(self, resource_registry_cache):
35
+ """
36
+ Scenario: Add service with metadata
37
+ WHEN adding a service with fields `name`, `type`, `path`, `domain`, `port`
38
+ THEN all fields SHALL be preserved in the cache
39
+ AND retrieval SHALL return the exact same data
40
+ """
41
+ cache = resource_registry_cache
42
+ service = {
43
+ "name": "identity-backend",
44
+ "type": "backend",
45
+ "path": "services/product/identity/identity-backend",
46
+ "stack": "identity",
47
+ "port": 4001
48
+ }
49
+
50
+ self._add_to_cache(cache, service["path"], service)
51
+ retrieved = self._get_from_cache(cache, service["path"])
52
+
53
+ assert retrieved["name"] == "identity-backend"
54
+ assert retrieved["type"] == "backend"
55
+ assert retrieved["stack"] == "identity"
56
+ assert retrieved["port"] == 4001
57
+
58
+ def test_prevent_duplicate_adds(self, resource_registry_cache):
59
+ """
60
+ Scenario: Prevent duplicate adds
61
+ WHEN attempting to add a service with a path already in cache
62
+ THEN the new service data SHALL overwrite the old data (update semantics)
63
+ AND the cache SHALL contain only one entry for that path
64
+ """
65
+ cache = resource_registry_cache
66
+ path = "services/product/test/service"
67
+
68
+ # Add initial version
69
+ self._add_to_cache(cache, path, {"name": "v1", "port": 4000})
70
+
71
+ # Add updated version (should overwrite)
72
+ self._add_to_cache(cache, path, {"name": "v2", "port": 4001})
73
+
74
+ # Retrieve and verify
75
+ retrieved = self._get_from_cache(cache, path)
76
+ assert retrieved["name"] == "v2"
77
+ assert retrieved["port"] == 4001
78
+
79
+ def test_remove_existing_service(self, resource_registry_cache):
80
+ """
81
+ Scenario: Remove existing service
82
+ WHEN `CacheOps.remove_resource_from_cache()` is called with a path in cache
83
+ THEN the service SHALL be removed from `_DISCOVERY_CACHE`
84
+ AND subsequent `CacheOps.has_resource_in_cache()` SHALL return `False`
85
+ """
86
+ cache = resource_registry_cache
87
+ path = "services/product/test/service"
88
+
89
+ # Add and verify
90
+ self._add_to_cache(cache, path, {"name": "test"})
91
+ assert self._has_in_cache(cache, path)
92
+
93
+ # Remove
94
+ self._remove_from_cache(cache, path)
95
+
96
+ # Verify removed
97
+ assert not self._has_in_cache(cache, path)
98
+
99
+ def test_remove_non_existent_service(self, resource_registry_cache):
100
+ """
101
+ Scenario: Remove non-existent service
102
+ WHEN attempting to remove a path not in cache
103
+ THEN it SHALL complete without error (idempotent)
104
+ AND other cache entries SHALL remain unaffected
105
+ """
106
+ cache = resource_registry_cache
107
+
108
+ # Add a service
109
+ self._add_to_cache(cache, "path1", {"name": "service1"})
110
+
111
+ # Try to remove non-existent path
112
+ self._remove_from_cache(cache, "nonexistent") # Should not raise
113
+
114
+ # Verify other entries unaffected
115
+ assert self._has_in_cache(cache, "path1")
116
+
117
+ def test_check_resource_existence(self, resource_registry_cache):
118
+ """
119
+ Scenario: Check service existence
120
+ WHEN `CacheOps.has_resource_in_cache()` is called with a cached path
121
+ THEN it SHALL return `True`
122
+ """
123
+ cache = resource_registry_cache
124
+ path = "services/product/test/service"
125
+
126
+ self._add_to_cache(cache, path, {"name": "test"})
127
+
128
+ assert self._has_in_cache(cache, path)
129
+
130
+ def test_check_non_existent_service(self, resource_registry_cache):
131
+ """
132
+ Scenario: Check non-existent service
133
+ WHEN `CacheOps.has_resource_in_cache()` is called with an unknown path
134
+ THEN it SHALL return `False`
135
+ """
136
+ cache = resource_registry_cache
137
+
138
+ assert not self._has_in_cache(cache, "nonexistent")
139
+
140
+ def test_get_resource_by_path(self, resource_registry_cache):
141
+ """
142
+ Scenario: Get service by path
143
+ WHEN `CacheOps.get_resource_by_path_from_cache()` is called with a cached path
144
+ THEN it SHALL return the full service object
145
+ """
146
+ cache = resource_registry_cache
147
+ service = {"name": "test", "type": "backend"}
148
+ path = "services/product/test/service"
149
+
150
+ self._add_to_cache(cache, path, service)
151
+ retrieved = self._get_from_cache(cache, path)
152
+
153
+ assert retrieved == service
154
+
155
+ def test_get_non_existent_resource_returns_none(self, resource_registry_cache):
156
+ """
157
+ Scenario: Get non-existent service
158
+ WHEN `CacheOps.get_resource_by_path_from_cache()` is called with an unknown path
159
+ THEN it SHALL return `None` (not raise an exception)
160
+ """
161
+ cache = resource_registry_cache
162
+
163
+ result = self._get_from_cache(cache, "nonexistent")
164
+
165
+ assert result is None
166
+
167
+ # Helper methods to simulate cache operations
168
+ def _add_to_cache(self, cache, path, service):
169
+ if "resource_path_map" not in cache:
170
+ cache["resource_path_map"] = {}
171
+ cache["resource_path_map"][path] = service
172
+
173
+ def _get_from_cache(self, cache, path):
174
+ return cache.get("resource_path_map", {}).get(path)
175
+
176
+ def _remove_from_cache(self, cache, path):
177
+ if path in cache.get("resource_path_map", {}):
178
+ del cache["resource_path_map"][path]
179
+
180
+ def _has_in_cache(self, cache, path):
181
+ return path in cache.get("resource_path_map", {})
@@ -0,0 +1,142 @@
1
+ """
2
+ Tests for registry cache persistence.
3
+ Tests saving and loading the cache from the filesystem.
4
+ """
5
+
6
+ import json
7
+ import pytest
8
+ from pathlib import Path
9
+
10
+
11
+ @pytest.mark.cache
12
+ class TestCachePersistence:
13
+ """Tests for cache persistence operations."""
14
+
15
+ def test_save_cache_to_file(self, temp_dir, resource_registry_cache):
16
+ """
17
+ Scenario: Save cache to file
18
+ WHEN the cache is saved to a JSON file
19
+ THEN the file SHALL exist with valid JSON
20
+ AND it SHALL contain all cached services
21
+ """
22
+ cache_file = temp_dir / "discovery-cache.json"
23
+
24
+ # Add services to cache
25
+ cache = resource_registry_cache
26
+ cache["resource_path_map"] = {
27
+ "path1": {"name": "service1"},
28
+ "path2": {"name": "service2"}
29
+ }
30
+
31
+ # Save to file
32
+ self._save_cache(cache, cache_file)
33
+
34
+ # Verify file exists
35
+ assert cache_file.exists()
36
+
37
+ # Verify content
38
+ with open(cache_file) as f:
39
+ data = json.load(f)
40
+
41
+ assert "resource_path_map" in data
42
+ assert len(data["resource_path_map"]) == 2
43
+
44
+ def test_load_cache_from_file(self, temp_dir, resource_registry_cache):
45
+ """
46
+ Scenario: Load cache from file
47
+ WHEN loading cache from a previously saved JSON file
48
+ THEN `_DISCOVERY_CACHE` SHALL be populated with the saved services
49
+ AND all query operations SHALL work on the loaded data
50
+ """
51
+ cache_file = temp_dir / "discovery-cache.json"
52
+
53
+ # Create saved cache file
54
+ saved_data = {
55
+ "resource_path_map": {
56
+ "path1": {"name": "service1", "type": "backend"},
57
+ "path2": {"name": "service2", "type": "frontend"}
58
+ }
59
+ }
60
+ with open(cache_file, "w") as f:
61
+ json.dump(saved_data, f)
62
+
63
+ # Load cache
64
+ cache = resource_registry_cache
65
+ self._load_cache(cache, cache_file)
66
+
67
+ # Verify loaded
68
+ assert "path1" in cache["resource_path_map"]
69
+ assert cache["resource_path_map"]["path1"]["name"] == "service1"
70
+ assert cache["resource_path_map"]["path2"]["type"] == "frontend"
71
+
72
+ def test_cache_survives_process_restart(self, temp_dir, resource_registry_cache):
73
+ """
74
+ Scenario: Cache survives process restart
75
+ WHEN the Tilt process restarts
76
+ AND the cache file exists
77
+ THEN the cache SHALL be reloaded automatically
78
+ AND no services SHALL need re-discovery
79
+ """
80
+ cache_file = temp_dir / "discovery-cache.json"
81
+
82
+ # Simulate first run - save cache
83
+ cache1 = resource_registry_cache
84
+ cache1["resource_path_map"] = {
85
+ "services/test/service1": {"name": "service1"},
86
+ "services/test/service2": {"name": "service2"}
87
+ }
88
+ self._save_cache(cache1, cache_file)
89
+
90
+ # Simulate restart - new cache object, load from file
91
+ cache2 = {"initialized": False, "resource_path_map": {}}
92
+ self._load_cache(cache2, cache_file)
93
+
94
+ # Verify cache survived
95
+ assert "services/test/service1" in cache2["resource_path_map"]
96
+ assert "services/test/service2" in cache2["resource_path_map"]
97
+ assert cache2["resource_path_map"]["services/test/service1"]["name"] == "service1"
98
+
99
+ def test_save_and_load_empty_cache(self, temp_dir):
100
+ """Test saving and loading an empty cache."""
101
+ cache_file = temp_dir / "empty-cache.json"
102
+
103
+ empty_cache = {"resource_path_map": {}}
104
+ self._save_cache(empty_cache, cache_file)
105
+
106
+ loaded_cache = {"resource_path_map": {}}
107
+ self._load_cache(loaded_cache, cache_file)
108
+
109
+ assert len(loaded_cache["resource_path_map"]) == 0
110
+
111
+ def test_cache_file_format(self, temp_dir):
112
+ """Test that cache file has expected format."""
113
+ cache_file = temp_dir / "cache.json"
114
+
115
+ cache = {
116
+ "initialized": True,
117
+ "resource_path_map": {
118
+ "path": {"name": "test"}
119
+ }
120
+ }
121
+ self._save_cache(cache, cache_file)
122
+
123
+ with open(cache_file) as f:
124
+ content = f.read()
125
+
126
+ # Should be valid JSON
127
+ data = json.loads(content)
128
+ assert "resource_path_map" in data
129
+
130
+ # Helper methods
131
+ def _save_cache(self, cache, path):
132
+ with open(path, "w") as f:
133
+ json.dump(cache, f, indent=2)
134
+
135
+ def _load_cache(self, cache, path):
136
+ if not path.exists():
137
+ return
138
+
139
+ with open(path) as f:
140
+ data = json.load(f)
141
+
142
+ cache.update(data)