@atlashub/smartstack-cli 3.39.0 → 3.40.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/.documentation/apex.html +644 -644
- package/.documentation/css/styles.css +2320 -2320
- package/.documentation/init.html +1377 -1377
- package/.documentation/js/app.js +780 -780
- package/.documentation/prd-json-v2.0.0.md +396 -396
- package/.documentation/testing-ba-e2e.md +462 -462
- package/config/default-config.json +95 -95
- package/config/mcp-defaults.json +62 -62
- package/config/settings.json +53 -53
- package/config/settings.local.example.json +16 -16
- package/dist/index.js.map +1 -1
- package/dist/mcp-entry.mjs +6 -4
- package/dist/mcp-entry.mjs.map +1 -1
- package/package.json +115 -115
- package/scripts/extract-api-endpoints.ts +325 -325
- package/scripts/extract-business-rules.ts +440 -440
- package/scripts/generate-doc-with-mock-ui.ts +804 -804
- package/scripts/health-check.sh +168 -168
- package/scripts/postinstall.js +18 -18
- package/templates/agents/action.md +37 -37
- package/templates/agents/ba-reader.md +378 -378
- package/templates/agents/ba-writer.md +861 -861
- package/templates/agents/code-reviewer.md +163 -163
- package/templates/agents/db-reader.md +149 -149
- package/templates/agents/docs-context-reader.md +143 -143
- package/templates/agents/docs-sync-checker.md +122 -122
- package/templates/agents/efcore/conflicts.md +84 -84
- package/templates/agents/efcore/db-deploy.md +74 -74
- package/templates/agents/efcore/db-reset.md +85 -85
- package/templates/agents/efcore/db-seed.md +61 -61
- package/templates/agents/efcore/db-status.md +86 -86
- package/templates/agents/efcore/migration.md +186 -186
- package/templates/agents/efcore/rebase-snapshot.md +108 -108
- package/templates/agents/efcore/scan.md +92 -92
- package/templates/agents/efcore/squash.md +161 -161
- package/templates/agents/explore-codebase.md +66 -66
- package/templates/agents/explore-docs.md +98 -98
- package/templates/agents/fix-grammar.md +50 -50
- package/templates/agents/gitflow/abort.md +45 -45
- package/templates/agents/gitflow/cleanup.md +96 -96
- package/templates/agents/gitflow/commit.md +236 -236
- package/templates/agents/gitflow/exec.md +48 -48
- package/templates/agents/gitflow/finish.md +146 -146
- package/templates/agents/gitflow/init-clone.md +199 -199
- package/templates/agents/gitflow/init-detect.md +137 -137
- package/templates/agents/gitflow/init-validate.md +225 -225
- package/templates/agents/gitflow/init.md +340 -340
- package/templates/agents/gitflow/merge.md +145 -145
- package/templates/agents/gitflow/plan.md +42 -42
- package/templates/agents/gitflow/pr.md +191 -191
- package/templates/agents/gitflow/review.md +49 -49
- package/templates/agents/gitflow/start.md +147 -147
- package/templates/agents/gitflow/status.md +95 -95
- package/templates/agents/mcp-healthcheck.md +163 -163
- package/templates/agents/snipper.md +37 -37
- package/templates/agents/websearch.md +46 -46
- package/templates/hooks/appsettings-guard.sh +76 -76
- package/templates/hooks/docs-drift-check.md +96 -96
- package/templates/hooks/ef-migration-check.md +139 -139
- package/templates/hooks/hooks.json +58 -58
- package/templates/hooks/mcp-check.md +64 -64
- package/templates/hooks/ralph-mcp-logger.sh +46 -46
- package/templates/hooks/ralph-session-end.sh +69 -69
- package/templates/hooks/stop-hook.sh +177 -177
- package/templates/hooks/wsl-dotnet-cleanup.sh +24 -24
- package/templates/mcp-scaffolding/component.tsx.hbs +318 -318
- package/templates/mcp-scaffolding/controller.cs.hbs +192 -192
- package/templates/mcp-scaffolding/entity-extension.cs.hbs +239 -239
- package/templates/mcp-scaffolding/frontend/api-client.ts.hbs +116 -116
- package/templates/mcp-scaffolding/frontend/nav-routes.ts.hbs +133 -133
- package/templates/mcp-scaffolding/frontend/routes.tsx.hbs +126 -126
- package/templates/mcp-scaffolding/migrations/seed-roles.cs.hbs +261 -261
- package/templates/mcp-scaffolding/service-extension.cs.hbs +53 -53
- package/templates/mcp-scaffolding/tests/controller.test.cs.hbs +436 -436
- package/templates/mcp-scaffolding/tests/entity.test.cs.hbs +239 -239
- package/templates/mcp-scaffolding/tests/repository.test.cs.hbs +441 -441
- package/templates/mcp-scaffolding/tests/security.test.cs.hbs +442 -442
- package/templates/mcp-scaffolding/tests/service.test.cs.hbs +402 -402
- package/templates/mcp-scaffolding/tests/validator.test.cs.hbs +428 -428
- package/templates/project/DependencyInjection.Application.cs.template +25 -25
- package/templates/project/DependencyInjection.Infrastructure.cs.template +61 -61
- package/templates/project/DesignTimeExtensionsDbContextFactory.cs.template +70 -70
- package/templates/project/ExampleEntity.cs.template +116 -116
- package/templates/project/ExampleEntityConfiguration.cs.template +64 -64
- package/templates/project/ExampleService.cs.template +146 -146
- package/templates/project/ExtensionsDbContext.cs.template +41 -41
- package/templates/project/IExtensionsDbContext.cs.template +22 -22
- package/templates/project/Program.cs.template +47 -47
- package/templates/project/README.md +79 -79
- package/templates/project/api.ts.template +12 -12
- package/templates/project/appsettings.json.template +170 -170
- package/templates/project/claude-settings.json.template +5 -5
- package/templates/project/test-frontend/msw/handlers.ts +58 -58
- package/templates/project/test-frontend/msw/server.ts +25 -25
- package/templates/project/test-frontend/setup.ts +16 -16
- package/templates/project/test-frontend/test-utils.tsx +59 -59
- package/templates/project/test-frontend/vitest.config.ts +31 -31
- package/templates/ralph/README.md +93 -93
- package/templates/ralph/ralph.config.yaml +113 -113
- package/templates/scripts/setup-ralph-loop.sh +173 -173
- package/templates/skills/_resources/config-safety.md +61 -61
- package/templates/skills/_resources/context-digest-template.md +53 -53
- package/templates/skills/_resources/doc-context-cache.md +60 -60
- package/templates/skills/_resources/docs-manifest-schema.md +155 -155
- package/templates/skills/_resources/formatting-guide.md +124 -124
- package/templates/skills/_resources/mcp-validate-documentation-spec.md +181 -181
- package/templates/skills/_shared.md +228 -228
- package/templates/skills/admin/SKILL.md +48 -48
- package/templates/skills/ai-prompt/SKILL.md +107 -107
- package/templates/skills/ai-prompt/steps/step-00-init.md +47 -47
- package/templates/skills/ai-prompt/steps/step-01-implementation.md +122 -122
- package/templates/skills/apex/SKILL.md +168 -168
- package/templates/skills/apex/_shared.md +141 -141
- package/templates/skills/apex/references/agent-teams-protocol.md +164 -164
- package/templates/skills/apex/references/analysis-methods.md +141 -141
- package/templates/skills/apex/references/challenge-questions.md +145 -145
- package/templates/skills/apex/references/code-generation.md +412 -412
- package/templates/skills/apex/references/core-seed-data.md +1437 -1437
- package/templates/skills/apex/references/error-classification.md +144 -144
- package/templates/skills/apex/references/examine-build-validation.md +82 -82
- package/templates/skills/apex/references/execution-frontend-gates.md +177 -177
- package/templates/skills/apex/references/execution-frontend-patterns.md +105 -105
- package/templates/skills/apex/references/execution-layer1-rules.md +96 -96
- package/templates/skills/apex/references/initialization-challenge-flow.md +110 -110
- package/templates/skills/apex/references/planning-layer-mapping.md +151 -151
- package/templates/skills/apex/references/post-checks.md +1584 -1584
- package/templates/skills/apex/references/smartstack-api.md +1053 -1053
- package/templates/skills/apex/references/smartstack-frontend.md +1571 -1571
- package/templates/skills/apex/references/smartstack-layers.md +402 -402
- package/templates/skills/apex/steps/step-00-init.md +307 -307
- package/templates/skills/apex/steps/step-01-analyze.md +165 -165
- package/templates/skills/apex/steps/step-02-plan.md +144 -144
- package/templates/skills/apex/steps/step-03-execute.md +328 -328
- package/templates/skills/apex/steps/step-04-examine.md +263 -263
- package/templates/skills/apex/steps/step-05-deep-review.md +129 -129
- package/templates/skills/apex/steps/step-06-resolve.md +101 -101
- package/templates/skills/apex/steps/step-07-tests.md +238 -238
- package/templates/skills/apex/steps/step-08-run-tests.md +125 -125
- package/templates/skills/application/SKILL.md +4 -4
- package/templates/skills/application/references/application-roles-template.md +227 -227
- package/templates/skills/application/references/backend-controller-hierarchy.md +58 -58
- package/templates/skills/application/references/backend-entity-seeding.md +72 -72
- package/templates/skills/application/references/backend-seeding-and-dto-output.md +83 -83
- package/templates/skills/application/references/backend-table-prefix-mapping.md +79 -79
- package/templates/skills/application/references/backend-verification.md +88 -88
- package/templates/skills/application/references/frontend-i18n-and-output.md +67 -67
- package/templates/skills/application/references/frontend-route-naming.md +117 -117
- package/templates/skills/application/references/frontend-route-wiring-app-tsx.md +107 -107
- package/templates/skills/application/references/frontend-verification.md +156 -156
- package/templates/skills/application/references/migration-checklist-troubleshooting.md +1 -1
- package/templates/skills/application/references/provider-template.md +177 -177
- package/templates/skills/application/references/roles-client-project-handling.md +55 -55
- package/templates/skills/application/references/roles-fallback-procedure.md +149 -149
- package/templates/skills/application/references/test-coverage-requirements.md +213 -213
- package/templates/skills/application/references/test-frontend.md +73 -73
- package/templates/skills/application/references/test-prerequisites.md +72 -72
- package/templates/skills/application/steps/step-05-frontend.md +176 -176
- package/templates/skills/application/steps/step-06-migration.md +193 -193
- package/templates/skills/application/steps/step-07-tests.md +356 -356
- package/templates/skills/application/steps/step-08-documentation.md +137 -137
- package/templates/skills/application/templates-backend.md +463 -463
- package/templates/skills/application/templates-frontend.md +685 -685
- package/templates/skills/application/templates-i18n.md +520 -520
- package/templates/skills/application/templates-seed.md +1096 -1096
- package/templates/skills/business-analyse/SKILL.md +327 -327
- package/templates/skills/business-analyse/_architecture.md +123 -123
- package/templates/skills/business-analyse/_elicitation.md +206 -206
- package/templates/skills/business-analyse/_module-loop.md +115 -115
- package/templates/skills/business-analyse/_shared.md +383 -383
- package/templates/skills/business-analyse/_suggestions.md +34 -34
- package/templates/skills/business-analyse/html/ba-interactive.html +4477 -4477
- package/templates/skills/business-analyse/html/build-html.js +77 -77
- package/templates/skills/business-analyse/html/src/scripts/01-data-init.js +150 -150
- package/templates/skills/business-analyse/html/src/scripts/02-navigation.js +227 -227
- package/templates/skills/business-analyse/html/src/scripts/03-render-cadrage.js +199 -199
- package/templates/skills/business-analyse/html/src/scripts/04-render-modules.js +205 -205
- package/templates/skills/business-analyse/html/src/scripts/05-render-specs.js +647 -647
- package/templates/skills/business-analyse/html/src/scripts/06-render-consolidation.js +195 -195
- package/templates/skills/business-analyse/html/src/scripts/07-render-handoff.js +92 -92
- package/templates/skills/business-analyse/html/src/scripts/08-editing.js +135 -135
- package/templates/skills/business-analyse/html/src/scripts/09-export.js +168 -168
- package/templates/skills/business-analyse/html/src/scripts/10-comments.js +171 -171
- package/templates/skills/business-analyse/html/src/scripts/11-review-panel.js +166 -166
- package/templates/skills/business-analyse/html/src/styles/01-variables.css +38 -38
- package/templates/skills/business-analyse/html/src/styles/02-layout.css +101 -101
- package/templates/skills/business-analyse/html/src/styles/03-navigation.css +120 -120
- package/templates/skills/business-analyse/html/src/styles/04-cards.css +196 -196
- package/templates/skills/business-analyse/html/src/styles/05-modules.css +454 -454
- package/templates/skills/business-analyse/html/src/styles/06-wireframes.css +272 -272
- package/templates/skills/business-analyse/html/src/styles/07-comments.css +184 -184
- package/templates/skills/business-analyse/html/src/styles/08-review-panel.css +241 -241
- package/templates/skills/business-analyse/html/src/template.html +516 -516
- package/templates/skills/business-analyse/patterns/suggestion-catalog.md +546 -546
- package/templates/skills/business-analyse/questionnaire/00-application.md +160 -160
- package/templates/skills/business-analyse/questionnaire/00b-project.md +85 -85
- package/templates/skills/business-analyse/questionnaire/01-context.md +185 -185
- package/templates/skills/business-analyse/questionnaire/02-stakeholders.md +189 -189
- package/templates/skills/business-analyse/questionnaire/03-scope.md +164 -164
- package/templates/skills/business-analyse/questionnaire/04-data.md +88 -88
- package/templates/skills/business-analyse/questionnaire/05-integrations.md +58 -58
- package/templates/skills/business-analyse/questionnaire/06-security.md +68 -68
- package/templates/skills/business-analyse/questionnaire/07-ui.md +76 -76
- package/templates/skills/business-analyse/questionnaire/08-performance.md +42 -42
- package/templates/skills/business-analyse/questionnaire/09-constraints.md +45 -45
- package/templates/skills/business-analyse/questionnaire/10-documentation.md +43 -43
- package/templates/skills/business-analyse/questionnaire/11-data-lifecycle.md +59 -59
- package/templates/skills/business-analyse/questionnaire/12-migration.md +58 -58
- package/templates/skills/business-analyse/questionnaire/13-cross-module.md +69 -69
- package/templates/skills/business-analyse/questionnaire/14-risk-assumptions.md +135 -135
- package/templates/skills/business-analyse/questionnaire/15-success-metrics.md +136 -136
- package/templates/skills/business-analyse/questionnaire.md +337 -337
- package/templates/skills/business-analyse/react/application-viewer.md +242 -242
- package/templates/skills/business-analyse/react/components.md +551 -551
- package/templates/skills/business-analyse/react/i18n-template.md +306 -306
- package/templates/skills/business-analyse/references/acceptance-criteria.md +169 -169
- package/templates/skills/business-analyse/references/agent-module-prompt.md +362 -362
- package/templates/skills/business-analyse/references/agent-pooling-best-practices.md +557 -557
- package/templates/skills/business-analyse/references/analysis-semantic-checks.md +190 -190
- package/templates/skills/business-analyse/references/cache-warming-strategy.md +566 -566
- package/templates/skills/business-analyse/references/cadrage-challenge-patterns.md +41 -41
- package/templates/skills/business-analyse/references/cadrage-coverage-matrix.md +74 -74
- package/templates/skills/business-analyse/references/cadrage-pre-analysis.md +115 -115
- package/templates/skills/business-analyse/references/cadrage-shared-modules.md +68 -69
- package/templates/skills/business-analyse/references/cadrage-structure-cards.md +85 -85
- package/templates/skills/business-analyse/references/compilation-structure-cards.md +297 -297
- package/templates/skills/business-analyse/references/consolidation-structural-checks.md +107 -107
- package/templates/skills/business-analyse/references/deploy-data-build.md +180 -180
- package/templates/skills/business-analyse/references/deploy-modes.md +118 -118
- package/templates/skills/business-analyse/references/detection-strategies.md +424 -424
- package/templates/skills/business-analyse/references/entity-architecture-decision.md +218 -218
- package/templates/skills/business-analyse/references/handoff-file-templates.md +120 -120
- package/templates/skills/business-analyse/references/handoff-mappings.md +81 -81
- package/templates/skills/business-analyse/references/handoff-seeddata-generation.md +312 -312
- package/templates/skills/business-analyse/references/html-data-mapping.md +299 -299
- package/templates/skills/business-analyse/references/init-schema-deployment.md +65 -65
- package/templates/skills/business-analyse/references/naming-conventions.md +243 -243
- package/templates/skills/business-analyse/references/prd-generation.md +258 -258
- package/templates/skills/business-analyse/references/review-data-mapping.md +363 -363
- package/templates/skills/business-analyse/references/robustness-checks.md +542 -542
- package/templates/skills/business-analyse/references/spec-auto-inference.md +111 -111
- package/templates/skills/business-analyse/references/team-orchestration.md +1022 -1022
- package/templates/skills/business-analyse/references/ui-dashboard-spec.md +85 -85
- package/templates/skills/business-analyse/references/ui-resource-cards.md +259 -259
- package/templates/skills/business-analyse/references/validate-incremental-html.md +121 -121
- package/templates/skills/business-analyse/references/validation-checklist.md +347 -347
- package/templates/skills/business-analyse/references/wireframe-svg-style-guide.md +335 -335
- package/templates/skills/business-analyse/schemas/application-schema.json +453 -453
- package/templates/skills/business-analyse/schemas/feature-schema.json +53 -53
- package/templates/skills/business-analyse/schemas/project-schema.json +485 -485
- package/templates/skills/business-analyse/schemas/sections/analysis-schema.json +201 -201
- package/templates/skills/business-analyse/schemas/sections/discovery-schema.json +82 -82
- package/templates/skills/business-analyse/schemas/sections/handoff-schema.json +80 -80
- package/templates/skills/business-analyse/schemas/sections/metadata-schema.json +70 -70
- package/templates/skills/business-analyse/schemas/sections/specification-schema.json +547 -547
- package/templates/skills/business-analyse/schemas/sections/validation-schema.json +93 -93
- package/templates/skills/business-analyse/schemas/shared/common-defs.json +226 -226
- package/templates/skills/business-analyse/steps/step-00-init.md +575 -576
- package/templates/skills/business-analyse/steps/step-01-cadrage.md +767 -767
- package/templates/skills/business-analyse/steps/step-01b-applications.md +419 -419
- package/templates/skills/business-analyse/steps/step-02-decomposition.md +387 -387
- package/templates/skills/business-analyse/steps/step-03a-data.md +16 -16
- package/templates/skills/business-analyse/steps/step-03a1-setup.md +506 -506
- package/templates/skills/business-analyse/steps/step-03a2-analysis.md +252 -252
- package/templates/skills/business-analyse/steps/step-03b-ui.md +425 -425
- package/templates/skills/business-analyse/steps/step-03c-compile.md +611 -611
- package/templates/skills/business-analyse/steps/step-03d-validate.md +783 -783
- package/templates/skills/business-analyse/steps/step-04-consolidation.md +17 -17
- package/templates/skills/business-analyse/steps/step-04a-collect.md +415 -415
- package/templates/skills/business-analyse/steps/step-04b-analyze.md +163 -163
- package/templates/skills/business-analyse/steps/step-04c-decide.md +186 -186
- package/templates/skills/business-analyse/steps/step-05a-handoff.md +840 -840
- package/templates/skills/business-analyse/steps/step-05b-deploy.md +522 -522
- package/templates/skills/business-analyse/steps/step-05c-ralph-readiness.md +703 -703
- package/templates/skills/business-analyse/steps/step-06-review.md +278 -278
- package/templates/skills/business-analyse/templates/tpl-frd.md +168 -168
- package/templates/skills/business-analyse/templates/tpl-handoff.md +186 -186
- package/templates/skills/business-analyse/templates/tpl-launch-displays.md +59 -59
- package/templates/skills/business-analyse/templates/tpl-progress.md +172 -172
- package/templates/skills/business-analyse/templates-frd.md +476 -476
- package/templates/skills/business-analyse/templates-react.md +574 -574
- package/templates/skills/cc-agent/SKILL.md +129 -129
- package/templates/skills/cc-agent/references/agent-behavior-patterns.md +95 -95
- package/templates/skills/cc-agent/references/agent-frontmatter.md +213 -213
- package/templates/skills/cc-agent/references/permission-modes.md +102 -102
- package/templates/skills/cc-agent/references/tools-reference.md +144 -144
- package/templates/skills/cc-agent/steps/step-00-init.md +134 -134
- package/templates/skills/cc-agent/steps/step-01-design.md +186 -186
- package/templates/skills/cc-agent/steps/step-02-generate.md +131 -131
- package/templates/skills/cc-agent/steps/step-03-validate.md +130 -130
- package/templates/skills/cc-agent/templates/agent-categorized.md +67 -67
- package/templates/skills/cc-agent/templates/agent-standalone.md +56 -56
- package/templates/skills/cc-agent/templates/agent-with-skills.md +94 -94
- package/templates/skills/cc-audit/SKILL.md +108 -108
- package/templates/skills/cc-audit/references/agent-checklist.md +91 -91
- package/templates/skills/cc-audit/references/hook-checklist.md +110 -110
- package/templates/skills/cc-audit/references/skill-checklist.md +70 -70
- package/templates/skills/cc-audit/steps/step-00-init.md +98 -98
- package/templates/skills/cc-audit/steps/step-01-scan.md +142 -142
- package/templates/skills/cc-audit/steps/step-02-analyze.md +158 -158
- package/templates/skills/cc-audit/steps/step-03-report.md +142 -142
- package/templates/skills/cc-skill/SKILL.md +134 -134
- package/templates/skills/cc-skill/references/best-practices.md +167 -167
- package/templates/skills/cc-skill/references/frontmatter-reference.md +182 -182
- package/templates/skills/cc-skill/references/skill-patterns.md +199 -199
- package/templates/skills/cc-skill/steps/step-00-init.md +119 -119
- package/templates/skills/cc-skill/steps/step-01-design.md +199 -199
- package/templates/skills/cc-skill/steps/step-02-generate.md +145 -145
- package/templates/skills/cc-skill/steps/step-03-steps.md +151 -151
- package/templates/skills/cc-skill/steps/step-04-validate.md +124 -124
- package/templates/skills/cc-skill/templates/skill-forked.md +85 -85
- package/templates/skills/cc-skill/templates/skill-progressive.md +102 -102
- package/templates/skills/cc-skill/templates/skill-simple.md +75 -75
- package/templates/skills/cc-skill/templates/step-template.md +82 -82
- package/templates/skills/check-version/SKILL.md +196 -196
- package/templates/skills/controller/SKILL.md +162 -162
- package/templates/skills/controller/postman-templates.md +614 -614
- package/templates/skills/controller/references/controller-code-templates.md +159 -159
- package/templates/skills/controller/references/mcp-scaffold-workflow.md +209 -209
- package/templates/skills/controller/references/permission-sync-templates.md +149 -149
- package/templates/skills/controller/steps/step-00-init.md +193 -191
- package/templates/skills/controller/steps/step-01-analyze.md +146 -146
- package/templates/skills/controller/steps/step-02-plan.md +176 -176
- package/templates/skills/controller/steps/step-03-generate.md +189 -189
- package/templates/skills/controller/steps/step-04-perms.md +80 -80
- package/templates/skills/controller/steps/step-05-validate.md +107 -107
- package/templates/skills/controller/templates.md +1555 -1555
- package/templates/skills/debug/SKILL.md +70 -70
- package/templates/skills/debug/references/team-protocol.md +232 -232
- package/templates/skills/debug/steps/step-00-init.md +57 -57
- package/templates/skills/debug/steps/step-01-analyze.md +219 -219
- package/templates/skills/debug/steps/step-02-resolve.md +85 -85
- package/templates/skills/documentation/SKILL.md +132 -132
- package/templates/skills/documentation/data-schema.md +227 -227
- package/templates/skills/documentation/steps/step-00-init.md +70 -70
- package/templates/skills/documentation/steps/step-01-scan.md +113 -113
- package/templates/skills/documentation/steps/step-02-generate.md +231 -231
- package/templates/skills/documentation/steps/step-03-validate.md +251 -238
- package/templates/skills/documentation/templates.md +662 -663
- package/templates/skills/efcore/SKILL.md +167 -167
- package/templates/skills/efcore/references/both-contexts.md +32 -32
- package/templates/skills/efcore/references/database-operations.md +67 -67
- package/templates/skills/efcore/references/destructive-operations.md +38 -38
- package/templates/skills/efcore/references/reset-operations.md +81 -81
- package/templates/skills/efcore/references/seed-methods.md +86 -86
- package/templates/skills/efcore/references/shared-init-functions.md +250 -250
- package/templates/skills/efcore/references/sql-objects-injection.md +61 -61
- package/templates/skills/efcore/references/troubleshooting.md +81 -81
- package/templates/skills/efcore/references/zero-downtime-patterns.md +227 -227
- package/templates/skills/efcore/steps/db/step-deploy.md +217 -217
- package/templates/skills/efcore/steps/db/step-reset.md +186 -186
- package/templates/skills/efcore/steps/db/step-seed.md +166 -166
- package/templates/skills/efcore/steps/db/step-status.md +173 -173
- package/templates/skills/efcore/steps/migration/step-00-init.md +102 -102
- package/templates/skills/efcore/steps/migration/step-01-check.md +164 -164
- package/templates/skills/efcore/steps/migration/step-02-create.md +160 -160
- package/templates/skills/efcore/steps/migration/step-03-validate.md +168 -168
- package/templates/skills/efcore/steps/rebase-snapshot/step-00-init.md +173 -173
- package/templates/skills/efcore/steps/rebase-snapshot/step-01-backup.md +100 -100
- package/templates/skills/efcore/steps/rebase-snapshot/step-02-fetch.md +115 -115
- package/templates/skills/efcore/steps/rebase-snapshot/step-03-create.md +112 -112
- package/templates/skills/efcore/steps/rebase-snapshot/step-04-validate.md +157 -157
- package/templates/skills/efcore/steps/shared/step-00-init.md +131 -131
- package/templates/skills/efcore/steps/squash/step-00-init.md +141 -141
- package/templates/skills/efcore/steps/squash/step-01-backup.md +120 -120
- package/templates/skills/efcore/steps/squash/step-02-fetch.md +168 -168
- package/templates/skills/efcore/steps/squash/step-03-create.md +184 -184
- package/templates/skills/efcore/steps/squash/step-04-validate.md +174 -174
- package/templates/skills/explore/SKILL.md +98 -98
- package/templates/skills/feature-full/SKILL.md +111 -111
- package/templates/skills/feature-full/steps/step-00-init.md +57 -57
- package/templates/skills/feature-full/steps/step-01-implementation.md +120 -120
- package/templates/skills/gitflow/SKILL.md +377 -377
- package/templates/skills/gitflow/_shared.md +620 -620
- package/templates/skills/gitflow/phases/abort.md +189 -189
- package/templates/skills/gitflow/phases/cleanup.md +234 -234
- package/templates/skills/gitflow/phases/status.md +192 -192
- package/templates/skills/gitflow/references/commit-message-generation.md +58 -58
- package/templates/skills/gitflow/references/commit-migration-validation.md +49 -49
- package/templates/skills/gitflow/references/finish-cleanup.md +55 -55
- package/templates/skills/gitflow/references/finish-version-bumping.md +45 -45
- package/templates/skills/gitflow/references/init-config-template.md +135 -135
- package/templates/skills/gitflow/references/init-environment-detection.md +41 -41
- package/templates/skills/gitflow/references/init-name-normalization.md +103 -103
- package/templates/skills/gitflow/references/init-questions.md +185 -185
- package/templates/skills/gitflow/references/init-structure-creation.md +75 -75
- package/templates/skills/gitflow/references/init-version-detection.md +21 -21
- package/templates/skills/gitflow/references/init-workspace-detection.md +43 -43
- package/templates/skills/gitflow/references/merge-ci-status.md +36 -36
- package/templates/skills/gitflow/references/merge-execution.md +62 -62
- package/templates/skills/gitflow/references/merge-pr-context.md +76 -76
- package/templates/skills/gitflow/references/plan-template.md +69 -69
- package/templates/skills/gitflow/references/pr-build-checks.md +60 -60
- package/templates/skills/gitflow/references/pr-generation.md +58 -58
- package/templates/skills/gitflow/references/start-branch-normalization.md +28 -28
- package/templates/skills/gitflow/references/start-efcore-preflight.md +70 -70
- package/templates/skills/gitflow/references/start-local-config.md +113 -113
- package/templates/skills/gitflow/references/start-worktree-creation.md +50 -50
- package/templates/skills/gitflow/references/sync-push-verify.md +44 -44
- package/templates/skills/gitflow/references/sync-rebase-conflicts.md +38 -38
- package/templates/skills/gitflow/steps/step-commit.md +199 -199
- package/templates/skills/gitflow/steps/step-finish.md +147 -147
- package/templates/skills/gitflow/steps/step-init.md +190 -190
- package/templates/skills/gitflow/steps/step-merge.md +85 -85
- package/templates/skills/gitflow/steps/step-plan.md +151 -151
- package/templates/skills/gitflow/steps/step-pr.md +199 -199
- package/templates/skills/gitflow/steps/step-start.md +195 -195
- package/templates/skills/gitflow/steps/step-sync.md +161 -161
- package/templates/skills/gitflow/templates/config.json +72 -72
- package/templates/skills/mcp/SKILL.md +62 -62
- package/templates/skills/mcp/steps/step-01-healthcheck.md +108 -108
- package/templates/skills/mcp/steps/step-02-tools.md +73 -73
- package/templates/skills/notification/SKILL.md +173 -173
- package/templates/skills/quick-search/SKILL.md +99 -99
- package/templates/skills/ralph-loop/SKILL.md +234 -234
- package/templates/skills/ralph-loop/references/category-completeness.md +185 -185
- package/templates/skills/ralph-loop/references/category-rules.md +96 -96
- package/templates/skills/ralph-loop/references/compact-loop.md +300 -300
- package/templates/skills/ralph-loop/references/init-resume-recovery.md +127 -127
- package/templates/skills/ralph-loop/references/module-transition.md +151 -151
- package/templates/skills/ralph-loop/references/multi-module-queue.md +171 -171
- package/templates/skills/ralph-loop/references/parallel-execution.md +246 -246
- package/templates/skills/ralph-loop/references/section-splitting.md +439 -439
- package/templates/skills/ralph-loop/references/task-transform-legacy.md +256 -256
- package/templates/skills/ralph-loop/references/team-orchestration.md +547 -547
- package/templates/skills/ralph-loop/steps/step-00-init.md +150 -150
- package/templates/skills/ralph-loop/steps/step-01-task.md +174 -174
- package/templates/skills/ralph-loop/steps/step-02-execute.md +177 -177
- package/templates/skills/ralph-loop/steps/step-03-commit.md +92 -92
- package/templates/skills/ralph-loop/steps/step-04-check.md +207 -207
- package/templates/skills/ralph-loop/steps/step-05-report.md +175 -175
- package/templates/skills/refactor/SKILL.md +56 -56
- package/templates/skills/refactor/steps/step-01-discover.md +60 -60
- package/templates/skills/refactor/steps/step-02-execute.md +67 -67
- package/templates/skills/review-code/SKILL.md +94 -94
- package/templates/skills/review-code/references/clean-code-principles.md +292 -292
- package/templates/skills/review-code/references/code-quality-metrics.md +174 -174
- package/templates/skills/review-code/references/feedback-patterns.md +149 -149
- package/templates/skills/review-code/references/owasp-api-top10.md +243 -243
- package/templates/skills/review-code/references/security-checklist.md +212 -212
- package/templates/skills/review-code/steps/step-01-smartstack.md +96 -96
- package/templates/skills/review-code/steps/step-02-detailed-review.md +80 -80
- package/templates/skills/review-code/steps/step-03-react.md +44 -44
- package/templates/skills/ui-components/SKILL.md +137 -137
- package/templates/skills/ui-components/accessibility.md +170 -170
- package/templates/skills/ui-components/patterns/dashboard-chart.md +327 -327
- package/templates/skills/ui-components/patterns/data-table.md +39 -39
- package/templates/skills/ui-components/patterns/entity-card.md +77 -77
- package/templates/skills/ui-components/patterns/grid-layout.md +91 -91
- package/templates/skills/ui-components/patterns/kanban.md +43 -43
- package/templates/skills/ui-components/responsive-guidelines.md +278 -278
- package/templates/skills/ui-components/style-guide.md +113 -113
- package/templates/skills/utils/SKILL.md +44 -44
- package/templates/skills/utils/subcommands/test-web-config.md +152 -152
- package/templates/skills/utils/subcommands/test-web.md +123 -123
- package/templates/skills/validate/SKILL.md +181 -181
- package/templates/skills/validate-feature/SKILL.md +101 -101
- package/templates/skills/validate-feature/references/api-smoke-tests.md +140 -140
- package/templates/skills/validate-feature/references/db-validation-checks.md +180 -180
- package/templates/skills/validate-feature/steps/step-00-dependencies.md +121 -121
- package/templates/skills/validate-feature/steps/step-01-compile.md +39 -39
- package/templates/skills/validate-feature/steps/step-02-unit-tests.md +45 -45
- package/templates/skills/validate-feature/steps/step-03-integration-tests.md +53 -53
- package/templates/skills/validate-feature/steps/step-04-api-smoke.md +94 -94
- package/templates/skills/validate-feature/steps/step-05-db-validation.md +149 -149
- package/templates/skills/workflow/SKILL.md +127 -127
- package/templates/skills/workflow/steps/step-00-init.md +57 -57
- package/templates/skills/workflow/steps/step-01-implementation.md +84 -84
- package/templates/test-web/api-health.json +38 -38
- package/templates/test-web/minimal.json +19 -19
- package/templates/test-web/npm-package.json +46 -46
- package/templates/test-web/seo-check.json +54 -54
|
@@ -1,436 +1,436 @@
|
|
|
1
|
-
{{!-- SmartStack Controller Test Template --}}
|
|
2
|
-
{{!-- Generates integration tests for API controllers following SmartStack conventions --}}
|
|
3
|
-
|
|
4
|
-
using FluentAssertions;
|
|
5
|
-
using Microsoft.AspNetCore.Mvc.Testing;
|
|
6
|
-
using Microsoft.Extensions.DependencyInjection;
|
|
7
|
-
using Moq;
|
|
8
|
-
using System.Net;
|
|
9
|
-
using System.Net.Http.Json;
|
|
10
|
-
using Xunit;
|
|
11
|
-
using {{namespace}}.Api;
|
|
12
|
-
using {{namespace}}.Api.Controllers;
|
|
13
|
-
using {{namespace}}.Application.DTOs;
|
|
14
|
-
using {{namespace}}.Application.Interfaces;
|
|
15
|
-
using {{namespace}}.Domain.Entities;
|
|
16
|
-
|
|
17
|
-
namespace {{namespace}}.Tests.Integration.Controllers;
|
|
18
|
-
|
|
19
|
-
/// <summary>
|
|
20
|
-
/// Integration tests for <see cref="{{name}}Controller"/>.
|
|
21
|
-
/// Follows SmartStack testing conventions: {Method}_When{Condition}_Should{Result}
|
|
22
|
-
/// </summary>
|
|
23
|
-
public class {{name}}ControllerTests : IClassFixture<WebApplicationFactory<Program>>
|
|
24
|
-
{
|
|
25
|
-
private readonly WebApplicationFactory<Program> _factory;
|
|
26
|
-
private readonly HttpClient _client;
|
|
27
|
-
private readonly Mock<I{{name}}Service> _mockService;
|
|
28
|
-
|
|
29
|
-
public {{name}}ControllerTests(WebApplicationFactory<Program> factory)
|
|
30
|
-
{
|
|
31
|
-
_mockService = new Mock<I{{name}}Service>();
|
|
32
|
-
|
|
33
|
-
_factory = factory.WithWebHostBuilder(builder =>
|
|
34
|
-
{
|
|
35
|
-
builder.ConfigureServices(services =>
|
|
36
|
-
{
|
|
37
|
-
// Remove the real service and add mock
|
|
38
|
-
var descriptor = services.SingleOrDefault(
|
|
39
|
-
d => d.ServiceType == typeof(I{{name}}Service));
|
|
40
|
-
if (descriptor != null)
|
|
41
|
-
services.Remove(descriptor);
|
|
42
|
-
|
|
43
|
-
services.AddScoped(_ => _mockService.Object);
|
|
44
|
-
});
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
_client = _factory.CreateClient();
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
#region GET Tests
|
|
51
|
-
|
|
52
|
-
[Fact]
|
|
53
|
-
public async Task GetAll_WhenCalled_ShouldReturn200WithList()
|
|
54
|
-
{
|
|
55
|
-
// Arrange
|
|
56
|
-
var items = new PaginatedResult<{{name}}Response>
|
|
57
|
-
{
|
|
58
|
-
Items = new List<{{name}}Response>
|
|
59
|
-
{
|
|
60
|
-
new() { Id = Guid.NewGuid(), Code = "TEST-001" },
|
|
61
|
-
new() { Id = Guid.NewGuid(), Code = "TEST-002" }
|
|
62
|
-
},
|
|
63
|
-
Total = 2,
|
|
64
|
-
Page = 1,
|
|
65
|
-
PageSize = 10
|
|
66
|
-
};
|
|
67
|
-
_mockService.Setup(x => x.GetAllAsync(It.IsAny<PaginationParams>(), It.IsAny<CancellationToken>()))
|
|
68
|
-
.ReturnsAsync(items);
|
|
69
|
-
|
|
70
|
-
// Act
|
|
71
|
-
var response = await _client.GetAsync("/api/{{lowerName}}");
|
|
72
|
-
|
|
73
|
-
// Assert
|
|
74
|
-
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
|
75
|
-
var result = await response.Content.ReadFromJsonAsync<PaginatedResult<{{name}}Response>>();
|
|
76
|
-
result.Should().NotBeNull();
|
|
77
|
-
result!.Items.Should().HaveCount(2);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
[Fact]
|
|
81
|
-
public async Task GetById_WhenExists_ShouldReturn200()
|
|
82
|
-
{
|
|
83
|
-
// Arrange
|
|
84
|
-
var id = Guid.NewGuid();
|
|
85
|
-
var item = new {{name}}Response { Id = id, Code = "TEST-001" };
|
|
86
|
-
_mockService.Setup(x => x.GetByIdAsync(id, It.IsAny<CancellationToken>()))
|
|
87
|
-
.ReturnsAsync(item);
|
|
88
|
-
|
|
89
|
-
// Act
|
|
90
|
-
var response = await _client.GetAsync($"/api/{{lowerName}}/{id}");
|
|
91
|
-
|
|
92
|
-
// Assert
|
|
93
|
-
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
|
94
|
-
var result = await response.Content.ReadFromJsonAsync<{{name}}Response>();
|
|
95
|
-
result!.Id.Should().Be(id);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
[Fact]
|
|
99
|
-
public async Task GetById_WhenNotExists_ShouldReturn404()
|
|
100
|
-
{
|
|
101
|
-
// Arrange
|
|
102
|
-
var id = Guid.NewGuid();
|
|
103
|
-
_mockService.Setup(x => x.GetByIdAsync(id, It.IsAny<CancellationToken>()))
|
|
104
|
-
.ReturnsAsync(({{name}}Response?)null);
|
|
105
|
-
|
|
106
|
-
// Act
|
|
107
|
-
var response = await _client.GetAsync($"/api/{{lowerName}}/{id}");
|
|
108
|
-
|
|
109
|
-
// Assert
|
|
110
|
-
response.StatusCode.Should().Be(HttpStatusCode.NotFound);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
#endregion
|
|
114
|
-
|
|
115
|
-
#region POST Tests
|
|
116
|
-
|
|
117
|
-
[Fact]
|
|
118
|
-
public async Task Create_WhenValidData_ShouldReturn201()
|
|
119
|
-
{
|
|
120
|
-
// Arrange
|
|
121
|
-
var request = new Create{{name}}Request { Code = "NEW-001" };
|
|
122
|
-
var created = new {{name}}Response { Id = Guid.NewGuid(), Code = request.Code };
|
|
123
|
-
_mockService.Setup(x => x.CreateAsync(It.IsAny<Create{{name}}Request>(), It.IsAny<CancellationToken>()))
|
|
124
|
-
.ReturnsAsync(created);
|
|
125
|
-
|
|
126
|
-
// Act
|
|
127
|
-
var response = await _client.PostAsJsonAsync("/api/{{lowerName}}", request);
|
|
128
|
-
|
|
129
|
-
// Assert
|
|
130
|
-
response.StatusCode.Should().Be(HttpStatusCode.Created);
|
|
131
|
-
response.Headers.Location.Should().NotBeNull();
|
|
132
|
-
var result = await response.Content.ReadFromJsonAsync<{{name}}Response>();
|
|
133
|
-
result!.Code.Should().Be(request.Code);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
[Fact]
|
|
137
|
-
public async Task Create_WhenInvalidData_ShouldReturn400()
|
|
138
|
-
{
|
|
139
|
-
// Arrange
|
|
140
|
-
var request = new Create{{name}}Request { Code = "" }; // Invalid
|
|
141
|
-
|
|
142
|
-
// Act
|
|
143
|
-
var response = await _client.PostAsJsonAsync("/api/{{lowerName}}", request);
|
|
144
|
-
|
|
145
|
-
// Assert
|
|
146
|
-
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
[Fact]
|
|
150
|
-
public async Task Create_WhenDuplicate_ShouldReturn409()
|
|
151
|
-
{
|
|
152
|
-
// Arrange
|
|
153
|
-
var request = new Create{{name}}Request { Code = "EXISTING" };
|
|
154
|
-
_mockService.Setup(x => x.CreateAsync(It.IsAny<Create{{name}}Request>(), It.IsAny<CancellationToken>()))
|
|
155
|
-
.ThrowsAsync(new BusinessException("Code already exists"));
|
|
156
|
-
|
|
157
|
-
// Act
|
|
158
|
-
var response = await _client.PostAsJsonAsync("/api/{{lowerName}}", request);
|
|
159
|
-
|
|
160
|
-
// Assert
|
|
161
|
-
response.StatusCode.Should().Be(HttpStatusCode.Conflict);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
#endregion
|
|
165
|
-
|
|
166
|
-
#region PUT Tests
|
|
167
|
-
|
|
168
|
-
[Fact]
|
|
169
|
-
public async Task Update_WhenValidData_ShouldReturn200()
|
|
170
|
-
{
|
|
171
|
-
// Arrange
|
|
172
|
-
var id = Guid.NewGuid();
|
|
173
|
-
var request = new Update{{name}}Request { /* properties */ };
|
|
174
|
-
var updated = new {{name}}Response { Id = id, Code = "UPDATED" };
|
|
175
|
-
_mockService.Setup(x => x.UpdateAsync(id, It.IsAny<Update{{name}}Request>(), It.IsAny<CancellationToken>()))
|
|
176
|
-
.ReturnsAsync(updated);
|
|
177
|
-
|
|
178
|
-
// Act
|
|
179
|
-
var response = await _client.PutAsJsonAsync($"/api/{{lowerName}}/{id}", request);
|
|
180
|
-
|
|
181
|
-
// Assert
|
|
182
|
-
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
[Fact]
|
|
186
|
-
public async Task Update_WhenNotExists_ShouldReturn404()
|
|
187
|
-
{
|
|
188
|
-
// Arrange
|
|
189
|
-
var id = Guid.NewGuid();
|
|
190
|
-
var request = new Update{{name}}Request();
|
|
191
|
-
_mockService.Setup(x => x.UpdateAsync(id, It.IsAny<Update{{name}}Request>(), It.IsAny<CancellationToken>()))
|
|
192
|
-
.ThrowsAsync(new NotFoundException($"{{name}} {id} not found"));
|
|
193
|
-
|
|
194
|
-
// Act
|
|
195
|
-
var response = await _client.PutAsJsonAsync($"/api/{{lowerName}}/{id}", request);
|
|
196
|
-
|
|
197
|
-
// Assert
|
|
198
|
-
response.StatusCode.Should().Be(HttpStatusCode.NotFound);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
[Fact]
|
|
202
|
-
public async Task Update_WhenConcurrencyConflict_ShouldReturn409()
|
|
203
|
-
{
|
|
204
|
-
// Arrange
|
|
205
|
-
var id = Guid.NewGuid();
|
|
206
|
-
var request = new Update{{name}}Request();
|
|
207
|
-
_mockService.Setup(x => x.UpdateAsync(id, It.IsAny<Update{{name}}Request>(), It.IsAny<CancellationToken>()))
|
|
208
|
-
.ThrowsAsync(new ConcurrencyException("Concurrency conflict"));
|
|
209
|
-
|
|
210
|
-
// Act
|
|
211
|
-
var response = await _client.PutAsJsonAsync($"/api/{{lowerName}}/{id}", request);
|
|
212
|
-
|
|
213
|
-
// Assert
|
|
214
|
-
response.StatusCode.Should().Be(HttpStatusCode.Conflict);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
#endregion
|
|
218
|
-
|
|
219
|
-
#region DELETE Tests
|
|
220
|
-
|
|
221
|
-
[Fact]
|
|
222
|
-
public async Task Delete_WhenExists_ShouldReturn204()
|
|
223
|
-
{
|
|
224
|
-
// Arrange
|
|
225
|
-
var id = Guid.NewGuid();
|
|
226
|
-
_mockService.Setup(x => x.DeleteAsync(id, It.IsAny<CancellationToken>()))
|
|
227
|
-
.Returns(Task.CompletedTask);
|
|
228
|
-
|
|
229
|
-
// Act
|
|
230
|
-
var response = await _client.DeleteAsync($"/api/{{lowerName}}/{id}");
|
|
231
|
-
|
|
232
|
-
// Assert
|
|
233
|
-
response.StatusCode.Should().Be(HttpStatusCode.NoContent);
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
[Fact]
|
|
237
|
-
public async Task Delete_WhenNotExists_ShouldReturn404()
|
|
238
|
-
{
|
|
239
|
-
// Arrange
|
|
240
|
-
var id = Guid.NewGuid();
|
|
241
|
-
_mockService.Setup(x => x.DeleteAsync(id, It.IsAny<CancellationToken>()))
|
|
242
|
-
.ThrowsAsync(new NotFoundException($"{{name}} {id} not found"));
|
|
243
|
-
|
|
244
|
-
// Act
|
|
245
|
-
var response = await _client.DeleteAsync($"/api/{{lowerName}}/{id}");
|
|
246
|
-
|
|
247
|
-
// Assert
|
|
248
|
-
response.StatusCode.Should().Be(HttpStatusCode.NotFound);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
#endregion
|
|
252
|
-
|
|
253
|
-
{{#if includeAuthorization}}
|
|
254
|
-
#region Authorization Tests
|
|
255
|
-
|
|
256
|
-
[Fact]
|
|
257
|
-
public async Task GetAll_WhenUnauthorized_ShouldReturn401()
|
|
258
|
-
{
|
|
259
|
-
// Arrange
|
|
260
|
-
var unauthenticatedClient = _factory.CreateClient();
|
|
261
|
-
// Don't add auth header
|
|
262
|
-
|
|
263
|
-
// Act
|
|
264
|
-
var response = await unauthenticatedClient.GetAsync("/api/{{lowerName}}");
|
|
265
|
-
|
|
266
|
-
// Assert
|
|
267
|
-
response.StatusCode.Should().Be(HttpStatusCode.Unauthorized);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
[Fact]
|
|
271
|
-
public async Task Create_WhenForbidden_ShouldReturn403()
|
|
272
|
-
{
|
|
273
|
-
// Arrange - User without create permission
|
|
274
|
-
var request = new Create{{name}}Request { Code = "TEST" };
|
|
275
|
-
|
|
276
|
-
// Act
|
|
277
|
-
var response = await _client.PostAsJsonAsync("/api/{{lowerName}}", request);
|
|
278
|
-
|
|
279
|
-
// Assert
|
|
280
|
-
response.StatusCode.Should().Be(HttpStatusCode.Forbidden);
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
[Fact]
|
|
284
|
-
public async Task Delete_WhenNotAdmin_ShouldReturn403()
|
|
285
|
-
{
|
|
286
|
-
// Arrange
|
|
287
|
-
var id = Guid.NewGuid();
|
|
288
|
-
|
|
289
|
-
// Act
|
|
290
|
-
var response = await _client.DeleteAsync($"/api/{{lowerName}}/{id}");
|
|
291
|
-
|
|
292
|
-
// Assert
|
|
293
|
-
response.StatusCode.Should().Be(HttpStatusCode.Forbidden);
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
#endregion
|
|
297
|
-
{{/if}}
|
|
298
|
-
|
|
299
|
-
{{#unless isSystemEntity}}
|
|
300
|
-
#region Tenant Isolation Tests
|
|
301
|
-
|
|
302
|
-
[Fact]
|
|
303
|
-
public async Task GetAll_ShouldOnlyReturnCurrentTenantData()
|
|
304
|
-
{
|
|
305
|
-
// Arrange
|
|
306
|
-
var tenantAId = Guid.NewGuid();
|
|
307
|
-
var tenantAItems = new PaginatedResult<{{name}}Response>
|
|
308
|
-
{
|
|
309
|
-
Items = new List<{{name}}Response>
|
|
310
|
-
{
|
|
311
|
-
new() { Id = Guid.NewGuid(), TenantId = tenantAId, Code = "A-001" }
|
|
312
|
-
},
|
|
313
|
-
Total = 1,
|
|
314
|
-
Page = 1,
|
|
315
|
-
PageSize = 10
|
|
316
|
-
};
|
|
317
|
-
_mockService.Setup(x => x.GetAllAsync(It.IsAny<PaginationParams>(), It.IsAny<CancellationToken>()))
|
|
318
|
-
.ReturnsAsync(tenantAItems);
|
|
319
|
-
|
|
320
|
-
// Act
|
|
321
|
-
var response = await _client.GetAsync("/api/{{lowerName}}");
|
|
322
|
-
|
|
323
|
-
// Assert
|
|
324
|
-
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
|
325
|
-
var result = await response.Content.ReadFromJsonAsync<PaginatedResult<{{name}}Response>>();
|
|
326
|
-
result.Should().NotBeNull();
|
|
327
|
-
result!.Items.Should().OnlyContain(x => x.TenantId == tenantAId);
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
[Fact]
|
|
331
|
-
public async Task GetById_WhenDifferentTenant_ShouldReturn404()
|
|
332
|
-
{
|
|
333
|
-
// Arrange
|
|
334
|
-
var id = Guid.NewGuid();
|
|
335
|
-
// Entity belongs to different tenant
|
|
336
|
-
_mockService.Setup(x => x.GetByIdAsync(id, It.IsAny<CancellationToken>()))
|
|
337
|
-
.ReturnsAsync(({{name}}Response?)null);
|
|
338
|
-
|
|
339
|
-
// Act
|
|
340
|
-
var response = await _client.GetAsync($"/api/{{lowerName}}/{id}");
|
|
341
|
-
|
|
342
|
-
// Assert
|
|
343
|
-
response.StatusCode.Should().Be(HttpStatusCode.NotFound);
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
#endregion
|
|
347
|
-
{{/unless}}
|
|
348
|
-
|
|
349
|
-
#region Validation Tests
|
|
350
|
-
|
|
351
|
-
[Theory]
|
|
352
|
-
[InlineData(null)]
|
|
353
|
-
[InlineData("")]
|
|
354
|
-
[InlineData(" ")]
|
|
355
|
-
public async Task Create_WhenInvalidCode_ShouldReturn400(string? invalidCode)
|
|
356
|
-
{
|
|
357
|
-
// Arrange
|
|
358
|
-
var request = new Create{{name}}Request { Code = invalidCode! };
|
|
359
|
-
|
|
360
|
-
// Act
|
|
361
|
-
var response = await _client.PostAsJsonAsync("/api/{{lowerName}}", request);
|
|
362
|
-
|
|
363
|
-
// Assert
|
|
364
|
-
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
[Fact]
|
|
368
|
-
public async Task Create_WhenCodeTooLong_ShouldReturn400()
|
|
369
|
-
{
|
|
370
|
-
// Arrange
|
|
371
|
-
var request = new Create{{name}}Request
|
|
372
|
-
{
|
|
373
|
-
Code = new string('A', 256) // Exceeds max length
|
|
374
|
-
};
|
|
375
|
-
|
|
376
|
-
// Act
|
|
377
|
-
var response = await _client.PostAsJsonAsync("/api/{{lowerName}}", request);
|
|
378
|
-
|
|
379
|
-
// Assert
|
|
380
|
-
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
#endregion
|
|
384
|
-
|
|
385
|
-
#region Pagination Tests
|
|
386
|
-
|
|
387
|
-
[Fact]
|
|
388
|
-
public async Task GetAll_WithPagination_ShouldReturnPagedResults()
|
|
389
|
-
{
|
|
390
|
-
// Arrange
|
|
391
|
-
var items = Enumerable.Range(1, 100)
|
|
392
|
-
.Select(i => new {{name}}Response { Id = Guid.NewGuid(), Code = $"TEST-{i:D3}" })
|
|
393
|
-
.ToList();
|
|
394
|
-
var paginatedResult = new PaginatedResult<{{name}}Response>
|
|
395
|
-
{
|
|
396
|
-
Items = items.Take(10).ToList(),
|
|
397
|
-
Total = 100,
|
|
398
|
-
Page = 1,
|
|
399
|
-
PageSize = 10
|
|
400
|
-
};
|
|
401
|
-
_mockService.Setup(x => x.GetAllAsync(It.IsAny<PaginationParams>(), It.IsAny<CancellationToken>()))
|
|
402
|
-
.ReturnsAsync(paginatedResult);
|
|
403
|
-
|
|
404
|
-
// Act
|
|
405
|
-
var response = await _client.GetAsync("/api/{{lowerName}}?page=1&pageSize=10");
|
|
406
|
-
|
|
407
|
-
// Assert
|
|
408
|
-
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
|
409
|
-
var result = await response.Content.ReadFromJsonAsync<PaginatedResult<{{name}}Response>>();
|
|
410
|
-
result.Should().NotBeNull();
|
|
411
|
-
result!.Items.Should().HaveCount(10);
|
|
412
|
-
result.Total.Should().Be(100);
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
#endregion
|
|
416
|
-
|
|
417
|
-
#region Error Response Format Tests
|
|
418
|
-
|
|
419
|
-
[Fact]
|
|
420
|
-
public async Task Create_WhenValidationFails_ShouldReturnProblemDetails()
|
|
421
|
-
{
|
|
422
|
-
// Arrange
|
|
423
|
-
var request = new Create{{name}}Request { Code = "" };
|
|
424
|
-
|
|
425
|
-
// Act
|
|
426
|
-
var response = await _client.PostAsJsonAsync("/api/{{lowerName}}", request);
|
|
427
|
-
|
|
428
|
-
// Assert
|
|
429
|
-
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
|
|
430
|
-
var problemDetails = await response.Content.ReadFromJsonAsync<ProblemDetails>();
|
|
431
|
-
problemDetails.Should().NotBeNull();
|
|
432
|
-
problemDetails!.Status.Should().Be(400);
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
#endregion
|
|
436
|
-
}
|
|
1
|
+
{{!-- SmartStack Controller Test Template --}}
|
|
2
|
+
{{!-- Generates integration tests for API controllers following SmartStack conventions --}}
|
|
3
|
+
|
|
4
|
+
using FluentAssertions;
|
|
5
|
+
using Microsoft.AspNetCore.Mvc.Testing;
|
|
6
|
+
using Microsoft.Extensions.DependencyInjection;
|
|
7
|
+
using Moq;
|
|
8
|
+
using System.Net;
|
|
9
|
+
using System.Net.Http.Json;
|
|
10
|
+
using Xunit;
|
|
11
|
+
using {{namespace}}.Api;
|
|
12
|
+
using {{namespace}}.Api.Controllers;
|
|
13
|
+
using {{namespace}}.Application.DTOs;
|
|
14
|
+
using {{namespace}}.Application.Interfaces;
|
|
15
|
+
using {{namespace}}.Domain.Entities;
|
|
16
|
+
|
|
17
|
+
namespace {{namespace}}.Tests.Integration.Controllers;
|
|
18
|
+
|
|
19
|
+
/// <summary>
|
|
20
|
+
/// Integration tests for <see cref="{{name}}Controller"/>.
|
|
21
|
+
/// Follows SmartStack testing conventions: {Method}_When{Condition}_Should{Result}
|
|
22
|
+
/// </summary>
|
|
23
|
+
public class {{name}}ControllerTests : IClassFixture<WebApplicationFactory<Program>>
|
|
24
|
+
{
|
|
25
|
+
private readonly WebApplicationFactory<Program> _factory;
|
|
26
|
+
private readonly HttpClient _client;
|
|
27
|
+
private readonly Mock<I{{name}}Service> _mockService;
|
|
28
|
+
|
|
29
|
+
public {{name}}ControllerTests(WebApplicationFactory<Program> factory)
|
|
30
|
+
{
|
|
31
|
+
_mockService = new Mock<I{{name}}Service>();
|
|
32
|
+
|
|
33
|
+
_factory = factory.WithWebHostBuilder(builder =>
|
|
34
|
+
{
|
|
35
|
+
builder.ConfigureServices(services =>
|
|
36
|
+
{
|
|
37
|
+
// Remove the real service and add mock
|
|
38
|
+
var descriptor = services.SingleOrDefault(
|
|
39
|
+
d => d.ServiceType == typeof(I{{name}}Service));
|
|
40
|
+
if (descriptor != null)
|
|
41
|
+
services.Remove(descriptor);
|
|
42
|
+
|
|
43
|
+
services.AddScoped(_ => _mockService.Object);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
_client = _factory.CreateClient();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
#region GET Tests
|
|
51
|
+
|
|
52
|
+
[Fact]
|
|
53
|
+
public async Task GetAll_WhenCalled_ShouldReturn200WithList()
|
|
54
|
+
{
|
|
55
|
+
// Arrange
|
|
56
|
+
var items = new PaginatedResult<{{name}}Response>
|
|
57
|
+
{
|
|
58
|
+
Items = new List<{{name}}Response>
|
|
59
|
+
{
|
|
60
|
+
new() { Id = Guid.NewGuid(), Code = "TEST-001" },
|
|
61
|
+
new() { Id = Guid.NewGuid(), Code = "TEST-002" }
|
|
62
|
+
},
|
|
63
|
+
Total = 2,
|
|
64
|
+
Page = 1,
|
|
65
|
+
PageSize = 10
|
|
66
|
+
};
|
|
67
|
+
_mockService.Setup(x => x.GetAllAsync(It.IsAny<PaginationParams>(), It.IsAny<CancellationToken>()))
|
|
68
|
+
.ReturnsAsync(items);
|
|
69
|
+
|
|
70
|
+
// Act
|
|
71
|
+
var response = await _client.GetAsync("/api/{{lowerName}}");
|
|
72
|
+
|
|
73
|
+
// Assert
|
|
74
|
+
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
|
75
|
+
var result = await response.Content.ReadFromJsonAsync<PaginatedResult<{{name}}Response>>();
|
|
76
|
+
result.Should().NotBeNull();
|
|
77
|
+
result!.Items.Should().HaveCount(2);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
[Fact]
|
|
81
|
+
public async Task GetById_WhenExists_ShouldReturn200()
|
|
82
|
+
{
|
|
83
|
+
// Arrange
|
|
84
|
+
var id = Guid.NewGuid();
|
|
85
|
+
var item = new {{name}}Response { Id = id, Code = "TEST-001" };
|
|
86
|
+
_mockService.Setup(x => x.GetByIdAsync(id, It.IsAny<CancellationToken>()))
|
|
87
|
+
.ReturnsAsync(item);
|
|
88
|
+
|
|
89
|
+
// Act
|
|
90
|
+
var response = await _client.GetAsync($"/api/{{lowerName}}/{id}");
|
|
91
|
+
|
|
92
|
+
// Assert
|
|
93
|
+
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
|
94
|
+
var result = await response.Content.ReadFromJsonAsync<{{name}}Response>();
|
|
95
|
+
result!.Id.Should().Be(id);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
[Fact]
|
|
99
|
+
public async Task GetById_WhenNotExists_ShouldReturn404()
|
|
100
|
+
{
|
|
101
|
+
// Arrange
|
|
102
|
+
var id = Guid.NewGuid();
|
|
103
|
+
_mockService.Setup(x => x.GetByIdAsync(id, It.IsAny<CancellationToken>()))
|
|
104
|
+
.ReturnsAsync(({{name}}Response?)null);
|
|
105
|
+
|
|
106
|
+
// Act
|
|
107
|
+
var response = await _client.GetAsync($"/api/{{lowerName}}/{id}");
|
|
108
|
+
|
|
109
|
+
// Assert
|
|
110
|
+
response.StatusCode.Should().Be(HttpStatusCode.NotFound);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
#endregion
|
|
114
|
+
|
|
115
|
+
#region POST Tests
|
|
116
|
+
|
|
117
|
+
[Fact]
|
|
118
|
+
public async Task Create_WhenValidData_ShouldReturn201()
|
|
119
|
+
{
|
|
120
|
+
// Arrange
|
|
121
|
+
var request = new Create{{name}}Request { Code = "NEW-001" };
|
|
122
|
+
var created = new {{name}}Response { Id = Guid.NewGuid(), Code = request.Code };
|
|
123
|
+
_mockService.Setup(x => x.CreateAsync(It.IsAny<Create{{name}}Request>(), It.IsAny<CancellationToken>()))
|
|
124
|
+
.ReturnsAsync(created);
|
|
125
|
+
|
|
126
|
+
// Act
|
|
127
|
+
var response = await _client.PostAsJsonAsync("/api/{{lowerName}}", request);
|
|
128
|
+
|
|
129
|
+
// Assert
|
|
130
|
+
response.StatusCode.Should().Be(HttpStatusCode.Created);
|
|
131
|
+
response.Headers.Location.Should().NotBeNull();
|
|
132
|
+
var result = await response.Content.ReadFromJsonAsync<{{name}}Response>();
|
|
133
|
+
result!.Code.Should().Be(request.Code);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
[Fact]
|
|
137
|
+
public async Task Create_WhenInvalidData_ShouldReturn400()
|
|
138
|
+
{
|
|
139
|
+
// Arrange
|
|
140
|
+
var request = new Create{{name}}Request { Code = "" }; // Invalid
|
|
141
|
+
|
|
142
|
+
// Act
|
|
143
|
+
var response = await _client.PostAsJsonAsync("/api/{{lowerName}}", request);
|
|
144
|
+
|
|
145
|
+
// Assert
|
|
146
|
+
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
[Fact]
|
|
150
|
+
public async Task Create_WhenDuplicate_ShouldReturn409()
|
|
151
|
+
{
|
|
152
|
+
// Arrange
|
|
153
|
+
var request = new Create{{name}}Request { Code = "EXISTING" };
|
|
154
|
+
_mockService.Setup(x => x.CreateAsync(It.IsAny<Create{{name}}Request>(), It.IsAny<CancellationToken>()))
|
|
155
|
+
.ThrowsAsync(new BusinessException("Code already exists"));
|
|
156
|
+
|
|
157
|
+
// Act
|
|
158
|
+
var response = await _client.PostAsJsonAsync("/api/{{lowerName}}", request);
|
|
159
|
+
|
|
160
|
+
// Assert
|
|
161
|
+
response.StatusCode.Should().Be(HttpStatusCode.Conflict);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
#endregion
|
|
165
|
+
|
|
166
|
+
#region PUT Tests
|
|
167
|
+
|
|
168
|
+
[Fact]
|
|
169
|
+
public async Task Update_WhenValidData_ShouldReturn200()
|
|
170
|
+
{
|
|
171
|
+
// Arrange
|
|
172
|
+
var id = Guid.NewGuid();
|
|
173
|
+
var request = new Update{{name}}Request { /* properties */ };
|
|
174
|
+
var updated = new {{name}}Response { Id = id, Code = "UPDATED" };
|
|
175
|
+
_mockService.Setup(x => x.UpdateAsync(id, It.IsAny<Update{{name}}Request>(), It.IsAny<CancellationToken>()))
|
|
176
|
+
.ReturnsAsync(updated);
|
|
177
|
+
|
|
178
|
+
// Act
|
|
179
|
+
var response = await _client.PutAsJsonAsync($"/api/{{lowerName}}/{id}", request);
|
|
180
|
+
|
|
181
|
+
// Assert
|
|
182
|
+
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
[Fact]
|
|
186
|
+
public async Task Update_WhenNotExists_ShouldReturn404()
|
|
187
|
+
{
|
|
188
|
+
// Arrange
|
|
189
|
+
var id = Guid.NewGuid();
|
|
190
|
+
var request = new Update{{name}}Request();
|
|
191
|
+
_mockService.Setup(x => x.UpdateAsync(id, It.IsAny<Update{{name}}Request>(), It.IsAny<CancellationToken>()))
|
|
192
|
+
.ThrowsAsync(new NotFoundException($"{{name}} {id} not found"));
|
|
193
|
+
|
|
194
|
+
// Act
|
|
195
|
+
var response = await _client.PutAsJsonAsync($"/api/{{lowerName}}/{id}", request);
|
|
196
|
+
|
|
197
|
+
// Assert
|
|
198
|
+
response.StatusCode.Should().Be(HttpStatusCode.NotFound);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
[Fact]
|
|
202
|
+
public async Task Update_WhenConcurrencyConflict_ShouldReturn409()
|
|
203
|
+
{
|
|
204
|
+
// Arrange
|
|
205
|
+
var id = Guid.NewGuid();
|
|
206
|
+
var request = new Update{{name}}Request();
|
|
207
|
+
_mockService.Setup(x => x.UpdateAsync(id, It.IsAny<Update{{name}}Request>(), It.IsAny<CancellationToken>()))
|
|
208
|
+
.ThrowsAsync(new ConcurrencyException("Concurrency conflict"));
|
|
209
|
+
|
|
210
|
+
// Act
|
|
211
|
+
var response = await _client.PutAsJsonAsync($"/api/{{lowerName}}/{id}", request);
|
|
212
|
+
|
|
213
|
+
// Assert
|
|
214
|
+
response.StatusCode.Should().Be(HttpStatusCode.Conflict);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
#endregion
|
|
218
|
+
|
|
219
|
+
#region DELETE Tests
|
|
220
|
+
|
|
221
|
+
[Fact]
|
|
222
|
+
public async Task Delete_WhenExists_ShouldReturn204()
|
|
223
|
+
{
|
|
224
|
+
// Arrange
|
|
225
|
+
var id = Guid.NewGuid();
|
|
226
|
+
_mockService.Setup(x => x.DeleteAsync(id, It.IsAny<CancellationToken>()))
|
|
227
|
+
.Returns(Task.CompletedTask);
|
|
228
|
+
|
|
229
|
+
// Act
|
|
230
|
+
var response = await _client.DeleteAsync($"/api/{{lowerName}}/{id}");
|
|
231
|
+
|
|
232
|
+
// Assert
|
|
233
|
+
response.StatusCode.Should().Be(HttpStatusCode.NoContent);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
[Fact]
|
|
237
|
+
public async Task Delete_WhenNotExists_ShouldReturn404()
|
|
238
|
+
{
|
|
239
|
+
// Arrange
|
|
240
|
+
var id = Guid.NewGuid();
|
|
241
|
+
_mockService.Setup(x => x.DeleteAsync(id, It.IsAny<CancellationToken>()))
|
|
242
|
+
.ThrowsAsync(new NotFoundException($"{{name}} {id} not found"));
|
|
243
|
+
|
|
244
|
+
// Act
|
|
245
|
+
var response = await _client.DeleteAsync($"/api/{{lowerName}}/{id}");
|
|
246
|
+
|
|
247
|
+
// Assert
|
|
248
|
+
response.StatusCode.Should().Be(HttpStatusCode.NotFound);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
#endregion
|
|
252
|
+
|
|
253
|
+
{{#if includeAuthorization}}
|
|
254
|
+
#region Authorization Tests
|
|
255
|
+
|
|
256
|
+
[Fact]
|
|
257
|
+
public async Task GetAll_WhenUnauthorized_ShouldReturn401()
|
|
258
|
+
{
|
|
259
|
+
// Arrange
|
|
260
|
+
var unauthenticatedClient = _factory.CreateClient();
|
|
261
|
+
// Don't add auth header
|
|
262
|
+
|
|
263
|
+
// Act
|
|
264
|
+
var response = await unauthenticatedClient.GetAsync("/api/{{lowerName}}");
|
|
265
|
+
|
|
266
|
+
// Assert
|
|
267
|
+
response.StatusCode.Should().Be(HttpStatusCode.Unauthorized);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
[Fact]
|
|
271
|
+
public async Task Create_WhenForbidden_ShouldReturn403()
|
|
272
|
+
{
|
|
273
|
+
// Arrange - User without create permission
|
|
274
|
+
var request = new Create{{name}}Request { Code = "TEST" };
|
|
275
|
+
|
|
276
|
+
// Act
|
|
277
|
+
var response = await _client.PostAsJsonAsync("/api/{{lowerName}}", request);
|
|
278
|
+
|
|
279
|
+
// Assert
|
|
280
|
+
response.StatusCode.Should().Be(HttpStatusCode.Forbidden);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
[Fact]
|
|
284
|
+
public async Task Delete_WhenNotAdmin_ShouldReturn403()
|
|
285
|
+
{
|
|
286
|
+
// Arrange
|
|
287
|
+
var id = Guid.NewGuid();
|
|
288
|
+
|
|
289
|
+
// Act
|
|
290
|
+
var response = await _client.DeleteAsync($"/api/{{lowerName}}/{id}");
|
|
291
|
+
|
|
292
|
+
// Assert
|
|
293
|
+
response.StatusCode.Should().Be(HttpStatusCode.Forbidden);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
#endregion
|
|
297
|
+
{{/if}}
|
|
298
|
+
|
|
299
|
+
{{#unless isSystemEntity}}
|
|
300
|
+
#region Tenant Isolation Tests
|
|
301
|
+
|
|
302
|
+
[Fact]
|
|
303
|
+
public async Task GetAll_ShouldOnlyReturnCurrentTenantData()
|
|
304
|
+
{
|
|
305
|
+
// Arrange
|
|
306
|
+
var tenantAId = Guid.NewGuid();
|
|
307
|
+
var tenantAItems = new PaginatedResult<{{name}}Response>
|
|
308
|
+
{
|
|
309
|
+
Items = new List<{{name}}Response>
|
|
310
|
+
{
|
|
311
|
+
new() { Id = Guid.NewGuid(), TenantId = tenantAId, Code = "A-001" }
|
|
312
|
+
},
|
|
313
|
+
Total = 1,
|
|
314
|
+
Page = 1,
|
|
315
|
+
PageSize = 10
|
|
316
|
+
};
|
|
317
|
+
_mockService.Setup(x => x.GetAllAsync(It.IsAny<PaginationParams>(), It.IsAny<CancellationToken>()))
|
|
318
|
+
.ReturnsAsync(tenantAItems);
|
|
319
|
+
|
|
320
|
+
// Act
|
|
321
|
+
var response = await _client.GetAsync("/api/{{lowerName}}");
|
|
322
|
+
|
|
323
|
+
// Assert
|
|
324
|
+
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
|
325
|
+
var result = await response.Content.ReadFromJsonAsync<PaginatedResult<{{name}}Response>>();
|
|
326
|
+
result.Should().NotBeNull();
|
|
327
|
+
result!.Items.Should().OnlyContain(x => x.TenantId == tenantAId);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
[Fact]
|
|
331
|
+
public async Task GetById_WhenDifferentTenant_ShouldReturn404()
|
|
332
|
+
{
|
|
333
|
+
// Arrange
|
|
334
|
+
var id = Guid.NewGuid();
|
|
335
|
+
// Entity belongs to different tenant
|
|
336
|
+
_mockService.Setup(x => x.GetByIdAsync(id, It.IsAny<CancellationToken>()))
|
|
337
|
+
.ReturnsAsync(({{name}}Response?)null);
|
|
338
|
+
|
|
339
|
+
// Act
|
|
340
|
+
var response = await _client.GetAsync($"/api/{{lowerName}}/{id}");
|
|
341
|
+
|
|
342
|
+
// Assert
|
|
343
|
+
response.StatusCode.Should().Be(HttpStatusCode.NotFound);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
#endregion
|
|
347
|
+
{{/unless}}
|
|
348
|
+
|
|
349
|
+
#region Validation Tests
|
|
350
|
+
|
|
351
|
+
[Theory]
|
|
352
|
+
[InlineData(null)]
|
|
353
|
+
[InlineData("")]
|
|
354
|
+
[InlineData(" ")]
|
|
355
|
+
public async Task Create_WhenInvalidCode_ShouldReturn400(string? invalidCode)
|
|
356
|
+
{
|
|
357
|
+
// Arrange
|
|
358
|
+
var request = new Create{{name}}Request { Code = invalidCode! };
|
|
359
|
+
|
|
360
|
+
// Act
|
|
361
|
+
var response = await _client.PostAsJsonAsync("/api/{{lowerName}}", request);
|
|
362
|
+
|
|
363
|
+
// Assert
|
|
364
|
+
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
[Fact]
|
|
368
|
+
public async Task Create_WhenCodeTooLong_ShouldReturn400()
|
|
369
|
+
{
|
|
370
|
+
// Arrange
|
|
371
|
+
var request = new Create{{name}}Request
|
|
372
|
+
{
|
|
373
|
+
Code = new string('A', 256) // Exceeds max length
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
// Act
|
|
377
|
+
var response = await _client.PostAsJsonAsync("/api/{{lowerName}}", request);
|
|
378
|
+
|
|
379
|
+
// Assert
|
|
380
|
+
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
#endregion
|
|
384
|
+
|
|
385
|
+
#region Pagination Tests
|
|
386
|
+
|
|
387
|
+
[Fact]
|
|
388
|
+
public async Task GetAll_WithPagination_ShouldReturnPagedResults()
|
|
389
|
+
{
|
|
390
|
+
// Arrange
|
|
391
|
+
var items = Enumerable.Range(1, 100)
|
|
392
|
+
.Select(i => new {{name}}Response { Id = Guid.NewGuid(), Code = $"TEST-{i:D3}" })
|
|
393
|
+
.ToList();
|
|
394
|
+
var paginatedResult = new PaginatedResult<{{name}}Response>
|
|
395
|
+
{
|
|
396
|
+
Items = items.Take(10).ToList(),
|
|
397
|
+
Total = 100,
|
|
398
|
+
Page = 1,
|
|
399
|
+
PageSize = 10
|
|
400
|
+
};
|
|
401
|
+
_mockService.Setup(x => x.GetAllAsync(It.IsAny<PaginationParams>(), It.IsAny<CancellationToken>()))
|
|
402
|
+
.ReturnsAsync(paginatedResult);
|
|
403
|
+
|
|
404
|
+
// Act
|
|
405
|
+
var response = await _client.GetAsync("/api/{{lowerName}}?page=1&pageSize=10");
|
|
406
|
+
|
|
407
|
+
// Assert
|
|
408
|
+
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
|
409
|
+
var result = await response.Content.ReadFromJsonAsync<PaginatedResult<{{name}}Response>>();
|
|
410
|
+
result.Should().NotBeNull();
|
|
411
|
+
result!.Items.Should().HaveCount(10);
|
|
412
|
+
result.Total.Should().Be(100);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
#endregion
|
|
416
|
+
|
|
417
|
+
#region Error Response Format Tests
|
|
418
|
+
|
|
419
|
+
[Fact]
|
|
420
|
+
public async Task Create_WhenValidationFails_ShouldReturnProblemDetails()
|
|
421
|
+
{
|
|
422
|
+
// Arrange
|
|
423
|
+
var request = new Create{{name}}Request { Code = "" };
|
|
424
|
+
|
|
425
|
+
// Act
|
|
426
|
+
var response = await _client.PostAsJsonAsync("/api/{{lowerName}}", request);
|
|
427
|
+
|
|
428
|
+
// Assert
|
|
429
|
+
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
|
|
430
|
+
var problemDetails = await response.Content.ReadFromJsonAsync<ProblemDetails>();
|
|
431
|
+
problemDetails.Should().NotBeNull();
|
|
432
|
+
problemDetails!.Status.Should().Be(400);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
#endregion
|
|
436
|
+
}
|