---
name: step-07-tests
description: Generate backend and security tests using MCP tools
prev_step: steps/step-06-migration.md
next_step: steps/step-08-documentation.md
---

# Step 7: Test Generation

## MANDATORY EXECUTION RULES

- ALWAYS use MCP `scaffold_tests` for test generation
- ALWAYS generate at least 3 test categories: controller, service, entity
- ALWAYS follow naming convention: `{Method}_When{Condition}_Should{Result}`
- YOU ARE AN ORCHESTRATOR calling MCP, not a generator

## YOUR TASK

Use MCP tools to generate test files covering:
1. Controller integration tests (API endpoints)
2. Service unit tests (business logic)
3. Entity unit tests (domain rules)
4. Validator unit tests (input validation)
5. Repository integration tests (data access)
6. Security tests (OWASP coverage)

---

## AVAILABLE STATE

From previous steps:

| Variable | Description |
|----------|-------------|
| `{entity_name}` | PascalCase entity name |
| `{entity_code}` | kebab-case code |
| `{full_path}` | Complete navigation path (navRoute) |
| `{namespace}` | .NET namespace |
| `{api_route}` | API endpoint path |

---

## PRE-REQUISITES

### 1. Verify Test Project Exists

Check that the solution contains a test project:

```bash
# Look for existing test project
ls *Tests*/*.csproj 2>/dev/null || ls tests/*/*.csproj 2>/dev/null
```

If NO test project exists, create one:

```bash
dotnet new xunit -n {SolutionName}.Tests -o tests/{SolutionName}.Tests
dotnet sln add tests/{SolutionName}.Tests/{SolutionName}.Tests.csproj
```

### 2. Verify Required NuGet Packages

The test project MUST have these packages:

| Package | Purpose |
|---------|---------|
| `xunit` | Test framework |
| `FluentAssertions` | Assertion library |
| `Moq` | Mocking framework |
| `Microsoft.AspNetCore.Mvc.Testing` | Integration test support |
| `Microsoft.EntityFrameworkCore.InMemory` | In-memory DB for repository tests |
| `FluentValidation.TestHelper` | Validator testing support |

```bash
cd tests/{SolutionName}.Tests
dotnet add package FluentAssertions
dotnet add package Moq
dotnet add package Microsoft.AspNetCore.Mvc.Testing
dotnet add package Microsoft.EntityFrameworkCore.InMemory
dotnet add package FluentValidation.TestHelper
```

---

## EXECUTION SEQUENCE

### 1. Generate Controller Tests

```
Tool: mcp__smartstack__scaffold_tests
Args:
  type: "controller"
  name: "{entity_name}"
  options:
    namespace: "{namespace}"
    navRoute: "{full_path}"
    includeAuthorization: true
    isSystemEntity: false
```

This generates:
- `Tests/Integration/Controllers/{EntityName}ControllerTests.cs`

**Coverage:**
- GET all → 200 with list
- GET by ID → 200 when exists, 404 when not
- POST → 201 when valid, 400 when invalid, 409 when duplicate
- PUT → 200 when valid, 404 when not exists, 409 on concurrency
- DELETE → 204 when exists, 404 when not
- Authorization → 401 unauthenticated, 403 forbidden
- Tenant isolation → only current tenant data
- Pagination → paged results
- Error format → ProblemDetails

### 2. Generate Service Tests

```
Tool: mcp__smartstack__scaffold_tests
Args:
  type: "service"
  name: "{entity_name}"
  options:
    namespace: "{namespace}"
    includeSoftDelete: true
    isSystemEntity: false
```

This generates:
- `Tests/Unit/Services/{EntityName}ServiceTests.cs`

**Coverage:**
- GetById → entity when exists, null when not
- GetAll → list when entities exist, empty when none
- Create → success, duplicate code rejection
- Update → success, not found exception
- Delete → soft delete, already deleted, not found
- Tenant isolation → correct tenant assignment
- Error handling → repository failure, concurrency conflict

### 3. Generate Entity Tests

```
Tool: mcp__smartstack__scaffold_tests
Args:
  type: "entity"
  name: "{entity_name}"
  options:
    namespace: "{namespace}"
    includeSoftDelete: true
    includeAudit: true
    isSystemEntity: false
```

This generates:
- `Tests/Unit/Domain/{EntityName}Tests.cs`

**Coverage:**
- Factory method Create → valid data, empty tenant, invalid code
- Update → audit fields
- Soft delete → mark as deleted, already deleted, restore
- Audit trail → creation fields, update fields
- Tenant isolation → immutable TenantId

### 4. Generate Validator Tests

```
Tool: mcp__smartstack__scaffold_tests
Args:
  type: "validator"
  name: "{entity_name}"
  options:
    namespace: "{namespace}"
```

This generates:
- `Tests/Unit/Validators/{EntityName}ValidatorTests.cs`

**Coverage:**
- Code validation → valid, null/empty, max length, valid format, invalid characters
- Name validation → valid, max length
- Update validation → valid data, optional null fields
- Security → XSS, SQL injection, prototype pollution
- Cross-field validation → dependent fields
- Multiple errors → all errors returned

### 5. Generate Repository Tests

```
Tool: mcp__smartstack__scaffold_tests
Args:
  type: "repository"
  name: "{entity_name}"
  options:
    namespace: "{namespace}"
    includeSoftDelete: true
    isSystemEntity: false
```

This generates:
- `Tests/Integration/Repositories/{EntityName}RepositoryTests.cs`

**Coverage:**
- GetById → exists, not exists, soft deleted
- GetAll → entities exist, empty, exclude soft deleted
- Add → persist, generate ID
- Update → persist changes
- Remove → delete entity
- ExistsByCode → exists, not exists, soft deleted
- Tenant isolation → current tenant only, cross-tenant returns null
- Pagination → correct page and counts
- Concurrency → concurrent modification exception

### 6. Generate Security Tests

```
Tool: mcp__smartstack__scaffold_tests
Args:
  type: "security"
  name: "{entity_name}"
  options:
    namespace: "{namespace}"
    isSystemEntity: false
```

This generates:
- `Tests/Security/{EntityName}SecurityTests.cs`

**Coverage:**
- Authentication → 401 for unauthenticated, expired token, malformed token
- Authorization → 403 for insufficient permissions
- Tenant isolation → cross-tenant access returns 404, tenant ID not injectable
- Input validation → XSS prevention, SQL injection prevention, path traversal, prototype pollution
- IDOR → no data leakage via ID guessing
- Rate limiting → 429 on excessive requests
- Sensitive data → no passwords/secrets/stack traces exposed
- CORS → no wildcard origins
- Security headers → X-Content-Type-Options, X-Frame-Options, X-XSS-Protection

---

## POST-GENERATION VERIFICATION (MANDATORY)

### 1. Naming Convention Check (BLOCKING)

All test methods MUST follow: `{Method}_When{Condition}_Should{Result}`

```
FORBIDDEN patterns:
  Test1, Test2, TestMethod, MyTest
  Should_Return_OK, test_get_all

REQUIRED pattern:
  GetAll_WhenCalled_ShouldReturn200WithList
  Create_WhenDuplicateCode_ShouldThrowException
  Delete_WhenNotAuthenticated_ShouldReturn401
```

### 2. Test Structure Check (BLOCKING)

All tests MUST follow Arrange-Act-Assert:

```csharp
// ✅ CORRECT
[Fact]
public async Task GetById_WhenExists_ShouldReturn200()
{
    // Arrange
    var id = Guid.NewGuid();
    // ...

    // Act
    var response = await _client.GetAsync($"/api/{entityCode}/{id}");

    // Assert
    response.StatusCode.Should().Be(HttpStatusCode.OK);
}

// ❌ FORBIDDEN - No AAA structure
[Fact]
public async Task TestGetById()
{
    var response = await _client.GetAsync("/api/test/123");
    Assert.True(response.IsSuccessStatusCode);
}
```

### 3. Dependencies Check (BLOCKING)

Verify test project references:
- `FluentAssertions` (NOT `Assert.Equal`)
- `Moq` (NOT manual fakes)
- `xunit` (NOT NUnit or MSTest)

### 4. Coverage Check

Minimum test coverage per category:

| Category | Minimum Tests |
|----------|---------------|
| Controller | 10 (CRUD + auth + tenant) |
| Service | 8 (CRUD + errors) |
| Entity | 5 (factory + update + validation) |
| Validator | 8 (code + name + security) |
| Repository | 8 (CRUD + tenant + pagination) |
| Security | 10 (auth + injection + headers) |

### 5. Build Check (BLOCKING)

```bash
dotnet build tests/{SolutionName}.Tests/{SolutionName}.Tests.csproj
```

If build fails, fix compilation errors before proceeding.

### 6. Test Run (NON-BLOCKING)

```bash
dotnet test tests/{SolutionName}.Tests/{SolutionName}.Tests.csproj --no-build
```

Note: Some tests may fail initially because the service/repository implementations are stubs. This is expected. The test structure and conventions must be correct.

---

## PRESENT OUTPUT TO USER

```markdown
## Test Suite Generated

### Test Files

| File | Category | Tests |
|------|----------|-------|
| `Tests/Integration/Controllers/{EntityName}ControllerTests.cs` | Controller | ~12 |
| `Tests/Unit/Services/{EntityName}ServiceTests.cs` | Service | ~10 |
| `Tests/Unit/Domain/{EntityName}Tests.cs` | Entity | ~8 |
| `Tests/Unit/Validators/{EntityName}ValidatorTests.cs` | Validator | ~12 |
| `Tests/Integration/Repositories/{EntityName}RepositoryTests.cs` | Repository | ~12 |
| `Tests/Security/{EntityName}SecurityTests.cs` | Security | ~15 |

### Test Categories

| Category | Coverage |
|----------|----------|
| CRUD Operations | ✅ All endpoints tested |
| Authentication | ✅ 401 for unauthenticated |
| Authorization | ✅ 403 for insufficient permissions |
| Tenant Isolation | ✅ Cross-tenant access blocked |
| Input Validation | ✅ XSS, SQL injection, path traversal |
| Error Handling | ✅ ProblemDetails format |
| Concurrency | ✅ Optimistic locking |
| Pagination | ✅ Paged results |

### Naming Convention
All tests follow: `{Method}_When{Condition}_Should{Result}`

### Required NuGet Packages
- xunit
- FluentAssertions
- Moq
- Microsoft.AspNetCore.Mvc.Testing
- Microsoft.EntityFrameworkCore.InMemory
- FluentValidation.TestHelper

### Next Steps
1. Implement service stubs to make tests pass
2. Run `dotnet test` to verify test compilation
3. Achieve >80% code coverage
```

---

## SUCCESS METRICS

- Test project exists in solution
- All 6 test categories generated
- Naming convention `{Method}_When{Condition}_Should{Result}` respected
- Arrange-Act-Assert structure in all tests
- FluentAssertions used (not Assert.Equal)
- Moq used for mocking
- Test project builds successfully
- Minimum test count met per category

## FAILURE MODES

- Test project doesn't exist → Create it first
- Missing NuGet packages → Install them
- Build failures → Fix compilation errors
- Wrong naming convention → Rename tests
- Missing test categories → Generate missing ones

---

## END OF WORKFLOW

This is the final step of the /application skill workflow. The user now has:
- Full navigation structure (step 01)
- RBAC permissions (step 02)
- Role mappings (step 03)
- Client seed provider (step 03b, if applicable)
- Backend CRUD API (step 04)
- Frontend components + i18n + routes (step 05)
- Database migration (step 06)
- **Complete test suite (step 07)**
