agim-cli 1.2.147 → 1.2.148
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 +58 -0
- package/dist/core/skills/builtin/ECC_LICENSE +21 -0
- package/dist/core/skills/builtin/ECC_NOTICE.md +22 -0
- package/dist/core/skills/builtin/accessibility/SKILL.md +146 -0
- package/dist/core/skills/builtin/agent-eval/SKILL.md +145 -0
- package/dist/core/skills/builtin/agent-harness-construction/SKILL.md +73 -0
- package/dist/core/skills/builtin/agent-introspection-debugging/SKILL.md +153 -0
- package/dist/core/skills/builtin/agentic-engineering/SKILL.md +63 -0
- package/dist/core/skills/builtin/ai-first-engineering/SKILL.md +51 -0
- package/dist/core/skills/builtin/ai-regression-testing/SKILL.md +385 -0
- package/dist/core/skills/builtin/android-clean-architecture/SKILL.md +339 -0
- package/dist/core/skills/builtin/angular-developer/SKILL.md +154 -0
- package/dist/core/skills/builtin/angular-developer/references/angular-animations.md +160 -0
- package/dist/core/skills/builtin/angular-developer/references/angular-aria.md +410 -0
- package/dist/core/skills/builtin/angular-developer/references/cli.md +86 -0
- package/dist/core/skills/builtin/angular-developer/references/component-harnesses.md +59 -0
- package/dist/core/skills/builtin/angular-developer/references/component-styling.md +91 -0
- package/dist/core/skills/builtin/angular-developer/references/components.md +117 -0
- package/dist/core/skills/builtin/angular-developer/references/creating-services.md +97 -0
- package/dist/core/skills/builtin/angular-developer/references/data-resolvers.md +69 -0
- package/dist/core/skills/builtin/angular-developer/references/define-routes.md +67 -0
- package/dist/core/skills/builtin/angular-developer/references/defining-providers.md +72 -0
- package/dist/core/skills/builtin/angular-developer/references/di-fundamentals.md +120 -0
- package/dist/core/skills/builtin/angular-developer/references/e2e-testing.md +56 -0
- package/dist/core/skills/builtin/angular-developer/references/effects.md +83 -0
- package/dist/core/skills/builtin/angular-developer/references/hierarchical-injectors.md +43 -0
- package/dist/core/skills/builtin/angular-developer/references/host-elements.md +80 -0
- package/dist/core/skills/builtin/angular-developer/references/injection-context.md +63 -0
- package/dist/core/skills/builtin/angular-developer/references/inputs.md +101 -0
- package/dist/core/skills/builtin/angular-developer/references/linked-signal.md +59 -0
- package/dist/core/skills/builtin/angular-developer/references/loading-strategies.md +61 -0
- package/dist/core/skills/builtin/angular-developer/references/mcp.md +108 -0
- package/dist/core/skills/builtin/angular-developer/references/navigate-to-routes.md +69 -0
- package/dist/core/skills/builtin/angular-developer/references/outputs.md +86 -0
- package/dist/core/skills/builtin/angular-developer/references/reactive-forms.md +122 -0
- package/dist/core/skills/builtin/angular-developer/references/rendering-strategies.md +44 -0
- package/dist/core/skills/builtin/angular-developer/references/resource.md +77 -0
- package/dist/core/skills/builtin/angular-developer/references/route-animations.md +56 -0
- package/dist/core/skills/builtin/angular-developer/references/route-guards.md +52 -0
- package/dist/core/skills/builtin/angular-developer/references/router-lifecycle.md +45 -0
- package/dist/core/skills/builtin/angular-developer/references/router-testing.md +87 -0
- package/dist/core/skills/builtin/angular-developer/references/show-routes-with-outlets.md +68 -0
- package/dist/core/skills/builtin/angular-developer/references/signal-forms.md +795 -0
- package/dist/core/skills/builtin/angular-developer/references/signals-overview.md +94 -0
- package/dist/core/skills/builtin/angular-developer/references/tailwind-css.md +69 -0
- package/dist/core/skills/builtin/angular-developer/references/template-driven-forms.md +114 -0
- package/dist/core/skills/builtin/angular-developer/references/testing-fundamentals.md +65 -0
- package/dist/core/skills/builtin/api-connector-builder/SKILL.md +120 -0
- package/dist/core/skills/builtin/api-design/SKILL.md +523 -0
- package/dist/core/skills/builtin/architecture-decision-records/SKILL.md +179 -0
- package/dist/core/skills/builtin/article-writing/SKILL.md +79 -0
- package/dist/core/skills/builtin/automation-audit-ops/SKILL.md +142 -0
- package/dist/core/skills/builtin/autonomous-agent-harness/SKILL.md +273 -0
- package/dist/core/skills/builtin/autonomous-loops/SKILL.md +610 -0
- package/dist/core/skills/builtin/backend-patterns/SKILL.md +561 -0
- package/dist/core/skills/builtin/benchmark/SKILL.md +93 -0
- package/dist/core/skills/builtin/benchmark-optimization-loop/SKILL.md +69 -0
- package/dist/core/skills/builtin/blueprint/SKILL.md +105 -0
- package/dist/core/skills/builtin/browser-qa/SKILL.md +87 -0
- package/dist/core/skills/builtin/bun-runtime/SKILL.md +84 -0
- package/dist/core/skills/builtin/cisco-ios-patterns/SKILL.md +163 -0
- package/dist/core/skills/builtin/claude-devfleet/SKILL.md +111 -0
- package/dist/core/skills/builtin/click-path-audit/SKILL.md +244 -0
- package/dist/core/skills/builtin/clickhouse-io/SKILL.md +439 -0
- package/dist/core/skills/builtin/code-tour/SKILL.md +236 -0
- package/dist/core/skills/builtin/codebase-onboarding/SKILL.md +233 -0
- package/dist/core/skills/builtin/codehealth-mcp/SKILL.md +166 -0
- package/dist/core/skills/builtin/coding-standards/SKILL.md +550 -0
- package/dist/core/skills/builtin/compose-multiplatform-patterns/SKILL.md +299 -0
- package/dist/core/skills/builtin/config-gc/SKILL.md +119 -0
- package/dist/core/skills/builtin/content-hash-cache-pattern/SKILL.md +161 -0
- package/dist/core/skills/builtin/context-budget/SKILL.md +135 -0
- package/dist/core/skills/builtin/continuous-agent-loop/SKILL.md +45 -0
- package/dist/core/skills/builtin/continuous-learning/SKILL.md +131 -0
- package/dist/core/skills/builtin/continuous-learning/config.json +18 -0
- package/dist/core/skills/builtin/continuous-learning/evaluate-session.sh +69 -0
- package/dist/core/skills/builtin/continuous-learning-v2/SKILL.md +360 -0
- package/dist/core/skills/builtin/continuous-learning-v2/agents/observer-loop.sh +335 -0
- package/dist/core/skills/builtin/continuous-learning-v2/agents/observer.md +198 -0
- package/dist/core/skills/builtin/continuous-learning-v2/agents/session-guardian.sh +150 -0
- package/dist/core/skills/builtin/continuous-learning-v2/agents/start-observer.sh +248 -0
- package/dist/core/skills/builtin/continuous-learning-v2/config.json +8 -0
- package/dist/core/skills/builtin/continuous-learning-v2/hooks/observe.sh +498 -0
- package/dist/core/skills/builtin/continuous-learning-v2/scripts/detect-project.sh +322 -0
- package/dist/core/skills/builtin/continuous-learning-v2/scripts/instinct-cli.py +1914 -0
- package/dist/core/skills/builtin/continuous-learning-v2/scripts/lib/homunculus-dir.sh +31 -0
- package/dist/core/skills/builtin/continuous-learning-v2/scripts/migrate-homunculus.sh +62 -0
- package/dist/core/skills/builtin/continuous-learning-v2/scripts/test_parse_instinct.py +1045 -0
- package/dist/core/skills/builtin/cost-aware-llm-pipeline/SKILL.md +183 -0
- package/dist/core/skills/builtin/cost-tracking/SKILL.md +147 -0
- package/dist/core/skills/builtin/council/SKILL.md +203 -0
- package/dist/core/skills/builtin/cpp-coding-standards/SKILL.md +723 -0
- package/dist/core/skills/builtin/cpp-testing/SKILL.md +324 -0
- package/dist/core/skills/builtin/crosspost/SKILL.md +111 -0
- package/dist/core/skills/builtin/csharp-testing/SKILL.md +321 -0
- package/dist/core/skills/builtin/customs-trade-compliance/SKILL.md +263 -0
- package/dist/core/skills/builtin/dart-flutter-patterns/SKILL.md +563 -0
- package/dist/core/skills/builtin/dashboard-builder/SKILL.md +108 -0
- package/dist/core/skills/builtin/data-scraper-agent/SKILL.md +764 -0
- package/dist/core/skills/builtin/data-throughput-accelerator/SKILL.md +72 -0
- package/dist/core/skills/builtin/database-migrations/SKILL.md +429 -0
- package/dist/core/skills/builtin/deep-research/SKILL.md +159 -0
- package/dist/core/skills/builtin/defi-amm-security/SKILL.md +166 -0
- package/dist/core/skills/builtin/deployment-patterns/SKILL.md +427 -0
- package/dist/core/skills/builtin/design-system/SKILL.md +82 -0
- package/dist/core/skills/builtin/django-celery/SKILL.md +457 -0
- package/dist/core/skills/builtin/django-patterns/SKILL.md +734 -0
- package/dist/core/skills/builtin/django-security/SKILL.md +593 -0
- package/dist/core/skills/builtin/django-tdd/SKILL.md +729 -0
- package/dist/core/skills/builtin/django-verification/SKILL.md +469 -0
- package/dist/core/skills/builtin/dmux-workflows/SKILL.md +191 -0
- package/dist/core/skills/builtin/docker-patterns/SKILL.md +364 -0
- package/dist/core/skills/builtin/documentation-lookup/SKILL.md +90 -0
- package/dist/core/skills/builtin/dotnet-patterns/SKILL.md +321 -0
- package/dist/core/skills/builtin/dynamic-workflow-mode/SKILL.md +123 -0
- package/dist/core/skills/builtin/e2e-testing/SKILL.md +326 -0
- package/dist/core/skills/builtin/email-ops/SKILL.md +121 -0
- package/dist/core/skills/builtin/energy-procurement/SKILL.md +228 -0
- package/dist/core/skills/builtin/enterprise-agent-ops/SKILL.md +50 -0
- package/dist/core/skills/builtin/error-handling/SKILL.md +376 -0
- package/dist/core/skills/builtin/eval-harness/SKILL.md +270 -0
- package/dist/core/skills/builtin/evm-token-decimals/SKILL.md +130 -0
- package/dist/core/skills/builtin/exa-search/SKILL.md +107 -0
- package/dist/core/skills/builtin/fal-ai-media/SKILL.md +288 -0
- package/dist/core/skills/builtin/fastapi-patterns/SKILL.md +513 -0
- package/dist/core/skills/builtin/finance-billing-ops/SKILL.md +127 -0
- package/dist/core/skills/builtin/flox-environments/SKILL.md +496 -0
- package/dist/core/skills/builtin/flutter-dart-code-review/SKILL.md +435 -0
- package/dist/core/skills/builtin/foundation-models-on-device/SKILL.md +243 -0
- package/dist/core/skills/builtin/frontend-a11y/SKILL.md +445 -0
- package/dist/core/skills/builtin/frontend-design-direction/SKILL.md +92 -0
- package/dist/core/skills/builtin/frontend-patterns/SKILL.md +656 -0
- package/dist/core/skills/builtin/frontend-slides/SKILL.md +184 -0
- package/dist/core/skills/builtin/frontend-slides/STYLE_PRESETS.md +330 -0
- package/dist/core/skills/builtin/frontend-slides/animation-patterns.md +122 -0
- package/dist/core/skills/builtin/frontend-slides/html-template.md +419 -0
- package/dist/core/skills/builtin/frontend-slides/scripts/export-pdf.sh +418 -0
- package/dist/core/skills/builtin/frontend-slides/scripts/extract-pptx.py +96 -0
- package/dist/core/skills/builtin/frontend-slides/viewport-base.css +153 -0
- package/dist/core/skills/builtin/fsharp-testing/SKILL.md +280 -0
- package/dist/core/skills/builtin/gan-style-harness/SKILL.md +278 -0
- package/dist/core/skills/builtin/gateguard/SKILL.md +132 -0
- package/dist/core/skills/builtin/git-workflow/SKILL.md +715 -0
- package/dist/core/skills/builtin/github-ops/SKILL.md +144 -0
- package/dist/core/skills/builtin/golang-patterns/SKILL.md +674 -0
- package/dist/core/skills/builtin/golang-testing/SKILL.md +720 -0
- package/dist/core/skills/builtin/healthcare-cdss-patterns/SKILL.md +245 -0
- package/dist/core/skills/builtin/healthcare-emr-patterns/SKILL.md +159 -0
- package/dist/core/skills/builtin/healthcare-eval-harness/SKILL.md +207 -0
- package/dist/core/skills/builtin/healthcare-phi-compliance/SKILL.md +145 -0
- package/dist/core/skills/builtin/hermes-imports/SKILL.md +88 -0
- package/dist/core/skills/builtin/hexagonal-architecture/SKILL.md +276 -0
- package/dist/core/skills/builtin/hipaa-compliance/SKILL.md +78 -0
- package/dist/core/skills/builtin/hookify-rules/SKILL.md +128 -0
- package/dist/core/skills/builtin/inherit-legacy-style/SKILL.md +156 -0
- package/dist/core/skills/builtin/intent-driven-development/SKILL.md +360 -0
- package/dist/core/skills/builtin/inventory-demand-planning/SKILL.md +247 -0
- package/dist/core/skills/builtin/ios-icon-gen/SKILL.md +157 -0
- package/dist/core/skills/builtin/ios-icon-gen/scripts/generate_icons.swift +258 -0
- package/dist/core/skills/builtin/ios-icon-gen/scripts/iconify_gen.sh +235 -0
- package/dist/core/skills/builtin/iterative-retrieval/SKILL.md +211 -0
- package/dist/core/skills/builtin/java-coding-standards/SKILL.md +383 -0
- package/dist/core/skills/builtin/jira-integration/SKILL.md +302 -0
- package/dist/core/skills/builtin/jpa-patterns/SKILL.md +151 -0
- package/dist/core/skills/builtin/knowledge-ops/SKILL.md +154 -0
- package/dist/core/skills/builtin/kotlin-coroutines-flows/SKILL.md +284 -0
- package/dist/core/skills/builtin/kotlin-exposed-patterns/SKILL.md +719 -0
- package/dist/core/skills/builtin/kotlin-ktor-patterns/SKILL.md +689 -0
- package/dist/core/skills/builtin/kotlin-patterns/SKILL.md +711 -0
- package/dist/core/skills/builtin/kotlin-testing/SKILL.md +824 -0
- package/dist/core/skills/builtin/kubernetes-patterns/SKILL.md +755 -0
- package/dist/core/skills/builtin/laravel-patterns/SKILL.md +415 -0
- package/dist/core/skills/builtin/laravel-plugin-discovery/SKILL.md +229 -0
- package/dist/core/skills/builtin/laravel-security/SKILL.md +947 -0
- package/dist/core/skills/builtin/laravel-tdd/SKILL.md +674 -0
- package/dist/core/skills/builtin/laravel-verification/SKILL.md +179 -0
- package/dist/core/skills/builtin/latency-critical-systems/SKILL.md +73 -0
- package/dist/core/skills/builtin/lead-intelligence/SKILL.md +321 -0
- package/dist/core/skills/builtin/lead-intelligence/agents/enrichment-agent.md +85 -0
- package/dist/core/skills/builtin/lead-intelligence/agents/mutual-mapper.md +75 -0
- package/dist/core/skills/builtin/lead-intelligence/agents/outreach-drafter.md +98 -0
- package/dist/core/skills/builtin/lead-intelligence/agents/signal-scorer.md +60 -0
- package/dist/core/skills/builtin/liquid-glass-design/SKILL.md +279 -0
- package/dist/core/skills/builtin/llm-trading-agent-security/SKILL.md +146 -0
- package/dist/core/skills/builtin/logistics-exception-management/SKILL.md +222 -0
- package/dist/core/skills/builtin/make-interfaces-feel-better/SKILL.md +151 -0
- package/dist/core/skills/builtin/market-research/SKILL.md +75 -0
- package/dist/core/skills/builtin/marketing-campaign/SKILL.md +113 -0
- package/dist/core/skills/builtin/mcp-server-patterns/SKILL.md +69 -0
- package/dist/core/skills/builtin/messages-ops/SKILL.md +104 -0
- package/dist/core/skills/builtin/mle-workflow/SKILL.md +346 -0
- package/dist/core/skills/builtin/motion-advanced/SKILL.md +596 -0
- package/dist/core/skills/builtin/motion-foundations/SKILL.md +299 -0
- package/dist/core/skills/builtin/motion-patterns/SKILL.md +434 -0
- package/dist/core/skills/builtin/motion-ui/SKILL.md +575 -0
- package/dist/core/skills/builtin/mysql-patterns/SKILL.md +412 -0
- package/dist/core/skills/builtin/nanoclaw-repl/SKILL.md +33 -0
- package/dist/core/skills/builtin/nestjs-patterns/SKILL.md +230 -0
- package/dist/core/skills/builtin/netmiko-ssh-automation/SKILL.md +173 -0
- package/dist/core/skills/builtin/network-bgp-diagnostics/SKILL.md +167 -0
- package/dist/core/skills/builtin/network-config-validation/SKILL.md +210 -0
- package/dist/core/skills/builtin/network-interface-health/SKILL.md +152 -0
- package/dist/core/skills/builtin/nextjs-turbopack/SKILL.md +57 -0
- package/dist/core/skills/builtin/nodejs-keccak256/SKILL.md +102 -0
- package/dist/core/skills/builtin/nutrient-document-processing/SKILL.md +167 -0
- package/dist/core/skills/builtin/nuxt4-patterns/SKILL.md +100 -0
- package/dist/core/skills/builtin/openclaw-persona-forge/SKILL.md +288 -0
- package/dist/core/skills/builtin/openclaw-persona-forge/gacha.py +224 -0
- package/dist/core/skills/builtin/openclaw-persona-forge/gacha.sh +5 -0
- package/dist/core/skills/builtin/openclaw-persona-forge/references/avatar-style.md +124 -0
- package/dist/core/skills/builtin/openclaw-persona-forge/references/boundary-rules.md +53 -0
- package/dist/core/skills/builtin/openclaw-persona-forge/references/error-handling.md +53 -0
- package/dist/core/skills/builtin/openclaw-persona-forge/references/identity-tension.md +48 -0
- package/dist/core/skills/builtin/openclaw-persona-forge/references/naming-system.md +39 -0
- package/dist/core/skills/builtin/openclaw-persona-forge/references/output-template.md +166 -0
- package/dist/core/skills/builtin/opensource-pipeline/SKILL.md +255 -0
- package/dist/core/skills/builtin/orch-add-feature/SKILL.md +44 -0
- package/dist/core/skills/builtin/orch-build-mvp/SKILL.md +48 -0
- package/dist/core/skills/builtin/orch-change-feature/SKILL.md +42 -0
- package/dist/core/skills/builtin/orch-fix-defect/SKILL.md +42 -0
- package/dist/core/skills/builtin/orch-pipeline/SKILL.md +120 -0
- package/dist/core/skills/builtin/orch-refine-code/SKILL.md +43 -0
- package/dist/core/skills/builtin/parallel-execution-optimizer/SKILL.md +72 -0
- package/dist/core/skills/builtin/perl-patterns/SKILL.md +504 -0
- package/dist/core/skills/builtin/perl-security/SKILL.md +503 -0
- package/dist/core/skills/builtin/perl-testing/SKILL.md +475 -0
- package/dist/core/skills/builtin/plan-orchestrate/SKILL.md +262 -0
- package/dist/core/skills/builtin/plankton-code-quality/SKILL.md +236 -0
- package/dist/core/skills/builtin/postgres-patterns/SKILL.md +147 -0
- package/dist/core/skills/builtin/prediction-market-oracle-research/SKILL.md +63 -0
- package/dist/core/skills/builtin/prediction-market-risk-review/SKILL.md +60 -0
- package/dist/core/skills/builtin/prisma-patterns/SKILL.md +371 -0
- package/dist/core/skills/builtin/product-capability/SKILL.md +141 -0
- package/dist/core/skills/builtin/product-lens/SKILL.md +92 -0
- package/dist/core/skills/builtin/production-audit/SKILL.md +206 -0
- package/dist/core/skills/builtin/production-scheduling/SKILL.md +238 -0
- package/dist/core/skills/builtin/prompt-optimizer/SKILL.md +398 -0
- package/dist/core/skills/builtin/python-patterns/SKILL.md +750 -0
- package/dist/core/skills/builtin/python-testing/SKILL.md +816 -0
- package/dist/core/skills/builtin/pytorch-patterns/SKILL.md +396 -0
- package/dist/core/skills/builtin/quality-nonconformance/SKILL.md +260 -0
- package/dist/core/skills/builtin/quarkus-patterns/SKILL.md +722 -0
- package/dist/core/skills/builtin/quarkus-security/SKILL.md +467 -0
- package/dist/core/skills/builtin/quarkus-tdd/SKILL.md +811 -0
- package/dist/core/skills/builtin/quarkus-verification/SKILL.md +479 -0
- package/dist/core/skills/builtin/ralphinho-rfc-pipeline/SKILL.md +67 -0
- package/dist/core/skills/builtin/react-patterns/SKILL.md +341 -0
- package/dist/core/skills/builtin/react-performance/SKILL.md +574 -0
- package/dist/core/skills/builtin/react-testing/SKILL.md +423 -0
- package/dist/core/skills/builtin/recsys-pipeline-architect/SKILL.md +114 -0
- package/dist/core/skills/builtin/recursive-decision-ledger/SKILL.md +79 -0
- package/dist/core/skills/builtin/redis-patterns/SKILL.md +403 -0
- package/dist/core/skills/builtin/regex-vs-llm-structured-text/SKILL.md +220 -0
- package/dist/core/skills/builtin/repo-scan/SKILL.md +78 -0
- package/dist/core/skills/builtin/research-ops/SKILL.md +112 -0
- package/dist/core/skills/builtin/returns-reverse-logistics/SKILL.md +240 -0
- package/dist/core/skills/builtin/rules-distill/SKILL.md +264 -0
- package/dist/core/skills/builtin/rules-distill/scripts/scan-rules.sh +58 -0
- package/dist/core/skills/builtin/rules-distill/scripts/scan-skills.sh +129 -0
- package/dist/core/skills/builtin/rust-patterns/SKILL.md +499 -0
- package/dist/core/skills/builtin/rust-testing/SKILL.md +500 -0
- package/dist/core/skills/builtin/safety-guard/SKILL.md +75 -0
- package/dist/core/skills/builtin/santa-method/SKILL.md +306 -0
- package/dist/core/skills/builtin/scientific-db-pubmed-database/SKILL.md +175 -0
- package/dist/core/skills/builtin/scientific-db-uspto-database/SKILL.md +177 -0
- package/dist/core/skills/builtin/scientific-pkg-gget/SKILL.md +166 -0
- package/dist/core/skills/builtin/scientific-thinking-literature-review/SKILL.md +192 -0
- package/dist/core/skills/builtin/scientific-thinking-scholar-evaluation/SKILL.md +160 -0
- package/dist/core/skills/builtin/search-first/SKILL.md +182 -0
- package/dist/core/skills/builtin/security-bounty-hunter/SKILL.md +99 -0
- package/dist/core/skills/builtin/security-review/SKILL.md +503 -0
- package/dist/core/skills/builtin/security-review/cloud-infrastructure-security.md +361 -0
- package/dist/core/skills/builtin/security-scan/SKILL.md +165 -0
- package/dist/core/skills/builtin/seo/SKILL.md +154 -0
- package/dist/core/skills/builtin/skill-comply/SKILL.md +58 -0
- package/dist/core/skills/builtin/skill-comply/fixtures/compliant_trace.jsonl +5 -0
- package/dist/core/skills/builtin/skill-comply/fixtures/noncompliant_trace.jsonl +3 -0
- package/dist/core/skills/builtin/skill-comply/fixtures/tdd_spec.yaml +44 -0
- package/dist/core/skills/builtin/skill-comply/prompts/classifier.md +24 -0
- package/dist/core/skills/builtin/skill-comply/prompts/scenario_generator.md +62 -0
- package/dist/core/skills/builtin/skill-comply/prompts/spec_generator.md +42 -0
- package/dist/core/skills/builtin/skill-comply/pyproject.toml +15 -0
- package/dist/core/skills/builtin/skill-comply/scripts/__init__.py +0 -0
- package/dist/core/skills/builtin/skill-comply/scripts/classifier.py +85 -0
- package/dist/core/skills/builtin/skill-comply/scripts/grader.py +124 -0
- package/dist/core/skills/builtin/skill-comply/scripts/parser.py +107 -0
- package/dist/core/skills/builtin/skill-comply/scripts/report.py +170 -0
- package/dist/core/skills/builtin/skill-comply/scripts/run.py +127 -0
- package/dist/core/skills/builtin/skill-comply/scripts/runner.py +186 -0
- package/dist/core/skills/builtin/skill-comply/scripts/scenario_generator.py +70 -0
- package/dist/core/skills/builtin/skill-comply/scripts/spec_generator.py +72 -0
- package/dist/core/skills/builtin/skill-comply/scripts/utils.py +13 -0
- package/dist/core/skills/builtin/skill-comply/tests/test_grader.py +197 -0
- package/dist/core/skills/builtin/skill-comply/tests/test_parser.py +90 -0
- package/dist/core/skills/builtin/skill-comply/tests/test_runner.py +172 -0
- package/dist/core/skills/builtin/skill-scout/SKILL.md +140 -0
- package/dist/core/skills/builtin/skill-stocktake/SKILL.md +194 -0
- package/dist/core/skills/builtin/skill-stocktake/scripts/quick-diff.sh +87 -0
- package/dist/core/skills/builtin/skill-stocktake/scripts/save-results.sh +56 -0
- package/dist/core/skills/builtin/skill-stocktake/scripts/scan.sh +170 -0
- package/dist/core/skills/builtin/springboot-patterns/SKILL.md +314 -0
- package/dist/core/skills/builtin/springboot-security/SKILL.md +272 -0
- package/dist/core/skills/builtin/springboot-tdd/SKILL.md +158 -0
- package/dist/core/skills/builtin/springboot-verification/SKILL.md +231 -0
- package/dist/core/skills/builtin/strategic-compact/SKILL.md +135 -0
- package/dist/core/skills/builtin/swift-actor-persistence/SKILL.md +143 -0
- package/dist/core/skills/builtin/swift-concurrency-6-2/SKILL.md +216 -0
- package/dist/core/skills/builtin/swift-protocol-di-testing/SKILL.md +190 -0
- package/dist/core/skills/builtin/swiftui-patterns/SKILL.md +259 -0
- package/dist/core/skills/builtin/tdd-workflow/SKILL.md +463 -0
- package/dist/core/skills/builtin/team-agent-orchestration/SKILL.md +110 -0
- package/dist/core/skills/builtin/team-builder/SKILL.md +168 -0
- package/dist/core/skills/builtin/terminal-ops/SKILL.md +109 -0
- package/dist/core/skills/builtin/tinystruct-patterns/SKILL.md +203 -0
- package/dist/core/skills/builtin/tinystruct-patterns/references/architecture.md +90 -0
- package/dist/core/skills/builtin/tinystruct-patterns/references/data-handling.md +60 -0
- package/dist/core/skills/builtin/tinystruct-patterns/references/database.md +99 -0
- package/dist/core/skills/builtin/tinystruct-patterns/references/routing.md +64 -0
- package/dist/core/skills/builtin/tinystruct-patterns/references/system-usage.md +97 -0
- package/dist/core/skills/builtin/tinystruct-patterns/references/testing.md +72 -0
- package/dist/core/skills/builtin/token-budget-advisor/SKILL.md +133 -0
- package/dist/core/skills/builtin/ui-demo/SKILL.md +465 -0
- package/dist/core/skills/builtin/ui-to-vue/SKILL.md +134 -0
- package/dist/core/skills/builtin/uncloud/SKILL.md +343 -0
- package/dist/core/skills/builtin/unified-notifications-ops/SKILL.md +187 -0
- package/dist/core/skills/builtin/verification-loop/SKILL.md +126 -0
- package/dist/core/skills/builtin/video-editing/SKILL.md +310 -0
- package/dist/core/skills/builtin/videodb/SKILL.md +374 -0
- package/dist/core/skills/builtin/videodb/reference/api-reference.md +550 -0
- package/dist/core/skills/builtin/videodb/reference/capture-reference.md +407 -0
- package/dist/core/skills/builtin/videodb/reference/capture.md +101 -0
- package/dist/core/skills/builtin/videodb/reference/editor.md +443 -0
- package/dist/core/skills/builtin/videodb/reference/generative.md +331 -0
- package/dist/core/skills/builtin/videodb/reference/rtstream-reference.md +564 -0
- package/dist/core/skills/builtin/videodb/reference/rtstream.md +65 -0
- package/dist/core/skills/builtin/videodb/reference/search.md +230 -0
- package/dist/core/skills/builtin/videodb/reference/streaming.md +406 -0
- package/dist/core/skills/builtin/videodb/reference/use-cases.md +118 -0
- package/dist/core/skills/builtin/videodb/scripts/ws_listener.py +282 -0
- package/dist/core/skills/builtin/visa-doc-translate/README.md +86 -0
- package/dist/core/skills/builtin/visa-doc-translate/SKILL.md +117 -0
- package/dist/core/skills/builtin/vite-patterns/SKILL.md +449 -0
- package/dist/core/skills/builtin/windows-desktop-e2e/SKILL.md +887 -0
- package/dist/core/skills/builtin/x-api/SKILL.md +234 -0
- package/dist/core/skills/loader.js +11 -0
- package/dist/core/skills/loader.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,674 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: laravel-tdd
|
|
3
|
+
description: [ECC] Laravel testing strategies with PHPUnit, Pest, model factories, HTTP tests, Sanctum authentication testing, mocking, and coverage.
|
|
4
|
+
origin: ECC
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Laravel Testing with TDD
|
|
8
|
+
|
|
9
|
+
Test-driven development for Laravel applications using PHPUnit, Pest, Laravel factories, and testing helpers.
|
|
10
|
+
|
|
11
|
+
## When to Activate
|
|
12
|
+
|
|
13
|
+
- Writing new Laravel applications or features
|
|
14
|
+
- Implementing API endpoints with Sanctum or Passport authentication
|
|
15
|
+
- Testing Eloquent models, relationships, scopes, and accessors
|
|
16
|
+
- Setting up testing infrastructure for Laravel projects
|
|
17
|
+
- Writing feature tests for HTTP controllers and form requests
|
|
18
|
+
- Mocking external services (queues, mail, notifications, HTTP)
|
|
19
|
+
|
|
20
|
+
## TDD Workflow for Laravel
|
|
21
|
+
|
|
22
|
+
### Red-Green-Refactor Cycle
|
|
23
|
+
|
|
24
|
+
```php
|
|
25
|
+
// Step 1: RED — Write a failing test
|
|
26
|
+
public function test_a_product_can_be_created(): void
|
|
27
|
+
{
|
|
28
|
+
$product = Product::factory()->create(['name' => 'Test Product']);
|
|
29
|
+
$this->assertDatabaseHas('products', ['name' => 'Test Product']);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Step 2: GREEN — Write the migration, model, and factory
|
|
33
|
+
// Step 3: REFACTOR — Improve while keeping tests green
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Setup
|
|
37
|
+
|
|
38
|
+
### PHPUnit Configuration
|
|
39
|
+
|
|
40
|
+
```xml
|
|
41
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
42
|
+
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
43
|
+
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
|
|
44
|
+
bootstrap="vendor/autoload.php"
|
|
45
|
+
colors="true">
|
|
46
|
+
<testsuites>
|
|
47
|
+
<testsuite name="Unit">
|
|
48
|
+
<directory suffix="Test.php">tests/Unit</directory>
|
|
49
|
+
</testsuite>
|
|
50
|
+
<testsuite name="Feature">
|
|
51
|
+
<directory suffix="Test.php">tests/Feature</directory>
|
|
52
|
+
</testsuite>
|
|
53
|
+
</testsuites>
|
|
54
|
+
<php>
|
|
55
|
+
<env name="APP_ENV" value="testing"/>
|
|
56
|
+
<env name="BCRYPT_ROUNDS" value="4"/>
|
|
57
|
+
<env name="CACHE_STORE" value="array"/>
|
|
58
|
+
<env name="DB_CONNECTION" value="sqlite"/>
|
|
59
|
+
<env name="DB_DATABASE" value=":memory:"/>
|
|
60
|
+
<env name="MAIL_MAILER" value="array"/>
|
|
61
|
+
<env name="QUEUE_CONNECTION" value="sync"/>
|
|
62
|
+
<env name="SESSION_DRIVER" value="array"/>
|
|
63
|
+
</php>
|
|
64
|
+
</phpunit>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Base TestCase Setup
|
|
68
|
+
|
|
69
|
+
```php
|
|
70
|
+
namespace Tests;
|
|
71
|
+
|
|
72
|
+
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
|
|
73
|
+
|
|
74
|
+
abstract class TestCase extends BaseTestCase
|
|
75
|
+
{
|
|
76
|
+
protected function setUp(): void
|
|
77
|
+
{
|
|
78
|
+
parent::setUp();
|
|
79
|
+
// Call $this->withoutExceptionHandling() only in tests that
|
|
80
|
+
// test non-HTTP exceptions; it suppresses assertStatus() etc.
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Helper: Authenticate and return user
|
|
84
|
+
protected function actingAsUser(): mixed
|
|
85
|
+
{
|
|
86
|
+
$user = \App\Models\User::factory()->create();
|
|
87
|
+
$this->actingAs($user);
|
|
88
|
+
return $user;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
protected function actingAsAdmin(): mixed
|
|
92
|
+
{
|
|
93
|
+
$admin = \App\Models\User::factory()->admin()->create();
|
|
94
|
+
$this->actingAs($admin);
|
|
95
|
+
return $admin;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Model Factories
|
|
101
|
+
|
|
102
|
+
```php
|
|
103
|
+
// database/factories/UserFactory.php
|
|
104
|
+
class UserFactory extends Factory
|
|
105
|
+
{
|
|
106
|
+
protected static ?string $password = null;
|
|
107
|
+
|
|
108
|
+
public function definition(): array
|
|
109
|
+
{
|
|
110
|
+
return [
|
|
111
|
+
'name' => fake()->name(),
|
|
112
|
+
'email' => fake()->unique()->safeEmail(),
|
|
113
|
+
'email_verified_at' => now(),
|
|
114
|
+
'password' => static::$password ??= Hash::make('password'),
|
|
115
|
+
'remember_token' => Str::random(10),
|
|
116
|
+
'role' => 'user',
|
|
117
|
+
];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
public function admin(): static
|
|
121
|
+
{
|
|
122
|
+
return $this->state(fn (array $attributes) => ['role' => 'admin']);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
public function unverified(): static
|
|
126
|
+
{
|
|
127
|
+
return $this->state(fn (array $attributes) => ['email_verified_at' => null]);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// database/factories/ProductFactory.php
|
|
132
|
+
class ProductFactory extends Factory
|
|
133
|
+
{
|
|
134
|
+
public function definition(): array
|
|
135
|
+
{
|
|
136
|
+
return [
|
|
137
|
+
'name' => fake()->unique()->words(3, true),
|
|
138
|
+
'slug' => fn (array $attrs) => Str::slug($attrs['name']),
|
|
139
|
+
'description' => fake()->paragraph(),
|
|
140
|
+
'price' => fake()->numberBetween(100, 100000),
|
|
141
|
+
'stock' => fake()->numberBetween(0, 100),
|
|
142
|
+
'is_active' => true,
|
|
143
|
+
'user_id' => UserFactory::new(),
|
|
144
|
+
];
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
public function outOfStock(): static
|
|
148
|
+
{
|
|
149
|
+
return $this->state(fn (array $attributes) => ['stock' => 0]);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Using Factories
|
|
155
|
+
|
|
156
|
+
```php
|
|
157
|
+
$user = User::factory()->create();
|
|
158
|
+
$admin = User::factory()->admin()->create();
|
|
159
|
+
$product = Product::factory()->create(['user_id' => $user->id]);
|
|
160
|
+
$products = Product::factory()->count(10)->create();
|
|
161
|
+
$draft = Product::factory()->make(); // Not persisted
|
|
162
|
+
|
|
163
|
+
// With relationships
|
|
164
|
+
$user = User::factory()->has(Product::factory()->count(3))->create();
|
|
165
|
+
|
|
166
|
+
// Sequences
|
|
167
|
+
User::factory()->count(3)->sequence(
|
|
168
|
+
['role' => 'admin'], ['role' => 'editor'], ['role' => 'user'],
|
|
169
|
+
)->create();
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Model Testing
|
|
173
|
+
|
|
174
|
+
```php
|
|
175
|
+
namespace Tests\Unit\Models;
|
|
176
|
+
|
|
177
|
+
use App\Models\User;
|
|
178
|
+
use App\Models\Product;
|
|
179
|
+
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
180
|
+
use Tests\TestCase;
|
|
181
|
+
|
|
182
|
+
class UserTest extends TestCase
|
|
183
|
+
{
|
|
184
|
+
use RefreshDatabase;
|
|
185
|
+
|
|
186
|
+
public function test_it_hides_sensitive_attributes(): void
|
|
187
|
+
{
|
|
188
|
+
$user = User::factory()->create();
|
|
189
|
+
$this->assertArrayNotHasKey('password', $user->toArray());
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
public function test_admin_scope_returns_only_admins(): void
|
|
193
|
+
{
|
|
194
|
+
User::factory()->admin()->create();
|
|
195
|
+
User::factory()->count(3)->create();
|
|
196
|
+
|
|
197
|
+
$this->assertCount(1, User::admin()->get());
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
class ProductTest extends TestCase
|
|
202
|
+
{
|
|
203
|
+
use RefreshDatabase;
|
|
204
|
+
|
|
205
|
+
public function test_active_scope_filters_correctly(): void
|
|
206
|
+
{
|
|
207
|
+
Product::factory()->count(3)->create(['is_active' => true]);
|
|
208
|
+
Product::factory()->count(2)->create(['is_active' => false]);
|
|
209
|
+
|
|
210
|
+
$this->assertCount(3, Product::active()->get());
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
public function test_it_belongs_to_a_user(): void
|
|
214
|
+
{
|
|
215
|
+
$user = User::factory()->create();
|
|
216
|
+
$product = Product::factory()->create(['user_id' => $user->id]);
|
|
217
|
+
|
|
218
|
+
$this->assertTrue($product->user->is($user));
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## Feature / HTTP Testing
|
|
224
|
+
|
|
225
|
+
```php
|
|
226
|
+
namespace Tests\Feature\Http\Controllers;
|
|
227
|
+
|
|
228
|
+
use App\Models\Product;
|
|
229
|
+
use App\Models\User;
|
|
230
|
+
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
231
|
+
use Tests\TestCase;
|
|
232
|
+
|
|
233
|
+
class ProductControllerTest extends TestCase
|
|
234
|
+
{
|
|
235
|
+
use RefreshDatabase;
|
|
236
|
+
|
|
237
|
+
public function test_guests_are_redirected_to_login(): void
|
|
238
|
+
{
|
|
239
|
+
$this->get(route('products.create'))->assertRedirect(route('login'));
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
public function test_it_stores_a_new_product(): void
|
|
243
|
+
{
|
|
244
|
+
$user = User::factory()->create();
|
|
245
|
+
$this->actingAs($user);
|
|
246
|
+
|
|
247
|
+
$response = $this->post(route('products.store'), [
|
|
248
|
+
'name' => 'New Product',
|
|
249
|
+
'description' => 'Description',
|
|
250
|
+
'price' => 2999,
|
|
251
|
+
'stock' => 10,
|
|
252
|
+
]);
|
|
253
|
+
|
|
254
|
+
$response->assertRedirect(route('products.index'));
|
|
255
|
+
$this->assertDatabaseHas('products', [
|
|
256
|
+
'name' => 'New Product',
|
|
257
|
+
'user_id' => $user->id,
|
|
258
|
+
]);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
public function test_it_validates_required_fields(): void
|
|
262
|
+
{
|
|
263
|
+
$this->actingAs(User::factory()->create());
|
|
264
|
+
$this->post(route('products.store'), [])
|
|
265
|
+
->assertSessionHasErrors(['name', 'price']);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
public function test_users_cannot_modify_others_products(): void
|
|
269
|
+
{
|
|
270
|
+
$owner = User::factory()->create();
|
|
271
|
+
$attacker = User::factory()->create();
|
|
272
|
+
$product = Product::factory()->create(['user_id' => $owner->id]);
|
|
273
|
+
|
|
274
|
+
$this->actingAs($attacker)
|
|
275
|
+
->delete(route('products.destroy', $product))
|
|
276
|
+
->assertForbidden();
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
## JSON API Testing
|
|
282
|
+
|
|
283
|
+
```php
|
|
284
|
+
namespace Tests\Feature\Http\Controllers\Api;
|
|
285
|
+
|
|
286
|
+
use App\Models\Product;
|
|
287
|
+
use App\Models\User;
|
|
288
|
+
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
289
|
+
use Tests\TestCase;
|
|
290
|
+
|
|
291
|
+
class ProductApiTest extends TestCase
|
|
292
|
+
{
|
|
293
|
+
use RefreshDatabase;
|
|
294
|
+
|
|
295
|
+
public function test_unauthenticated_requests_are_rejected(): void
|
|
296
|
+
{
|
|
297
|
+
$this->getJson('/api/products')->assertUnauthorized();
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
public function test_it_lists_paginated_products(): void
|
|
301
|
+
{
|
|
302
|
+
$user = User::factory()->create();
|
|
303
|
+
Product::factory()->count(5)->create(['user_id' => $user->id]);
|
|
304
|
+
|
|
305
|
+
$response = $this->actingAs($user)->getJson('/api/products');
|
|
306
|
+
|
|
307
|
+
$response->assertOk();
|
|
308
|
+
$response->assertJsonCount(5, 'data');
|
|
309
|
+
$response->assertJsonStructure([
|
|
310
|
+
'data' => [['id', 'name', 'price']],
|
|
311
|
+
'meta' => ['current_page', 'last_page', 'total'],
|
|
312
|
+
]);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
public function test_it_creates_a_product(): void
|
|
316
|
+
{
|
|
317
|
+
$user = User::factory()->create();
|
|
318
|
+
|
|
319
|
+
$response = $this->actingAs($user)->postJson('/api/products', [
|
|
320
|
+
'name' => 'API Product',
|
|
321
|
+
'price' => 4999,
|
|
322
|
+
]);
|
|
323
|
+
|
|
324
|
+
$response->assertCreated();
|
|
325
|
+
$response->assertJsonPath('data.name', 'API Product');
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
public function test_users_cannot_delete_others_products(): void
|
|
329
|
+
{
|
|
330
|
+
$owner = User::factory()->create();
|
|
331
|
+
$attacker = User::factory()->create();
|
|
332
|
+
$product = Product::factory()->create(['user_id' => $owner->id]);
|
|
333
|
+
|
|
334
|
+
$this->actingAs($attacker)
|
|
335
|
+
->deleteJson("/api/products/{$product->id}")
|
|
336
|
+
->assertForbidden();
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
## Sanctum API Auth Testing
|
|
342
|
+
|
|
343
|
+
```php
|
|
344
|
+
namespace Tests\Feature\Http\Controllers\Api;
|
|
345
|
+
|
|
346
|
+
use App\Models\User;
|
|
347
|
+
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
348
|
+
use Illuminate\Support\Facades\Hash;
|
|
349
|
+
use Tests\TestCase;
|
|
350
|
+
|
|
351
|
+
class AuthControllerTest extends TestCase
|
|
352
|
+
{
|
|
353
|
+
use RefreshDatabase;
|
|
354
|
+
|
|
355
|
+
public function test_users_can_register(): void
|
|
356
|
+
{
|
|
357
|
+
$response = $this->postJson('/api/register', [
|
|
358
|
+
'name' => 'Test User',
|
|
359
|
+
'email' => 'test@example.com',
|
|
360
|
+
'password' => 'Password123!',
|
|
361
|
+
'password_confirmation' => 'Password123!',
|
|
362
|
+
]);
|
|
363
|
+
|
|
364
|
+
$response->assertCreated();
|
|
365
|
+
$response->assertJsonStructure(['data' => ['user', 'token']]);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
public function test_users_can_login(): void
|
|
369
|
+
{
|
|
370
|
+
User::factory()->create([
|
|
371
|
+
'email' => 'test@example.com',
|
|
372
|
+
'password' => Hash::make('Password123!'),
|
|
373
|
+
]);
|
|
374
|
+
|
|
375
|
+
$response = $this->postJson('/api/login', [
|
|
376
|
+
'email' => 'test@example.com',
|
|
377
|
+
'password' => 'Password123!',
|
|
378
|
+
]);
|
|
379
|
+
|
|
380
|
+
$response->assertOk();
|
|
381
|
+
$response->assertJsonStructure(['data' => ['token']]);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
public function test_users_cannot_login_with_wrong_password(): void
|
|
385
|
+
{
|
|
386
|
+
User::factory()->create(['email' => 'test@example.com']);
|
|
387
|
+
|
|
388
|
+
$this->postJson('/api/login', [
|
|
389
|
+
'email' => 'test@example.com',
|
|
390
|
+
'password' => 'wrong',
|
|
391
|
+
])->assertUnprocessable();
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
public function test_token_bearer_authenticates_requests(): void
|
|
395
|
+
{
|
|
396
|
+
$user = User::factory()->create();
|
|
397
|
+
$token = $user->createToken('test')->plainTextToken;
|
|
398
|
+
|
|
399
|
+
$this->withToken($token)
|
|
400
|
+
->getJson('/api/user')
|
|
401
|
+
->assertOk()
|
|
402
|
+
->assertJsonPath('data.email', $user->email);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
## Mocking and Fakes
|
|
408
|
+
|
|
409
|
+
### HTTP Fake
|
|
410
|
+
|
|
411
|
+
```php
|
|
412
|
+
use Illuminate\Support\Facades\Http;
|
|
413
|
+
|
|
414
|
+
public function test_it_handles_successful_payment(): void
|
|
415
|
+
{
|
|
416
|
+
Http::fake([
|
|
417
|
+
'api.stripe.com/*' => Http::response(['id' => 'pi_123', 'status' => 'succeeded'], 200),
|
|
418
|
+
]);
|
|
419
|
+
|
|
420
|
+
$result = (new PaymentService())->charge(2999);
|
|
421
|
+
$this->assertTrue($result->success);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
public function test_it_handles_gateway_failure(): void
|
|
425
|
+
{
|
|
426
|
+
Http::fake([
|
|
427
|
+
'api.stripe.com/*' => Http::response(['error' => 'card_declined'], 402),
|
|
428
|
+
]);
|
|
429
|
+
|
|
430
|
+
$this->expectException(PaymentFailedException::class);
|
|
431
|
+
(new PaymentService())->charge(2999);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
public function test_it_retries_on_timeout(): void
|
|
435
|
+
{
|
|
436
|
+
Http::fake([
|
|
437
|
+
'api.stripe.com/*' => Http::sequence()
|
|
438
|
+
->pushStatus(408)
|
|
439
|
+
->pushStatus(200),
|
|
440
|
+
]);
|
|
441
|
+
|
|
442
|
+
$this->assertTrue((new PaymentService())->charge(2999)->success);
|
|
443
|
+
}
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
### Mail Fake
|
|
447
|
+
|
|
448
|
+
```php
|
|
449
|
+
Mail::fake();
|
|
450
|
+
|
|
451
|
+
$order->sendConfirmation();
|
|
452
|
+
|
|
453
|
+
Mail::assertSent(OrderConfirmation::class, function ($mail) use ($order) {
|
|
454
|
+
return $mail->hasTo($order->user->email);
|
|
455
|
+
});
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
### Notification Fake
|
|
459
|
+
|
|
460
|
+
```php
|
|
461
|
+
Notification::fake();
|
|
462
|
+
|
|
463
|
+
$user->notify(new WelcomeUser());
|
|
464
|
+
|
|
465
|
+
Notification::assertSentTo($user, WelcomeUser::class);
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
### Queue Fake
|
|
469
|
+
|
|
470
|
+
```php
|
|
471
|
+
Queue::fake();
|
|
472
|
+
|
|
473
|
+
ProcessImage::dispatch($product);
|
|
474
|
+
|
|
475
|
+
Queue::assertPushed(ProcessImage::class, function ($job) use ($product) {
|
|
476
|
+
return $job->product->id === $product->id;
|
|
477
|
+
});
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
### Storage Fake
|
|
481
|
+
|
|
482
|
+
```php
|
|
483
|
+
Storage::fake('public');
|
|
484
|
+
|
|
485
|
+
$file = UploadedFile::fake()->image('photo.jpg', 200, 200);
|
|
486
|
+
|
|
487
|
+
$response = $this->actingAs($user)->post('/avatar', [
|
|
488
|
+
'avatar' => $file,
|
|
489
|
+
]);
|
|
490
|
+
|
|
491
|
+
$response->assertSessionHasNoErrors();
|
|
492
|
+
Storage::disk('public')->assertExists('avatars/' . $file->hashName());
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
### Event Fake
|
|
496
|
+
|
|
497
|
+
```php
|
|
498
|
+
Event::fake();
|
|
499
|
+
|
|
500
|
+
$order->markAsShipped();
|
|
501
|
+
|
|
502
|
+
Event::assertDispatched(OrderShipped::class, function ($event) use ($order) {
|
|
503
|
+
return $event->order->id === $order->id;
|
|
504
|
+
});
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
## Artisan Command Tests
|
|
508
|
+
|
|
509
|
+
```php
|
|
510
|
+
public function test_it_sends_newsletters(): void
|
|
511
|
+
{
|
|
512
|
+
Mail::fake();
|
|
513
|
+
User::factory()->count(5)->create(['subscribed' => true]);
|
|
514
|
+
|
|
515
|
+
$this->artisan('newsletter:send')
|
|
516
|
+
->expectsOutput('Sending newsletter to 5 subscribers...')
|
|
517
|
+
->assertExitCode(0);
|
|
518
|
+
|
|
519
|
+
Mail::assertSent(NewsletterMail::class, 5);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
public function test_it_handles_no_subscribers(): void
|
|
523
|
+
{
|
|
524
|
+
$this->artisan('newsletter:send')
|
|
525
|
+
->expectsOutput('No subscribers found.')
|
|
526
|
+
->assertExitCode(0);
|
|
527
|
+
}
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
## Authorization Tests
|
|
531
|
+
|
|
532
|
+
```php
|
|
533
|
+
public function test_users_can_update_own_posts(): void
|
|
534
|
+
{
|
|
535
|
+
$user = User::factory()->create();
|
|
536
|
+
$post = Post::factory()->create(['user_id' => $user->id]);
|
|
537
|
+
|
|
538
|
+
$this->actingAs($user)
|
|
539
|
+
->put(route('posts.update', $post), ['title' => 'Updated'])
|
|
540
|
+
->assertRedirect();
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
public function test_users_cannot_update_others_posts(): void
|
|
544
|
+
{
|
|
545
|
+
$post = Post::factory()->create();
|
|
546
|
+
$this->actingAs(User::factory()->create())
|
|
547
|
+
->put(route('posts.update', $post), ['title' => 'Hacked'])
|
|
548
|
+
->assertForbidden();
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
public function test_gate_before_grants_super_admin_full_access(): void
|
|
552
|
+
{
|
|
553
|
+
$super = User::factory()->create(['role' => 'super-admin']);
|
|
554
|
+
$post = Post::factory()->create();
|
|
555
|
+
|
|
556
|
+
$this->actingAs($super)
|
|
557
|
+
->delete(route('posts.destroy', $post))
|
|
558
|
+
->assertRedirect();
|
|
559
|
+
|
|
560
|
+
$this->assertSoftDeleted($post);
|
|
561
|
+
}
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
## Pest Feature Tests
|
|
565
|
+
|
|
566
|
+
```php
|
|
567
|
+
<?php
|
|
568
|
+
|
|
569
|
+
use App\Models\Product;
|
|
570
|
+
use App\Models\User;
|
|
571
|
+
|
|
572
|
+
uses(\Illuminate\Foundation\Testing\RefreshDatabase::class);
|
|
573
|
+
|
|
574
|
+
beforeEach(function () {
|
|
575
|
+
$this->user = User::factory()->create();
|
|
576
|
+
$this->actingAs($this->user);
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
it('lists products', function () {
|
|
580
|
+
Product::factory()->count(3)->create(['user_id' => $this->user->id]);
|
|
581
|
+
|
|
582
|
+
$this->get(route('products.index'))
|
|
583
|
+
->assertOk()
|
|
584
|
+
->assertViewHas('products');
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
it('creates a product with valid data', function () {
|
|
588
|
+
$this->post(route('products.store'), [
|
|
589
|
+
'name' => 'Test Product', 'price' => 1999,
|
|
590
|
+
])->assertRedirect();
|
|
591
|
+
|
|
592
|
+
$this->assertDatabaseHas('products', ['name' => 'Test Product']);
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
it('fails validation without required fields', function () {
|
|
596
|
+
$this->post(route('products.store'), [])
|
|
597
|
+
->assertSessionHasErrors(['name', 'price']);
|
|
598
|
+
});
|
|
599
|
+
|
|
600
|
+
it('authorizes updates', function () {
|
|
601
|
+
$other = User::factory()->create();
|
|
602
|
+
$product = Product::factory()->create(['user_id' => $other->id]);
|
|
603
|
+
|
|
604
|
+
$this->put(route('products.update', $product), ['name' => 'Hacked'])
|
|
605
|
+
->assertForbidden();
|
|
606
|
+
});
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
## Coverage
|
|
610
|
+
|
|
611
|
+
```bash
|
|
612
|
+
# PHPUnit (use clover output for CI threshold checks)
|
|
613
|
+
vendor/bin/phpunit --coverage-html coverage --coverage-clover clover.xml
|
|
614
|
+
|
|
615
|
+
# Pest (built-in threshold support)
|
|
616
|
+
vendor/bin/pest --coverage --min=80
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
### Coverage Goals
|
|
620
|
+
|
|
621
|
+
| Component | Target |
|
|
622
|
+
|-----------|--------|
|
|
623
|
+
| Models | 95%+ |
|
|
624
|
+
| Actions/Services | 90%+ |
|
|
625
|
+
| Form Requests | 90%+ |
|
|
626
|
+
| Controllers | 85%+ |
|
|
627
|
+
| Policies | 95%+ |
|
|
628
|
+
| Overall | 80%+ |
|
|
629
|
+
|
|
630
|
+
## Testing Best Practices
|
|
631
|
+
|
|
632
|
+
### DO
|
|
633
|
+
|
|
634
|
+
- Use factories over manual `create()` calls
|
|
635
|
+
- One logical assertion per test
|
|
636
|
+
- Descriptive names: `test_guests_cannot_create_products`
|
|
637
|
+
- Test edge cases and authorization boundaries
|
|
638
|
+
- Mock external services with `Http::fake()`, `Mail::fake()`
|
|
639
|
+
- Use `RefreshDatabase` for clean state
|
|
640
|
+
|
|
641
|
+
### DON'T
|
|
642
|
+
|
|
643
|
+
- Don't test Laravel internals (trust the framework)
|
|
644
|
+
- Don't make tests dependent on each other
|
|
645
|
+
- Don't over-mock — mock only service boundaries
|
|
646
|
+
- Don't test private methods — test through the public interface
|
|
647
|
+
- Don't couple tests to HTML structure
|
|
648
|
+
|
|
649
|
+
## Quick Reference
|
|
650
|
+
|
|
651
|
+
| Pattern | Usage |
|
|
652
|
+
|---------|-------|
|
|
653
|
+
| `RefreshDatabase` | Reset database between tests |
|
|
654
|
+
| `$this->actingAs($user)` | Authenticate as user |
|
|
655
|
+
| `$this->withToken($token)` | Bearer token auth for APIs |
|
|
656
|
+
| `Model::factory()->create()` | Create model with factory |
|
|
657
|
+
| `Model::factory()->count(5)->create()` | Create multiple records |
|
|
658
|
+
| `Http::fake([...])` | Mock HTTP calls |
|
|
659
|
+
| `Mail::fake()` | Trap sent mail |
|
|
660
|
+
| `Notification::fake()` | Trap sent notifications |
|
|
661
|
+
| `Queue::fake()` | Trap queued jobs |
|
|
662
|
+
| `Event::fake()` | Trap dispatched events |
|
|
663
|
+
| `Storage::fake('public')` | Trap file operations |
|
|
664
|
+
| `assertDatabaseHas` | Assert DB row exists |
|
|
665
|
+
| `assertSoftDeleted` | Assert soft-delete |
|
|
666
|
+
| `assertSessionHasErrors` | Assert validation errors |
|
|
667
|
+
| `assertForbidden` | Assert 403 status |
|
|
668
|
+
|
|
669
|
+
## Related Skills
|
|
670
|
+
|
|
671
|
+
- `laravel-patterns` — Laravel architecture, Eloquent, routing, and API patterns
|
|
672
|
+
- `laravel-security` — Laravel authentication, authorization, and secure coding
|
|
673
|
+
- `tdd-workflow` — The repo-wide RED -> GREEN -> REFACTOR loop
|
|
674
|
+
- `backend-patterns` — General backend API and database patterns
|