@trieungoctam/vibekit 1.0.0
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/LICENSE +21 -0
- package/README.md +85 -0
- package/agents/debugger.md +158 -0
- package/agents/docs-manager.md +220 -0
- package/agents/planner.md +129 -0
- package/agents/researcher.md +58 -0
- package/agents/reviewer.md +152 -0
- package/agents/tester.md +126 -0
- package/bin/vibekit.js +18 -0
- package/hooks/lib/ck-config-utils.cjs +831 -0
- package/hooks/lib/colors.cjs +95 -0
- package/hooks/lib/config-counter.cjs +103 -0
- package/hooks/lib/context-builder.cjs +616 -0
- package/hooks/lib/git-info-cache.cjs +143 -0
- package/hooks/lib/hook-logger.cjs +92 -0
- package/hooks/lib/privacy-checker.cjs +297 -0
- package/hooks/lib/project-detector.cjs +474 -0
- package/hooks/lib/scout-checker.cjs +263 -0
- package/hooks/lib/transcript-parser.cjs +181 -0
- package/hooks/post-edit-simplify-reminder.cjs +156 -0
- package/hooks/privacy-block.cjs +166 -0
- package/hooks/scout-block.cjs +147 -0
- package/hooks/session-init.cjs +360 -0
- package/package.json +41 -0
- package/rules/development-rules.md +52 -0
- package/rules/documentation-management.md +121 -0
- package/rules/orchestration-protocol.md +43 -0
- package/rules/primary-workflow.md +57 -0
- package/rules/team-coordination-rules.md +90 -0
- package/skills/ai/agent-browser/SKILL.md +294 -0
- package/skills/ai/agent-browser/references/.gitkeep +0 -0
- package/skills/ai/agent-browser/references/agent-browser-vs-chrome-devtools.md +112 -0
- package/skills/ai/agent-browser/references/browserbase-cloud-setup.md +161 -0
- package/skills/ai/ai-artist/SKILL.md +122 -0
- package/skills/ai/ai-artist/data/awesome-prompts.csv +3592 -0
- package/skills/ai/ai-artist/data/lighting.csv +19 -0
- package/skills/ai/ai-artist/data/nano-banana-templates.csv +17 -0
- package/skills/ai/ai-artist/data/platforms.csv +11 -0
- package/skills/ai/ai-artist/data/styles.csv +26 -0
- package/skills/ai/ai-artist/data/techniques.csv +19 -0
- package/skills/ai/ai-artist/data/use-cases.csv +16 -0
- package/skills/ai/ai-artist/references/advanced-techniques.md +184 -0
- package/skills/ai/ai-artist/references/awesome-nano-banana-pro-prompts.md +8575 -0
- package/skills/ai/ai-artist/references/domain-code.md +66 -0
- package/skills/ai/ai-artist/references/domain-data.md +72 -0
- package/skills/ai/ai-artist/references/domain-marketing.md +66 -0
- package/skills/ai/ai-artist/references/domain-patterns.md +33 -0
- package/skills/ai/ai-artist/references/domain-writing.md +68 -0
- package/skills/ai/ai-artist/references/image-prompting.md +141 -0
- package/skills/ai/ai-artist/references/llm-prompting.md +165 -0
- package/skills/ai/ai-artist/references/nano-banana.md +136 -0
- package/skills/ai/ai-artist/references/reasoning-techniques.md +201 -0
- package/skills/ai/ai-artist/references/validation-workflow.md +117 -0
- package/skills/ai/ai-artist/scripts/core.py +197 -0
- package/skills/ai/ai-artist/scripts/extract_prompts.py +102 -0
- package/skills/ai/ai-artist/scripts/generate.py +370 -0
- package/skills/ai/ai-artist/scripts/search.py +147 -0
- package/skills/ai/ai-multimodal/.env.example +204 -0
- package/skills/ai/ai-multimodal/SKILL.md +110 -0
- package/skills/ai/ai-multimodal/references/audio-processing.md +387 -0
- package/skills/ai/ai-multimodal/references/image-generation.md +939 -0
- package/skills/ai/ai-multimodal/references/music-generation.md +311 -0
- package/skills/ai/ai-multimodal/references/video-analysis.md +515 -0
- package/skills/ai/ai-multimodal/references/video-generation.md +457 -0
- package/skills/ai/ai-multimodal/references/vision-understanding.md +492 -0
- package/skills/ai/ai-multimodal/scripts/.coverage +0 -0
- package/skills/ai/ai-multimodal/scripts/check_setup.py +315 -0
- package/skills/ai/ai-multimodal/scripts/document_converter.py +395 -0
- package/skills/ai/ai-multimodal/scripts/gemini_batch_process.py +1185 -0
- package/skills/ai/ai-multimodal/scripts/media_optimizer.py +506 -0
- package/skills/ai/ai-multimodal/scripts/requirements.txt +26 -0
- package/skills/ai/ai-multimodal/scripts/tests/.coverage +0 -0
- package/skills/ai/ai-multimodal/scripts/tests/requirements.txt +20 -0
- package/skills/ai/ai-multimodal/scripts/tests/test_document_converter.py +74 -0
- package/skills/ai/ai-multimodal/scripts/tests/test_gemini_batch_process.py +362 -0
- package/skills/ai/ai-multimodal/scripts/tests/test_media_optimizer.py +373 -0
- package/skills/ai/mcp-management/README.md +219 -0
- package/skills/ai/mcp-management/SKILL.md +210 -0
- package/skills/ai/mcp-management/assets/tools.json +3146 -0
- package/skills/ai/mcp-management/references/configuration.md +114 -0
- package/skills/ai/mcp-management/references/gemini-cli-integration.md +221 -0
- package/skills/ai/mcp-management/references/mcp-protocol.md +116 -0
- package/skills/ai/mcp-management/scripts/.env.example +10 -0
- package/skills/ai/mcp-management/scripts/cli.ts +195 -0
- package/skills/ai/mcp-management/scripts/dist/analyze-tools.js +70 -0
- package/skills/ai/mcp-management/scripts/dist/cli.js +160 -0
- package/skills/ai/mcp-management/scripts/dist/mcp-client.js +183 -0
- package/skills/ai/mcp-management/scripts/mcp-client.ts +230 -0
- package/skills/ai/mcp-management/scripts/package.json +20 -0
- package/skills/ai/mcp-management/scripts/tsconfig.json +15 -0
- package/skills/core/brainstorm/SKILL.md +164 -0
- package/skills/core/brainstorm/scripts/frame-template.html +214 -0
- package/skills/core/brainstorm/scripts/helper.js +88 -0
- package/skills/core/brainstorm/scripts/server.cjs +338 -0
- package/skills/core/brainstorm/scripts/start-server.sh +153 -0
- package/skills/core/brainstorm/scripts/stop-server.sh +55 -0
- package/skills/core/brainstorm/spec-document-reviewer-prompt.md +49 -0
- package/skills/core/brainstorm/visual-companion.md +286 -0
- package/skills/core/code-review/SKILL.md +147 -0
- package/skills/core/code-review/references/code-review-reception.md +113 -0
- package/skills/core/code-review/references/codebase-scan-workflow.md +29 -0
- package/skills/core/code-review/references/edge-case-scouting.md +119 -0
- package/skills/core/code-review/references/parallel-review-workflow.md +69 -0
- package/skills/core/code-review/references/requesting-code-review.md +116 -0
- package/skills/core/code-review/references/task-management-reviews.md +140 -0
- package/skills/core/code-review/references/verification-before-completion.md +139 -0
- package/skills/core/cook/README.md +86 -0
- package/skills/core/cook/SKILL.md +113 -0
- package/skills/core/cook/references/intent-detection.md +101 -0
- package/skills/core/cook/references/review-cycle.md +75 -0
- package/skills/core/cook/references/subagent-patterns.md +75 -0
- package/skills/core/cook/references/workflow-steps.md +172 -0
- package/skills/core/debug/SKILL.md +121 -0
- package/skills/core/debug/references/defense-in-depth.md +124 -0
- package/skills/core/debug/references/frontend-verification.md +103 -0
- package/skills/core/debug/references/investigation-methodology.md +101 -0
- package/skills/core/debug/references/log-and-ci-analysis.md +97 -0
- package/skills/core/debug/references/performance-diagnostics.md +113 -0
- package/skills/core/debug/references/reporting-standards.md +122 -0
- package/skills/core/debug/references/root-cause-tracing.md +122 -0
- package/skills/core/debug/references/systematic-debugging.md +102 -0
- package/skills/core/debug/references/task-management-debugging.md +155 -0
- package/skills/core/debug/references/verification.md +123 -0
- package/skills/core/debug/scripts/find-polluter.sh +63 -0
- package/skills/core/debug/scripts/find-polluter.test.md +102 -0
- package/skills/core/execute/SKILL.md +70 -0
- package/skills/core/fix/SKILL.md +111 -0
- package/skills/core/fix/references/complexity-assessment.md +72 -0
- package/skills/core/fix/references/mode-selection.md +46 -0
- package/skills/core/fix/references/parallel-exploration.md +100 -0
- package/skills/core/fix/references/review-cycle.md +77 -0
- package/skills/core/fix/references/skill-activation-matrix.md +78 -0
- package/skills/core/fix/references/task-orchestration.md +103 -0
- package/skills/core/fix/references/workflow-ci.md +28 -0
- package/skills/core/fix/references/workflow-deep.md +122 -0
- package/skills/core/fix/references/workflow-logs.md +72 -0
- package/skills/core/fix/references/workflow-quick.md +59 -0
- package/skills/core/fix/references/workflow-standard.md +111 -0
- package/skills/core/fix/references/workflow-test.md +75 -0
- package/skills/core/fix/references/workflow-types.md +33 -0
- package/skills/core/fix/references/workflow-ui.md +75 -0
- package/skills/core/plan/SKILL.md +145 -0
- package/skills/core/plan/plan-document-reviewer-prompt.md +49 -0
- package/skills/core/subagent-dev/SKILL.md +277 -0
- package/skills/core/subagent-dev/code-quality-reviewer-prompt.md +26 -0
- package/skills/core/subagent-dev/implementer-prompt.md +113 -0
- package/skills/core/subagent-dev/spec-reviewer-prompt.md +61 -0
- package/skills/core/tdd/SKILL.md +371 -0
- package/skills/core/tdd/testing-anti-patterns.md +299 -0
- package/skills/core/test/SKILL.md +109 -0
- package/skills/core/test/references/report-format.md +58 -0
- package/skills/core/test/references/test-execution-workflow.md +103 -0
- package/skills/core/test/references/ui-testing-workflow.md +65 -0
- package/skills/core/verify/SKILL.md +139 -0
- package/skills/dev/backend-dev/SKILL.md +96 -0
- package/skills/dev/backend-dev/references/backend-api-design.md +495 -0
- package/skills/dev/backend-dev/references/backend-architecture.md +454 -0
- package/skills/dev/backend-dev/references/backend-authentication.md +338 -0
- package/skills/dev/backend-dev/references/backend-code-quality.md +659 -0
- package/skills/dev/backend-dev/references/backend-debugging.md +904 -0
- package/skills/dev/backend-dev/references/backend-devops.md +494 -0
- package/skills/dev/backend-dev/references/backend-mindset.md +387 -0
- package/skills/dev/backend-dev/references/backend-performance.md +397 -0
- package/skills/dev/backend-dev/references/backend-security.md +290 -0
- package/skills/dev/backend-dev/references/backend-technologies.md +256 -0
- package/skills/dev/backend-dev/references/backend-testing.md +429 -0
- package/skills/dev/context-engineering/SKILL.md +108 -0
- package/skills/dev/context-engineering/references/context-compression.md +84 -0
- package/skills/dev/context-engineering/references/context-degradation.md +93 -0
- package/skills/dev/context-engineering/references/context-fundamentals.md +75 -0
- package/skills/dev/context-engineering/references/context-optimization.md +82 -0
- package/skills/dev/context-engineering/references/evaluation.md +89 -0
- package/skills/dev/context-engineering/references/memory-systems.md +88 -0
- package/skills/dev/context-engineering/references/multi-agent-patterns.md +90 -0
- package/skills/dev/context-engineering/references/project-development.md +97 -0
- package/skills/dev/context-engineering/references/runtime-awareness.md +202 -0
- package/skills/dev/context-engineering/references/tool-design.md +86 -0
- package/skills/dev/context-engineering/scripts/compression_evaluator.py +349 -0
- package/skills/dev/context-engineering/scripts/context_analyzer.py +317 -0
- package/skills/dev/context-engineering/scripts/tests/test_edge_cases.py +246 -0
- package/skills/dev/databases/SKILL.md +84 -0
- package/skills/dev/databases/analytics.md +198 -0
- package/skills/dev/databases/db-design.md +188 -0
- package/skills/dev/databases/incremental-etl.md +213 -0
- package/skills/dev/databases/references/mongodb-aggregation.md +447 -0
- package/skills/dev/databases/references/mongodb-atlas.md +465 -0
- package/skills/dev/databases/references/mongodb-crud.md +408 -0
- package/skills/dev/databases/references/mongodb-indexing.md +442 -0
- package/skills/dev/databases/references/postgresql-administration.md +594 -0
- package/skills/dev/databases/references/postgresql-performance.md +527 -0
- package/skills/dev/databases/references/postgresql-psql-cli.md +467 -0
- package/skills/dev/databases/references/postgresql-queries.md +475 -0
- package/skills/dev/databases/scripts/.coverage +0 -0
- package/skills/dev/databases/scripts/db_backup.py +502 -0
- package/skills/dev/databases/scripts/db_migrate.py +426 -0
- package/skills/dev/databases/scripts/db_performance_check.py +457 -0
- package/skills/dev/databases/scripts/requirements.txt +20 -0
- package/skills/dev/databases/scripts/tests/coverage-db.json +1 -0
- package/skills/dev/databases/scripts/tests/requirements.txt +4 -0
- package/skills/dev/databases/scripts/tests/test_db_backup.py +340 -0
- package/skills/dev/databases/scripts/tests/test_db_migrate.py +277 -0
- package/skills/dev/databases/scripts/tests/test_db_performance_check.py +370 -0
- package/skills/dev/databases/stacks/bigquery.md +231 -0
- package/skills/dev/databases/stacks/d1_cloudflare.md +137 -0
- package/skills/dev/databases/stacks/mysql.md +216 -0
- package/skills/dev/databases/stacks/postgres.md +235 -0
- package/skills/dev/databases/stacks/sqlite.md +244 -0
- package/skills/dev/databases/transactional.md +176 -0
- package/skills/dev/devops/.env.example +76 -0
- package/skills/dev/devops/SKILL.md +97 -0
- package/skills/dev/devops/references/browser-rendering.md +305 -0
- package/skills/dev/devops/references/cloudflare-d1-kv.md +123 -0
- package/skills/dev/devops/references/cloudflare-platform.md +271 -0
- package/skills/dev/devops/references/cloudflare-r2-storage.md +280 -0
- package/skills/dev/devops/references/cloudflare-workers-advanced.md +312 -0
- package/skills/dev/devops/references/cloudflare-workers-apis.md +309 -0
- package/skills/dev/devops/references/cloudflare-workers-basics.md +418 -0
- package/skills/dev/devops/references/docker-basics.md +297 -0
- package/skills/dev/devops/references/docker-compose.md +292 -0
- package/skills/dev/devops/references/gcloud-platform.md +297 -0
- package/skills/dev/devops/references/gcloud-services.md +304 -0
- package/skills/dev/devops/references/kubernetes-basics.md +99 -0
- package/skills/dev/devops/references/kubernetes-helm-advanced.md +75 -0
- package/skills/dev/devops/references/kubernetes-helm.md +81 -0
- package/skills/dev/devops/references/kubernetes-kubectl.md +74 -0
- package/skills/dev/devops/references/kubernetes-security-advanced.md +98 -0
- package/skills/dev/devops/references/kubernetes-security.md +95 -0
- package/skills/dev/devops/references/kubernetes-troubleshooting-advanced.md +74 -0
- package/skills/dev/devops/references/kubernetes-troubleshooting.md +49 -0
- package/skills/dev/devops/references/kubernetes-workflows-advanced.md +75 -0
- package/skills/dev/devops/references/kubernetes-workflows.md +78 -0
- package/skills/dev/devops/scripts/cloudflare_deploy.py +269 -0
- package/skills/dev/devops/scripts/docker_optimize.py +332 -0
- package/skills/dev/devops/scripts/requirements.txt +20 -0
- package/skills/dev/devops/scripts/tests/requirements.txt +3 -0
- package/skills/dev/devops/scripts/tests/test_cloudflare_deploy.py +285 -0
- package/skills/dev/devops/scripts/tests/test_docker_optimize.py +436 -0
- package/skills/dev/frontend-design/SKILL.md +78 -0
- package/skills/dev/frontend-design/references/ai-multimodal-overview.md +165 -0
- package/skills/dev/frontend-design/references/analysis-best-practices.md +80 -0
- package/skills/dev/frontend-design/references/analysis-prompts.md +141 -0
- package/skills/dev/frontend-design/references/analysis-techniques.md +118 -0
- package/skills/dev/frontend-design/references/animejs.md +396 -0
- package/skills/dev/frontend-design/references/asset-generation.md +337 -0
- package/skills/dev/frontend-design/references/design-extraction-overview.md +71 -0
- package/skills/dev/frontend-design/references/extraction-best-practices.md +141 -0
- package/skills/dev/frontend-design/references/extraction-output-templates.md +162 -0
- package/skills/dev/frontend-design/references/extraction-prompts.md +127 -0
- package/skills/dev/frontend-design/references/technical-accessibility.md +119 -0
- package/skills/dev/frontend-design/references/technical-best-practices.md +97 -0
- package/skills/dev/frontend-design/references/technical-optimization.md +44 -0
- package/skills/dev/frontend-design/references/technical-overview.md +90 -0
- package/skills/dev/frontend-design/references/technical-workflows.md +150 -0
- package/skills/dev/frontend-design/references/visual-analysis-overview.md +95 -0
- package/skills/dev/frontend-design/references/workflow-3d.md +102 -0
- package/skills/dev/frontend-design/references/workflow-describe.md +87 -0
- package/skills/dev/frontend-design/references/workflow-immersive.md +87 -0
- package/skills/dev/frontend-design/references/workflow-quick.md +57 -0
- package/skills/dev/frontend-design/references/workflow-screenshot.md +63 -0
- package/skills/dev/frontend-design/references/workflow-video.md +74 -0
- package/skills/dev/frontend-dev/SKILL.md +400 -0
- package/skills/dev/frontend-dev/resources/common-patterns.md +331 -0
- package/skills/dev/frontend-dev/resources/complete-examples.md +872 -0
- package/skills/dev/frontend-dev/resources/component-patterns.md +502 -0
- package/skills/dev/frontend-dev/resources/data-fetching.md +767 -0
- package/skills/dev/frontend-dev/resources/file-organization.md +502 -0
- package/skills/dev/frontend-dev/resources/loading-and-error-states.md +501 -0
- package/skills/dev/frontend-dev/resources/performance.md +406 -0
- package/skills/dev/frontend-dev/resources/routing-guide.md +364 -0
- package/skills/dev/frontend-dev/resources/styling-guide.md +428 -0
- package/skills/dev/frontend-dev/resources/typescript-standards.md +418 -0
- package/skills/dev/git/SKILL.md +114 -0
- package/skills/dev/git/references/branch-management.md +88 -0
- package/skills/dev/git/references/commit-standards.md +46 -0
- package/skills/dev/git/references/gh-cli-guide.md +109 -0
- package/skills/dev/git/references/safety-protocols.md +69 -0
- package/skills/dev/git/references/workflow-commit.md +58 -0
- package/skills/dev/git/references/workflow-merge.md +48 -0
- package/skills/dev/git/references/workflow-pr.md +58 -0
- package/skills/dev/git/references/workflow-push.md +52 -0
- package/skills/dev/git-worktree/SKILL.md +218 -0
- package/skills/utils/ask/SKILL.md +58 -0
- package/skills/utils/bootstrap/SKILL.md +101 -0
- package/skills/utils/bootstrap/references/shared-phases.md +59 -0
- package/skills/utils/bootstrap/references/workflow-auto.md +52 -0
- package/skills/utils/bootstrap/references/workflow-fast.md +50 -0
- package/skills/utils/bootstrap/references/workflow-full.md +60 -0
- package/skills/utils/bootstrap/references/workflow-parallel.md +59 -0
- package/skills/utils/ck-help/SKILL.md +102 -0
- package/skills/utils/ck-help/scripts/ck-help.py +1321 -0
- package/skills/utils/ck-help/scripts/commands_data.yaml +3 -0
- package/skills/utils/ck-help/scripts/skills_data.yaml +593 -0
- package/skills/utils/copywriting/SKILL.md +94 -0
- package/skills/utils/copywriting/references/copy-formulas.md +150 -0
- package/skills/utils/copywriting/references/cta-patterns.md +168 -0
- package/skills/utils/copywriting/references/email-copy.md +193 -0
- package/skills/utils/copywriting/references/headline-templates.md +140 -0
- package/skills/utils/copywriting/references/landing-page-copy.md +175 -0
- package/skills/utils/copywriting/references/power-words.md +189 -0
- package/skills/utils/copywriting/references/social-media-copy.md +222 -0
- package/skills/utils/copywriting/references/workflow-cro.md +83 -0
- package/skills/utils/copywriting/references/workflow-enhance.md +32 -0
- package/skills/utils/copywriting/references/workflow-fast.md +29 -0
- package/skills/utils/copywriting/references/workflow-good.md +39 -0
- package/skills/utils/copywriting/references/writing-styles.md +247 -0
- package/skills/utils/copywriting/scripts/extract-writing-styles.py +308 -0
- package/skills/utils/copywriting/templates/copy-brief.md +49 -0
- package/skills/utils/docs/SKILL.md +55 -0
- package/skills/utils/docs/references/init-workflow.md +32 -0
- package/skills/utils/docs/references/summarize-workflow.md +18 -0
- package/skills/utils/docs/references/update-workflow.md +59 -0
- package/skills/utils/journal/SKILL.md +11 -0
- package/skills/utils/kanban/SKILL.md +99 -0
- package/skills/utils/preview/SKILL.md +75 -0
- package/skills/utils/preview/references/generation-modes.md +95 -0
- package/skills/utils/preview/references/view-mode.md +42 -0
- package/skills/utils/repomix/SKILL.md +248 -0
- package/skills/utils/repomix/references/configuration.md +211 -0
- package/skills/utils/repomix/references/usage-patterns.md +232 -0
- package/skills/utils/repomix/scripts/.coverage +0 -0
- package/skills/utils/repomix/scripts/README.md +179 -0
- package/skills/utils/repomix/scripts/repomix_batch.py +455 -0
- package/skills/utils/repomix/scripts/repos.example.json +15 -0
- package/skills/utils/repomix/scripts/requirements.txt +15 -0
- package/skills/utils/repomix/scripts/tests/test_repomix_batch.py +531 -0
- package/skills/utils/research/SKILL.md +171 -0
- package/skills/utils/scout/SKILL.md +89 -0
- package/skills/utils/scout/references/external-scouting.md +140 -0
- package/skills/utils/scout/references/internal-scouting.md +119 -0
- package/skills/utils/scout/references/task-management-scouting.md +125 -0
- package/skills/utils/sequential-thinking/.env.example +8 -0
- package/skills/utils/sequential-thinking/README.md +183 -0
- package/skills/utils/sequential-thinking/SKILL.md +95 -0
- package/skills/utils/sequential-thinking/package.json +31 -0
- package/skills/utils/sequential-thinking/references/advanced-strategies.md +79 -0
- package/skills/utils/sequential-thinking/references/advanced-techniques.md +76 -0
- package/skills/utils/sequential-thinking/references/core-patterns.md +95 -0
- package/skills/utils/sequential-thinking/references/examples-api.md +88 -0
- package/skills/utils/sequential-thinking/references/examples-architecture.md +94 -0
- package/skills/utils/sequential-thinking/references/examples-debug.md +90 -0
- package/skills/utils/sequential-thinking/scripts/format-thought.js +159 -0
- package/skills/utils/sequential-thinking/scripts/process-thought.js +236 -0
- package/skills/utils/sequential-thinking/tests/format-thought.test.js +133 -0
- package/skills/utils/sequential-thinking/tests/process-thought.test.js +215 -0
- package/skills/utils/write-skill/SKILL.md +655 -0
- package/skills/utils/write-skill/anthropic-best-practices.md +1150 -0
- package/skills/utils/write-skill/examples/CLAUDE_MD_TESTING.md +189 -0
- package/skills/utils/write-skill/graphviz-conventions.dot +172 -0
- package/skills/utils/write-skill/persuasion-principles.md +187 -0
- package/skills/utils/write-skill/render-graphs.js +168 -0
- package/skills/utils/write-skill/testing-skills-with-subagents.md +384 -0
- package/src/commands/init.js +238 -0
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
"""Tests for db_backup.py"""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import sys
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from unittest.mock import Mock, patch, MagicMock, call
|
|
8
|
+
|
|
9
|
+
import pytest
|
|
10
|
+
|
|
11
|
+
# Add parent directory to path
|
|
12
|
+
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
13
|
+
|
|
14
|
+
from db_backup import BackupInfo, BackupManager
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@pytest.fixture
|
|
18
|
+
def temp_backup_dir(tmp_path):
|
|
19
|
+
"""Create temporary backup directory."""
|
|
20
|
+
backup_dir = tmp_path / "backups"
|
|
21
|
+
backup_dir.mkdir()
|
|
22
|
+
return str(backup_dir)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@pytest.fixture
|
|
26
|
+
def sample_backup_info():
|
|
27
|
+
"""Create sample backup info."""
|
|
28
|
+
return BackupInfo(
|
|
29
|
+
filename="test_backup_20250101_120000.dump",
|
|
30
|
+
database_type="mongodb",
|
|
31
|
+
database_name="testdb",
|
|
32
|
+
timestamp=datetime.now(),
|
|
33
|
+
size_bytes=1024000,
|
|
34
|
+
compressed=True,
|
|
35
|
+
verified=True
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class TestBackupInfo:
|
|
40
|
+
"""Test BackupInfo dataclass."""
|
|
41
|
+
|
|
42
|
+
def test_backup_info_creation(self):
|
|
43
|
+
"""Test creating backup info object."""
|
|
44
|
+
info = BackupInfo(
|
|
45
|
+
filename="backup.dump",
|
|
46
|
+
database_type="mongodb",
|
|
47
|
+
database_name="mydb",
|
|
48
|
+
timestamp=datetime.now(),
|
|
49
|
+
size_bytes=1024,
|
|
50
|
+
compressed=False
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
assert info.filename == "backup.dump"
|
|
54
|
+
assert info.database_type == "mongodb"
|
|
55
|
+
assert info.database_name == "mydb"
|
|
56
|
+
assert info.size_bytes == 1024
|
|
57
|
+
assert not info.compressed
|
|
58
|
+
assert not info.verified
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class TestBackupManager:
|
|
62
|
+
"""Test BackupManager class."""
|
|
63
|
+
|
|
64
|
+
def test_init(self, temp_backup_dir):
|
|
65
|
+
"""Test manager initialization."""
|
|
66
|
+
manager = BackupManager("mongodb", temp_backup_dir)
|
|
67
|
+
|
|
68
|
+
assert manager.db_type == "mongodb"
|
|
69
|
+
assert Path(temp_backup_dir).exists()
|
|
70
|
+
|
|
71
|
+
@patch('subprocess.run')
|
|
72
|
+
def test_backup_mongodb(self, mock_run, temp_backup_dir):
|
|
73
|
+
"""Test MongoDB backup creation."""
|
|
74
|
+
mock_run.return_value = Mock(returncode=0, stderr="")
|
|
75
|
+
|
|
76
|
+
manager = BackupManager("mongodb", temp_backup_dir)
|
|
77
|
+
backup_info = manager.create_backup(
|
|
78
|
+
"mongodb://localhost",
|
|
79
|
+
"testdb",
|
|
80
|
+
compress=False,
|
|
81
|
+
verify=False
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
assert backup_info is not None
|
|
85
|
+
assert backup_info.database_type == "mongodb"
|
|
86
|
+
assert backup_info.database_name == "testdb"
|
|
87
|
+
mock_run.assert_called_once()
|
|
88
|
+
|
|
89
|
+
@patch('subprocess.run')
|
|
90
|
+
def test_backup_postgres(self, mock_run, temp_backup_dir):
|
|
91
|
+
"""Test PostgreSQL backup creation."""
|
|
92
|
+
mock_run.return_value = Mock(returncode=0, stderr="")
|
|
93
|
+
|
|
94
|
+
manager = BackupManager("postgres", temp_backup_dir)
|
|
95
|
+
|
|
96
|
+
with patch('builtins.open', create=True) as mock_open:
|
|
97
|
+
mock_open.return_value.__enter__.return_value = MagicMock()
|
|
98
|
+
|
|
99
|
+
backup_info = manager.create_backup(
|
|
100
|
+
"postgresql://localhost/testdb",
|
|
101
|
+
"testdb",
|
|
102
|
+
compress=False,
|
|
103
|
+
verify=False
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
assert backup_info is not None
|
|
107
|
+
assert backup_info.database_type == "postgres"
|
|
108
|
+
assert backup_info.database_name == "testdb"
|
|
109
|
+
|
|
110
|
+
def test_backup_postgres_no_database(self, temp_backup_dir):
|
|
111
|
+
"""Test PostgreSQL backup without database name."""
|
|
112
|
+
manager = BackupManager("postgres", temp_backup_dir)
|
|
113
|
+
backup_info = manager.create_backup(
|
|
114
|
+
"postgresql://localhost",
|
|
115
|
+
database=None,
|
|
116
|
+
compress=False,
|
|
117
|
+
verify=False
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
assert backup_info is None
|
|
121
|
+
|
|
122
|
+
@patch('subprocess.run')
|
|
123
|
+
def test_backup_with_compression(self, mock_run, temp_backup_dir):
|
|
124
|
+
"""Test backup with compression."""
|
|
125
|
+
mock_run.return_value = Mock(returncode=0, stderr="")
|
|
126
|
+
|
|
127
|
+
manager = BackupManager("mongodb", temp_backup_dir)
|
|
128
|
+
|
|
129
|
+
with patch('shutil.make_archive') as mock_archive, \
|
|
130
|
+
patch('shutil.rmtree') as mock_rmtree:
|
|
131
|
+
|
|
132
|
+
backup_info = manager.create_backup(
|
|
133
|
+
"mongodb://localhost",
|
|
134
|
+
"testdb",
|
|
135
|
+
compress=True,
|
|
136
|
+
verify=False
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
assert backup_info is not None
|
|
140
|
+
assert backup_info.compressed
|
|
141
|
+
mock_archive.assert_called_once()
|
|
142
|
+
|
|
143
|
+
def test_save_and_load_metadata(self, temp_backup_dir, sample_backup_info):
|
|
144
|
+
"""Test saving and loading backup metadata."""
|
|
145
|
+
manager = BackupManager("mongodb", temp_backup_dir)
|
|
146
|
+
|
|
147
|
+
# Save metadata
|
|
148
|
+
manager._save_metadata(sample_backup_info)
|
|
149
|
+
|
|
150
|
+
# Check file was created
|
|
151
|
+
metadata_file = Path(temp_backup_dir) / f"{sample_backup_info.filename}.json"
|
|
152
|
+
assert metadata_file.exists()
|
|
153
|
+
|
|
154
|
+
# Load metadata
|
|
155
|
+
with open(metadata_file) as f:
|
|
156
|
+
data = json.load(f)
|
|
157
|
+
assert data["filename"] == sample_backup_info.filename
|
|
158
|
+
assert data["database_type"] == "mongodb"
|
|
159
|
+
assert data["database_name"] == "testdb"
|
|
160
|
+
|
|
161
|
+
def test_list_backups(self, temp_backup_dir, sample_backup_info):
|
|
162
|
+
"""Test listing backups."""
|
|
163
|
+
manager = BackupManager("mongodb", temp_backup_dir)
|
|
164
|
+
|
|
165
|
+
# Create test backup metadata
|
|
166
|
+
manager._save_metadata(sample_backup_info)
|
|
167
|
+
|
|
168
|
+
# List backups
|
|
169
|
+
backups = manager.list_backups()
|
|
170
|
+
|
|
171
|
+
assert len(backups) == 1
|
|
172
|
+
assert backups[0].filename == sample_backup_info.filename
|
|
173
|
+
assert backups[0].database_name == "testdb"
|
|
174
|
+
|
|
175
|
+
@patch('subprocess.run')
|
|
176
|
+
def test_restore_mongodb(self, mock_run, temp_backup_dir):
|
|
177
|
+
"""Test MongoDB restore."""
|
|
178
|
+
mock_run.return_value = Mock(returncode=0, stderr="")
|
|
179
|
+
|
|
180
|
+
manager = BackupManager("mongodb", temp_backup_dir)
|
|
181
|
+
|
|
182
|
+
# Create dummy backup file
|
|
183
|
+
backup_file = Path(temp_backup_dir) / "test_backup.dump"
|
|
184
|
+
backup_file.touch()
|
|
185
|
+
|
|
186
|
+
result = manager.restore_backup(
|
|
187
|
+
"test_backup.dump",
|
|
188
|
+
"mongodb://localhost"
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
assert result is True
|
|
192
|
+
mock_run.assert_called_once()
|
|
193
|
+
|
|
194
|
+
@patch('subprocess.run')
|
|
195
|
+
def test_restore_postgres(self, mock_run, temp_backup_dir):
|
|
196
|
+
"""Test PostgreSQL restore."""
|
|
197
|
+
mock_run.return_value = Mock(returncode=0, stderr="")
|
|
198
|
+
|
|
199
|
+
manager = BackupManager("postgres", temp_backup_dir)
|
|
200
|
+
|
|
201
|
+
# Create dummy backup file
|
|
202
|
+
backup_file = Path(temp_backup_dir) / "test_backup.sql"
|
|
203
|
+
backup_file.write_text("SELECT 1;")
|
|
204
|
+
|
|
205
|
+
with patch('builtins.open', create=True) as mock_open:
|
|
206
|
+
mock_open.return_value.__enter__.return_value = MagicMock()
|
|
207
|
+
|
|
208
|
+
result = manager.restore_backup(
|
|
209
|
+
"test_backup.sql",
|
|
210
|
+
"postgresql://localhost/testdb"
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
assert result is True
|
|
214
|
+
|
|
215
|
+
def test_restore_nonexistent_backup(self, temp_backup_dir):
|
|
216
|
+
"""Test restore with non-existent backup file."""
|
|
217
|
+
manager = BackupManager("mongodb", temp_backup_dir)
|
|
218
|
+
|
|
219
|
+
result = manager.restore_backup(
|
|
220
|
+
"nonexistent.dump",
|
|
221
|
+
"mongodb://localhost"
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
assert result is False
|
|
225
|
+
|
|
226
|
+
def test_restore_dry_run(self, temp_backup_dir):
|
|
227
|
+
"""Test restore in dry-run mode."""
|
|
228
|
+
manager = BackupManager("mongodb", temp_backup_dir)
|
|
229
|
+
|
|
230
|
+
# Create dummy backup file
|
|
231
|
+
backup_file = Path(temp_backup_dir) / "test_backup.dump"
|
|
232
|
+
backup_file.touch()
|
|
233
|
+
|
|
234
|
+
result = manager.restore_backup(
|
|
235
|
+
"test_backup.dump",
|
|
236
|
+
"mongodb://localhost",
|
|
237
|
+
dry_run=True
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
assert result is True
|
|
241
|
+
|
|
242
|
+
def test_cleanup_old_backups(self, temp_backup_dir):
|
|
243
|
+
"""Test cleaning up old backups."""
|
|
244
|
+
manager = BackupManager("mongodb", temp_backup_dir)
|
|
245
|
+
|
|
246
|
+
# Create old backup file (simulate by setting mtime)
|
|
247
|
+
old_backup = Path(temp_backup_dir) / "old_backup.dump"
|
|
248
|
+
old_backup.touch()
|
|
249
|
+
|
|
250
|
+
# Set mtime to 10 days ago
|
|
251
|
+
old_time = datetime.now().timestamp() - (10 * 24 * 3600)
|
|
252
|
+
os.utime(old_backup, (old_time, old_time))
|
|
253
|
+
|
|
254
|
+
# Cleanup with 7-day retention
|
|
255
|
+
removed = manager.cleanup_old_backups(retention_days=7)
|
|
256
|
+
|
|
257
|
+
assert removed == 1
|
|
258
|
+
assert not old_backup.exists()
|
|
259
|
+
|
|
260
|
+
def test_cleanup_dry_run(self, temp_backup_dir):
|
|
261
|
+
"""Test cleanup in dry-run mode."""
|
|
262
|
+
manager = BackupManager("mongodb", temp_backup_dir)
|
|
263
|
+
|
|
264
|
+
# Create old backup file
|
|
265
|
+
old_backup = Path(temp_backup_dir) / "old_backup.dump"
|
|
266
|
+
old_backup.touch()
|
|
267
|
+
|
|
268
|
+
old_time = datetime.now().timestamp() - (10 * 24 * 3600)
|
|
269
|
+
os.utime(old_backup, (old_time, old_time))
|
|
270
|
+
|
|
271
|
+
# Cleanup with dry-run
|
|
272
|
+
removed = manager.cleanup_old_backups(retention_days=7, dry_run=True)
|
|
273
|
+
|
|
274
|
+
assert removed == 1
|
|
275
|
+
assert old_backup.exists() # File should still exist
|
|
276
|
+
|
|
277
|
+
def test_verify_backup(self, temp_backup_dir, sample_backup_info):
|
|
278
|
+
"""Test backup verification."""
|
|
279
|
+
manager = BackupManager("mongodb", temp_backup_dir)
|
|
280
|
+
|
|
281
|
+
# Create dummy backup file
|
|
282
|
+
backup_file = Path(temp_backup_dir) / sample_backup_info.filename
|
|
283
|
+
backup_file.write_text("backup data")
|
|
284
|
+
|
|
285
|
+
result = manager._verify_backup(sample_backup_info)
|
|
286
|
+
|
|
287
|
+
assert result is True
|
|
288
|
+
|
|
289
|
+
def test_verify_empty_backup(self, temp_backup_dir, sample_backup_info):
|
|
290
|
+
"""Test verification of empty backup file."""
|
|
291
|
+
manager = BackupManager("mongodb", temp_backup_dir)
|
|
292
|
+
|
|
293
|
+
# Create empty backup file
|
|
294
|
+
backup_file = Path(temp_backup_dir) / sample_backup_info.filename
|
|
295
|
+
backup_file.touch()
|
|
296
|
+
|
|
297
|
+
result = manager._verify_backup(sample_backup_info)
|
|
298
|
+
|
|
299
|
+
assert result is False
|
|
300
|
+
|
|
301
|
+
def test_format_size(self, temp_backup_dir):
|
|
302
|
+
"""Test size formatting."""
|
|
303
|
+
manager = BackupManager("mongodb", temp_backup_dir)
|
|
304
|
+
|
|
305
|
+
assert manager._format_size(500) == "500.00 B"
|
|
306
|
+
assert manager._format_size(1024) == "1.00 KB"
|
|
307
|
+
assert manager._format_size(1024 * 1024) == "1.00 MB"
|
|
308
|
+
assert manager._format_size(1024 * 1024 * 1024) == "1.00 GB"
|
|
309
|
+
|
|
310
|
+
def test_get_size_file(self, temp_backup_dir):
|
|
311
|
+
"""Test getting size of file."""
|
|
312
|
+
manager = BackupManager("mongodb", temp_backup_dir)
|
|
313
|
+
|
|
314
|
+
test_file = Path(temp_backup_dir) / "test.txt"
|
|
315
|
+
test_file.write_text("test data")
|
|
316
|
+
|
|
317
|
+
size = manager._get_size(test_file)
|
|
318
|
+
|
|
319
|
+
assert size > 0
|
|
320
|
+
|
|
321
|
+
def test_get_size_directory(self, temp_backup_dir):
|
|
322
|
+
"""Test getting size of directory."""
|
|
323
|
+
manager = BackupManager("mongodb", temp_backup_dir)
|
|
324
|
+
|
|
325
|
+
test_dir = Path(temp_backup_dir) / "test_dir"
|
|
326
|
+
test_dir.mkdir()
|
|
327
|
+
(test_dir / "file1.txt").write_text("data1")
|
|
328
|
+
(test_dir / "file2.txt").write_text("data2")
|
|
329
|
+
|
|
330
|
+
size = manager._get_size(test_dir)
|
|
331
|
+
|
|
332
|
+
assert size > 0
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
# Import os for cleanup test
|
|
336
|
+
import os
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
if __name__ == "__main__":
|
|
340
|
+
pytest.main([__file__, "-v"])
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
"""Tests for db_migrate.py"""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from unittest.mock import Mock, patch, MagicMock
|
|
9
|
+
|
|
10
|
+
import pytest
|
|
11
|
+
|
|
12
|
+
# Add parent directory to path
|
|
13
|
+
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
14
|
+
|
|
15
|
+
from db_migrate import Migration, MigrationManager
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@pytest.fixture
|
|
19
|
+
def temp_migrations_dir(tmp_path):
|
|
20
|
+
"""Create temporary migrations directory."""
|
|
21
|
+
migrations_dir = tmp_path / "migrations"
|
|
22
|
+
migrations_dir.mkdir()
|
|
23
|
+
return str(migrations_dir)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@pytest.fixture
|
|
27
|
+
def mock_mongo_client():
|
|
28
|
+
"""Mock MongoDB client."""
|
|
29
|
+
mock_client = MagicMock()
|
|
30
|
+
mock_db = MagicMock()
|
|
31
|
+
mock_client.get_default_database.return_value = mock_db
|
|
32
|
+
mock_client.server_info.return_value = {}
|
|
33
|
+
return mock_client, mock_db
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@pytest.fixture
|
|
37
|
+
def mock_postgres_conn():
|
|
38
|
+
"""Mock PostgreSQL connection."""
|
|
39
|
+
mock_conn = MagicMock()
|
|
40
|
+
mock_cursor = MagicMock()
|
|
41
|
+
mock_conn.cursor.return_value.__enter__.return_value = mock_cursor
|
|
42
|
+
return mock_conn, mock_cursor
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class TestMigration:
|
|
46
|
+
"""Test Migration dataclass."""
|
|
47
|
+
|
|
48
|
+
def test_migration_creation(self):
|
|
49
|
+
"""Test creating migration object."""
|
|
50
|
+
migration = Migration(
|
|
51
|
+
id="20250101120000",
|
|
52
|
+
name="test_migration",
|
|
53
|
+
timestamp=datetime.now(),
|
|
54
|
+
database_type="mongodb"
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
assert migration.id == "20250101120000"
|
|
58
|
+
assert migration.name == "test_migration"
|
|
59
|
+
assert migration.database_type == "mongodb"
|
|
60
|
+
assert not migration.applied
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class TestMigrationManager:
|
|
64
|
+
"""Test MigrationManager class."""
|
|
65
|
+
|
|
66
|
+
def test_init(self, temp_migrations_dir):
|
|
67
|
+
"""Test manager initialization."""
|
|
68
|
+
manager = MigrationManager("mongodb", "mongodb://localhost", temp_migrations_dir)
|
|
69
|
+
|
|
70
|
+
assert manager.db_type == "mongodb"
|
|
71
|
+
assert manager.connection_string == "mongodb://localhost"
|
|
72
|
+
assert Path(temp_migrations_dir).exists()
|
|
73
|
+
|
|
74
|
+
@patch('db_migrate.MongoClient')
|
|
75
|
+
def test_connect_mongodb(self, mock_client_class, temp_migrations_dir, mock_mongo_client):
|
|
76
|
+
"""Test MongoDB connection."""
|
|
77
|
+
mock_client, mock_db = mock_mongo_client
|
|
78
|
+
mock_client_class.return_value = mock_client
|
|
79
|
+
|
|
80
|
+
manager = MigrationManager("mongodb", "mongodb://localhost", temp_migrations_dir)
|
|
81
|
+
result = manager.connect()
|
|
82
|
+
|
|
83
|
+
assert result is True
|
|
84
|
+
assert manager.client == mock_client
|
|
85
|
+
assert manager.db == mock_db
|
|
86
|
+
|
|
87
|
+
@patch('db_migrate.psycopg2')
|
|
88
|
+
def test_connect_postgres(self, mock_psycopg2, temp_migrations_dir, mock_postgres_conn):
|
|
89
|
+
"""Test PostgreSQL connection."""
|
|
90
|
+
mock_conn, mock_cursor = mock_postgres_conn
|
|
91
|
+
mock_psycopg2.connect.return_value = mock_conn
|
|
92
|
+
|
|
93
|
+
manager = MigrationManager("postgres", "postgresql://localhost", temp_migrations_dir)
|
|
94
|
+
result = manager.connect()
|
|
95
|
+
|
|
96
|
+
assert result is True
|
|
97
|
+
assert manager.conn == mock_conn
|
|
98
|
+
|
|
99
|
+
def test_connect_unsupported_db(self, temp_migrations_dir):
|
|
100
|
+
"""Test connection with unsupported database type."""
|
|
101
|
+
manager = MigrationManager("unsupported", "connection_string", temp_migrations_dir)
|
|
102
|
+
result = manager.connect()
|
|
103
|
+
|
|
104
|
+
assert result is False
|
|
105
|
+
|
|
106
|
+
def test_generate_migration(self, temp_migrations_dir):
|
|
107
|
+
"""Test migration generation."""
|
|
108
|
+
manager = MigrationManager("mongodb", "mongodb://localhost", temp_migrations_dir)
|
|
109
|
+
migration = manager.generate_migration("test_migration")
|
|
110
|
+
|
|
111
|
+
assert migration is not None
|
|
112
|
+
assert migration.name == "test_migration"
|
|
113
|
+
|
|
114
|
+
# Check file was created
|
|
115
|
+
migration_files = list(Path(temp_migrations_dir).glob("*.json"))
|
|
116
|
+
assert len(migration_files) == 1
|
|
117
|
+
|
|
118
|
+
# Check file content
|
|
119
|
+
with open(migration_files[0]) as f:
|
|
120
|
+
data = json.load(f)
|
|
121
|
+
assert data["name"] == "test_migration"
|
|
122
|
+
assert data["database_type"] == "mongodb"
|
|
123
|
+
|
|
124
|
+
def test_generate_migration_dry_run(self, temp_migrations_dir):
|
|
125
|
+
"""Test migration generation in dry-run mode."""
|
|
126
|
+
manager = MigrationManager("postgres", "postgresql://localhost", temp_migrations_dir)
|
|
127
|
+
migration = manager.generate_migration("test_migration", dry_run=True)
|
|
128
|
+
|
|
129
|
+
assert migration is not None
|
|
130
|
+
|
|
131
|
+
# Check no file was created
|
|
132
|
+
migration_files = list(Path(temp_migrations_dir).glob("*.json"))
|
|
133
|
+
assert len(migration_files) == 0
|
|
134
|
+
|
|
135
|
+
def test_get_pending_migrations(self, temp_migrations_dir):
|
|
136
|
+
"""Test getting pending migrations."""
|
|
137
|
+
manager = MigrationManager("mongodb", "mongodb://localhost", temp_migrations_dir)
|
|
138
|
+
|
|
139
|
+
# Create test migration file
|
|
140
|
+
migration_data = {
|
|
141
|
+
"id": "20250101120000",
|
|
142
|
+
"name": "test_migration",
|
|
143
|
+
"timestamp": datetime.now().isoformat(),
|
|
144
|
+
"database_type": "mongodb",
|
|
145
|
+
"mongodb_operations": []
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
migration_file = Path(temp_migrations_dir) / "20250101120000_test.json"
|
|
149
|
+
with open(migration_file, "w") as f:
|
|
150
|
+
json.dump(migration_data, f)
|
|
151
|
+
|
|
152
|
+
# Mock database connection
|
|
153
|
+
with patch.object(manager, 'db', MagicMock()):
|
|
154
|
+
manager.db.migrations.find.return_value = []
|
|
155
|
+
|
|
156
|
+
pending = manager.get_pending_migrations()
|
|
157
|
+
|
|
158
|
+
assert len(pending) == 1
|
|
159
|
+
assert pending[0].id == "20250101120000"
|
|
160
|
+
assert pending[0].name == "test_migration"
|
|
161
|
+
|
|
162
|
+
@patch('db_migrate.MongoClient')
|
|
163
|
+
def test_apply_mongodb_migration(self, mock_client_class, temp_migrations_dir, mock_mongo_client):
|
|
164
|
+
"""Test applying MongoDB migration."""
|
|
165
|
+
mock_client, mock_db = mock_mongo_client
|
|
166
|
+
mock_client_class.return_value = mock_client
|
|
167
|
+
|
|
168
|
+
manager = MigrationManager("mongodb", "mongodb://localhost", temp_migrations_dir)
|
|
169
|
+
manager.connect()
|
|
170
|
+
|
|
171
|
+
migration = Migration(
|
|
172
|
+
id="20250101120000",
|
|
173
|
+
name="test_migration",
|
|
174
|
+
timestamp=datetime.now(),
|
|
175
|
+
database_type="mongodb",
|
|
176
|
+
mongodb_operations=[
|
|
177
|
+
{
|
|
178
|
+
"operation": "createIndex",
|
|
179
|
+
"collection": "users",
|
|
180
|
+
"index": {"email": 1},
|
|
181
|
+
"options": {}
|
|
182
|
+
}
|
|
183
|
+
]
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
result = manager.apply_migration(migration)
|
|
187
|
+
|
|
188
|
+
assert result is True
|
|
189
|
+
mock_db["users"].create_index.assert_called_once()
|
|
190
|
+
mock_db.migrations.insert_one.assert_called_once()
|
|
191
|
+
|
|
192
|
+
def test_apply_migration_dry_run(self, temp_migrations_dir):
|
|
193
|
+
"""Test applying migration in dry-run mode."""
|
|
194
|
+
manager = MigrationManager("mongodb", "mongodb://localhost", temp_migrations_dir)
|
|
195
|
+
|
|
196
|
+
migration = Migration(
|
|
197
|
+
id="20250101120000",
|
|
198
|
+
name="test_migration",
|
|
199
|
+
timestamp=datetime.now(),
|
|
200
|
+
database_type="mongodb",
|
|
201
|
+
mongodb_operations=[]
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
result = manager.apply_migration(migration, dry_run=True)
|
|
205
|
+
|
|
206
|
+
assert result is True
|
|
207
|
+
|
|
208
|
+
@patch('db_migrate.psycopg2')
|
|
209
|
+
def test_rollback_postgres_migration(self, mock_psycopg2, temp_migrations_dir, mock_postgres_conn):
|
|
210
|
+
"""Test rolling back PostgreSQL migration."""
|
|
211
|
+
mock_conn, mock_cursor = mock_postgres_conn
|
|
212
|
+
mock_psycopg2.connect.return_value = mock_conn
|
|
213
|
+
|
|
214
|
+
manager = MigrationManager("postgres", "postgresql://localhost", temp_migrations_dir)
|
|
215
|
+
manager.connect()
|
|
216
|
+
|
|
217
|
+
# Create migration file
|
|
218
|
+
migration_data = {
|
|
219
|
+
"id": "20250101120000",
|
|
220
|
+
"name": "test_migration",
|
|
221
|
+
"timestamp": datetime.now().isoformat(),
|
|
222
|
+
"database_type": "postgres",
|
|
223
|
+
"up_sql": "CREATE TABLE test (id INT);",
|
|
224
|
+
"down_sql": "DROP TABLE test;"
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
migration_file = Path(temp_migrations_dir) / "20250101120000_test.json"
|
|
228
|
+
with open(migration_file, "w") as f:
|
|
229
|
+
json.dump(migration_data, f)
|
|
230
|
+
|
|
231
|
+
result = manager.rollback_migration("20250101120000")
|
|
232
|
+
|
|
233
|
+
assert result is True
|
|
234
|
+
# Verify SQL was executed
|
|
235
|
+
assert mock_cursor.execute.call_count >= 1
|
|
236
|
+
|
|
237
|
+
def test_rollback_migration_not_found(self, temp_migrations_dir):
|
|
238
|
+
"""Test rollback with non-existent migration."""
|
|
239
|
+
manager = MigrationManager("mongodb", "mongodb://localhost", temp_migrations_dir)
|
|
240
|
+
|
|
241
|
+
result = manager.rollback_migration("99999999999999")
|
|
242
|
+
|
|
243
|
+
assert result is False
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def test_migration_sorting(temp_migrations_dir):
|
|
247
|
+
"""Test that migrations are applied in correct order."""
|
|
248
|
+
manager = MigrationManager("mongodb", "mongodb://localhost", temp_migrations_dir)
|
|
249
|
+
|
|
250
|
+
# Create multiple migration files
|
|
251
|
+
for i in range(3):
|
|
252
|
+
migration_data = {
|
|
253
|
+
"id": f"2025010112000{i}",
|
|
254
|
+
"name": f"migration_{i}",
|
|
255
|
+
"timestamp": datetime.now().isoformat(),
|
|
256
|
+
"database_type": "mongodb",
|
|
257
|
+
"mongodb_operations": []
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
migration_file = Path(temp_migrations_dir) / f"2025010112000{i}_test.json"
|
|
261
|
+
with open(migration_file, "w") as f:
|
|
262
|
+
json.dump(migration_data, f)
|
|
263
|
+
|
|
264
|
+
with patch.object(manager, 'db', MagicMock()):
|
|
265
|
+
manager.db.migrations.find.return_value = []
|
|
266
|
+
|
|
267
|
+
pending = manager.get_pending_migrations()
|
|
268
|
+
|
|
269
|
+
# Check they're in order
|
|
270
|
+
assert len(pending) == 3
|
|
271
|
+
assert pending[0].id == "20250101120000"
|
|
272
|
+
assert pending[1].id == "20250101120001"
|
|
273
|
+
assert pending[2].id == "20250101120002"
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
if __name__ == "__main__":
|
|
277
|
+
pytest.main([__file__, "-v"])
|