# API Smoke Test Procedure

> **Reference for:** step-04-api-smoke.md
> **Purpose:** Validate CRUD endpoints with real HTTP requests
> **Scope:** GET all, POST create, GET by ID, PUT update, DELETE, GET verify deleted

---

## Startup Sequence

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

# Wait for readiness (with crash detection)
API_CRASHED=false
for i in $(seq 1 30); do
  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 → Jump to section "Crash Classification" below
```

---

## CRUD Test Sequence

```bash
# Get auth token (optional)
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')

# Headers
AUTH_HEADER="Authorization: Bearer $TOKEN"
TENANT_HEADER="X-Tenant-Id: 11111111-1111-1111-1111-111111111111"
CONTENT_TYPE="Content-Type: application/json"
```

### Test 1: GET All (expect 200)
```bash
curl -s -w "\nHTTP %{http_code}" \
  -H "$AUTH_HEADER" -H "$TENANT_HEADER" \
  http://localhost:5099/api/{entity_code}
```

### Test 2: POST Create (expect 201 or 200)
```bash
CREATED=$(curl -s -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})
ENTITY_ID=$(echo "$CREATED" | jq -r '.id // .Id')
```

### Test 3: GET By ID (expect 200)
```bash
curl -s -w "\nHTTP %{http_code}" \
  -H "$AUTH_HEADER" -H "$TENANT_HEADER" \
  http://localhost:5099/api/{entity_code}/$ENTITY_ID
```

### Test 4: PUT Update (expect 200)
```bash
curl -s -X PUT -w "\nHTTP %{http_code}" \
  -H "$AUTH_HEADER" -H "$TENANT_HEADER" -H "$CONTENT_TYPE" \
  -d '{"code":"smoke-test-01","name":"Updated Name"}' \
  http://localhost:5099/api/{entity_code}/$ENTITY_ID
```

### Test 5: DELETE (expect 204)
```bash
curl -s -X DELETE -w "\nHTTP %{http_code}" \
  -H "$AUTH_HEADER" -H "$TENANT_HEADER" \
  http://localhost:5099/api/{entity_code}/$ENTITY_ID
```

### Test 6: GET Deleted (expect 404)
```bash
curl -s -w "\nHTTP %{http_code}" \
  -H "$AUTH_HEADER" -H "$TENANT_HEADER" \
  http://localhost:5099/api/{entity_code}/$ENTITY_ID
```

---

## HTTP Error Classification

| Status | Error | Cause | Fix |
|--------|-------|-------|-----|
| 401 | Unauthorized | Auth not configured | Check JWT token generation |
| 403 | Forbidden | Permissions missing | Verify PermissionConfiguration seeding |
| 404 | Not Found | Route not registered | Check NavRoute attribute on controller |
| 500 | Internal Server Error | Unhandled exception | Check API startup logs |
| Connection refused | API won't start | Startup config error | Review appsettings.json |

---

## Crash Classification

If API process dies during startup, classify error from `/tmp/api-smoke-output.log`:

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

**For MISSING_PACKAGE:**
1. Extract assembly name (text between single quotes)
2. Extract version (Version=X.X.X.X segment)
3. Map to NuGet: assembly name usually equals package name
4. Output: `FIX: dotnet add {ApiProject} package {PackageName} --version {Major.Minor.Patch}`

---

## Cleanup

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