fosmvvm-serverrequest-test-generator
Generate ServerRequest tests using VaporTesting. Covers typed request/response validation for Show, Create, Update, and Delete operations.
安装 / 下载方式
TotalClaw CLI推荐
totalclaw install clawskills:clawskills~foscomputerservices-fosmvvm-serverrequest-test-generatorcURL直接下载,无需登录
curl -fsSL https://skills.taituai.com/api/skills/clawskills%3Aclawskills~foscomputerservices-fosmvvm-serverrequest-test-generator/file -o foscomputerservices-fosmvvm-serverrequest-test-generator.md# FOSMVVM ServerRequest Test Generator
Generate test files for ServerRequest types using VaporTesting infrastructure.
## Conceptual Foundation
> For full architecture context, see [FOSMVVMArchitecture.md](../../docs/FOSMVVMArchitecture.md) | [OpenClaw reference]({baseDir}/references/FOSMVVMArchitecture.md)
ServerRequest testing uses **VaporTesting** infrastructure to send typed requests through the full server stack:
```
┌─────────────────────────────────────────────────────────────────────┐
│ ServerRequest Test Flow │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Test Code: │
│ let request = MyRequest(query: .init(...)) │
│ app.testing().test(request, locale: en) { response in } │
│ │
│ Infrastructure handles: │
│ • Path derivation from type name (MyRequest → /my) │
│ • HTTP method from action (ShowRequest → GET) │
│ • Query/body encoding │
│ • Header injection (locale, version) │
│ • Response decoding to ResponseBody type │
│ │
│ You verify: │
│ • response.status (HTTPStatus) │
│ • response.body (R.ResponseBody? - typed!) │
│ • response.error (R.ResponseError? - typed!) │
│ │
└─────────────────────────────────────────────────────────────────────┘
```
---
## STOP AND READ THIS
**Testing ServerRequests uses VaporTesting infrastructure. No manual URL construction. Ever.**
```
┌──────────────────────────────────────────────────────────────────────┐
│ SERVERREQUEST TESTING USES TestingApplicationTester │
├──────────────────────────────────────────────────────────────────────┤
│ │
│ 1. Configure Vapor Application with routes │
│ 2. Use app.testing().test(request, locale:) { response in } │
│ 3. Verify response.status, response.body, response.error │
│ │
│ TestingServerRequestResponse<R> provides TYPED access to: │
│ • status: HTTPStatus │
│ • headers: HTTPHeaders │
│ • body: R.ResponseBody? ← Auto-decoded! │
│ • error: R.ResponseError? ← Auto-decoded! │
│ │
└──────────────────────────────────────────────────────────────────────┘
```
### What You Must NEVER Do
```swift
// ❌ WRONG - manual URL construction
let url = URL(string: "http://localhost:8080/my_request?query=value")!
let response = try await URLSession.shared.data(from: url)
// ❌ WRONG - string path with method
try await app.test(.GET, "/my_request") { response in }
// ❌ WRONG - manual JSON encoding/decoding
let json = try JSONEncoder().encode(requestBody)
let decoded = try JSONDecoder().decode(ResponseBody.self, from: data)
// ❌ WRONG - constructing TestingHTTPRequest manually
let httpRequest = TestingHTTPRequest(method: .GET, url: "/path", headers: headers)
try await app.testing().performTest(request: httpRequest)
```
### What You Must ALWAYS Do
```swift
// ✅ RIGHT - Use TestingApplicationTester.test() with ServerRequest
let request = MyShowRequest(query: .init(userId: userId))
try await app.testing().test(request, locale: en) { response in
#expect(response.status == .ok)
#expect(response.body?.viewModel.name == "Expected Name")
}
// ✅ RIGHT - Test multiple locales
for locale in [en, es] {
try await app.testing().test(request, locale: locale) { response in
#expect(response.status == .ok)
// Localized values are automatically handled
}
}
// ✅ RIGHT - Test error responses
let badRequest = MyShowRequest(query: .init(userId: invalidId))
try await app.testing().test(badRequest, locale: en) { response in
#expect(response.status == .notFound)
#expect(response.error != nil)
}
```
**The path is derived from the ServerRequest type. HTTP method comes from the action. Headers are automatic. You NEVER write URL strings or decode JSON manually.**
---
## When to Use This Skill
- Testing any ServerRequest implementation
- Verifying server responses for CRUD operations
- Testing error handling and edge cases
- Multi-locale response verification
- Integration testing between client request types and server controllers
**If you're about to write `URLSession`, `app.test(.GET, "/path")`, or manual JSON decoding, STOP and use this skill instead.**
## What This Skill Generates
| File | Location | Purpose |
|------|----------|---------|
| `{Feature}RequestTests.swift` | `Tests/{Target}Tests/Requests/` | Test suite for ServerRequest |
| Test YAML (if needed) | `Tests/{Target}Tests/TestYAML/` | Localization for test ViewModels |
## Project Structure Configuration
| Placeholder | Description | Example |
|-------------|-------------|---------|
| `{Feature}` | Feature or entity name (PascalCase) | `Idea`, `User`, `Dashboard` |
| `{Target}` | Server test target | `WebServerTests`, `AppTests` |
| `{ViewModelsTarget}` | Shared ViewModels SPM target | `ViewModels` |
| `{WebServerTarget}` | Server-side target | `WebServer`, `AppServer` |
| `{ResourceDir}` | YAML resource directory | `TestYAML`, `Resources` |
---
## Key Types
### TestingServerRequestResponse<R>
Wraps HTTP response with typed access:
| Property | Type | Description |
|----------|------|-------------|
| `status` | `HTTPStatus` | HTTP status code (.ok, .notFound, etc.) |
| `headers` | `HTTPHeaders` | Response headers |
| `body` | `R.ResponseBody?` | **Typed** response body (auto-decoded) |
| `error` | `R.ResponseError?` | **Typed** error (auto-decoded) |
### TestingApplicationTester Extension
```swift
func test<R: ServerRequest>(
_ request: R,
locale: Locale = en,
headers: HTTPHeaders = [:],
afterResponse: (TestingServerRequestResponse<R>) async throws -> Void
) async throws -> any TestingApplicationTester
```
### Convenience Locales
Available on `TestingApplicationTester`:
- `en` - English
- `enUS` - English (US)
- `enGB` - English (UK)
- `es` - Spanish
---
## Test Structure
### Basic Test Suite
```swift
import FOSFoundation
@testable import FOSMVVM
import FOSTesting
import FOSTestingVapor
import Foundation
import Testing
import Vapor
import VaporTesting
@Suite("MyFeature Request Tests")
struct MyFeatureRequestTests {
@Test func showRequest_success() async throws {
try await withTestApp { app in
let request = MyShowRequest(query: .init(id: validId))
try await app.testing().test(request, locale: en) { response in
#expect(response.status == .ok)
#expect(response.body?.viewModel != nil)
}
}
}
@Test func showRequest_notFound() async throws {
try await withTestApp { app in
let request = MyShowRequest(query: .init(id: invalidId))
try await app.testing().test(request, locale: en) { response in
#expect(response.status == .notFound)
}
}
}
}
private func withTestApp(_ test: (Application) async throws -> Void) async throws {
try await withApp { app in
// Configure routes
try app.rou