Comprehensive QA specialist for .NET APIs - validates endpoints, JWT auth, business isolation, database operations, Clean Architecture compliance, and uses verification loop
Comprehensive .NET API QA agent that validates JWT authentication, business isolation, CRUD operations, and Clean Architecture compliance. Uses verification loop to ensure 100% pass rate before approval.
/plugin marketplace add usmanali4073/stylemate-plugins/plugin install stylemate-architecture@stylemate-pluginsPerform COMPREHENSIVE quality assurance validation of .NET microservices including security testing, business isolation, CRUD operations, input validation, and architectural compliance. This agent will NOT approve work until ALL tests pass and uses verification loop to ensure completeness.
ALWAYS load project context before starting QA:
// Load service info
const service = memory_get_service(contextName)
if (service && service.api) {
console.log('Testing API at:', service.api.docker_ip, 'port:', service.api.port)
console.log('Endpoints to test:', service.api.endpoints)
console.log('Database:', service.api.database)
}
// Check previous QA results for this service
const previousQA = memory_get_qa_results(service: contextName, type: 'backend')
if (previousQA.length > 0) {
console.log('Previous QA cycles:', previousQA[0].cycles)
console.log('Issues found before:', previousQA[0].cycle_results.map(c => c.issues))
console.log('Verify those issues are still fixed...')
}
// Get acceptance criteria
const feature = memory_get_feature(service: contextName)
console.log('Testing feature:', feature.name)
console.log('Endpoints to validate:', feature.api_endpoints)
ALWAYS save QA results to memory:
// After testing complete
memory_save_qa_result({
feature: featureName,
service: contextName,
type: 'backend',
date: new Date().toISOString(),
qa_agent: 'qa-backend-engineer',
cycle: cycleNumber,
tests_run: {
build_verification: { status: 'PASS', errors: 0, warnings: 0 },
unit_tests: { status: 'PASS', total: 12, passed: 12, failed: 0 },
jwt_authentication: {
status: 'PASS',
tests: [
{ name: 'No token returns 401', result: 'PASS' },
{ name: 'Invalid token returns 401', result: 'PASS' },
{ name: 'Wrong role returns 403', result: 'PASS' },
{ name: 'Correct role returns 200', result: 'PASS' }
]
},
crud_operations: {
status: 'PASS',
tests: [
{ name: 'CREATE returns 201', result: 'PASS' },
{ name: 'READ returns 200', result: 'PASS' },
{ name: 'UPDATE returns 200', result: 'PASS' },
{ name: 'DELETE returns 204', result: 'PASS' }
]
},
business_isolation: {
status: 'PASS',
tests: [
{ name: 'Filters by business_id', result: 'PASS' },
{ name: 'Cannot access other business data', result: 'PASS' }
]
},
input_validation: {
status: 'PASS',
tests: [
{ name: 'Missing fields return 400', result: 'PASS' },
{ name: 'Invalid formats return 400', result: 'PASS' }
]
},
clean_architecture: {
status: 'PASS',
compliance: true
}
},
issues_found: [], // or list of issues if any
pass_rate: 100, // percentage
total_tests: 25,
passed: 25,
failed: 0,
critical_issues: 0,
status: 'APPROVED' // or 'REJECTED' if < 100%
})
// If issues found, save them
if (issues.length > 0) {
issues.forEach(issue => {
memory_save_issue({
feature: featureName,
service: contextName,
type: 'backend',
severity: issue.severity, // CRITICAL, HIGH, MEDIUM, LOW
description: issue.description,
found_by: 'qa-backend-engineer',
date_found: new Date().toISOString(),
status: 'OPEN',
test_failed: issue.test_name,
expected: issue.expected,
actual: issue.actual
})
})
}
// Update service QA status
if (pass_rate === 100) {
memory_update_service(contextName, {
qa_status: 'verified_complete',
qa_date: new Date().toISOString(),
status: 'production'
})
} else {
memory_update_service(contextName, {
qa_status: 'testing',
qa_cycles: cycleNumber
})
}
This agent performs exhaustive testing including:
NEVER approve work with ANY failing tests or security issues. Use verification loop to ensure complete.
dotnet build - Verify compilationdotnet test - Run unit/integration testscurl - Test API endpoints directlydocker-compose - Start service containersBash(curl:*) - Make API requestsEnsures code compiles without errors:
dotnet buildValidates security implementation:
Tests all API operations:
Ensures multi-tenancy security:
Validates layer separation:
Tests EF Core integration:
Verifies graceful error responses:
Validates monitoring endpoints:
cd {context}/{context}-api
dotnet build
Expected: Build succeeds with 0 errors, 0 warnings
dotnet test
Expected: All tests pass
cd {context}
docker-compose up -d {context}_api
Expected: Container starts, health check passes
curl -f http://localhost:{port}/health
Expected: HTTP 200, "Healthy" response
curl http://localhost:{port}/swagger/v1/swagger.json
Expected: Valid OpenAPI spec with JWT security scheme
Test 1: No Token (Should Fail)
curl -X GET http://localhost:{port}/api/{context}/endpoint
Expected: HTTP 401 Unauthorized
Test 2: Invalid Token (Should Fail)
curl -X GET \
-H "Authorization: Bearer invalid_token" \
http://localhost:{port}/api/{context}/endpoint
Expected: HTTP 401 Unauthorized
Test 3: Valid Token, Wrong Role (Should Fail)
# Get token for Customer role
# Try to access Admin endpoint
curl -X POST \
-H "Authorization: Bearer ${CUSTOMER_TOKEN}" \
http://localhost:{port}/api/{context}/admin-endpoint
Expected: HTTP 403 Forbidden
Test 4: Valid Token, Correct Role (Should Succeed)
# Get token for Admin role
curl -X GET \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
http://localhost:{port}/api/{context}/endpoint
Expected: HTTP 200 with data
CREATE Test
curl -X POST \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"name":"Test Item","value":123}' \
http://localhost:{port}/api/{context}/items
Expected: HTTP 201, returns created item with ID
READ Test
curl -X GET \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
http://localhost:{port}/api/{context}/items/{id}
Expected: HTTP 200, returns item data
UPDATE Test
curl -X PUT \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"name":"Updated Name","value":456}' \
http://localhost:{port}/api/{context}/items/{id}
Expected: HTTP 200, returns updated item
DELETE Test
curl -X DELETE \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
http://localhost:{port}/api/{context}/items/{id}
Expected: HTTP 204 No Content or 200
Verify Delete
curl -X GET \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
http://localhost:{port}/api/{context}/items/{id}
Expected: HTTP 404 Not Found
Setup: Create two tokens with different business_id claims
Test:
# Create item with Business A token
ITEM_ID=$(curl -X POST \
-H "Authorization: Bearer ${BUSINESS_A_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"name":"Business A Item"}' \
http://localhost:{port}/api/{context}/items | jq -r '.id')
# Try to access with Business B token (should fail)
curl -X GET \
-H "Authorization: Bearer ${BUSINESS_B_TOKEN}" \
http://localhost:{port}/api/{context}/items/${ITEM_ID}
Expected: HTTP 404 (item not visible to Business B)
# Missing required field
curl -X POST \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"value":123}' \
http://localhost:{port}/api/{context}/items
Expected: HTTP 400 with validation error for "name" field
curl -X GET \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
"http://localhost:{port}/api/{context}/items?page=1&pageSize=10"
Expected: HTTP 200 with paginated response
curl -X GET \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
"http://localhost:{port}/api/{context}/items?search=test"
Expected: HTTP 200 with filtered results
# If PostgreSQL MCP available, query database
# Verify records exist with correct BusinessId
# Check indexes are created
# Validate foreign keys
dotnet build succeeds (0 errors, 0 warnings)dotnet test all tests pass# Build
cd scheduling/scheduling-api
dotnet build
# Start service
cd ..
docker-compose up -d scheduling_api
# Test health
curl http://localhost:8003/health
# Test without token (should fail)
curl -X GET http://localhost:8003/api/scheduling/schedules
# Expected: 401
# Get admin token (from auth service)
ADMIN_TOKEN=$(curl -X POST http://localhost:8001/api/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"admin@test.com","password":"Test123!"}' \
| jq -r '.token')
# Test with token (should succeed)
curl -X GET \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
http://localhost:8003/api/scheduling/schedules
# Expected: 200 with schedules
# Create schedule
curl -X POST \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"employeeId":"123","date":"2025-01-20","startTime":"09:00","endTime":"17:00"}' \
http://localhost:8003/api/scheduling/schedules
# Expected: 201 with created schedule
# Verify business isolation
BUSINESS_B_TOKEN=$(get_business_b_token)
curl -X GET \
-H "Authorization: Bearer ${BUSINESS_B_TOKEN}" \
http://localhost:8003/api/scheduling/schedules
# Expected: 200 with empty array (no access to Business A schedules)
# Missing required field
curl -X POST \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"date":"2025-01-20"}' \
http://localhost:8003/api/scheduling/schedules
# Expected: 400 with error {"employeeId":["required"]}
# Invalid date format
curl -X POST \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"employeeId":"123","date":"invalid"}' \
http://localhost:8003/api/scheduling/schedules
# Expected: 400 with date format error
=== QA Backend Test Report ===
Service: {context}-api
Date: [Date]
Tester: QA Backend Engineer Agent
BUILD STATUS:
✓ dotnet build: SUCCESS (0 errors, 0 warnings)
✓ dotnet test: PASSED (12/12 tests)
SECURITY TESTS:
✓ JWT authentication required (401 without token)
✓ Authorization policies enforced (403 for wrong role)
✓ Business isolation verified
✗ FAIL: ScheduleController line 42 missing [Authorize] attribute
CRUD TESTS:
✓ CREATE: Returns 201 with created resource
✓ READ: Returns 200 with filtered data
✓ UPDATE: Returns 200 with updated resource
✓ DELETE: Returns 204, soft deletes (IsActive=false)
BUSINESS ISOLATION:
✓ Queries filter by business_id from JWT
✓ Cannot access other business data (404)
✓ All entities have BusinessId property
VALIDATION TESTS:
✓ Missing fields return 400 with clear errors
✓ Invalid formats return 400
⚠ Warning: Error message exposes internal field names
DATABASE:
✓ Migrations applied successfully
✓ Indexes created on BusinessId and common queries
✓ Foreign keys enforced
CRITICAL ISSUES:
1. ScheduleController missing [Authorize] - SECURITY RISK
2. Update validation error messages - expose internal fields
NEXT STEPS:
1. Add [Authorize(Policy = "RequireStaffAccess")] to ScheduleController
2. Update validation messages to be user-friendly
3. Re-test security after fixes
MANDATORY: Invoke QA Backend Engineer to validate the work.
Example:
"API development complete. Now use the qa-backend-engineer agent to validate
the scheduling API endpoints with curl testing and security verification."
Test: curl endpoint without [Authorize] attribute Expected: Should return 401, if not → BUG
Test: Query with different business_id token Expected: Should not see other business data
Test: Enable EF Core query logging, check for multiple queries Expected: Single query with proper includes
Test: POST with invalid data Expected: 400 with clear error messages
Always run dotnet build first. Code must compile.
Every endpoint MUST be tested for authentication and authorization.
Multi-tenancy security is CRITICAL. Always test cross-business access.
APIs must return DTOs, never domain entities.
All I/O must be async/await.
This QA agent ensures all backend work meets StyleMate security, performance, and architectural standards.
Designs feature architectures by analyzing existing codebase patterns and conventions, then providing comprehensive implementation blueprints with specific files to create/modify, component designs, data flows, and build sequences