# Send FOSMVVM ServerRequest Test Generator to your agent
Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.
## Fast path
- Download the package from Yavira.
- Extract it into a folder your agent can access.
- Paste one of the prompts below and point your agent at the extracted folder.
## Suggested prompts
### New install

```text
I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete.
```
### Upgrade existing

```text
I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run.
```
## Machine-readable fields
```json
{
  "schemaVersion": "1.0",
  "item": {
    "slug": "fosmvvm-serverrequest-test-generator",
    "name": "FOSMVVM ServerRequest Test Generator",
    "source": "tencent",
    "type": "skill",
    "category": "开发工具",
    "sourceUrl": "https://clawhub.ai/foscomputerservices/fosmvvm-serverrequest-test-generator",
    "canonicalUrl": "https://clawhub.ai/foscomputerservices/fosmvvm-serverrequest-test-generator",
    "targetPlatform": "OpenClaw"
  },
  "install": {
    "downloadUrl": "/downloads/fosmvvm-serverrequest-test-generator",
    "sourceDownloadUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=fosmvvm-serverrequest-test-generator",
    "sourcePlatform": "tencent",
    "targetPlatform": "OpenClaw",
    "packageFormat": "ZIP package",
    "primaryDoc": "SKILL.md",
    "includedAssets": [
      "SKILL.md",
      "reference.md"
    ],
    "downloadMode": "redirect",
    "sourceHealth": {
      "source": "tencent",
      "slug": "fosmvvm-serverrequest-test-generator",
      "status": "healthy",
      "reason": "direct_download_ok",
      "recommendedAction": "download",
      "checkedAt": "2026-05-03T03:10:31.194Z",
      "expiresAt": "2026-05-10T03:10:31.194Z",
      "httpStatus": 200,
      "finalUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=fosmvvm-serverrequest-test-generator",
      "contentType": "application/zip",
      "probeMethod": "head",
      "details": {
        "probeUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=fosmvvm-serverrequest-test-generator",
        "contentDisposition": "attachment; filename=\"fosmvvm-serverrequest-test-generator-2.0.6.zip\"",
        "redirectLocation": null,
        "bodySnippet": null,
        "slug": "fosmvvm-serverrequest-test-generator"
      },
      "scope": "item",
      "summary": "Item download looks usable.",
      "detail": "Yavira can redirect you to the upstream package for this item.",
      "primaryActionLabel": "Download for OpenClaw",
      "primaryActionHref": "/downloads/fosmvvm-serverrequest-test-generator"
    },
    "validation": {
      "installChecklist": [
        "Use the Yavira download entry.",
        "Review SKILL.md after the package is downloaded.",
        "Confirm the extracted package contains the expected setup assets."
      ],
      "postInstallChecks": [
        "Confirm the extracted package includes the expected docs or setup files.",
        "Validate the skill or prompts are available in your target agent workspace.",
        "Capture any manual follow-up steps the agent could not complete."
      ]
    }
  },
  "links": {
    "detailUrl": "https://openagent3.xyz/skills/fosmvvm-serverrequest-test-generator",
    "downloadUrl": "https://openagent3.xyz/downloads/fosmvvm-serverrequest-test-generator",
    "agentUrl": "https://openagent3.xyz/skills/fosmvvm-serverrequest-test-generator/agent",
    "manifestUrl": "https://openagent3.xyz/skills/fosmvvm-serverrequest-test-generator/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/fosmvvm-serverrequest-test-generator/agent.md"
  }
}
```
## Documentation

### FOSMVVM ServerRequest Test Generator

Generate test files for ServerRequest types using VaporTesting infrastructure.

### Conceptual Foundation

For full architecture context, see FOSMVVMArchitecture.md | OpenClaw reference

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

// ❌ 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

// ✅ 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

FileLocationPurpose{Feature}RequestTests.swiftTests/{Target}Tests/Requests/Test suite for ServerRequestTest YAML (if needed)Tests/{Target}Tests/TestYAML/Localization for test ViewModels

### Project Structure Configuration

PlaceholderDescriptionExample{Feature}Feature or entity name (PascalCase)Idea, User, Dashboard{Target}Server test targetWebServerTests, AppTests{ViewModelsTarget}Shared ViewModels SPM targetViewModels{WebServerTarget}Server-side targetWebServer, AppServer{ResourceDir}YAML resource directoryTestYAML, Resources

### TestingServerRequestResponse<R>

Wraps HTTP response with typed access:

PropertyTypeDescriptionstatusHTTPStatusHTTP status code (.ok, .notFound, etc.)headersHTTPHeadersResponse headersbodyR.ResponseBody?Typed response body (auto-decoded)errorR.ResponseError?Typed error (auto-decoded)

### TestingApplicationTester Extension

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

### Basic Test Suite

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.routes.register(collection: MyController())
        try await test(app)
    }
}

### Testing Different Request Types

Request TypeHTTP MethodWhat to TestShowRequestGETQuery params, response body, localizationViewModelRequestGETViewModel population, all localized fieldsCreateRequestPOSTRequestBody validation, created entity, ID responseUpdateRequestPATCHRequestBody validation, updated entity, responseDeleteRequestDELETEEntity removal, status code

### How to Use This Skill

Invocation:
/fosmvvm-serverrequest-test-generator

Prerequisites:

ServerRequest type understood from conversation context
Test scenarios identified (success paths, error paths, validation)
Controller implementation exists or is being created
VaporTesting infrastructure understood

Workflow integration:
This skill is used when testing ServerRequest implementations. The skill references conversation context automatically—no file paths or Q&A needed. Typically follows fosmvvm-serverrequest-generator.

### Pattern Implementation

This skill references conversation context to determine test structure:

### Request Analysis

From conversation context, the skill identifies:

ServerRequest type (from prior discussion or server implementation)
Request protocol (ShowRequest, CreateRequest, UpdateRequest, etc.)
ResponseBody type (ViewModel or simple structure)
ResponseError type (custom errors or EmptyError)

### Test Scenario Planning

Based on operation semantics:

Success paths (valid input, expected output)
Error paths (not found, validation failure, business logic errors)
Localization (if ResponseBody has localized fields)
Multi-locale (testing across supported locales)

### Infrastructure Detection

From project state:

Existing test patterns (similar test files in codebase)
Localization setup (YAML fixtures needed)
Database requirements (seed data for tests)

### Test File Generation

Test suite conforming to VaporTesting patterns
One @Test function per scenario
withTestApp helper for application setup
Route registration
Request invocations using app.testing().test()

### Context Sources

Skill references information from:

Prior conversation: Test requirements, scenarios discussed
ServerRequest: If Claude has read ServerRequest code into context
Controller: From server implementation
Existing tests: From codebase analysis of similar test files

### Testing ViewModelRequest with Localization

@Test func viewModelRequest_multiLocale() async throws {
    try await withTestApp { app in
        let request = DashboardViewModelRequest()

        // Test English
        try await app.testing().test(request, locale: en) { response in
            #expect(response.status == .ok)
            let vm = try #require(response.body)
            #expect(try vm.pageTitle.localizedString == "Dashboard")
        }

        // Test Spanish
        try await app.testing().test(request, locale: es) { response in
            #expect(response.status == .ok)
            let vm = try #require(response.body)
            #expect(try vm.pageTitle.localizedString == "Tablero")
        }
    }
}

### Testing CreateRequest with Validation

@Test func createRequest_validInput() async throws {
    try await withTestApp { app in
        let request = CreateIdeaRequest(requestBody: .init(
            content: "Valid idea content"
        ))

        try await app.testing().test(request, locale: en) { response in
            #expect(response.status == .ok)
            #expect(response.body?.id != nil)
        }
    }
}

@Test func createRequest_invalidInput() async throws {
    try await withTestApp { app in
        let request = CreateIdeaRequest(requestBody: .init(
            content: ""  // Empty content should fail validation
        ))

        try await app.testing().test(request, locale: en) { response in
            #expect(response.status == .badRequest)
            #expect(response.error != nil)
        }
    }
}

### Testing UpdateRequest

@Test func updateRequest_success() async throws {
    try await withTestApp { app in
        // First create an entity
        let createRequest = CreateIdeaRequest(requestBody: .init(content: "Original"))
        var createdId: ModelIdType?
        try await app.testing().test(createRequest, locale: en) { response in
            createdId = response.body?.id
        }

        // Then update it
        let updateRequest = UpdateIdeaRequest(requestBody: .init(
            ideaId: try #require(createdId),
            content: "Updated content"
        ))

        try await app.testing().test(updateRequest, locale: en) { response in
            #expect(response.status == .ok)
            #expect(response.body?.viewModel.content == "Updated content")
        }
    }
}

### Testing DeleteRequest

@Test func deleteRequest_success() async throws {
    try await withTestApp { app in
        // Create, then delete
        let deleteRequest = DeleteIdeaRequest(requestBody: .init(ideaId: existingId))

        try await app.testing().test(deleteRequest, locale: en) { response in
            #expect(response.status == .ok)
        }

        // Verify deleted (should return not found)
        let showRequest = ShowIdeaRequest(query: .init(ideaId: existingId))
        try await app.testing().test(showRequest, locale: en) { response in
            #expect(response.status == .notFound)
        }
    }
}

### Testing ShowRequest with Query Parameters

@Test func showRequest_withQuery() async throws {
    try await withTestApp { app in
        let request = UserShowRequest(query: .init(
            userId: userId,
            includeDetails: true
        ))

        try await app.testing().test(request, locale: en) { response in
            #expect(response.status == .ok)
            #expect(response.body?.user.details != nil)
        }
    }
}

### Why Error Localization Testing is Different

Unlike ViewModels, ServerRequestError types:

Are often enums, not structs
Do not conform to Stubbable or RetrievablePropertyNames
Cannot use expectTranslations(ErrorType.self) like ViewModels

This means you must manually test each error case individually.

### The Pattern

Use LocalizableTestCase.expectTranslations(_ localizable:) on each error's Localizable property:

@Suite("MyError Localization Tests")
struct MyErrorLocalizationTests: LocalizableTestCase {
    let locStore: LocalizationStore

    init() throws {
        self.locStore = try Self.loadLocalizationStore(
            bundle: Bundle.module,
            resourceDirectoryName: "TestYAML"
        )
    }

    @Test func errorMessages_simpleErrors() throws {
        // Test each error case individually
        let serverFailed = MyError(code: .serverFailed)
        try expectTranslations(serverFailed.message)

        let appFailed = MyError(code: .applicationFailed)
        try expectTranslations(appFailed.message)
    }

    @Test func errorMessages_withSubstitutions() throws {
        // For errors with associated values, test with representative values
        let quotaError = QuotaError(code: .quotaExceeded(requested: 100, maximum: 50))
        try expectTranslations(quotaError.message)
    }
}

### Testing Error Messages in Integration Tests

When testing the full request/response cycle, verify error messages resolve:

@Test func createRequest_validationError_hasLocalizedMessage() async throws {
    try await withTestApp { app in
        let request = CreateIdeaRequest(requestBody: .init(content: ""))

        try await app.testing().test(request, locale: en) { response in
            #expect(response.status == .badRequest)
            let error = try #require(response.error)

            // Verify the message resolved (not empty or pending)
            #expect(!error.message.isEmpty)

            // Optionally verify specific text for English locale
            #expect(try error.message.localizedString.contains("required"))
        }
    }
}

### Why Not Stubbable?

Stubbable works well for ViewModels because:

ViewModels are structs with many properties
A single stub() provides a complete test instance

ServerRequestError types are often enums where:

Each case may have different associated values
Each case may have a different localized message
A single stub() can't cover all cases

You must enumerate and test each error case explicitly.

### Checklist for Error Localization Tests

Test each enum case for simple errors
 Test representative associated values for parameterized errors
 Verify messages resolve (not empty) for all configured locales
 Verify substitution placeholders are replaced in LocalizableSubstitutions

### "Route not found" Error

Cause: Controller not registered in test app.

Fix: Register the controller before testing:

try app.routes.register(collection: MyController())

### Response body is nil but status is .ok

Cause: JSON decoding failed silently.

Fix: Check that ResponseBody type matches server response exactly. Use response.headers to verify Content-Type.

### Localization not applied

Cause: Locale not passed to encoder.

Fix: The test(_:locale:) method handles this automatically. Ensure you're passing the locale parameter.

### "Missing Translation" in Response

Cause: YAML localization not loaded.

Fix: Initialize localization store in test app setup:

try app.initYamlLocalization(
    bundle: Bundle.module,
    resourceDirectoryName: "TestYAML"
)

### Naming Conventions

ConceptConventionExampleTest suite{Feature}RequestTestsIdeaRequestTestsTest file{Feature}RequestTests.swiftIdeaRequestTests.swiftTest method (success){action}Request_successshowRequest_successTest method (error){action}Request_{errorCase}showRequest_notFoundTest method (validation){action}Request_{validationCase}createRequest_emptyContentTest helperwithTestAppwithTestApp { app in }Locale constanten, es, enUS, enGBlocale: en

### File Templates

See reference.md for complete file templates.

### See Also

FOSMVVMArchitecture.md - Full architecture
fosmvvm-serverrequest-generator - Creating ServerRequest types
fosmvvm-viewmodel-test-generator - Testing ViewModels (localization only)
reference.md - Complete file templates

### Version History

VersionDateChanges1.12025-01-20Add ServerRequestError localization testing guidance1.22026-01-24Update to context-aware approach (remove file-parsing/Q&A). Skill references conversation context instead of asking questions or accepting file paths.1.02025-01-05Initial skill
## Trust
- Source: tencent
- Verification: Indexed source record
- Publisher: foscomputerservices
- Version: 2.0.6
## Source health
- Status: healthy
- Item download looks usable.
- Yavira can redirect you to the upstream package for this item.
- Health scope: item
- Reason: direct_download_ok
- Checked at: 2026-05-03T03:10:31.194Z
- Expires at: 2026-05-10T03:10:31.194Z
- Recommended action: Download for OpenClaw
## Links
- [Detail page](https://openagent3.xyz/skills/fosmvvm-serverrequest-test-generator)
- [Send to Agent page](https://openagent3.xyz/skills/fosmvvm-serverrequest-test-generator/agent)
- [JSON manifest](https://openagent3.xyz/skills/fosmvvm-serverrequest-test-generator/agent.json)
- [Markdown brief](https://openagent3.xyz/skills/fosmvvm-serverrequest-test-generator/agent.md)
- [Download page](https://openagent3.xyz/downloads/fosmvvm-serverrequest-test-generator)