@tdk-landscape/tdk-cli-core 1.3.53 → 1.3.54
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.
- package/CHANGELOG.md +4 -0
- package/cli/dist/generator/extension-fetch.js +16 -9
- package/cli/package.json +1 -1
- package/package.json +16 -1
- package/.claude/skills/layer-autoresearch/SKILL.md +0 -48
- package/.claude/skills/tdk-troubleshoot/SKILL.md +0 -222
- package/.github/ISSUE_TEMPLATE/bug_report.yml +0 -40
- package/.github/ISSUE_TEMPLATE/config.yml +0 -11
- package/.github/ISSUE_TEMPLATE/feature_request.yml +0 -18
- package/.github/ISSUE_TEMPLATE/premium_license.yml +0 -43
- package/.github/dependabot.yml +0 -33
- package/.github/pull_request_template.md +0 -5
- package/.github/workflows/ci.yml +0 -200
- package/.github/workflows/release-binaries.yml +0 -139
- package/.github/zizmor.yml +0 -9
- package/.pre-commit-config.yaml +0 -57
- package/CODE_OF_CONDUCT.md +0 -9
- package/CONTRIBUTING.md +0 -42
- package/Makefile +0 -92
- package/SECURITY.md +0 -17
- package/benchmarks/results/container-scale-2026-09-25T20-54-49-736Z.json +0 -45
- package/benchmarks/results/container-scale-2026-09-25T21-01-20-540Z.json +0 -150
- package/benchmarks/results/container-scale-2026-09-25T21-15-01-441Z.json +0 -73
- package/benchmarks/results/container-scale-2026-09-25T22-09-01-630Z.json +0 -77
- package/benchmarks/results/container-scale-2026-09-25T22-18-28-371Z.json +0 -202
- package/cli/AGENTS.md +0 -390
- package/cli/DESIGN.md +0 -735
- package/cli/README.md +0 -292
- package/cli/biome.json +0 -40
- package/cli/dependency-graph.svg +0 -1135
- package/cli/dist/cli.d.ts +0 -3
- package/cli/dist/cli.d.ts.map +0 -1
- package/cli/dist/cli.js.map +0 -1
- package/cli/dist/commands/completion.d.ts +0 -3
- package/cli/dist/commands/completion.d.ts.map +0 -1
- package/cli/dist/commands/completion.js.map +0 -1
- package/cli/dist/commands/config.d.ts +0 -3
- package/cli/dist/commands/config.d.ts.map +0 -1
- package/cli/dist/commands/config.js.map +0 -1
- package/cli/dist/commands/doctor.d.ts +0 -19
- package/cli/dist/commands/doctor.d.ts.map +0 -1
- package/cli/dist/commands/doctor.js.map +0 -1
- package/cli/dist/commands/down.d.ts +0 -3
- package/cli/dist/commands/down.d.ts.map +0 -1
- package/cli/dist/commands/down.js.map +0 -1
- package/cli/dist/commands/help.d.ts +0 -2
- package/cli/dist/commands/help.d.ts.map +0 -1
- package/cli/dist/commands/help.js.map +0 -1
- package/cli/dist/commands/networks.d.ts +0 -3
- package/cli/dist/commands/networks.d.ts.map +0 -1
- package/cli/dist/commands/networks.js.map +0 -1
- package/cli/dist/commands/project.d.ts +0 -9
- package/cli/dist/commands/project.d.ts.map +0 -1
- package/cli/dist/commands/project.js.map +0 -1
- package/cli/dist/commands/projects.d.ts +0 -3
- package/cli/dist/commands/projects.d.ts.map +0 -1
- package/cli/dist/commands/projects.js.map +0 -1
- package/cli/dist/commands/resource.d.ts +0 -69
- package/cli/dist/commands/resource.d.ts.map +0 -1
- package/cli/dist/commands/resource.js.map +0 -1
- package/cli/dist/commands/resources.d.ts +0 -3
- package/cli/dist/commands/resources.d.ts.map +0 -1
- package/cli/dist/commands/resources.js.map +0 -1
- package/cli/dist/commands/stack.d.ts +0 -3
- package/cli/dist/commands/stack.d.ts.map +0 -1
- package/cli/dist/commands/stack.js.map +0 -1
- package/cli/dist/commands/stacks.d.ts +0 -3
- package/cli/dist/commands/stacks.d.ts.map +0 -1
- package/cli/dist/commands/stacks.js.map +0 -1
- package/cli/dist/commands/status.d.ts +0 -3
- package/cli/dist/commands/status.d.ts.map +0 -1
- package/cli/dist/commands/status.js.map +0 -1
- package/cli/dist/commands/ui.d.ts +0 -3
- package/cli/dist/commands/ui.d.ts.map +0 -1
- package/cli/dist/commands/ui.js.map +0 -1
- package/cli/dist/commands/up.d.ts +0 -3
- package/cli/dist/commands/up.d.ts.map +0 -1
- package/cli/dist/commands/up.js.map +0 -1
- package/cli/dist/commands/upgrade.d.ts +0 -14
- package/cli/dist/commands/upgrade.d.ts.map +0 -1
- package/cli/dist/commands/upgrade.js.map +0 -1
- package/cli/dist/commands/version.d.ts +0 -3
- package/cli/dist/commands/version.d.ts.map +0 -1
- package/cli/dist/commands/version.js.map +0 -1
- package/cli/dist/components/Accessible.d.ts +0 -8
- package/cli/dist/components/Accessible.d.ts.map +0 -1
- package/cli/dist/components/Accessible.js.map +0 -1
- package/cli/dist/components/BaseTooltip.d.ts +0 -4
- package/cli/dist/components/BaseTooltip.d.ts.map +0 -1
- package/cli/dist/components/BaseTooltip.js.map +0 -1
- package/cli/dist/components/DetailPanel.d.ts +0 -4
- package/cli/dist/components/DetailPanel.d.ts.map +0 -1
- package/cli/dist/components/DetailPanel.js.map +0 -1
- package/cli/dist/components/FileTree.d.ts +0 -4
- package/cli/dist/components/FileTree.d.ts.map +0 -1
- package/cli/dist/components/FileTree.js.map +0 -1
- package/cli/dist/components/ResourceSelectInput.d.ts +0 -4
- package/cli/dist/components/ResourceSelectInput.d.ts.map +0 -1
- package/cli/dist/components/ResourceSelectInput.js.map +0 -1
- package/cli/dist/components/ResourceTable.d.ts +0 -4
- package/cli/dist/components/ResourceTable.d.ts.map +0 -1
- package/cli/dist/components/ResourceTable.js.map +0 -1
- package/cli/dist/components/TabBar.d.ts +0 -4
- package/cli/dist/components/TabBar.d.ts.map +0 -1
- package/cli/dist/components/TabBar.js.map +0 -1
- package/cli/dist/components/Tooltip.d.ts +0 -12
- package/cli/dist/components/Tooltip.d.ts.map +0 -1
- package/cli/dist/components/Tooltip.js.map +0 -1
- package/cli/dist/components/index.d.ts +0 -8
- package/cli/dist/components/index.d.ts.map +0 -1
- package/cli/dist/components/index.js.map +0 -1
- package/cli/dist/config/platform-standards.d.ts +0 -159
- package/cli/dist/config/platform-standards.d.ts.map +0 -1
- package/cli/dist/config/platform-standards.js.map +0 -1
- package/cli/dist/generator/extension-fetch.d.ts +0 -25
- package/cli/dist/generator/extension-fetch.d.ts.map +0 -1
- package/cli/dist/generator/extension-fetch.js.map +0 -1
- package/cli/dist/generator/template-engine.d.ts +0 -62
- package/cli/dist/generator/template-engine.d.ts.map +0 -1
- package/cli/dist/generator/template-engine.js.map +0 -1
- package/cli/dist/index.d.ts +0 -11
- package/cli/dist/index.d.ts.map +0 -1
- package/cli/dist/index.js.map +0 -1
- package/cli/dist/types/index.d.ts +0 -257
- package/cli/dist/types/index.d.ts.map +0 -1
- package/cli/dist/types/index.js.map +0 -1
- package/cli/dist/utils/cache.d.ts +0 -19
- package/cli/dist/utils/cache.d.ts.map +0 -1
- package/cli/dist/utils/cache.js.map +0 -1
- package/cli/dist/utils/command-helpers.d.ts +0 -17
- package/cli/dist/utils/command-helpers.d.ts.map +0 -1
- package/cli/dist/utils/command-helpers.js.map +0 -1
- package/cli/dist/utils/constants.d.ts +0 -53
- package/cli/dist/utils/constants.d.ts.map +0 -1
- package/cli/dist/utils/constants.js.map +0 -1
- package/cli/dist/utils/discovery-context.d.ts +0 -4
- package/cli/dist/utils/discovery-context.d.ts.map +0 -1
- package/cli/dist/utils/discovery-context.js.map +0 -1
- package/cli/dist/utils/docker.d.ts +0 -4
- package/cli/dist/utils/docker.d.ts.map +0 -1
- package/cli/dist/utils/docker.js.map +0 -1
- package/cli/dist/utils/doctor-runtime.d.ts +0 -81
- package/cli/dist/utils/doctor-runtime.d.ts.map +0 -1
- package/cli/dist/utils/doctor-runtime.js.map +0 -1
- package/cli/dist/utils/env-validator.d.ts +0 -15
- package/cli/dist/utils/env-validator.d.ts.map +0 -1
- package/cli/dist/utils/env-validator.js.map +0 -1
- package/cli/dist/utils/errors.d.ts +0 -31
- package/cli/dist/utils/errors.d.ts.map +0 -1
- package/cli/dist/utils/errors.js.map +0 -1
- package/cli/dist/utils/file-helpers.d.ts +0 -14
- package/cli/dist/utils/file-helpers.d.ts.map +0 -1
- package/cli/dist/utils/file-helpers.js.map +0 -1
- package/cli/dist/utils/formatting.d.ts +0 -30
- package/cli/dist/utils/formatting.d.ts.map +0 -1
- package/cli/dist/utils/formatting.js.map +0 -1
- package/cli/dist/utils/paths.d.ts +0 -3
- package/cli/dist/utils/paths.d.ts.map +0 -1
- package/cli/dist/utils/paths.js.map +0 -1
- package/cli/dist/utils/port-assignment.d.ts +0 -6
- package/cli/dist/utils/port-assignment.d.ts.map +0 -1
- package/cli/dist/utils/port-assignment.js.map +0 -1
- package/cli/dist/utils/project-features.d.ts +0 -23
- package/cli/dist/utils/project-features.d.ts.map +0 -1
- package/cli/dist/utils/project-features.js.map +0 -1
- package/cli/dist/utils/project-templates.d.ts +0 -7
- package/cli/dist/utils/project-templates.d.ts.map +0 -1
- package/cli/dist/utils/project-templates.js.map +0 -1
- package/cli/dist/utils/prompt.d.ts +0 -28
- package/cli/dist/utils/prompt.d.ts.map +0 -1
- package/cli/dist/utils/prompt.js.map +0 -1
- package/cli/dist/utils/resource-features.d.ts +0 -29
- package/cli/dist/utils/resource-features.d.ts.map +0 -1
- package/cli/dist/utils/resource-features.js.map +0 -1
- package/cli/dist/utils/service-urls.d.ts +0 -30
- package/cli/dist/utils/service-urls.d.ts.map +0 -1
- package/cli/dist/utils/service-urls.js.map +0 -1
- package/cli/dist/utils/services.d.ts +0 -18
- package/cli/dist/utils/services.d.ts.map +0 -1
- package/cli/dist/utils/services.js.map +0 -1
- package/cli/dist/utils/stack-features.d.ts +0 -22
- package/cli/dist/utils/stack-features.d.ts.map +0 -1
- package/cli/dist/utils/stack-features.js.map +0 -1
- package/cli/dist/utils/tar.d.ts +0 -7
- package/cli/dist/utils/tar.d.ts.map +0 -1
- package/cli/dist/utils/tar.js.map +0 -1
- package/cli/dist/utils/tilt.d.ts +0 -20
- package/cli/dist/utils/tilt.d.ts.map +0 -1
- package/cli/dist/utils/tilt.js.map +0 -1
- package/cli/dist/utils/validation.d.ts +0 -16
- package/cli/dist/utils/validation.d.ts.map +0 -1
- package/cli/dist/utils/validation.js.map +0 -1
- package/cli/knip.json +0 -6
- package/cli/pnpm-lock.yaml +0 -2305
- package/cli/pnpm-workspace.yaml +0 -2
- package/cli/src/cli.ts +0 -72
- package/cli/src/commands/__tests__/always-enabled-infra.test.ts +0 -109
- package/cli/src/commands/__tests__/config.test.ts +0 -146
- package/cli/src/commands/__tests__/doctor.test.ts +0 -730
- package/cli/src/commands/__tests__/error-handling.test.ts +0 -78
- package/cli/src/commands/__tests__/phases-model.test.ts +0 -161
- package/cli/src/commands/__tests__/project-resource-features-e2e.test.ts +0 -187
- package/cli/src/commands/__tests__/project.test.ts +0 -68
- package/cli/src/commands/__tests__/resource.test.ts +0 -226
- package/cli/src/commands/__tests__/saas-starter-integration.test.ts +0 -194
- package/cli/src/commands/__tests__/stack-features-e2e.test.ts +0 -135
- package/cli/src/commands/__tests__/upgrade.test.ts +0 -198
- package/cli/src/commands/completion.ts +0 -300
- package/cli/src/commands/config.ts +0 -299
- package/cli/src/commands/doctor.ts +0 -926
- package/cli/src/commands/down.ts +0 -37
- package/cli/src/commands/help.ts +0 -143
- package/cli/src/commands/networks-design.md +0 -80
- package/cli/src/commands/networks.ts +0 -332
- package/cli/src/commands/project.ts +0 -456
- package/cli/src/commands/projects.ts +0 -82
- package/cli/src/commands/resource.ts +0 -652
- package/cli/src/commands/resources.ts +0 -95
- package/cli/src/commands/stack.ts +0 -128
- package/cli/src/commands/stacks.ts +0 -52
- package/cli/src/commands/status.ts +0 -88
- package/cli/src/commands/ui.tsx +0 -843
- package/cli/src/commands/up.ts +0 -169
- package/cli/src/commands/upgrade.ts +0 -628
- package/cli/src/commands/version.ts +0 -9
- package/cli/src/components/Accessible.tsx +0 -21
- package/cli/src/components/BaseTooltip.tsx +0 -66
- package/cli/src/components/DetailPanel.tsx +0 -146
- package/cli/src/components/FileTree.tsx +0 -89
- package/cli/src/components/ResourceSelectInput.tsx +0 -30
- package/cli/src/components/ResourceTable.tsx +0 -78
- package/cli/src/components/TabBar.tsx +0 -53
- package/cli/src/components/Tooltip.tsx +0 -11
- package/cli/src/components/index.ts +0 -7
- package/cli/src/config/platform-standards.test.ts +0 -11
- package/cli/src/config/platform-standards.ts +0 -196
- package/cli/src/generator/__tests__/extension-fetch.test.ts +0 -242
- package/cli/src/generator/__tests__/template-engine.test.ts +0 -70
- package/cli/src/generator/extension-fetch.ts +0 -286
- package/cli/src/generator/template-engine.ts +0 -752
- package/cli/src/index.ts +0 -86
- package/cli/src/templates.d.ts +0 -4
- package/cli/src/types/index.ts +0 -353
- package/cli/src/utils/__tests__/cache.test.ts +0 -98
- package/cli/src/utils/__tests__/formatting.test.ts +0 -170
- package/cli/src/utils/__tests__/paths.test.ts +0 -180
- package/cli/src/utils/__tests__/port-assignment.test.ts +0 -121
- package/cli/src/utils/__tests__/service-urls.test.ts +0 -174
- package/cli/src/utils/__tests__/services.test.ts +0 -531
- package/cli/src/utils/__tests__/tilt.test.ts +0 -172
- package/cli/src/utils/__tests__/validation.test.ts +0 -148
- package/cli/src/utils/cache.ts +0 -76
- package/cli/src/utils/command-helpers.ts +0 -54
- package/cli/src/utils/constants.ts +0 -130
- package/cli/src/utils/discovery-context.ts +0 -49
- package/cli/src/utils/docker.ts +0 -20
- package/cli/src/utils/doctor-runtime.ts +0 -644
- package/cli/src/utils/env-validator.ts +0 -139
- package/cli/src/utils/errors.ts +0 -168
- package/cli/src/utils/file-helpers.ts +0 -62
- package/cli/src/utils/formatting.ts +0 -215
- package/cli/src/utils/paths.ts +0 -53
- package/cli/src/utils/port-assignment.ts +0 -106
- package/cli/src/utils/project-features.ts +0 -118
- package/cli/src/utils/project-templates.ts +0 -29
- package/cli/src/utils/prompt.ts +0 -128
- package/cli/src/utils/resource-features.ts +0 -215
- package/cli/src/utils/service-urls.ts +0 -124
- package/cli/src/utils/services.ts +0 -364
- package/cli/src/utils/stack-features.ts +0 -30
- package/cli/src/utils/tar.ts +0 -24
- package/cli/src/utils/tilt.ts +0 -133
- package/cli/src/utils/validation.ts +0 -67
- package/cli/tsconfig.json +0 -25
- package/cli/tsconfig.typecheck.json +0 -8
- package/docs/FEATURES.md +0 -281
- package/docs/demo.svg +0 -217
- package/engine/docs/AUTODISCOVERY_TEST.md +0 -182
- package/engine/docs/C4-ARCHITECTURE-operations.md +0 -396
- package/engine/docs/C4-ARCHITECTURE-overview.md +0 -284
- package/engine/docs/C4-ARCHITECTURE.md +0 -17
- package/engine/docs/GOLDEN_IMAGE_INTEGRATION.md +0 -349
- package/engine/docs/MANIFEST_DRIVEN_ARCHITECTURE.md +0 -357
- package/engine/docs/README.md +0 -284
- package/install.sh +0 -19
- package/knip.json +0 -10
- package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/.openspec.yaml +0 -2
- package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/design.md +0 -102
- package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/proposal.md +0 -31
- package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/specs/vscode-tdk-notifications/spec.md +0 -48
- package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/specs/vscode-tdk-orchestration/spec.md +0 -63
- package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/specs/vscode-tdk-troubleshooting/spec.md +0 -56
- package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/tasks.md +0 -61
- package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/.openspec.yaml +0 -2
- package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/design.md +0 -114
- package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/proposal.md +0 -32
- package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/specs/manifest-field-migration/spec.md +0 -20
- package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/specs/resource-terminology-standard/spec.md +0 -20
- package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/specs/stack-terminology-standard/spec.md +0 -20
- package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/tasks.md +0 -194
- package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/.openspec.yaml +0 -2
- package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/design.md +0 -111
- package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/proposal.md +0 -33
- package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/specs/function-naming-alignment/spec.md +0 -12
- package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/specs/optional-parameter-patterns/spec.md +0 -18
- package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/specs/parameter-documentation/spec.md +0 -16
- package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/specs/parameter-naming-consistency/spec.md +0 -12
- package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/specs/parameter-ordering-standard/spec.md +0 -12
- package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/tasks.md +0 -194
- package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/.openspec.yaml +0 -2
- package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/design.md +0 -84
- package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/proposal.md +0 -31
- package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/specs/codebase-audit/spec.md +0 -27
- package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/specs/dead-code-cleanup/spec.md +0 -27
- package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/specs/test-gap-fill/spec.md +0 -23
- package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/specs/todo-resolution/spec.md +0 -27
- package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/tasks.md +0 -39
- package/openspec/changes/archive/2026-06-25-landscape-consolidation/.openspec.yaml +0 -2
- package/openspec/changes/archive/2026-06-25-landscape-consolidation/design.md +0 -80
- package/openspec/changes/archive/2026-06-25-landscape-consolidation/proposal.md +0 -40
- package/openspec/changes/archive/2026-06-25-landscape-consolidation/spec.md +0 -52
- package/openspec/changes/archive/2026-06-25-landscape-consolidation/tasks.md +0 -51
- package/openspec/changes/archive/2026-09-17-fix-generated-stack-boot/.openspec.yaml +0 -2
- package/openspec/changes/archive/2026-09-17-fix-generated-stack-boot/design.md +0 -43
- package/openspec/changes/archive/2026-09-17-fix-generated-stack-boot/proposal.md +0 -29
- package/openspec/changes/archive/2026-09-17-fix-generated-stack-boot/specs/self-contained-generated-projects/spec.md +0 -67
- package/openspec/changes/archive/2026-09-17-fix-generated-stack-boot/tasks.md +0 -27
- package/openspec/changes/optimize-runtime-docker-layers/.openspec.yaml +0 -3
- package/openspec/changes/optimize-runtime-docker-layers/design.md +0 -81
- package/openspec/changes/optimize-runtime-docker-layers/proposal.md +0 -44
- package/openspec/changes/optimize-runtime-docker-layers/specs/runtime-container-footprint/spec.md +0 -79
- package/openspec/changes/optimize-runtime-docker-layers/tasks.md +0 -36
- package/openspec/changes/prioritized-cold-start/.openspec.yaml +0 -4
- package/openspec/changes/prioritized-cold-start/design.md +0 -137
- package/openspec/changes/prioritized-cold-start/proposal.md +0 -41
- package/openspec/changes/prioritized-cold-start/specs/on-demand-resource-startup/spec.md +0 -98
- package/openspec/changes/prioritized-cold-start/tasks.md +0 -69
- package/openspec/config.yaml +0 -1
- package/openspec/specs/manifest-field-migration/spec.md +0 -24
- package/openspec/specs/resource-terminology-standard/spec.md +0 -24
- package/openspec/specs/self-contained-generated-projects/spec.md +0 -67
- package/openspec/specs/stack-terminology-standard/spec.md +0 -24
- package/platform/services/platform/proxy/service.yaml +0 -14
- package/scripts/audit-codebase.sh +0 -213
- package/scripts/autoresearch/layer-eval.sh +0 -65
- package/scripts/benchmark/README.md +0 -25
- package/scripts/benchmark/container-scale.ts +0 -503
- package/scripts/benchmark/pre-release-gate.sh +0 -9
- package/scripts/install-git-hooks.sh +0 -14
- package/scripts/measure-idle-footprint.sh +0 -58
- package/scripts/record-demo.sh +0 -43
- package/scripts/release-binaries.sh +0 -97
- package/scripts/setup-pre-commit.sh +0 -33
- package/scripts/stage-npm-engine.sh +0 -36
- package/tests/tilt-engine/__init__.py +0 -5
- package/tests/tilt-engine/conftest.py +0 -182
- package/tests/tilt-engine/fixtures/appointment-frontend-service.json +0 -11
- package/tests/tilt-engine/fixtures/identity-backend-service.json +0 -12
- package/tests/tilt-engine/fixtures/invalid-service.json +0 -4
- package/tests/tilt-engine/test_agents_md_generator.py +0 -341
- package/tests/tilt-engine/test_agents_md_integration.py +0 -378
- package/tests/tilt-engine/test_cache_operations.py +0 -181
- package/tests/tilt-engine/test_cache_persistence.py +0 -142
- package/tests/tilt-engine/test_code_executor.py +0 -152
- package/tests/tilt-engine/test_code_viewer.py +0 -127
- package/tests/tilt-engine/test_compose_port_anchors.py +0 -310
- package/tests/tilt-engine/test_config_inspector.py +0 -162
- package/tests/tilt-engine/test_daemon_focus_mode.py +0 -89
- package/tests/tilt-engine/test_daemon_polling.py +0 -122
- package/tests/tilt-engine/test_daemon_registration.py +0 -115
- package/tests/tilt-engine/test_daemon_validation.py +0 -107
- package/tests/tilt-engine/test_docker_generator_bugs.py +0 -493
- package/tests/tilt-engine/test_docker_prune.py +0 -119
- package/tests/tilt-engine/test_dockerignore_path_bug.py +0 -82
- package/tests/tilt-engine/test_file_browser.py +0 -193
- package/tests/tilt-engine/test_frontend_basepath_manifest.py +0 -27
- package/tests/tilt-engine/test_health_dependencies.py +0 -130
- package/tests/tilt-engine/test_health_endpoints.py +0 -118
- package/tests/tilt-engine/test_health_tilt_ui.py +0 -85
- package/tests/tilt-engine/test_runtime_container_footprint.py +0 -162
- package/tests/tilt-engine/test_sablier_defer_start.py +0 -237
- package/tests/tilt-engine/test_safety_aliases.py +0 -84
- package/tests/tilt-engine/test_snapshot_diff.py +0 -136
- package/tests/tilt-engine/test_snapshot_persistence.py +0 -163
- package/tests/tilt-engine/test_snapshot_scanning.py +0 -123
- package/tests/tilt-engine/test_standalone_project_boot.py +0 -61
- package/tests/tilt-engine/test_standalone_traefik.py +0 -160
- package/tests/tilt-engine/test_traefik_static_wake_route.py +0 -187
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Tests for config inspector component.
|
|
3
|
-
Tests the multi-pane comparison and editing features.
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
import os
|
|
7
|
-
import pytest
|
|
8
|
-
import hashlib
|
|
9
|
-
from pathlib import Path
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
@pytest.mark.ide
|
|
13
|
-
class TestConfigInspector:
|
|
14
|
-
"""Tests for config inspector functionality."""
|
|
15
|
-
|
|
16
|
-
def test_single_pane_default_view(self):
|
|
17
|
-
"""
|
|
18
|
-
Scenario: Single-pane default view
|
|
19
|
-
WHEN a user requests `GET /`
|
|
20
|
-
THEN it SHALL display a single pane with the first master config
|
|
21
|
-
"""
|
|
22
|
-
# Simulate default view
|
|
23
|
-
panes = [{"name": "TILT_RESOURCE_DEFAULTS.star", "content": "# Content"}]
|
|
24
|
-
|
|
25
|
-
assert len(panes) == 1, "Default view should have 1 pane"
|
|
26
|
-
assert "TILT_RESOURCE_DEFAULTS.star" in panes[0]["name"]
|
|
27
|
-
|
|
28
|
-
def test_dual_pane_comparison(self):
|
|
29
|
-
"""
|
|
30
|
-
Scenario: Dual-pane comparison
|
|
31
|
-
WHEN a user requests `GET /?master1=TILT_RESOURCE_DEFAULTS.star&master2=spec.master`
|
|
32
|
-
THEN it SHALL display two panes side-by-side
|
|
33
|
-
AND both configs SHALL be viewable simultaneously
|
|
34
|
-
"""
|
|
35
|
-
# Simulate dual-pane request
|
|
36
|
-
requested_masters = ["TILT_RESOURCE_DEFAULTS.star", "spec.master"]
|
|
37
|
-
|
|
38
|
-
panes = [
|
|
39
|
-
{"name": name, "content": f"# {name} content"}
|
|
40
|
-
for name in requested_masters
|
|
41
|
-
]
|
|
42
|
-
|
|
43
|
-
assert len(panes) == 2, "Dual-pane should have 2 panes"
|
|
44
|
-
assert all(p["name"] in requested_masters for p in panes)
|
|
45
|
-
|
|
46
|
-
def test_triple_pane_with_sync_scroll(self):
|
|
47
|
-
"""
|
|
48
|
-
Scenario: Triple-pane with sync scroll
|
|
49
|
-
WHEN a user requests `GET /?master1=A.star&master2=B.star&master3=C.star&sync=true`
|
|
50
|
-
THEN it SHALL display three panes
|
|
51
|
-
AND scrolling one pane SHALL sync scroll the others
|
|
52
|
-
"""
|
|
53
|
-
requested_masters = ["A.star", "B.star", "C.star"]
|
|
54
|
-
sync_enabled = True
|
|
55
|
-
|
|
56
|
-
panes = [
|
|
57
|
-
{"name": name, "content": f"# {name} content", "sync_scroll": sync_enabled}
|
|
58
|
-
for name in requested_masters
|
|
59
|
-
]
|
|
60
|
-
|
|
61
|
-
assert len(panes) == 3, "Triple-pane should have 3 panes"
|
|
62
|
-
assert all(p.get("sync_scroll") for p in panes), "All panes should have sync scroll enabled"
|
|
63
|
-
|
|
64
|
-
def test_edit_mode_toggle(self, temp_dir):
|
|
65
|
-
"""
|
|
66
|
-
Scenario: Edit mode toggle
|
|
67
|
-
WHEN a user clicks the "✏️ Edit Mode" button
|
|
68
|
-
THEN textareas SHALL become editable
|
|
69
|
-
AND save buttons SHALL appear for each pane
|
|
70
|
-
"""
|
|
71
|
-
# Simulate edit mode toggle
|
|
72
|
-
edit_mode = True
|
|
73
|
-
|
|
74
|
-
pane = {
|
|
75
|
-
"name": "test.star",
|
|
76
|
-
"content": "# Original",
|
|
77
|
-
"editable": edit_mode,
|
|
78
|
-
"dirty": False
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
assert pane["editable"], "Pane should be editable in edit mode"
|
|
82
|
-
|
|
83
|
-
# Simulate edit
|
|
84
|
-
pane["content"] = "# Modified"
|
|
85
|
-
pane["dirty"] = True
|
|
86
|
-
|
|
87
|
-
assert pane["dirty"], "Pane should be marked as dirty after edit"
|
|
88
|
-
|
|
89
|
-
def test_safe_file_saving_with_backups(self, temp_dir):
|
|
90
|
-
"""
|
|
91
|
-
Scenario: Safe file saving
|
|
92
|
-
WHEN a user saves changes in edit mode
|
|
93
|
-
THEN it SHALL create a backup in `.backups/` before writing
|
|
94
|
-
AND it SHALL use atomic write (temp file + rename)
|
|
95
|
-
"""
|
|
96
|
-
test_file = temp_dir / "test.star"
|
|
97
|
-
original_content = "# Original content"
|
|
98
|
-
test_file.write_text(original_content)
|
|
99
|
-
|
|
100
|
-
# Create backup directory
|
|
101
|
-
backup_dir = temp_dir / ".backups"
|
|
102
|
-
backup_dir.mkdir(exist_ok=True)
|
|
103
|
-
|
|
104
|
-
# Simulate safe save
|
|
105
|
-
new_content = "# Modified content"
|
|
106
|
-
self._safe_save_file(test_file, new_content, backup_dir)
|
|
107
|
-
|
|
108
|
-
# Verify backup created (backup files are named like "test.star.0")
|
|
109
|
-
backup_files = list(backup_dir.glob(f"{test_file.name}.*"))
|
|
110
|
-
assert len(backup_files) > 0, f"Backup should be created in {backup_dir}"
|
|
111
|
-
|
|
112
|
-
# Verify new content written
|
|
113
|
-
assert test_file.read_text() == new_content, "File should have new content"
|
|
114
|
-
|
|
115
|
-
# Verify backup has original content
|
|
116
|
-
backup_content = backup_files[0].read_text()
|
|
117
|
-
assert backup_content == original_content, "Backup should have original content"
|
|
118
|
-
|
|
119
|
-
def test_conflict_detection(self, temp_dir):
|
|
120
|
-
"""
|
|
121
|
-
Scenario: Conflict detection
|
|
122
|
-
WHEN a user attempts to save but the file was modified on disk since loading
|
|
123
|
-
THEN it SHALL return `409 Conflict` with current file hash
|
|
124
|
-
AND it SHALL ask user to refresh and retry
|
|
125
|
-
"""
|
|
126
|
-
test_file = temp_dir / "test.star"
|
|
127
|
-
original_content = "# Version 1"
|
|
128
|
-
test_file.write_text(original_content)
|
|
129
|
-
|
|
130
|
-
# Load file and compute hash
|
|
131
|
-
loaded_content = test_file.read_text()
|
|
132
|
-
loaded_hash = hashlib.sha256(loaded_content.encode()).hexdigest()
|
|
133
|
-
|
|
134
|
-
# Simulate external modification
|
|
135
|
-
test_file.write_text("# Version 2 (external change)")
|
|
136
|
-
|
|
137
|
-
# Try to save with old hash
|
|
138
|
-
current_content = test_file.read_text()
|
|
139
|
-
current_hash = hashlib.sha256(current_content.encode()).hexdigest()
|
|
140
|
-
|
|
141
|
-
# Hash mismatch = conflict
|
|
142
|
-
assert loaded_hash != current_hash, "Should detect conflict (hash mismatch)"
|
|
143
|
-
|
|
144
|
-
def _safe_save_file(self, path: Path, content: str, backup_dir: Path):
|
|
145
|
-
"""Simulate safe file save with backup."""
|
|
146
|
-
import tempfile
|
|
147
|
-
import shutil
|
|
148
|
-
|
|
149
|
-
# Create backup
|
|
150
|
-
if path.exists():
|
|
151
|
-
backup_path = backup_dir / f"{path.name}.{len(list(backup_dir.glob('*')))}"
|
|
152
|
-
shutil.copy2(path, backup_path)
|
|
153
|
-
|
|
154
|
-
# Atomic write using temp file
|
|
155
|
-
temp_fd, temp_path = tempfile.mkstemp(dir=path.parent)
|
|
156
|
-
try:
|
|
157
|
-
with os.fdopen(temp_fd, 'w') as f:
|
|
158
|
-
f.write(content)
|
|
159
|
-
os.replace(temp_path, path)
|
|
160
|
-
except Exception:
|
|
161
|
-
os.unlink(temp_path)
|
|
162
|
-
raise
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Tests for daemon focus mode compliance.
|
|
3
|
-
Tests that discovery respects focus mode stack restrictions.
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
import pytest
|
|
7
|
-
from pathlib import Path
|
|
8
|
-
import json
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
@pytest.mark.daemon
|
|
12
|
-
class TestDaemonFocusMode:
|
|
13
|
-
"""Tests for focus mode compliance."""
|
|
14
|
-
|
|
15
|
-
def test_focus_mode_restricts_discovery(self, temp_dir):
|
|
16
|
-
"""
|
|
17
|
-
Scenario: Focus mode restricts discovery
|
|
18
|
-
WHEN `--focus identity` is enabled and a new resource appears in `services/product/billing/`
|
|
19
|
-
THEN the daemon SHALL NOT trigger registration (queue it for later)
|
|
20
|
-
AND it SHALL log "⏳ Resource queued (focus mode)"
|
|
21
|
-
"""
|
|
22
|
-
# Simulate focus mode config
|
|
23
|
-
focus_config = {
|
|
24
|
-
"enabled": True,
|
|
25
|
-
"stacks": ["identity", "order"],
|
|
26
|
-
"excluded_paths": []
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
# Resource outside focus
|
|
30
|
-
resource_stack = "billing"
|
|
31
|
-
is_in_focus = resource_stack in focus_config["stacks"]
|
|
32
|
-
|
|
33
|
-
assert not is_in_focus, "billing should not be in focus mode"
|
|
34
|
-
|
|
35
|
-
# In real daemon, this would queue instead of registering
|
|
36
|
-
# For test, verify the filtering logic works
|
|
37
|
-
should_register = not focus_config["enabled"] or is_in_focus
|
|
38
|
-
assert not should_register, "Resource outside focus should not be registered immediately"
|
|
39
|
-
|
|
40
|
-
def test_focus_mode_allows_matching_resources(self, temp_dir):
|
|
41
|
-
"""
|
|
42
|
-
Scenario: Focus mode allows matching resources
|
|
43
|
-
WHEN `--focus identity` is enabled and a new resource appears in `services/product/identity/`
|
|
44
|
-
THEN the daemon SHALL trigger registration normally
|
|
45
|
-
"""
|
|
46
|
-
# Create resource in focus stack
|
|
47
|
-
focus_config = {
|
|
48
|
-
"enabled": True,
|
|
49
|
-
"stacks": ["identity", "order"],
|
|
50
|
-
"excluded_paths": []
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
resource_stack = "identity"
|
|
54
|
-
is_in_focus = resource_stack in focus_config["stacks"]
|
|
55
|
-
|
|
56
|
-
assert is_in_focus, "identity should be in focus mode"
|
|
57
|
-
|
|
58
|
-
# In focus - should register
|
|
59
|
-
should_register = not focus_config["enabled"] or is_in_focus
|
|
60
|
-
assert should_register, "Resource in focus should be registered"
|
|
61
|
-
|
|
62
|
-
def test_focus_mode_disabled_allows_all(self):
|
|
63
|
-
"""Test that when focus mode is disabled, all resources are registered."""
|
|
64
|
-
focus_config = {
|
|
65
|
-
"enabled": False,
|
|
66
|
-
"stacks": ["identity"],
|
|
67
|
-
"excluded_paths": []
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
resource_stack = "billing"
|
|
71
|
-
is_in_focus = resource_stack in focus_config["stacks"]
|
|
72
|
-
|
|
73
|
-
# Focus disabled - should register regardless
|
|
74
|
-
should_register = not focus_config["enabled"] or is_in_focus
|
|
75
|
-
assert should_register, "When focus mode disabled, all resources should register"
|
|
76
|
-
|
|
77
|
-
def test_focus_mode_stack_matching(self):
|
|
78
|
-
"""Test stack matching logic for focus mode."""
|
|
79
|
-
test_cases = [
|
|
80
|
-
({"enabled": True, "stacks": ["identity"]}, "identity", True),
|
|
81
|
-
({"enabled": True, "stacks": ["identity"]}, "billing", False),
|
|
82
|
-
({"enabled": True, "stacks": ["identity", "order"]}, "order", True),
|
|
83
|
-
({"enabled": False, "stacks": ["identity"]}, "billing", True),
|
|
84
|
-
]
|
|
85
|
-
|
|
86
|
-
for config, stack, expected in test_cases:
|
|
87
|
-
is_in_focus = stack in config["stacks"]
|
|
88
|
-
should_register = not config["enabled"] or is_in_focus
|
|
89
|
-
assert should_register == expected, f"Failed for {stack} with config {config}"
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Tests for discovery daemon polling behavior.
|
|
3
|
-
Tests the continuous monitoring and polling intervals.
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
import asyncio
|
|
7
|
-
import pytest
|
|
8
|
-
from pathlib import Path
|
|
9
|
-
from unittest.mock import Mock, patch, MagicMock
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
@pytest.mark.daemon
|
|
13
|
-
class TestDaemonPolling:
|
|
14
|
-
"""Tests for daemon polling functionality."""
|
|
15
|
-
|
|
16
|
-
@pytest.mark.asyncio
|
|
17
|
-
async def test_daemon_polls_at_configured_interval(self):
|
|
18
|
-
"""
|
|
19
|
-
Scenario: Daemon polls at configured interval
|
|
20
|
-
WHEN the daemon starts with `DISCOVERY_SCAN_INTERVAL=2`
|
|
21
|
-
THEN it SHALL call the scanner function every 2 seconds
|
|
22
|
-
"""
|
|
23
|
-
mock_scanner = Mock(return_value=[])
|
|
24
|
-
|
|
25
|
-
# Mock the daemon loop to run only 3 iterations
|
|
26
|
-
call_count = 0
|
|
27
|
-
async def mock_daemon_loop():
|
|
28
|
-
nonlocal call_count
|
|
29
|
-
for _ in range(3):
|
|
30
|
-
mock_scanner()
|
|
31
|
-
call_count += 1
|
|
32
|
-
await asyncio.sleep(0.1) # Fast sleep for testing
|
|
33
|
-
|
|
34
|
-
# Run the mock daemon
|
|
35
|
-
await mock_daemon_loop()
|
|
36
|
-
|
|
37
|
-
assert call_count == 3, f"Expected 3 polling calls, got {call_count}"
|
|
38
|
-
assert mock_scanner.call_count == 3
|
|
39
|
-
|
|
40
|
-
@pytest.mark.asyncio
|
|
41
|
-
async def test_daemon_stops_cleanly_on_signal(self):
|
|
42
|
-
"""
|
|
43
|
-
Scenario: Daemon can be stopped
|
|
44
|
-
WHEN the daemon receives a stop signal
|
|
45
|
-
THEN it SHALL cease polling and exit cleanly within 1 second
|
|
46
|
-
"""
|
|
47
|
-
stop_event = asyncio.Event()
|
|
48
|
-
poll_count = [0] # Use list to allow mutation in nested function
|
|
49
|
-
|
|
50
|
-
async def polling_loop():
|
|
51
|
-
while not stop_event.is_set():
|
|
52
|
-
poll_count[0] += 1
|
|
53
|
-
try:
|
|
54
|
-
await asyncio.wait_for(stop_event.wait(), timeout=0.1)
|
|
55
|
-
except asyncio.TimeoutError:
|
|
56
|
-
pass
|
|
57
|
-
|
|
58
|
-
# Start polling
|
|
59
|
-
task = asyncio.create_task(polling_loop())
|
|
60
|
-
|
|
61
|
-
# Let it poll a few times
|
|
62
|
-
await asyncio.sleep(0.25)
|
|
63
|
-
|
|
64
|
-
# Signal stop
|
|
65
|
-
stop_event.set()
|
|
66
|
-
|
|
67
|
-
# Wait for clean exit with timeout
|
|
68
|
-
try:
|
|
69
|
-
await asyncio.wait_for(task, timeout=1.0)
|
|
70
|
-
except asyncio.TimeoutError:
|
|
71
|
-
task.cancel()
|
|
72
|
-
|
|
73
|
-
assert poll_count[0] >= 2, "Should have polled at least twice"
|
|
74
|
-
assert task.done(), "Task should be completed"
|
|
75
|
-
|
|
76
|
-
def test_polling_respects_interval_setting(self):
|
|
77
|
-
"""Test that the daemon respects DISCOVERY_SCAN_INTERVAL."""
|
|
78
|
-
import os
|
|
79
|
-
|
|
80
|
-
# Default should be 5 seconds
|
|
81
|
-
interval = int(os.environ.get("DISCOVERY_SCAN_INTERVAL", "5"))
|
|
82
|
-
|
|
83
|
-
assert interval > 0, "Interval should be positive"
|
|
84
|
-
assert interval <= 60, "Interval should be reasonable (<= 60s)"
|
|
85
|
-
|
|
86
|
-
@pytest.mark.asyncio
|
|
87
|
-
async def test_polling_detects_changes_over_time(self, temp_dir):
|
|
88
|
-
"""Test that daemon detects changes across multiple polling cycles."""
|
|
89
|
-
from pathlib import Path
|
|
90
|
-
import json
|
|
91
|
-
|
|
92
|
-
# Create initial service
|
|
93
|
-
services_dir = temp_dir / "services" / "product"
|
|
94
|
-
services_dir.mkdir(parents=True)
|
|
95
|
-
|
|
96
|
-
resource_dir = services_dir / "initial" / "service"
|
|
97
|
-
resource_dir.mkdir(parents=True)
|
|
98
|
-
(resource_dir / "service.json").write_text(json.dumps({"name": "initial"}))
|
|
99
|
-
|
|
100
|
-
poll_results = []
|
|
101
|
-
|
|
102
|
-
async def mock_scan_cycle():
|
|
103
|
-
# Simulate scanning
|
|
104
|
-
import sys
|
|
105
|
-
sys.path.insert(0, str(Path("discovery").resolve()))
|
|
106
|
-
from resource_snapshot import get_current_services
|
|
107
|
-
|
|
108
|
-
services = get_current_services(str(services_dir))
|
|
109
|
-
poll_results.append(len(services))
|
|
110
|
-
|
|
111
|
-
# First poll - should find 1 service
|
|
112
|
-
await mock_scan_cycle()
|
|
113
|
-
assert poll_results[-1] == 1
|
|
114
|
-
|
|
115
|
-
# Add new service
|
|
116
|
-
new_dir = services_dir / "new" / "service"
|
|
117
|
-
new_dir.mkdir(parents=True)
|
|
118
|
-
(new_dir / "service.json").write_text(json.dumps({"name": "new"}))
|
|
119
|
-
|
|
120
|
-
# Second poll - should find 2 services
|
|
121
|
-
await mock_scan_cycle()
|
|
122
|
-
assert poll_results[-1] == 2
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Tests for daemon auto-registration functionality.
|
|
3
|
-
Tests the triggering of Tilt resource creation for new services.
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
import pytest
|
|
7
|
-
from pathlib import Path
|
|
8
|
-
from unittest.mock import Mock, patch, call
|
|
9
|
-
import json
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
@pytest.mark.daemon
|
|
13
|
-
class TestDaemonRegistration:
|
|
14
|
-
"""Tests for daemon auto-registration."""
|
|
15
|
-
|
|
16
|
-
def test_new_resource_triggers_registration(self, temp_dir):
|
|
17
|
-
"""
|
|
18
|
-
Scenario: New service triggers registration
|
|
19
|
-
WHEN the daemon detects a new service.json file not in the cache
|
|
20
|
-
THEN it SHALL emit a registration event with the service path and metadata
|
|
21
|
-
AND the Tilt resource for that service SHALL be created
|
|
22
|
-
"""
|
|
23
|
-
from pathlib import Path
|
|
24
|
-
import sys
|
|
25
|
-
sys.path.insert(0, str(Path("discovery").resolve()))
|
|
26
|
-
from resource_snapshot import diff_snapshots, save_snapshot, load_snapshot
|
|
27
|
-
|
|
28
|
-
original_dir = Path.cwd()
|
|
29
|
-
|
|
30
|
-
try:
|
|
31
|
-
# Set up initial snapshot
|
|
32
|
-
services_dir = temp_dir / "services" / "product"
|
|
33
|
-
initial_service = services_dir / "existing" / "service"
|
|
34
|
-
initial_service.mkdir(parents=True)
|
|
35
|
-
(initial_service / "service.json").write_text(json.dumps({"name": "existing"}))
|
|
36
|
-
|
|
37
|
-
import os
|
|
38
|
-
os.chdir(temp_dir)
|
|
39
|
-
|
|
40
|
-
# Save initial snapshot
|
|
41
|
-
from resource_snapshot import get_current_services
|
|
42
|
-
initial_services = get_current_services("services/product")
|
|
43
|
-
save_snapshot(initial_services)
|
|
44
|
-
|
|
45
|
-
# Simulate adding new service
|
|
46
|
-
new_service = services_dir / "new" / "service"
|
|
47
|
-
new_service.mkdir(parents=True)
|
|
48
|
-
(new_service / "service.json").write_text(json.dumps({"name": "new"}))
|
|
49
|
-
|
|
50
|
-
# Get current services and diff
|
|
51
|
-
current_services = get_current_services("services/product")
|
|
52
|
-
old_snapshot = load_snapshot()
|
|
53
|
-
|
|
54
|
-
added, removed, modified = diff_snapshots(old_snapshot, current_services)
|
|
55
|
-
|
|
56
|
-
# Verify new service was detected
|
|
57
|
-
assert len(added) == 1, f"Expected 1 new service, got {len(added)}"
|
|
58
|
-
assert any("new" in s for s in added), "Should detect the new service"
|
|
59
|
-
finally:
|
|
60
|
-
os.chdir(original_dir)
|
|
61
|
-
|
|
62
|
-
def test_unchanged_services_do_not_trigger(self, temp_dir):
|
|
63
|
-
"""
|
|
64
|
-
Scenario: No registration for unchanged services
|
|
65
|
-
WHEN the daemon scans and finds no changes from previous snapshot
|
|
66
|
-
THEN it SHALL NOT emit any registration events
|
|
67
|
-
"""
|
|
68
|
-
from pathlib import Path
|
|
69
|
-
import sys
|
|
70
|
-
import os
|
|
71
|
-
sys.path.insert(0, str(Path("discovery").resolve()))
|
|
72
|
-
from resource_snapshot import save_snapshot, load_snapshot, diff_snapshots, get_current_services
|
|
73
|
-
|
|
74
|
-
original_dir = Path.cwd()
|
|
75
|
-
|
|
76
|
-
try:
|
|
77
|
-
# Set up services
|
|
78
|
-
services_dir = temp_dir / "services" / "product"
|
|
79
|
-
resource_dir = services_dir / "stable" / "service"
|
|
80
|
-
resource_dir.mkdir(parents=True)
|
|
81
|
-
(resource_dir / "service.json").write_text(json.dumps({"name": "stable"}))
|
|
82
|
-
|
|
83
|
-
os.chdir(temp_dir)
|
|
84
|
-
|
|
85
|
-
# Save snapshot
|
|
86
|
-
services = get_current_services("services/product")
|
|
87
|
-
save_snapshot(services)
|
|
88
|
-
|
|
89
|
-
# Scan again (no changes)
|
|
90
|
-
current_services = get_current_services("services/product")
|
|
91
|
-
old_snapshot = load_snapshot()
|
|
92
|
-
|
|
93
|
-
added, removed, modified = diff_snapshots(old_snapshot, current_services)
|
|
94
|
-
|
|
95
|
-
# Verify no changes detected
|
|
96
|
-
assert len(added) == 0, "Should have no added services"
|
|
97
|
-
assert len(removed) == 0, "Should have no removed services"
|
|
98
|
-
assert len(modified) == 0, "Should have no modified services"
|
|
99
|
-
finally:
|
|
100
|
-
os.chdir(original_dir)
|
|
101
|
-
|
|
102
|
-
def test_registration_includes_resource_metadata(self):
|
|
103
|
-
"""Test that registration events include full service metadata."""
|
|
104
|
-
mock_registration_event = {
|
|
105
|
-
"path": "services/product/test/service.json",
|
|
106
|
-
"name": "test-service",
|
|
107
|
-
"type": "backend",
|
|
108
|
-
"stack": "test",
|
|
109
|
-
"port": 4000
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
assert "path" in mock_registration_event
|
|
113
|
-
assert "name" in mock_registration_event
|
|
114
|
-
assert "type" in mock_registration_event
|
|
115
|
-
assert "port" in mock_registration_event
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Tests for daemon service validation.
|
|
3
|
-
Tests that invalid service.json files are rejected before registration.
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
import pytest
|
|
7
|
-
import json
|
|
8
|
-
from pathlib import Path
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
@pytest.mark.daemon
|
|
12
|
-
class TestDaemonValidation:
|
|
13
|
-
"""Tests for service validation."""
|
|
14
|
-
|
|
15
|
-
def test_invalid_resource_json_rejected(self):
|
|
16
|
-
"""
|
|
17
|
-
Scenario: Invalid service.json rejected
|
|
18
|
-
WHEN a new service.json file has missing required fields (e.g., no `name`)
|
|
19
|
-
THEN the daemon SHALL log "❌ Invalid service.json at {path}"
|
|
20
|
-
AND it SHALL NOT attempt registration
|
|
21
|
-
"""
|
|
22
|
-
# Invalid service - missing required fields
|
|
23
|
-
invalid_service = {
|
|
24
|
-
"type": "backend",
|
|
25
|
-
"port": 4000
|
|
26
|
-
# Missing: name, domain, path
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
# Validate required fields
|
|
30
|
-
required_fields = ["name", "type", "domain", "path"]
|
|
31
|
-
missing_fields = [f for f in required_fields if f not in invalid_service]
|
|
32
|
-
|
|
33
|
-
assert len(missing_fields) > 0, "Should detect missing fields"
|
|
34
|
-
assert "name" in missing_fields, "Should detect missing 'name' field"
|
|
35
|
-
|
|
36
|
-
# In real daemon, this would be rejected
|
|
37
|
-
is_valid = len(missing_fields) == 0
|
|
38
|
-
assert not is_valid, "Invalid service should be rejected"
|
|
39
|
-
|
|
40
|
-
def test_valid_resource_json_accepted(self):
|
|
41
|
-
"""
|
|
42
|
-
Scenario: Valid service.json accepted
|
|
43
|
-
WHEN a new service.json file has all required fields (`name`, `type`, `domain`, `port`)
|
|
44
|
-
THEN the daemon SHALL proceed with registration
|
|
45
|
-
"""
|
|
46
|
-
# Valid service with all required fields
|
|
47
|
-
valid_service = {
|
|
48
|
-
"name": "test-service",
|
|
49
|
-
"type": "backend",
|
|
50
|
-
"stack": "test",
|
|
51
|
-
"port": 4000,
|
|
52
|
-
"path": "services/product/test/test-service"
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
required_fields = ["appName", "appType", "stack", "path"]
|
|
56
|
-
missing_fields = [f for f in required_fields if f not in valid_service]
|
|
57
|
-
|
|
58
|
-
is_valid = len(missing_fields) == 0
|
|
59
|
-
assert is_valid, "Valid service should be accepted"
|
|
60
|
-
|
|
61
|
-
def test_validation_detects_port_range(self):
|
|
62
|
-
"""Test that port is validated to be in reasonable range."""
|
|
63
|
-
test_cases = [
|
|
64
|
-
({"appName": "test", "appType": "backend", "stack": "test", "path": "p", "port": 3000}, True),
|
|
65
|
-
({"appName": "test", "appType": "backend", "stack": "test", "path": "p", "port": 9999}, True),
|
|
66
|
-
({"appName": "test", "appType": "backend", "stack": "test", "path": "p", "port": 80}, False), # Too low
|
|
67
|
-
({"appName": "test", "appType": "backend", "stack": "test", "path": "p", "port": 100000}, False), # Too high
|
|
68
|
-
]
|
|
69
|
-
|
|
70
|
-
for service, expected_valid in test_cases:
|
|
71
|
-
port = service.get("port", 0)
|
|
72
|
-
is_valid = 3000 <= port <= 9999
|
|
73
|
-
assert is_valid == expected_valid, f"Port {port} validation failed"
|
|
74
|
-
|
|
75
|
-
def test_validation_detects_invalid_type(self):
|
|
76
|
-
"""Test that appType is validated against allowed values."""
|
|
77
|
-
valid_types = ["backend", "frontend", "library", "migrator", "sdk", "worker"]
|
|
78
|
-
|
|
79
|
-
test_cases = [
|
|
80
|
-
("backend", True),
|
|
81
|
-
("frontend", True),
|
|
82
|
-
("microservice", False), # Invalid type
|
|
83
|
-
("api", False), # Invalid type
|
|
84
|
-
]
|
|
85
|
-
|
|
86
|
-
for app_type, expected_valid in test_cases:
|
|
87
|
-
is_valid = app_type in valid_types
|
|
88
|
-
assert is_valid == expected_valid, f"Type '{app_type}' validation failed"
|
|
89
|
-
|
|
90
|
-
def test_validation_allows_optional_fields(self):
|
|
91
|
-
"""Test that optional fields don't cause validation failure."""
|
|
92
|
-
resource_with_optional = {
|
|
93
|
-
"name": "test-service",
|
|
94
|
-
"type": "backend",
|
|
95
|
-
"stack": "test",
|
|
96
|
-
"port": 4000,
|
|
97
|
-
"path": "services/product/test/test-service",
|
|
98
|
-
# Optional fields
|
|
99
|
-
"features": ["prisma", "nats"],
|
|
100
|
-
"replicas": 2,
|
|
101
|
-
"healthCheck": True
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
required_fields = ["name", "type", "domain", "path"]
|
|
105
|
-
has_all_required = all(f in resource_with_optional for f in required_fields)
|
|
106
|
-
|
|
107
|
-
assert has_all_required, "Service with optional fields should still be valid"
|