@polymorphism-tech/morph-spec 4.12.0 → 4.13.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/README.md +21 -29
- package/bin/morph-spec.js +41 -24
- package/bin/task-manager.js +962 -1341
- package/bin/validate.js +1 -1
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +10 -11
- package/docs/QUICKSTART.md +15 -15
- package/framework/CLAUDE.md +5 -2
- package/framework/agents/README.md +3 -4
- package/framework/agents/ai-agents/ai-system-architect.md +6 -10
- package/framework/agents/architecture/seo-growth-hacker.md +166 -326
- package/framework/agents/architecture/standards-architect.md +21 -31
- package/framework/agents/backend/dotnet-senior.md +160 -86
- package/framework/agents/backend/ef-modeler.md +2 -6
- package/framework/agents/backend/hangfire-orchestrator.md +8 -9
- package/framework/agents/backend/ms-agent-expert.md +1 -1
- package/framework/agents/frontend/nextjs-expert.md +198 -133
- package/framework/agents/frontend/ui-ux-designer.md +226 -267
- package/framework/agents/infrastructure/infra-architect.md +191 -27
- package/framework/agents/integrations/clerk-auth.md +19 -26
- package/framework/agents/integrations/hangfire-integration.md +1 -4
- package/framework/agents.json +215 -1453
- package/framework/commands/morph-apply.md +4 -4
- package/framework/commands/morph-preflight.md +4 -26
- package/framework/commands/morph-proposal.md +19 -19
- package/framework/commands/morph-status.md +2 -2
- package/framework/commands/morph-troubleshoot.md +6 -13
- package/framework/hooks/README.md +1 -6
- package/framework/hooks/claude-code/post-tool-use/validator-feedback.js +4 -6
- package/framework/hooks/claude-code/pre-compact/save-morph-context.js +12 -7
- package/framework/hooks/claude-code/pre-tool-use/enforce-phase-writes.js +2 -2
- package/framework/hooks/claude-code/pre-tool-use/protect-spec-files.js +2 -2
- package/framework/hooks/claude-code/pre-tool-use/task-tracking-guard.js +5 -10
- package/framework/hooks/claude-code/session-start/inject-morph-context.js +63 -30
- package/framework/hooks/claude-code/session-start/resume-context.js +77 -0
- package/framework/hooks/claude-code/statusline.py +4 -5
- package/framework/hooks/claude-code/stop/validate-completion.js +4 -4
- package/framework/hooks/claude-code/user-prompt/enrich-prompt.js +8 -8
- package/framework/hooks/shared/compact-restore.js +28 -5
- package/framework/hooks/shared/phase-utils.js +4 -8
- package/framework/hooks/shared/state-reader.js +34 -3
- package/framework/phases.json +7 -45
- package/framework/rules/ai-agents-standards.md +15 -0
- package/framework/rules/asaas-standards.md +13 -0
- package/framework/rules/clerk-standards.md +13 -0
- package/framework/rules/csharp-standards.md +8 -4
- package/framework/rules/data-standards.md +18 -0
- package/framework/rules/dotnet-templates.md +87 -0
- package/framework/rules/frontend-standards.md +13 -13
- package/framework/rules/hangfire-standards.md +13 -0
- package/framework/rules/infrastructure-standards.md +27 -16
- package/framework/rules/morph-workflow.md +73 -13
- package/framework/rules/nextjs-standards.md +1 -0
- package/framework/rules/nextjs-templates.md +40 -0
- package/framework/rules/resend-standards.md +12 -0
- package/framework/rules/supabase-standards.md +29 -0
- package/framework/skills/README.md +6 -8
- package/framework/skills/level-0-meta/mcp-registry.json +21 -99
- package/framework/{standards/ai-agents/team-orchestration.md → skills/level-0-meta/morph-agent-teams/SKILL.md} +472 -479
- package/framework/skills/level-0-meta/morph-brainstorming/SKILL.md +28 -1
- package/framework/skills/level-0-meta/morph-checklist/SKILL.md +2 -2
- package/framework/skills/level-0-meta/morph-code-review/SKILL.md +1 -5
- package/framework/skills/level-0-meta/morph-code-review/scripts/scan-csharp.mjs +1 -1
- package/framework/skills/level-0-meta/morph-frontend-review/SKILL.md +23 -41
- package/framework/skills/level-0-meta/morph-init/SKILL.md +18 -14
- package/framework/skills/level-0-meta/morph-parallel-dispatch/SKILL.md +225 -0
- package/framework/skills/level-0-meta/morph-post-implementation/SKILL.md +20 -0
- package/framework/skills/level-0-meta/morph-tool-usage-guide/SKILL.md +4 -8
- package/framework/skills/level-0-meta/morph-tool-usage-guide/references/tools-per-phase.md +15 -59
- package/framework/skills/level-0-meta/morph-verification-before-completion/SKILL.md +7 -19
- package/framework/skills/level-0-meta/morph-verification-before-completion/scripts/check-phase-outputs.mjs +8 -8
- package/framework/skills/level-1-workflows/README.md +1 -1
- package/framework/skills/level-1-workflows/morph-phase-codebase-analysis/SKILL.md +49 -50
- package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +58 -124
- package/framework/skills/level-1-workflows/morph-phase-design/references/architecture-analysis-guide.md +5 -50
- package/framework/skills/level-1-workflows/morph-phase-design/references/spec-example.md +1 -1
- package/framework/skills/level-1-workflows/morph-phase-implement/SKILL.md +127 -159
- package/framework/skills/level-1-workflows/morph-phase-implement/prompts/implementer-prompt.md +4 -3
- package/framework/skills/level-1-workflows/morph-phase-implement/prompts/spec-reviewer-prompt.md +2 -2
- package/framework/skills/level-1-workflows/morph-phase-implement/references/recap-example.md +1 -1
- package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +17 -13
- package/framework/skills/level-1-workflows/morph-phase-tasks/SKILL.md +22 -40
- package/framework/skills/level-1-workflows/morph-phase-tasks/references/task-planning-patterns.md +0 -2
- package/framework/skills/level-1-workflows/morph-phase-tasks/references/tasks-example.md +5 -5
- package/framework/skills/level-1-workflows/morph-phase-uiux/SKILL.md +57 -77
- package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +1 -1
- package/framework/squad-templates/backend-only.json +6 -6
- package/framework/squad-templates/frontend-only.json +17 -8
- package/framework/squad-templates/full-stack.json +29 -24
- package/framework/standards/STANDARDS.json +1021 -2369
- package/framework/standards/backend/dotnet/fluent-validation-vsa.md +200 -0
- package/framework/standards/backend/dotnet/result-pattern.md +213 -0
- package/framework/standards/backend/dotnet/scalar-ui.md +172 -0
- package/framework/standards/backend/dotnet/scrutor.md +158 -0
- package/framework/standards/backend/dotnet/vsa-handler-patterns.md +274 -0
- package/framework/standards/frontend/nextjs/motion-patterns.md +1 -1
- package/framework/standards/infrastructure/supabase/mcp-setup.md +83 -193
- package/framework/standards/integration/mcp/mcp-tools.md +91 -77
- package/framework/templates/IDE-SNIPPETS.md +2 -6
- package/framework/templates/README.md +709 -814
- package/framework/templates/REGISTRY.json +223 -762
- package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -0
- package/framework/templates/code/dotnet/abstractions/error.cs +60 -0
- package/framework/templates/code/dotnet/abstractions/handler.cs +12 -0
- package/framework/templates/code/dotnet/abstractions/result.cs +74 -0
- package/framework/templates/code/dotnet/backend/repository.cs +148 -141
- package/framework/templates/code/dotnet/constants/api-tags.cs +6 -0
- package/framework/templates/code/dotnet/exceptions/exception-handler.cs +32 -0
- package/framework/templates/code/dotnet/extensions/database-extensions.cs +20 -0
- package/framework/templates/code/dotnet/extensions/handler-registration.cs +38 -0
- package/framework/templates/code/dotnet/extensions/health-checks.cs +24 -0
- package/framework/templates/code/dotnet/extensions/map-endpoints.cs +35 -0
- package/framework/templates/code/dotnet/extensions/result-extensions.cs +29 -0
- package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +21 -0
- package/framework/templates/code/dotnet/feature/entity.cs.hbs +8 -0
- package/framework/templates/code/dotnet/feature/errors.cs.hbs +8 -0
- package/framework/templates/code/dotnet/feature/handler.cs.hbs +24 -0
- package/framework/templates/code/dotnet/feature/request.cs.hbs +4 -0
- package/framework/templates/code/dotnet/feature/response.cs.hbs +4 -0
- package/framework/templates/code/dotnet/feature/validator.cs.hbs +15 -0
- package/framework/templates/code/dotnet/jobs/job.cs +21 -17
- package/framework/templates/code/dotnet/pipelines/logging-decorator.cs +31 -0
- package/framework/templates/code/dotnet/pipelines/validation-decorator.cs +97 -0
- package/framework/templates/code/dotnet/test.cs +192 -106
- package/framework/templates/context/ARCHITECTURE.md +89 -0
- package/framework/templates/context/CONTEXT-FEATURE.md +2 -2
- package/framework/templates/context/CONTEXT.md +180 -181
- package/framework/templates/context/design-system.md +1 -5
- package/framework/templates/docs/onboarding.md +2 -3
- package/framework/templates/docs/proposal.md +171 -182
- package/framework/templates/docs/spec.md +1 -3
- package/framework/templates/docs/ui-components.md +0 -72
- package/framework/templates/examples/design-system-examples.md +4 -4
- package/framework/templates/feature/decisions.md +0 -66
- package/framework/templates/feature/recap.md +1 -2
- package/framework/templates/infrastructure/github/README.md +56 -593
- package/framework/templates/integrations/clerk-config.cs +0 -41
- package/framework/templates/meta-prompts/parallel-workers/parallel-worker.md +2 -2
- package/framework/templates/meta-prompts/squad-leaders/backend-squad.md +1 -2
- package/framework/templates/meta-prompts/squad-leaders/frontend-squad.md +83 -126
- package/framework/templates/meta-prompts/validators/pre-commit-validator.md +2 -2
- package/framework/templates/project/validate.js +2 -4
- package/framework/templates/ui/design-system.css +1 -1
- package/framework/workflows/README.md +8 -8
- package/framework/workflows/configs/fast-track.json +51 -0
- package/framework/workflows/configs/full.json +68 -0
- package/framework/workflows/configs/standard.json +7 -3
- package/framework/workflows/docs/STORY-DRIVEN-DEVELOPMENT.md +1 -1
- package/framework/workflows/docs/enforcement-pipeline.md +18 -19
- package/framework/workflows/docs/full-morph.md +69 -76
- package/framework/workflows/docs/standard.md +49 -44
- package/framework/workflows/docs/ui-refresh.md +1 -1
- package/package.json +3 -3
- package/src/commands/agents/dispatch-agents.js +55 -116
- package/src/commands/context/context-refresh.js +79 -0
- package/src/commands/phase/phase-reset.js +1 -1
- package/src/commands/project/doctor.js +10 -32
- package/src/commands/project/init.js +12 -0
- package/src/commands/project/status.js +6 -12
- package/src/commands/scaffold/scaffold.js +131 -0
- package/src/commands/scope/escalate.js +1 -1
- package/src/commands/state/advance-phase.js +15 -29
- package/src/commands/state/state.js +20 -5
- package/src/commands/tasks/task.js +0 -30
- package/src/commands/validation/validate-feature.js +3 -10
- package/src/core/paths/output-schema.js +12 -13
- package/src/core/state/phase-state-machine.js +211 -217
- package/src/core/state/state-manager.js +9 -9
- package/src/lib/agents/agents-manager.js +2 -2
- package/src/lib/agents/output-collector.js +1 -2
- package/src/lib/detectors/design-system-detector.js +4 -4
- package/src/lib/detectors/index.js +1 -11
- package/src/lib/generators/architecture-scanner.js +640 -0
- package/src/lib/generators/context-generator.js +3 -3
- package/src/lib/generators/design-system-generator.js +508 -587
- package/src/lib/generators/index.js +12 -10
- package/src/lib/generators/metadata-extractor.js +386 -387
- package/src/lib/generators/recap-generator.js +4 -4
- package/src/lib/generators/scaffold-generator.js +317 -0
- package/src/lib/generators/settings-generator.js +0 -1
- package/src/lib/phase-chain/phase-validator.js +64 -42
- package/src/lib/stack/stack-profile.js +2 -2
- package/src/lib/standards/digest-builder.js +72 -285
- package/src/lib/tasks/task-parser.js +11 -5
- package/src/lib/validators/architecture/architecture-validator.js +259 -387
- package/src/lib/validators/content/content-validator.js +287 -287
- package/src/lib/validators/css/css-validator.js +340 -352
- package/src/lib/validators/packages/package-validator.js +347 -360
- package/src/lib/validators/shared/issue-counter.js +14 -18
- package/src/lib/validators/validation-runner.js +3 -19
- package/src/scripts/setup-infra.js +1 -1
- package/src/utils/agents-installer.js +24 -3
- package/src/utils/file-copier.js +1 -0
- package/src/utils/hooks-installer.js +11 -19
- package/src/utils/skills-installer.js +178 -178
- package/framework/agents/architecture/prompt-engineer.md +0 -195
- package/framework/agents/backend/api-designer.md +0 -106
- package/framework/agents/frontend/blazor-builder.md +0 -229
- package/framework/agents/frontend/nextjs-ui-specialist.md +0 -218
- package/framework/agents/infrastructure/azure-architect.md +0 -150
- package/framework/agents/infrastructure/azure-deploy-specialist.md +0 -707
- package/framework/agents/infrastructure/bicep-architect.md +0 -132
- package/framework/agents/infrastructure/container-specialist.md +0 -137
- package/framework/agents/infrastructure/devops-engineer.md +0 -126
- package/framework/agents/integrations/azure-identity.md +0 -148
- package/framework/agents/quality/code-analyzer.md +0 -239
- package/framework/agents/quality/testing-specialist.md +0 -130
- package/framework/hooks/claude-code/notification/approval-reminder.js +0 -57
- package/framework/hooks/claude-code/post-tool-use/context-refresh.js +0 -109
- package/framework/hooks/claude-code/post-tool-use/skill-reminder.js +0 -78
- package/framework/index/troubleshooting-index.json +0 -184
- package/framework/rules/testing-standards.md +0 -14
- package/framework/skills/level-0-meta/morph-replicate/SKILL.md +0 -230
- package/framework/skills/level-0-meta/morph-replicate/references/blazor-html-mapping.md +0 -70
- package/framework/skills/level-1-workflows/morph-phase-clarify/SKILL.md +0 -256
- package/framework/skills/level-1-workflows/morph-phase-clarify/references/clarifications-example.md +0 -117
- package/framework/skills/level-1-workflows/morph-phase-setup/SKILL.md +0 -269
- package/framework/standards/ai-agents/blazor-ui.md +0 -364
- package/framework/standards/architecture/ddd/aggregates.md +0 -120
- package/framework/standards/architecture/ddd/bounded-contexts.md +0 -105
- package/framework/standards/architecture/ddd/complexity-levels.md +0 -108
- package/framework/standards/architecture/ddd/entities.md +0 -99
- package/framework/standards/architecture/ddd/ubiquitous-language.md +0 -58
- package/framework/standards/architecture/ddd/value-objects.md +0 -124
- package/framework/standards/backend/api/minimal-api.md +0 -494
- package/framework/standards/backend/api/rest.md +0 -492
- package/framework/standards/backend/authentication/passkeys.md +0 -428
- package/framework/standards/backend/database/ef-core.md +0 -199
- package/framework/standards/backend/database/migrations.md +0 -393
- package/framework/standards/backend/database/repository-patterns.md +0 -528
- package/framework/standards/backend/dotnet/async.md +0 -366
- package/framework/standards/backend/dotnet/core.md +0 -117
- package/framework/standards/backend/dotnet/di.md +0 -439
- package/framework/standards/backend/dotnet/program-cs-checklist.md +0 -92
- package/framework/standards/context/analytics.md +0 -96
- package/framework/standards/context/bundles.md +0 -110
- package/framework/standards/context/priming.md +0 -78
- package/framework/standards/core/architecture.md +0 -185
- package/framework/standards/core/coding.md +0 -214
- package/framework/standards/core/git-branching-strategy.md +0 -403
- package/framework/standards/core/git.md +0 -185
- package/framework/standards/core/testing.md +0 -295
- package/framework/standards/data/nosql/blob-storage.md +0 -102
- package/framework/standards/data/vector-search/azure-ai-search.md +0 -121
- package/framework/standards/frontend/blazor/design-checklist.md +0 -222
- package/framework/standards/frontend/blazor/fluent-ui-setup.md +0 -595
- package/framework/standards/frontend/blazor/fluent-ui.md +0 -137
- package/framework/standards/frontend/blazor/html-conversion.md +0 -184
- package/framework/standards/frontend/blazor/lifecycle.md +0 -195
- package/framework/standards/frontend/blazor/pitfalls.md +0 -198
- package/framework/standards/frontend/blazor/state.md +0 -191
- package/framework/standards/frontend/design-system/aesthetic-direction.md +0 -266
- package/framework/standards/frontend/design-system/design-trends.md +0 -171
- package/framework/standards/infrastructure/azure/azure.md +0 -624
- package/framework/standards/infrastructure/azure/bicep/bicep-patterns.md +0 -422
- package/framework/standards/infrastructure/azure/devops/azure-devops-setup.md +0 -516
- package/framework/standards/infrastructure/azure/devops/local-development.md +0 -520
- package/framework/standards/infrastructure/azure/services/functions.md +0 -486
- package/framework/standards/infrastructure/azure/services/service-bus.md +0 -459
- package/framework/standards/infrastructure/azure/services/storage.md +0 -407
- package/framework/standards/infrastructure/vercel/vercel-database.md +0 -106
- package/framework/standards/integration/api/graphql.md +0 -91
- package/framework/standards/integration/api/grpc.md +0 -114
- package/framework/standards/integration/api/rest-design.md +0 -95
- package/framework/standards/integration/event-driven/cqrs.md +0 -101
- package/framework/standards/integration/event-driven/event-sourcing.md +0 -124
- package/framework/standards/integration/event-driven/service-bus.md +0 -95
- package/framework/standards/observability/logging.md +0 -131
- package/framework/standards/observability/metrics.md +0 -121
- package/framework/standards/observability/monitoring.md +0 -114
- package/framework/standards/observability/tracing.md +0 -132
- package/framework/standards/workflows/parallel-execution.md +0 -112
- package/framework/standards/workflows/thread-management.md +0 -113
- package/framework/templates/code/dotnet/backend/service.cs +0 -139
- package/framework/templates/code/dotnet/contracts/Commands.cs +0 -74
- package/framework/templates/code/dotnet/contracts/Entities.cs +0 -25
- package/framework/templates/code/dotnet/contracts/Queries.cs +0 -74
- package/framework/templates/code/dotnet/contracts/api-contracts.cs +0 -173
- package/framework/templates/code/dotnet/contracts/contracts-level1.cs +0 -69
- package/framework/templates/code/dotnet/contracts/contracts-level2.cs +0 -86
- package/framework/templates/code/dotnet/contracts/contracts-level3.cs +0 -41
- package/framework/templates/code/dotnet/frontend/component.razor +0 -239
- package/framework/templates/infrastructure/azure/Dockerfile.example +0 -82
- package/framework/templates/infrastructure/azure/README.md +0 -286
- package/framework/templates/infrastructure/azure/app-insights.bicep +0 -63
- package/framework/templates/infrastructure/azure/app-service.bicep +0 -164
- package/framework/templates/infrastructure/azure/container-app-env.bicep +0 -49
- package/framework/templates/infrastructure/azure/container-app.bicep +0 -156
- package/framework/templates/infrastructure/azure/deploy-checklist.md +0 -426
- package/framework/templates/infrastructure/azure/deploy.ps1 +0 -229
- package/framework/templates/infrastructure/azure/deploy.sh +0 -208
- package/framework/templates/infrastructure/azure/key-vault.bicep +0 -91
- package/framework/templates/infrastructure/azure/main.bicep +0 -189
- package/framework/templates/infrastructure/azure/parameters.dev.json +0 -29
- package/framework/templates/infrastructure/azure/parameters.prod.json +0 -29
- package/framework/templates/infrastructure/azure/parameters.staging.json +0 -29
- package/framework/templates/infrastructure/azure/sql-database.bicep +0 -103
- package/framework/templates/infrastructure/azure/storage.bicep +0 -106
- package/framework/templates/infrastructure/github/actions/azure-auth/action.yml.hbs +0 -22
- package/framework/templates/infrastructure/github/workflows/deploy-azure-app-service.yml.hbs +0 -61
- package/framework/templates/integrations/azure-identity-config.cs +0 -288
- package/framework/templates/project-structure/dotnet-ddd.md +0 -70
- package/framework/templates/saas/subscription.cs +0 -347
- package/framework/templates/saas/tenant.cs +0 -338
- package/framework/templates/ui/FluentDesignTheme.cs +0 -149
- package/framework/templates/ui/MudTheme.cs +0 -281
- package/framework/workflows/configs/nodejs-cli.json +0 -40
- package/src/commands/standards/context-budget.js +0 -89
- package/src/lib/standards/index.js +0 -7
- package/src/lib/standards/standards-context-injector.js +0 -302
- package/src/lib/validators/blazor/blazor-concurrency-analyzer.js +0 -316
- package/src/lib/validators/blazor/blazor-state-validator.js +0 -279
- package/src/lib/validators/blazor/blazor-validator.js +0 -369
- package/src/lib/validators/blazor/index.js +0 -9
|
@@ -1,295 +0,0 @@
|
|
|
1
|
-
# Testing Standards
|
|
2
|
-
|
|
3
|
-
> **Scope:** universal
|
|
4
|
-
> **Layer:** 0 (always load)
|
|
5
|
-
> **Keywords:** test, testing, unit, integration, xunit, nunit
|
|
6
|
-
> **Load When:** always
|
|
7
|
-
|
|
8
|
-
Test patterns and best practices for MORPH-SPEC projects
|
|
9
|
-
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
## Test Pyramid
|
|
13
|
-
|
|
14
|
-
```
|
|
15
|
-
/\
|
|
16
|
-
/ \ E2E Tests (10%)
|
|
17
|
-
/____\
|
|
18
|
-
/ \
|
|
19
|
-
/ Integration Tests (30%)
|
|
20
|
-
/________________\
|
|
21
|
-
/ \
|
|
22
|
-
/ Unit Tests (60%) \
|
|
23
|
-
/_______________________\
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
### Distribution
|
|
27
|
-
|
|
28
|
-
- **Unit Tests (60%)**: Fast, isolated, test single units
|
|
29
|
-
- **Integration Tests (30%)**: Test component interactions
|
|
30
|
-
- **E2E Tests (10%)**: Full user workflows
|
|
31
|
-
|
|
32
|
-
---
|
|
33
|
-
|
|
34
|
-
## Naming Conventions
|
|
35
|
-
|
|
36
|
-
### Format
|
|
37
|
-
|
|
38
|
-
```
|
|
39
|
-
MethodName_Scenario_ExpectedBehavior
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
### Examples
|
|
43
|
-
|
|
44
|
-
```csharp
|
|
45
|
-
// .NET (xUnit)
|
|
46
|
-
[Fact]
|
|
47
|
-
public void Login_ValidCredentials_ReturnsAuthToken() { }
|
|
48
|
-
|
|
49
|
-
[Fact]
|
|
50
|
-
public void Login_InvalidPassword_ThrowsUnauthorizedException() { }
|
|
51
|
-
|
|
52
|
-
[Theory]
|
|
53
|
-
[InlineData("user@example.com", "password123")]
|
|
54
|
-
public void Register_ValidInput_CreatesUser(string email, string password) { }
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
```typescript
|
|
58
|
-
// JavaScript (Jest/Vitest)
|
|
59
|
-
describe('UserService', () => {
|
|
60
|
-
it('should return auth token for valid credentials', () => {})
|
|
61
|
-
|
|
62
|
-
it('should throw error for invalid password', () => {})
|
|
63
|
-
|
|
64
|
-
it.each([
|
|
65
|
-
['user@example.com', 'password123'],
|
|
66
|
-
['admin@test.com', 'admin456']
|
|
67
|
-
])('should create user for %s', (email, password) => {})
|
|
68
|
-
})
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
---
|
|
72
|
-
|
|
73
|
-
## Unit Testing Best Practices
|
|
74
|
-
|
|
75
|
-
### Arrange-Act-Assert (AAA)
|
|
76
|
-
|
|
77
|
-
```csharp
|
|
78
|
-
[Fact]
|
|
79
|
-
public void CalculateTotal_WithDiscount_AppliesCorrectPercentage()
|
|
80
|
-
{
|
|
81
|
-
// Arrange
|
|
82
|
-
var cart = new ShoppingCart();
|
|
83
|
-
cart.AddItem(new Item { Price = 100 });
|
|
84
|
-
var discountService = new DiscountService();
|
|
85
|
-
|
|
86
|
-
// Act
|
|
87
|
-
var total = discountService.CalculateTotal(cart, discountPercent: 10);
|
|
88
|
-
|
|
89
|
-
// Assert
|
|
90
|
-
Assert.Equal(90, total);
|
|
91
|
-
}
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
### Mocking Dependencies
|
|
95
|
-
|
|
96
|
-
```csharp
|
|
97
|
-
[Fact]
|
|
98
|
-
public async Task GetUser_ValidId_ReturnsUser()
|
|
99
|
-
{
|
|
100
|
-
// Arrange
|
|
101
|
-
var mockRepo = new Mock<IUserRepository>();
|
|
102
|
-
mockRepo.Setup(r => r.GetByIdAsync(1))
|
|
103
|
-
.ReturnsAsync(new User { Id = 1, Name = "John" });
|
|
104
|
-
|
|
105
|
-
var service = new UserService(mockRepo.Object);
|
|
106
|
-
|
|
107
|
-
// Act
|
|
108
|
-
var user = await service.GetUserAsync(1);
|
|
109
|
-
|
|
110
|
-
// Assert
|
|
111
|
-
Assert.NotNull(user);
|
|
112
|
-
Assert.Equal("John", user.Name);
|
|
113
|
-
}
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
---
|
|
117
|
-
|
|
118
|
-
## Integration Testing
|
|
119
|
-
|
|
120
|
-
### Database Tests
|
|
121
|
-
|
|
122
|
-
```csharp
|
|
123
|
-
public class UserRepositoryTests : IClassFixture<DatabaseFixture>
|
|
124
|
-
{
|
|
125
|
-
private readonly AppDbContext _context;
|
|
126
|
-
|
|
127
|
-
public UserRepositoryTests(DatabaseFixture fixture)
|
|
128
|
-
{
|
|
129
|
-
_context = fixture.CreateContext();
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
[Fact]
|
|
133
|
-
public async Task CreateUser_ValidData_SavesToDatabase()
|
|
134
|
-
{
|
|
135
|
-
// Arrange
|
|
136
|
-
var repo = new UserRepository(_context);
|
|
137
|
-
var user = new User { Email = "test@example.com" };
|
|
138
|
-
|
|
139
|
-
// Act
|
|
140
|
-
await repo.CreateAsync(user);
|
|
141
|
-
await _context.SaveChangesAsync();
|
|
142
|
-
|
|
143
|
-
// Assert
|
|
144
|
-
var saved = await _context.Users.FirstOrDefaultAsync(u => u.Email == "test@example.com");
|
|
145
|
-
Assert.NotNull(saved);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
### API Tests
|
|
151
|
-
|
|
152
|
-
```csharp
|
|
153
|
-
public class AuthControllerTests : IClassFixture<WebApplicationFactory<Program>>
|
|
154
|
-
{
|
|
155
|
-
private readonly HttpClient _client;
|
|
156
|
-
|
|
157
|
-
public AuthControllerTests(WebApplicationFactory<Program> factory)
|
|
158
|
-
{
|
|
159
|
-
_client = factory.CreateClient();
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
[Fact]
|
|
163
|
-
public async Task Login_ValidCredentials_Returns200AndToken()
|
|
164
|
-
{
|
|
165
|
-
// Arrange
|
|
166
|
-
var request = new LoginRequest { Email = "user@test.com", Password = "password" };
|
|
167
|
-
|
|
168
|
-
// Act
|
|
169
|
-
var response = await _client.PostAsJsonAsync("/api/auth/login", request);
|
|
170
|
-
|
|
171
|
-
// Assert
|
|
172
|
-
response.EnsureSuccessStatusCode();
|
|
173
|
-
var result = await response.Content.ReadFromJsonAsync<AuthResponse>();
|
|
174
|
-
Assert.NotNull(result?.Token);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
---
|
|
180
|
-
|
|
181
|
-
## E2E Testing
|
|
182
|
-
|
|
183
|
-
### Playwright Example
|
|
184
|
-
|
|
185
|
-
```typescript
|
|
186
|
-
import { test, expect } from '@playwright/test';
|
|
187
|
-
|
|
188
|
-
test('user can login and access dashboard', async ({ page }) => {
|
|
189
|
-
// Navigate to login
|
|
190
|
-
await page.goto('/login');
|
|
191
|
-
|
|
192
|
-
// Fill credentials
|
|
193
|
-
await page.fill('input[name="email"]', 'user@example.com');
|
|
194
|
-
await page.fill('input[name="password"]', 'password123');
|
|
195
|
-
|
|
196
|
-
// Submit
|
|
197
|
-
await page.click('button[type="submit"]');
|
|
198
|
-
|
|
199
|
-
// Verify redirect to dashboard
|
|
200
|
-
await expect(page).toHaveURL('/dashboard');
|
|
201
|
-
await expect(page.locator('h1')).toContainText('Dashboard');
|
|
202
|
-
});
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
---
|
|
206
|
-
|
|
207
|
-
## Test Coverage
|
|
208
|
-
|
|
209
|
-
### Targets
|
|
210
|
-
|
|
211
|
-
- **Statements**: ≥ 80%
|
|
212
|
-
- **Branches**: ≥ 70%
|
|
213
|
-
- **Functions**: ≥ 80%
|
|
214
|
-
- **Lines**: ≥ 80%
|
|
215
|
-
|
|
216
|
-
### Running Coverage
|
|
217
|
-
|
|
218
|
-
```bash
|
|
219
|
-
# .NET
|
|
220
|
-
dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=opencover
|
|
221
|
-
|
|
222
|
-
# JavaScript
|
|
223
|
-
npm run test:coverage
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
---
|
|
227
|
-
|
|
228
|
-
## Best Practices
|
|
229
|
-
|
|
230
|
-
### DO
|
|
231
|
-
|
|
232
|
-
✅ Write tests before or with code (TDD)
|
|
233
|
-
✅ Test edge cases (null, empty, invalid input)
|
|
234
|
-
✅ Use descriptive test names
|
|
235
|
-
✅ Keep tests fast (< 1s per unit test)
|
|
236
|
-
✅ Mock external dependencies
|
|
237
|
-
✅ Test one thing per test
|
|
238
|
-
|
|
239
|
-
### DON'T
|
|
240
|
-
|
|
241
|
-
❌ Test implementation details (test behavior)
|
|
242
|
-
❌ Rely on test execution order
|
|
243
|
-
❌ Use real databases in unit tests
|
|
244
|
-
❌ Hardcode magic values (use constants)
|
|
245
|
-
❌ Skip tests (fix or remove)
|
|
246
|
-
❌ Write tests that depend on external services
|
|
247
|
-
|
|
248
|
-
---
|
|
249
|
-
|
|
250
|
-
## Test Fixtures and Cleanup
|
|
251
|
-
|
|
252
|
-
### xUnit
|
|
253
|
-
|
|
254
|
-
```csharp
|
|
255
|
-
public class DatabaseFixture : IDisposable
|
|
256
|
-
{
|
|
257
|
-
public AppDbContext CreateContext()
|
|
258
|
-
{
|
|
259
|
-
var options = new DbContextOptionsBuilder<AppDbContext>()
|
|
260
|
-
.UseInMemoryDatabase($"TestDb_{Guid.NewGuid()}")
|
|
261
|
-
.Options;
|
|
262
|
-
|
|
263
|
-
return new AppDbContext(options);
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
public void Dispose()
|
|
267
|
-
{
|
|
268
|
-
// Cleanup
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
```
|
|
272
|
-
|
|
273
|
-
### Jest
|
|
274
|
-
|
|
275
|
-
```typescript
|
|
276
|
-
beforeEach(() => {
|
|
277
|
-
// Setup before each test
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
afterEach(() => {
|
|
281
|
-
// Cleanup after each test
|
|
282
|
-
});
|
|
283
|
-
|
|
284
|
-
beforeAll(() => {
|
|
285
|
-
// Setup once before all tests
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
afterAll(() => {
|
|
289
|
-
// Cleanup once after all tests
|
|
290
|
-
});
|
|
291
|
-
```
|
|
292
|
-
|
|
293
|
-
---
|
|
294
|
-
|
|
295
|
-
*MORPH-SPEC by Polymorphism Tech*
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
# Data Standard: Azure Blob Storage
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
Object storage for files, images, documents, and large binary data.
|
|
5
|
-
|
|
6
|
-
## Setup
|
|
7
|
-
```xml
|
|
8
|
-
<PackageReference Include="Azure.Storage.Blobs" Version="12.*" />
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
```csharp
|
|
12
|
-
// Program.cs
|
|
13
|
-
builder.Services.AddSingleton(new BlobServiceClient(
|
|
14
|
-
new Uri(config["Storage:AccountUrl"]),
|
|
15
|
-
new DefaultAzureCredential())); // Managed Identity — no connection string
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
## Service Pattern
|
|
19
|
-
```csharp
|
|
20
|
-
public class BlobStorageService : IFileStorageService
|
|
21
|
-
{
|
|
22
|
-
private readonly BlobContainerClient _container;
|
|
23
|
-
|
|
24
|
-
public BlobStorageService(BlobServiceClient client, IConfiguration config)
|
|
25
|
-
{
|
|
26
|
-
_container = client.GetBlobContainerClient(config["Storage:ContainerName"]);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
public async Task<string> UploadAsync(Stream content, string fileName, string contentType)
|
|
30
|
-
{
|
|
31
|
-
var blobName = $"{DateTime.UtcNow:yyyy/MM/dd}/{Guid.NewGuid()}/{fileName}";
|
|
32
|
-
var blob = _container.GetBlobClient(blobName);
|
|
33
|
-
|
|
34
|
-
await blob.UploadAsync(content, new BlobHttpHeaders { ContentType = contentType });
|
|
35
|
-
return blobName; // Return blob path, not URL
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
public async Task<Stream> DownloadAsync(string blobName)
|
|
39
|
-
{
|
|
40
|
-
var blob = _container.GetBlobClient(blobName);
|
|
41
|
-
var response = await blob.DownloadAsync();
|
|
42
|
-
return response.Value.Content;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
public async Task DeleteAsync(string blobName)
|
|
46
|
-
{
|
|
47
|
-
var blob = _container.GetBlobClient(blobName);
|
|
48
|
-
await blob.DeleteIfExistsAsync();
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
public Uri GenerateSasUrl(string blobName, TimeSpan expiry)
|
|
52
|
-
{
|
|
53
|
-
var blob = _container.GetBlobClient(blobName);
|
|
54
|
-
return blob.GenerateSasUri(BlobSasPermissions.Read, DateTimeOffset.UtcNow.Add(expiry));
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
## Security Requirements
|
|
60
|
-
|
|
61
|
-
### Always Use Managed Identity
|
|
62
|
-
```csharp
|
|
63
|
-
// ✅ Managed Identity — no secrets
|
|
64
|
-
new BlobServiceClient(storageUri, new DefaultAzureCredential())
|
|
65
|
-
|
|
66
|
-
// ❌ Connection string — avoid
|
|
67
|
-
new BlobServiceClient(connectionString)
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
### SAS Tokens for Client Access
|
|
71
|
-
```csharp
|
|
72
|
-
// Generate short-lived SAS for client-side uploads
|
|
73
|
-
var sasUri = blob.GenerateSasUri(BlobSasPermissions.Write, DateTimeOffset.UtcNow.AddMinutes(15));
|
|
74
|
-
// Send SAS URL to client — they upload directly
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
### Container Access Levels
|
|
78
|
-
- `Private`: No public access (default for user documents)
|
|
79
|
-
- `Blob`: Public read for specific blobs (CDN-served images)
|
|
80
|
-
- `Container`: Public read for all blobs (avoid in most cases)
|
|
81
|
-
|
|
82
|
-
## Content Types + Validation
|
|
83
|
-
```csharp
|
|
84
|
-
private static readonly HashSet<string> AllowedTypes = ["image/jpeg", "image/png", "application/pdf"];
|
|
85
|
-
private const long MaxFileSizeBytes = 10 * 1024 * 1024; // 10 MB
|
|
86
|
-
|
|
87
|
-
public async Task<string> UploadValidatedAsync(IFormFile file)
|
|
88
|
-
{
|
|
89
|
-
if (!AllowedTypes.Contains(file.ContentType))
|
|
90
|
-
throw new ValidationException($"File type {file.ContentType} not allowed");
|
|
91
|
-
if (file.Length > MaxFileSizeBytes)
|
|
92
|
-
throw new ValidationException("File exceeds 10 MB limit");
|
|
93
|
-
|
|
94
|
-
return await UploadAsync(file.OpenReadStream(), file.FileName, file.ContentType);
|
|
95
|
-
}
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
## Blob Lifecycle (Cost Management)
|
|
99
|
-
- Move to Cool tier after 30 days
|
|
100
|
-
- Move to Archive tier after 90 days
|
|
101
|
-
- Delete after 365 days (unless legal hold)
|
|
102
|
-
- Configure via Storage Account Lifecycle Management rules (Bicep)
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
# Data Standard: Azure AI Search (Vector Search)
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
Azure AI Search provides vector and hybrid search for RAG (Retrieval-Augmented Generation) patterns.
|
|
5
|
-
|
|
6
|
-
## Index Design
|
|
7
|
-
```csharp
|
|
8
|
-
// Define index with vector field
|
|
9
|
-
var index = new SearchIndex("documents-index")
|
|
10
|
-
{
|
|
11
|
-
Fields =
|
|
12
|
-
[
|
|
13
|
-
new SimpleField("id", SearchFieldDataType.String) { IsKey = true },
|
|
14
|
-
new SearchableField("content") { AnalyzerName = "pt.microsoft" },
|
|
15
|
-
new SearchableField("title") { AnalyzerName = "pt.microsoft" },
|
|
16
|
-
new SimpleField("sourceUrl", SearchFieldDataType.String) { IsFilterable = true },
|
|
17
|
-
new SimpleField("category", SearchFieldDataType.String) { IsFilterable = true, IsFacetable = true },
|
|
18
|
-
new VectorSearchField("contentVector", dimensions: 1536, vectorSearchProfileName: "hnsw-profile")
|
|
19
|
-
],
|
|
20
|
-
VectorSearch = new VectorSearch
|
|
21
|
-
{
|
|
22
|
-
Profiles = { new VectorSearchProfile("hnsw-profile", "hnsw-config") },
|
|
23
|
-
Algorithms = { new HnswAlgorithmConfiguration("hnsw-config") }
|
|
24
|
-
}
|
|
25
|
-
};
|
|
26
|
-
await indexClient.CreateOrUpdateIndexAsync(index);
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
## Embedding Generation
|
|
30
|
-
```csharp
|
|
31
|
-
public class EmbeddingService : IEmbeddingService
|
|
32
|
-
{
|
|
33
|
-
private readonly OpenAIClient _client;
|
|
34
|
-
private const string EmbeddingModel = "text-embedding-ada-002"; // 1536 dimensions
|
|
35
|
-
|
|
36
|
-
public async Task<float[]> GenerateEmbeddingAsync(string text)
|
|
37
|
-
{
|
|
38
|
-
// Truncate to model limit
|
|
39
|
-
var truncated = text.Length > 8192 ? text[..8192] : text;
|
|
40
|
-
|
|
41
|
-
var response = await _client.GetEmbeddingsAsync(
|
|
42
|
-
new EmbeddingsOptions(EmbeddingModel, [truncated]));
|
|
43
|
-
|
|
44
|
-
return response.Value.Data[0].Embedding.ToArray();
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
public async Task<IReadOnlyList<float[]>> GenerateBatchEmbeddingsAsync(IReadOnlyList<string> texts)
|
|
48
|
-
{
|
|
49
|
-
var response = await _client.GetEmbeddingsAsync(
|
|
50
|
-
new EmbeddingsOptions(EmbeddingModel, texts));
|
|
51
|
-
return response.Value.Data.Select(d => d.Embedding.ToArray()).ToList();
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
## Document Indexing
|
|
57
|
-
```csharp
|
|
58
|
-
public class DocumentIndexer
|
|
59
|
-
{
|
|
60
|
-
public async Task IndexDocumentAsync(Document doc)
|
|
61
|
-
{
|
|
62
|
-
var embedding = await _embeddingService.GenerateEmbeddingAsync(doc.Content);
|
|
63
|
-
|
|
64
|
-
var searchDoc = new SearchDocument
|
|
65
|
-
{
|
|
66
|
-
["id"] = doc.Id.ToString(),
|
|
67
|
-
["content"] = doc.Content,
|
|
68
|
-
["title"] = doc.Title,
|
|
69
|
-
["sourceUrl"] = doc.SourceUrl,
|
|
70
|
-
["category"] = doc.Category,
|
|
71
|
-
["contentVector"] = embedding
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
await _searchClient.MergeOrUploadDocumentsAsync([searchDoc]);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
## Hybrid Search (Vector + Keyword)
|
|
80
|
-
```csharp
|
|
81
|
-
public async Task<IReadOnlyList<SearchResult>> SearchAsync(string query, string? category = null)
|
|
82
|
-
{
|
|
83
|
-
var queryEmbedding = await _embeddingService.GenerateEmbeddingAsync(query);
|
|
84
|
-
|
|
85
|
-
var options = new SearchOptions
|
|
86
|
-
{
|
|
87
|
-
Filter = category != null ? $"category eq '{category}'" : null,
|
|
88
|
-
Size = 10,
|
|
89
|
-
VectorSearch = new VectorSearchOptions
|
|
90
|
-
{
|
|
91
|
-
Queries =
|
|
92
|
-
{
|
|
93
|
-
new VectorizedQuery(queryEmbedding)
|
|
94
|
-
{
|
|
95
|
-
Fields = { "contentVector" },
|
|
96
|
-
KNearestNeighborsCount = 50
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
},
|
|
100
|
-
QueryType = SearchQueryType.Semantic,
|
|
101
|
-
SemanticSearch = new SemanticSearchOptions
|
|
102
|
-
{
|
|
103
|
-
SemanticConfigurationName = "my-semantic-config",
|
|
104
|
-
QueryCaption = new QueryCaption(QueryCaptionType.Extractive)
|
|
105
|
-
}
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
var response = await _searchClient.SearchAsync<SearchDocument>(query, options);
|
|
109
|
-
return await response.Value.GetResultsAsync().Select(r => new SearchResult(
|
|
110
|
-
r.Document["id"].ToString()!,
|
|
111
|
-
r.Document["content"].ToString()!,
|
|
112
|
-
r.Score ?? 0
|
|
113
|
-
)).ToListAsync();
|
|
114
|
-
}
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
## Cost Optimization
|
|
118
|
-
- Index only necessary fields (cost per GB/month)
|
|
119
|
-
- Use semantic ranking sparingly (higher cost per query)
|
|
120
|
-
- Cache frequent query embeddings (embeddings are stable for same text)
|
|
121
|
-
- Batch indexing for large document sets (avoid per-document API calls)
|