---
name: step-04-api-smoke
description: Start the API and test CRUD endpoints with real HTTP requests
prev_step: steps/step-03-integration-tests.md
---

# Step 4: API Smoke Test

## BLOCKING - All CRUD endpoints must respond correctly

### 1. Find the API project

```bash
# Find the API project
ls src/*Api*/*.csproj 2>/dev/null || ls *Api*/*.csproj 2>/dev/null
```

### 2. Start the API in the background (with output capture)

```bash
# Start the API on a test port, capturing stdout/stderr for error diagnosis
dotnet run --project {ApiProject} --urls "http://localhost:5099" > /tmp/api-smoke-output.log 2>&1 &
API_PID=$!
```

### 3. Wait for the API to be ready (with crash detection)

```bash
# Poll the health endpoint, checking for early process death
API_CRASHED=false
for i in $(seq 1 30); do
  # CRITICAL: Check if process is still alive FIRST
  if ! kill -0 $API_PID 2>/dev/null; then
    echo "API PROCESS CRASHED during startup"
    cat /tmp/api-smoke-output.log 2>/dev/null
    API_CRASHED=true
    break
  fi

  HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:5099/health 2>/dev/null)
  if [ "$HTTP_CODE" != "000" ]; then
    echo "API is ready (HTTP $HTTP_CODE)"
    break
  fi
  sleep 1
done
```

**If `API_CRASHED=true`:** Skip directly to section 8b (crash classification). Do NOT attempt CRUD tests.

### 4. Authenticate (if required)

```bash
# Try to login with default test credentials
TOKEN=$(curl -s -X POST http://localhost:5099/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"admin@smartstack.io","password":"Admin123!"}' \
  | jq -r '.token // .accessToken // empty')
```

If auth is not configured yet, tests may work without a token (depending on configuration).

### 5. Test CRUD endpoints

**Set common headers:**
```bash
AUTH_HEADER="Authorization: Bearer $TOKEN"
TENANT_HEADER="X-Tenant-Id: 11111111-1111-1111-1111-111111111111"
CONTENT_TYPE="Content-Type: application/json"
```

#### 5.1 GET all (expect 200)
```bash
curl -s -w "\n%{http_code}" \
  -H "$AUTH_HEADER" -H "$TENANT_HEADER" \
  http://localhost:5099/api/{entity_code}
```
Expected: HTTP 200

#### 5.2 POST create (expect 201 or 200)
```bash
CREATED=$(curl -s -w "\n%{http_code}" \
  -X POST \
  -H "$AUTH_HEADER" -H "$TENANT_HEADER" -H "$CONTENT_TYPE" \
  -d '{"code":"smoke-test-01","name":"Smoke Test Entity"}' \
  http://localhost:5099/api/{entity_code})
```
Expected: HTTP 201 or 200
Extract the created entity ID from the response.

#### 5.3 GET by ID (expect 200)
```bash
curl -s -w "\n%{http_code}" \
  -H "$AUTH_HEADER" -H "$TENANT_HEADER" \
  http://localhost:5099/api/{entity_code}/{created_id}
```
Expected: HTTP 200 with the entity data

#### 5.4 PUT update (expect 200)
```bash
curl -s -w "\n%{http_code}" \
  -X PUT \
  -H "$AUTH_HEADER" -H "$TENANT_HEADER" -H "$CONTENT_TYPE" \
  -d '{"code":"smoke-test-01","name":"Updated Smoke Test"}' \
  http://localhost:5099/api/{entity_code}/{created_id}
```
Expected: HTTP 200

#### 5.5 DELETE (expect 204)
```bash
curl -s -w "\n%{http_code}" \
  -X DELETE \
  -H "$AUTH_HEADER" -H "$TENANT_HEADER" \
  http://localhost:5099/api/{entity_code}/{created_id}
```
Expected: HTTP 204

#### 5.6 GET deleted (expect 404)
```bash
curl -s -w "\n%{http_code}" \
  -H "$AUTH_HEADER" -H "$TENANT_HEADER" \
  http://localhost:5099/api/{entity_code}/{created_id}
```
Expected: HTTP 404 (confirms deletion)

### 6. Stop the API

```bash
kill $API_PID 2>/dev/null
wait $API_PID 2>/dev/null
rm -f /tmp/api-smoke-output.log
```

### 7. Evaluate results

Build a report:

```markdown
## Feature Validation: {entity_name}

| Check | Result | Details |
|-------|--------|---------|
| Solution build | {build_result} | |
| Unit tests | {unit_test_result} | {unit_test_count} passed |
| Integration tests | {integration_test_result} | {integration_test_count} passed |
| GET /api/{entity_code} | {status} | |
| POST /api/{entity_code} | {status} | |
| GET /api/{entity_code}/{id} | {status} | |
| PUT /api/{entity_code}/{id} | {status} | |
| DELETE /api/{entity_code}/{id} | {status} | |
| GET deleted entity | {status} | |

### Overall: {ALL_PASS or FAILURES_DETECTED}
```

### 8. Handle HTTP failures

If any smoke test fails:
- **401 Unauthorized** -> Auth not configured, check JWT setup
- **403 Forbidden** -> Permissions not seeded, check PermissionConfiguration
- **404 Not Found** -> Route not registered, check controller NavRoute
- **500 Internal Server Error** -> Check API logs for exception details
- **Connection refused** -> API didn't start, check startup configuration

### 8b. Handle startup crash (if API_CRASHED=true)

If the API process died during startup, classify the error from the captured output:

| Error Pattern in Output | Classification | Actionable Fix |
|------------------------|----------------|----------------|
| `FileNotFoundException: Could not load file or assembly '{Name}, Version={V}'` | **MISSING_PACKAGE** | `dotnet add {ApiProject} package {Name} --version {V.Major}.{V.Minor}.{V.Patch}` |
| `FileNotFoundException: Could not load file or assembly '{Name}'` | **MISSING_ASSEMBLY** | Check .csproj references, ensure assembly is built |
| `TypeLoadException: Could not load type '{Type}' from assembly '{Asm}'` | **VERSION_MISMATCH** | `dotnet add {ApiProject} package {Asm} --version {CorrectVersion}` |
| `MissingMethodException` | **VERSION_MISMATCH** | Update package to matching version |
| `InvalidOperationException: Unable to resolve service for type '{Type}'` | **MISSING_DI** | Add DI registration for `{Type}` in `DependencyInjection.cs` or `Program.cs` |
| `SqlException` or connection errors | **DATABASE_ERROR** | Check connection string in `appsettings.json` |

**For MISSING_PACKAGE errors:**
1. Extract assembly name: the text between single quotes after "file or assembly"
2. Extract version: the `Version=X.X.X.X` part (first 3 segments = NuGet version)
3. Map to NuGet package: assembly name usually equals package name
4. Output: `FIX: dotnet add {ApiProject} package {PackageName} --version {Major.Minor.Patch}`
5. **If assembly starts with `SmartStack.`**: this is a framework bug in SmartStack.csproj, not a client project issue

**Report format for crashed API:**
```markdown
## Feature Validation: {entity_name}

| Check | Result | Details |
|-------|--------|---------|
| Dependencies | {dependency_check_result} | |
| Solution build | {build_result} | |
| Unit tests | {unit_test_result} | {unit_test_count} passed |
| Integration tests | {integration_test_result} | {integration_test_count} passed |
| API startup | **CRASHED** | {classification}: {error_summary} |

### Crash Details
**Classification:** {classification}
**Error:** {first line of relevant error}
**Fix:** `{fix_command}`

### Overall: FAILURES_DETECTED (API startup crash)
```

Report all findings to the user with actionable next steps.
