{
  "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": {
    "downloadMode": "redirect",
    "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",
    "installMethod": "Manual import",
    "extraction": "Extract archive",
    "prerequisites": [
      "OpenClaw"
    ],
    "packageFormat": "ZIP package",
    "includedAssets": [
      "SKILL.md",
      "reference.md"
    ],
    "primaryDoc": "SKILL.md",
    "quickSetup": [
      "Download the package from Yavira.",
      "Extract the archive and review SKILL.md first.",
      "Import or place the package into your OpenClaw setup."
    ],
    "agentAssist": {
      "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
      "steps": [
        "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."
      ],
      "prompts": [
        {
          "label": "New install",
          "body": "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."
        },
        {
          "label": "Upgrade existing",
          "body": "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."
        }
      ]
    },
    "sourceHealth": {
      "source": "tencent",
      "status": "healthy",
      "reason": "direct_download_ok",
      "recommendedAction": "download",
      "checkedAt": "2026-04-30T16:55:25.780Z",
      "expiresAt": "2026-05-07T16:55:25.780Z",
      "httpStatus": 200,
      "finalUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=network",
      "contentType": "application/zip",
      "probeMethod": "head",
      "details": {
        "probeUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=network",
        "contentDisposition": "attachment; filename=\"network-1.0.0.zip\"",
        "redirectLocation": null,
        "bodySnippet": null
      },
      "scope": "source",
      "summary": "Source download looks usable.",
      "detail": "Yavira can redirect you to the upstream package for this source.",
      "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."
      ]
    },
    "downloadPageUrl": "https://openagent3.xyz/downloads/fosmvvm-serverrequest-test-generator",
    "agentPageUrl": "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"
  },
  "agentAssist": {
    "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
    "steps": [
      "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."
    ],
    "prompts": [
      {
        "label": "New install",
        "body": "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."
      },
      {
        "label": "Upgrade existing",
        "body": "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."
      }
    ]
  },
  "documentation": {
    "source": "clawhub",
    "primaryDoc": "SKILL.md",
    "sections": [
      {
        "title": "FOSMVVM ServerRequest Test Generator",
        "body": "Generate test files for ServerRequest types using VaporTesting infrastructure."
      },
      {
        "title": "Conceptual Foundation",
        "body": "For full architecture context, see FOSMVVMArchitecture.md | OpenClaw reference\n\nServerRequest testing uses VaporTesting infrastructure to send typed requests through the full server stack:\n\n┌─────────────────────────────────────────────────────────────────────┐\n│                    ServerRequest Test Flow                           │\n├─────────────────────────────────────────────────────────────────────┤\n│                                                                      │\n│  Test Code:                                                          │\n│    let request = MyRequest(query: .init(...))                        │\n│    app.testing().test(request, locale: en) { response in }           │\n│                                                                      │\n│  Infrastructure handles:                                             │\n│    • Path derivation from type name (MyRequest → /my)                │\n│    • HTTP method from action (ShowRequest → GET)                     │\n│    • Query/body encoding                                             │\n│    • Header injection (locale, version)                              │\n│    • Response decoding to ResponseBody type                          │\n│                                                                      │\n│  You verify:                                                         │\n│    • response.status (HTTPStatus)                                    │\n│    • response.body (R.ResponseBody? - typed!)                        │\n│    • response.error (R.ResponseError? - typed!)                      │\n│                                                                      │\n└─────────────────────────────────────────────────────────────────────┘"
      },
      {
        "title": "STOP AND READ THIS",
        "body": "Testing ServerRequests uses VaporTesting infrastructure. No manual URL construction. Ever.\n\n┌──────────────────────────────────────────────────────────────────────┐\n│          SERVERREQUEST TESTING USES TestingApplicationTester          │\n├──────────────────────────────────────────────────────────────────────┤\n│                                                                       │\n│  1. Configure Vapor Application with routes                           │\n│  2. Use app.testing().test(request, locale:) { response in }          │\n│  3. Verify response.status, response.body, response.error             │\n│                                                                       │\n│  TestingServerRequestResponse<R> provides TYPED access to:            │\n│    • status: HTTPStatus                                               │\n│    • headers: HTTPHeaders                                             │\n│    • body: R.ResponseBody?     ← Auto-decoded!                        │\n│    • error: R.ResponseError?   ← Auto-decoded!                        │\n│                                                                       │\n└──────────────────────────────────────────────────────────────────────┘"
      },
      {
        "title": "What You Must NEVER Do",
        "body": "// ❌ WRONG - manual URL construction\nlet url = URL(string: \"http://localhost:8080/my_request?query=value\")!\nlet response = try await URLSession.shared.data(from: url)\n\n// ❌ WRONG - string path with method\ntry await app.test(.GET, \"/my_request\") { response in }\n\n// ❌ WRONG - manual JSON encoding/decoding\nlet json = try JSONEncoder().encode(requestBody)\nlet decoded = try JSONDecoder().decode(ResponseBody.self, from: data)\n\n// ❌ WRONG - constructing TestingHTTPRequest manually\nlet httpRequest = TestingHTTPRequest(method: .GET, url: \"/path\", headers: headers)\ntry await app.testing().performTest(request: httpRequest)"
      },
      {
        "title": "What You Must ALWAYS Do",
        "body": "// ✅ RIGHT - Use TestingApplicationTester.test() with ServerRequest\nlet request = MyShowRequest(query: .init(userId: userId))\ntry await app.testing().test(request, locale: en) { response in\n    #expect(response.status == .ok)\n    #expect(response.body?.viewModel.name == \"Expected Name\")\n}\n\n// ✅ RIGHT - Test multiple locales\nfor locale in [en, es] {\n    try await app.testing().test(request, locale: locale) { response in\n        #expect(response.status == .ok)\n        // Localized values are automatically handled\n    }\n}\n\n// ✅ RIGHT - Test error responses\nlet badRequest = MyShowRequest(query: .init(userId: invalidId))\ntry await app.testing().test(badRequest, locale: en) { response in\n    #expect(response.status == .notFound)\n    #expect(response.error != nil)\n}\n\nThe 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."
      },
      {
        "title": "When to Use This Skill",
        "body": "Testing any ServerRequest implementation\nVerifying server responses for CRUD operations\nTesting error handling and edge cases\nMulti-locale response verification\nIntegration testing between client request types and server controllers\n\nIf you're about to write URLSession, app.test(.GET, \"/path\"), or manual JSON decoding, STOP and use this skill instead."
      },
      {
        "title": "What This Skill Generates",
        "body": "FileLocationPurpose{Feature}RequestTests.swiftTests/{Target}Tests/Requests/Test suite for ServerRequestTest YAML (if needed)Tests/{Target}Tests/TestYAML/Localization for test ViewModels"
      },
      {
        "title": "Project Structure Configuration",
        "body": "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"
      },
      {
        "title": "TestingServerRequestResponse<R>",
        "body": "Wraps HTTP response with typed access:\n\nPropertyTypeDescriptionstatusHTTPStatusHTTP status code (.ok, .notFound, etc.)headersHTTPHeadersResponse headersbodyR.ResponseBody?Typed response body (auto-decoded)errorR.ResponseError?Typed error (auto-decoded)"
      },
      {
        "title": "TestingApplicationTester Extension",
        "body": "func test<R: ServerRequest>(\n    _ request: R,\n    locale: Locale = en,\n    headers: HTTPHeaders = [:],\n    afterResponse: (TestingServerRequestResponse<R>) async throws -> Void\n) async throws -> any TestingApplicationTester"
      },
      {
        "title": "Convenience Locales",
        "body": "Available on TestingApplicationTester:\n\nen - English\nenUS - English (US)\nenGB - English (UK)\nes - Spanish"
      },
      {
        "title": "Basic Test Suite",
        "body": "import FOSFoundation\n@testable import FOSMVVM\nimport FOSTesting\nimport FOSTestingVapor\nimport Foundation\nimport Testing\nimport Vapor\nimport VaporTesting\n\n@Suite(\"MyFeature Request Tests\")\nstruct MyFeatureRequestTests {\n    @Test func showRequest_success() async throws {\n        try await withTestApp { app in\n            let request = MyShowRequest(query: .init(id: validId))\n\n            try await app.testing().test(request, locale: en) { response in\n                #expect(response.status == .ok)\n                #expect(response.body?.viewModel != nil)\n            }\n        }\n    }\n\n    @Test func showRequest_notFound() async throws {\n        try await withTestApp { app in\n            let request = MyShowRequest(query: .init(id: invalidId))\n\n            try await app.testing().test(request, locale: en) { response in\n                #expect(response.status == .notFound)\n            }\n        }\n    }\n}\n\nprivate func withTestApp(_ test: (Application) async throws -> Void) async throws {\n    try await withApp { app in\n        // Configure routes\n        try app.routes.register(collection: MyController())\n        try await test(app)\n    }\n}"
      },
      {
        "title": "Testing Different Request Types",
        "body": "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"
      },
      {
        "title": "How to Use This Skill",
        "body": "Invocation:\n/fosmvvm-serverrequest-test-generator\n\nPrerequisites:\n\nServerRequest type understood from conversation context\nTest scenarios identified (success paths, error paths, validation)\nController implementation exists or is being created\nVaporTesting infrastructure understood\n\nWorkflow integration:\nThis 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."
      },
      {
        "title": "Pattern Implementation",
        "body": "This skill references conversation context to determine test structure:"
      },
      {
        "title": "Request Analysis",
        "body": "From conversation context, the skill identifies:\n\nServerRequest type (from prior discussion or server implementation)\nRequest protocol (ShowRequest, CreateRequest, UpdateRequest, etc.)\nResponseBody type (ViewModel or simple structure)\nResponseError type (custom errors or EmptyError)"
      },
      {
        "title": "Test Scenario Planning",
        "body": "Based on operation semantics:\n\nSuccess paths (valid input, expected output)\nError paths (not found, validation failure, business logic errors)\nLocalization (if ResponseBody has localized fields)\nMulti-locale (testing across supported locales)"
      },
      {
        "title": "Infrastructure Detection",
        "body": "From project state:\n\nExisting test patterns (similar test files in codebase)\nLocalization setup (YAML fixtures needed)\nDatabase requirements (seed data for tests)"
      },
      {
        "title": "Test File Generation",
        "body": "Test suite conforming to VaporTesting patterns\nOne @Test function per scenario\nwithTestApp helper for application setup\nRoute registration\nRequest invocations using app.testing().test()"
      },
      {
        "title": "Context Sources",
        "body": "Skill references information from:\n\nPrior conversation: Test requirements, scenarios discussed\nServerRequest: If Claude has read ServerRequest code into context\nController: From server implementation\nExisting tests: From codebase analysis of similar test files"
      },
      {
        "title": "Testing ViewModelRequest with Localization",
        "body": "@Test func viewModelRequest_multiLocale() async throws {\n    try await withTestApp { app in\n        let request = DashboardViewModelRequest()\n\n        // Test English\n        try await app.testing().test(request, locale: en) { response in\n            #expect(response.status == .ok)\n            let vm = try #require(response.body)\n            #expect(try vm.pageTitle.localizedString == \"Dashboard\")\n        }\n\n        // Test Spanish\n        try await app.testing().test(request, locale: es) { response in\n            #expect(response.status == .ok)\n            let vm = try #require(response.body)\n            #expect(try vm.pageTitle.localizedString == \"Tablero\")\n        }\n    }\n}"
      },
      {
        "title": "Testing CreateRequest with Validation",
        "body": "@Test func createRequest_validInput() async throws {\n    try await withTestApp { app in\n        let request = CreateIdeaRequest(requestBody: .init(\n            content: \"Valid idea content\"\n        ))\n\n        try await app.testing().test(request, locale: en) { response in\n            #expect(response.status == .ok)\n            #expect(response.body?.id != nil)\n        }\n    }\n}\n\n@Test func createRequest_invalidInput() async throws {\n    try await withTestApp { app in\n        let request = CreateIdeaRequest(requestBody: .init(\n            content: \"\"  // Empty content should fail validation\n        ))\n\n        try await app.testing().test(request, locale: en) { response in\n            #expect(response.status == .badRequest)\n            #expect(response.error != nil)\n        }\n    }\n}"
      },
      {
        "title": "Testing UpdateRequest",
        "body": "@Test func updateRequest_success() async throws {\n    try await withTestApp { app in\n        // First create an entity\n        let createRequest = CreateIdeaRequest(requestBody: .init(content: \"Original\"))\n        var createdId: ModelIdType?\n        try await app.testing().test(createRequest, locale: en) { response in\n            createdId = response.body?.id\n        }\n\n        // Then update it\n        let updateRequest = UpdateIdeaRequest(requestBody: .init(\n            ideaId: try #require(createdId),\n            content: \"Updated content\"\n        ))\n\n        try await app.testing().test(updateRequest, locale: en) { response in\n            #expect(response.status == .ok)\n            #expect(response.body?.viewModel.content == \"Updated content\")\n        }\n    }\n}"
      },
      {
        "title": "Testing DeleteRequest",
        "body": "@Test func deleteRequest_success() async throws {\n    try await withTestApp { app in\n        // Create, then delete\n        let deleteRequest = DeleteIdeaRequest(requestBody: .init(ideaId: existingId))\n\n        try await app.testing().test(deleteRequest, locale: en) { response in\n            #expect(response.status == .ok)\n        }\n\n        // Verify deleted (should return not found)\n        let showRequest = ShowIdeaRequest(query: .init(ideaId: existingId))\n        try await app.testing().test(showRequest, locale: en) { response in\n            #expect(response.status == .notFound)\n        }\n    }\n}"
      },
      {
        "title": "Testing ShowRequest with Query Parameters",
        "body": "@Test func showRequest_withQuery() async throws {\n    try await withTestApp { app in\n        let request = UserShowRequest(query: .init(\n            userId: userId,\n            includeDetails: true\n        ))\n\n        try await app.testing().test(request, locale: en) { response in\n            #expect(response.status == .ok)\n            #expect(response.body?.user.details != nil)\n        }\n    }\n}"
      },
      {
        "title": "Why Error Localization Testing is Different",
        "body": "Unlike ViewModels, ServerRequestError types:\n\nAre often enums, not structs\nDo not conform to Stubbable or RetrievablePropertyNames\nCannot use expectTranslations(ErrorType.self) like ViewModels\n\nThis means you must manually test each error case individually."
      },
      {
        "title": "The Pattern",
        "body": "Use LocalizableTestCase.expectTranslations(_ localizable:) on each error's Localizable property:\n\n@Suite(\"MyError Localization Tests\")\nstruct MyErrorLocalizationTests: LocalizableTestCase {\n    let locStore: LocalizationStore\n\n    init() throws {\n        self.locStore = try Self.loadLocalizationStore(\n            bundle: Bundle.module,\n            resourceDirectoryName: \"TestYAML\"\n        )\n    }\n\n    @Test func errorMessages_simpleErrors() throws {\n        // Test each error case individually\n        let serverFailed = MyError(code: .serverFailed)\n        try expectTranslations(serverFailed.message)\n\n        let appFailed = MyError(code: .applicationFailed)\n        try expectTranslations(appFailed.message)\n    }\n\n    @Test func errorMessages_withSubstitutions() throws {\n        // For errors with associated values, test with representative values\n        let quotaError = QuotaError(code: .quotaExceeded(requested: 100, maximum: 50))\n        try expectTranslations(quotaError.message)\n    }\n}"
      },
      {
        "title": "Testing Error Messages in Integration Tests",
        "body": "When testing the full request/response cycle, verify error messages resolve:\n\n@Test func createRequest_validationError_hasLocalizedMessage() async throws {\n    try await withTestApp { app in\n        let request = CreateIdeaRequest(requestBody: .init(content: \"\"))\n\n        try await app.testing().test(request, locale: en) { response in\n            #expect(response.status == .badRequest)\n            let error = try #require(response.error)\n\n            // Verify the message resolved (not empty or pending)\n            #expect(!error.message.isEmpty)\n\n            // Optionally verify specific text for English locale\n            #expect(try error.message.localizedString.contains(\"required\"))\n        }\n    }\n}"
      },
      {
        "title": "Why Not Stubbable?",
        "body": "Stubbable works well for ViewModels because:\n\nViewModels are structs with many properties\nA single stub() provides a complete test instance\n\nServerRequestError types are often enums where:\n\nEach case may have different associated values\nEach case may have a different localized message\nA single stub() can't cover all cases\n\nYou must enumerate and test each error case explicitly."
      },
      {
        "title": "Checklist for Error Localization Tests",
        "body": "Test each enum case for simple errors\n Test representative associated values for parameterized errors\n Verify messages resolve (not empty) for all configured locales\n Verify substitution placeholders are replaced in LocalizableSubstitutions"
      },
      {
        "title": "\"Route not found\" Error",
        "body": "Cause: Controller not registered in test app.\n\nFix: Register the controller before testing:\n\ntry app.routes.register(collection: MyController())"
      },
      {
        "title": "Response body is nil but status is .ok",
        "body": "Cause: JSON decoding failed silently.\n\nFix: Check that ResponseBody type matches server response exactly. Use response.headers to verify Content-Type."
      },
      {
        "title": "Localization not applied",
        "body": "Cause: Locale not passed to encoder.\n\nFix: The test(_:locale:) method handles this automatically. Ensure you're passing the locale parameter."
      },
      {
        "title": "\"Missing Translation\" in Response",
        "body": "Cause: YAML localization not loaded.\n\nFix: Initialize localization store in test app setup:\n\ntry app.initYamlLocalization(\n    bundle: Bundle.module,\n    resourceDirectoryName: \"TestYAML\"\n)"
      },
      {
        "title": "Naming Conventions",
        "body": "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"
      },
      {
        "title": "File Templates",
        "body": "See reference.md for complete file templates."
      },
      {
        "title": "See Also",
        "body": "FOSMVVMArchitecture.md - Full architecture\nfosmvvm-serverrequest-generator - Creating ServerRequest types\nfosmvvm-viewmodel-test-generator - Testing ViewModels (localization only)\nreference.md - Complete file templates"
      },
      {
        "title": "Version History",
        "body": "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"
      }
    ],
    "body": "FOSMVVM ServerRequest Test Generator\n\nGenerate test files for ServerRequest types using VaporTesting infrastructure.\n\nConceptual Foundation\n\nFor full architecture context, see FOSMVVMArchitecture.md | OpenClaw reference\n\nServerRequest testing uses VaporTesting infrastructure to send typed requests through the full server stack:\n\n┌─────────────────────────────────────────────────────────────────────┐\n│                    ServerRequest Test Flow                           │\n├─────────────────────────────────────────────────────────────────────┤\n│                                                                      │\n│  Test Code:                                                          │\n│    let request = MyRequest(query: .init(...))                        │\n│    app.testing().test(request, locale: en) { response in }           │\n│                                                                      │\n│  Infrastructure handles:                                             │\n│    • Path derivation from type name (MyRequest → /my)                │\n│    • HTTP method from action (ShowRequest → GET)                     │\n│    • Query/body encoding                                             │\n│    • Header injection (locale, version)                              │\n│    • Response decoding to ResponseBody type                          │\n│                                                                      │\n│  You verify:                                                         │\n│    • response.status (HTTPStatus)                                    │\n│    • response.body (R.ResponseBody? - typed!)                        │\n│    • response.error (R.ResponseError? - typed!)                      │\n│                                                                      │\n└─────────────────────────────────────────────────────────────────────┘\n\nSTOP AND READ THIS\n\nTesting ServerRequests uses VaporTesting infrastructure. No manual URL construction. Ever.\n\n┌──────────────────────────────────────────────────────────────────────┐\n│          SERVERREQUEST TESTING USES TestingApplicationTester          │\n├──────────────────────────────────────────────────────────────────────┤\n│                                                                       │\n│  1. Configure Vapor Application with routes                           │\n│  2. Use app.testing().test(request, locale:) { response in }          │\n│  3. Verify response.status, response.body, response.error             │\n│                                                                       │\n│  TestingServerRequestResponse<R> provides TYPED access to:            │\n│    • status: HTTPStatus                                               │\n│    • headers: HTTPHeaders                                             │\n│    • body: R.ResponseBody?     ← Auto-decoded!                        │\n│    • error: R.ResponseError?   ← Auto-decoded!                        │\n│                                                                       │\n└──────────────────────────────────────────────────────────────────────┘\n\nWhat You Must NEVER Do\n// ❌ WRONG - manual URL construction\nlet url = URL(string: \"http://localhost:8080/my_request?query=value\")!\nlet response = try await URLSession.shared.data(from: url)\n\n// ❌ WRONG - string path with method\ntry await app.test(.GET, \"/my_request\") { response in }\n\n// ❌ WRONG - manual JSON encoding/decoding\nlet json = try JSONEncoder().encode(requestBody)\nlet decoded = try JSONDecoder().decode(ResponseBody.self, from: data)\n\n// ❌ WRONG - constructing TestingHTTPRequest manually\nlet httpRequest = TestingHTTPRequest(method: .GET, url: \"/path\", headers: headers)\ntry await app.testing().performTest(request: httpRequest)\n\nWhat You Must ALWAYS Do\n// ✅ RIGHT - Use TestingApplicationTester.test() with ServerRequest\nlet request = MyShowRequest(query: .init(userId: userId))\ntry await app.testing().test(request, locale: en) { response in\n    #expect(response.status == .ok)\n    #expect(response.body?.viewModel.name == \"Expected Name\")\n}\n\n// ✅ RIGHT - Test multiple locales\nfor locale in [en, es] {\n    try await app.testing().test(request, locale: locale) { response in\n        #expect(response.status == .ok)\n        // Localized values are automatically handled\n    }\n}\n\n// ✅ RIGHT - Test error responses\nlet badRequest = MyShowRequest(query: .init(userId: invalidId))\ntry await app.testing().test(badRequest, locale: en) { response in\n    #expect(response.status == .notFound)\n    #expect(response.error != nil)\n}\n\n\nThe 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.\n\nWhen to Use This Skill\nTesting any ServerRequest implementation\nVerifying server responses for CRUD operations\nTesting error handling and edge cases\nMulti-locale response verification\nIntegration testing between client request types and server controllers\n\nIf you're about to write URLSession, app.test(.GET, \"/path\"), or manual JSON decoding, STOP and use this skill instead.\n\nWhat This Skill Generates\nFile\tLocation\tPurpose\n{Feature}RequestTests.swift\tTests/{Target}Tests/Requests/\tTest suite for ServerRequest\nTest YAML (if needed)\tTests/{Target}Tests/TestYAML/\tLocalization for test ViewModels\nProject Structure Configuration\nPlaceholder\tDescription\tExample\n{Feature}\tFeature or entity name (PascalCase)\tIdea, User, Dashboard\n{Target}\tServer test target\tWebServerTests, AppTests\n{ViewModelsTarget}\tShared ViewModels SPM target\tViewModels\n{WebServerTarget}\tServer-side target\tWebServer, AppServer\n{ResourceDir}\tYAML resource directory\tTestYAML, Resources\nKey Types\nTestingServerRequestResponse<R>\n\nWraps HTTP response with typed access:\n\nProperty\tType\tDescription\nstatus\tHTTPStatus\tHTTP status code (.ok, .notFound, etc.)\nheaders\tHTTPHeaders\tResponse headers\nbody\tR.ResponseBody?\tTyped response body (auto-decoded)\nerror\tR.ResponseError?\tTyped error (auto-decoded)\nTestingApplicationTester Extension\nfunc test<R: ServerRequest>(\n    _ request: R,\n    locale: Locale = en,\n    headers: HTTPHeaders = [:],\n    afterResponse: (TestingServerRequestResponse<R>) async throws -> Void\n) async throws -> any TestingApplicationTester\n\nConvenience Locales\n\nAvailable on TestingApplicationTester:\n\nen - English\nenUS - English (US)\nenGB - English (UK)\nes - Spanish\nTest Structure\nBasic Test Suite\nimport FOSFoundation\n@testable import FOSMVVM\nimport FOSTesting\nimport FOSTestingVapor\nimport Foundation\nimport Testing\nimport Vapor\nimport VaporTesting\n\n@Suite(\"MyFeature Request Tests\")\nstruct MyFeatureRequestTests {\n    @Test func showRequest_success() async throws {\n        try await withTestApp { app in\n            let request = MyShowRequest(query: .init(id: validId))\n\n            try await app.testing().test(request, locale: en) { response in\n                #expect(response.status == .ok)\n                #expect(response.body?.viewModel != nil)\n            }\n        }\n    }\n\n    @Test func showRequest_notFound() async throws {\n        try await withTestApp { app in\n            let request = MyShowRequest(query: .init(id: invalidId))\n\n            try await app.testing().test(request, locale: en) { response in\n                #expect(response.status == .notFound)\n            }\n        }\n    }\n}\n\nprivate func withTestApp(_ test: (Application) async throws -> Void) async throws {\n    try await withApp { app in\n        // Configure routes\n        try app.routes.register(collection: MyController())\n        try await test(app)\n    }\n}\n\nTesting Different Request Types\nRequest Type\tHTTP Method\tWhat to Test\nShowRequest\tGET\tQuery params, response body, localization\nViewModelRequest\tGET\tViewModel population, all localized fields\nCreateRequest\tPOST\tRequestBody validation, created entity, ID response\nUpdateRequest\tPATCH\tRequestBody validation, updated entity, response\nDeleteRequest\tDELETE\tEntity removal, status code\nHow to Use This Skill\n\nInvocation: /fosmvvm-serverrequest-test-generator\n\nPrerequisites:\n\nServerRequest type understood from conversation context\nTest scenarios identified (success paths, error paths, validation)\nController implementation exists or is being created\nVaporTesting infrastructure understood\n\nWorkflow 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.\n\nPattern Implementation\n\nThis skill references conversation context to determine test structure:\n\nRequest Analysis\n\nFrom conversation context, the skill identifies:\n\nServerRequest type (from prior discussion or server implementation)\nRequest protocol (ShowRequest, CreateRequest, UpdateRequest, etc.)\nResponseBody type (ViewModel or simple structure)\nResponseError type (custom errors or EmptyError)\nTest Scenario Planning\n\nBased on operation semantics:\n\nSuccess paths (valid input, expected output)\nError paths (not found, validation failure, business logic errors)\nLocalization (if ResponseBody has localized fields)\nMulti-locale (testing across supported locales)\nInfrastructure Detection\n\nFrom project state:\n\nExisting test patterns (similar test files in codebase)\nLocalization setup (YAML fixtures needed)\nDatabase requirements (seed data for tests)\nTest File Generation\nTest suite conforming to VaporTesting patterns\nOne @Test function per scenario\nwithTestApp helper for application setup\nRoute registration\nRequest invocations using app.testing().test()\nContext Sources\n\nSkill references information from:\n\nPrior conversation: Test requirements, scenarios discussed\nServerRequest: If Claude has read ServerRequest code into context\nController: From server implementation\nExisting tests: From codebase analysis of similar test files\nCommon Scenarios\nTesting ViewModelRequest with Localization\n@Test func viewModelRequest_multiLocale() async throws {\n    try await withTestApp { app in\n        let request = DashboardViewModelRequest()\n\n        // Test English\n        try await app.testing().test(request, locale: en) { response in\n            #expect(response.status == .ok)\n            let vm = try #require(response.body)\n            #expect(try vm.pageTitle.localizedString == \"Dashboard\")\n        }\n\n        // Test Spanish\n        try await app.testing().test(request, locale: es) { response in\n            #expect(response.status == .ok)\n            let vm = try #require(response.body)\n            #expect(try vm.pageTitle.localizedString == \"Tablero\")\n        }\n    }\n}\n\nTesting CreateRequest with Validation\n@Test func createRequest_validInput() async throws {\n    try await withTestApp { app in\n        let request = CreateIdeaRequest(requestBody: .init(\n            content: \"Valid idea content\"\n        ))\n\n        try await app.testing().test(request, locale: en) { response in\n            #expect(response.status == .ok)\n            #expect(response.body?.id != nil)\n        }\n    }\n}\n\n@Test func createRequest_invalidInput() async throws {\n    try await withTestApp { app in\n        let request = CreateIdeaRequest(requestBody: .init(\n            content: \"\"  // Empty content should fail validation\n        ))\n\n        try await app.testing().test(request, locale: en) { response in\n            #expect(response.status == .badRequest)\n            #expect(response.error != nil)\n        }\n    }\n}\n\nTesting UpdateRequest\n@Test func updateRequest_success() async throws {\n    try await withTestApp { app in\n        // First create an entity\n        let createRequest = CreateIdeaRequest(requestBody: .init(content: \"Original\"))\n        var createdId: ModelIdType?\n        try await app.testing().test(createRequest, locale: en) { response in\n            createdId = response.body?.id\n        }\n\n        // Then update it\n        let updateRequest = UpdateIdeaRequest(requestBody: .init(\n            ideaId: try #require(createdId),\n            content: \"Updated content\"\n        ))\n\n        try await app.testing().test(updateRequest, locale: en) { response in\n            #expect(response.status == .ok)\n            #expect(response.body?.viewModel.content == \"Updated content\")\n        }\n    }\n}\n\nTesting DeleteRequest\n@Test func deleteRequest_success() async throws {\n    try await withTestApp { app in\n        // Create, then delete\n        let deleteRequest = DeleteIdeaRequest(requestBody: .init(ideaId: existingId))\n\n        try await app.testing().test(deleteRequest, locale: en) { response in\n            #expect(response.status == .ok)\n        }\n\n        // Verify deleted (should return not found)\n        let showRequest = ShowIdeaRequest(query: .init(ideaId: existingId))\n        try await app.testing().test(showRequest, locale: en) { response in\n            #expect(response.status == .notFound)\n        }\n    }\n}\n\nTesting ShowRequest with Query Parameters\n@Test func showRequest_withQuery() async throws {\n    try await withTestApp { app in\n        let request = UserShowRequest(query: .init(\n            userId: userId,\n            includeDetails: true\n        ))\n\n        try await app.testing().test(request, locale: en) { response in\n            #expect(response.status == .ok)\n            #expect(response.body?.user.details != nil)\n        }\n    }\n}\n\nTesting ServerRequestError Localizations\nWhy Error Localization Testing is Different\n\nUnlike ViewModels, ServerRequestError types:\n\nAre often enums, not structs\nDo not conform to Stubbable or RetrievablePropertyNames\nCannot use expectTranslations(ErrorType.self) like ViewModels\n\nThis means you must manually test each error case individually.\n\nThe Pattern\n\nUse LocalizableTestCase.expectTranslations(_ localizable:) on each error's Localizable property:\n\n@Suite(\"MyError Localization Tests\")\nstruct MyErrorLocalizationTests: LocalizableTestCase {\n    let locStore: LocalizationStore\n\n    init() throws {\n        self.locStore = try Self.loadLocalizationStore(\n            bundle: Bundle.module,\n            resourceDirectoryName: \"TestYAML\"\n        )\n    }\n\n    @Test func errorMessages_simpleErrors() throws {\n        // Test each error case individually\n        let serverFailed = MyError(code: .serverFailed)\n        try expectTranslations(serverFailed.message)\n\n        let appFailed = MyError(code: .applicationFailed)\n        try expectTranslations(appFailed.message)\n    }\n\n    @Test func errorMessages_withSubstitutions() throws {\n        // For errors with associated values, test with representative values\n        let quotaError = QuotaError(code: .quotaExceeded(requested: 100, maximum: 50))\n        try expectTranslations(quotaError.message)\n    }\n}\n\nTesting Error Messages in Integration Tests\n\nWhen testing the full request/response cycle, verify error messages resolve:\n\n@Test func createRequest_validationError_hasLocalizedMessage() async throws {\n    try await withTestApp { app in\n        let request = CreateIdeaRequest(requestBody: .init(content: \"\"))\n\n        try await app.testing().test(request, locale: en) { response in\n            #expect(response.status == .badRequest)\n            let error = try #require(response.error)\n\n            // Verify the message resolved (not empty or pending)\n            #expect(!error.message.isEmpty)\n\n            // Optionally verify specific text for English locale\n            #expect(try error.message.localizedString.contains(\"required\"))\n        }\n    }\n}\n\nWhy Not Stubbable?\n\nStubbable works well for ViewModels because:\n\nViewModels are structs with many properties\nA single stub() provides a complete test instance\n\nServerRequestError types are often enums where:\n\nEach case may have different associated values\nEach case may have a different localized message\nA single stub() can't cover all cases\n\nYou must enumerate and test each error case explicitly.\n\nChecklist for Error Localization Tests\n Test each enum case for simple errors\n Test representative associated values for parameterized errors\n Verify messages resolve (not empty) for all configured locales\n Verify substitution placeholders are replaced in LocalizableSubstitutions\nTroubleshooting\n\"Route not found\" Error\n\nCause: Controller not registered in test app.\n\nFix: Register the controller before testing:\n\ntry app.routes.register(collection: MyController())\n\nResponse body is nil but status is .ok\n\nCause: JSON decoding failed silently.\n\nFix: Check that ResponseBody type matches server response exactly. Use response.headers to verify Content-Type.\n\nLocalization not applied\n\nCause: Locale not passed to encoder.\n\nFix: The test(_:locale:) method handles this automatically. Ensure you're passing the locale parameter.\n\n\"Missing Translation\" in Response\n\nCause: YAML localization not loaded.\n\nFix: Initialize localization store in test app setup:\n\ntry app.initYamlLocalization(\n    bundle: Bundle.module,\n    resourceDirectoryName: \"TestYAML\"\n)\n\nNaming Conventions\nConcept\tConvention\tExample\nTest suite\t{Feature}RequestTests\tIdeaRequestTests\nTest file\t{Feature}RequestTests.swift\tIdeaRequestTests.swift\nTest method (success)\t{action}Request_success\tshowRequest_success\nTest method (error)\t{action}Request_{errorCase}\tshowRequest_notFound\nTest method (validation)\t{action}Request_{validationCase}\tcreateRequest_emptyContent\nTest helper\twithTestApp\twithTestApp { app in }\nLocale constant\ten, es, enUS, enGB\tlocale: en\nFile Templates\n\nSee reference.md for complete file templates.\n\nSee Also\nFOSMVVMArchitecture.md - Full architecture\nfosmvvm-serverrequest-generator - Creating ServerRequest types\nfosmvvm-viewmodel-test-generator - Testing ViewModels (localization only)\nreference.md - Complete file templates\nVersion History\nVersion\tDate\tChanges\n1.1\t2025-01-20\tAdd ServerRequestError localization testing guidance\n1.2\t2026-01-24\tUpdate to context-aware approach (remove file-parsing/Q&A). Skill references conversation context instead of asking questions or accepting file paths.\n1.0\t2025-01-05\tInitial skill"
  },
  "trust": {
    "sourceLabel": "tencent",
    "provenanceUrl": "https://clawhub.ai/foscomputerservices/fosmvvm-serverrequest-test-generator",
    "publisherUrl": "https://clawhub.ai/foscomputerservices/fosmvvm-serverrequest-test-generator",
    "owner": "foscomputerservices",
    "version": "2.0.6",
    "license": null,
    "verificationStatus": "Indexed source record"
  },
  "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"
  }
}