Requirements
- Target platform
- OpenClaw
- Install method
- Manual import
- Extraction
- Extract archive
- Prerequisites
- OpenClaw
- Primary doc
- SKILL.md
Generate ServerRequest test code using VaporTesting with typed request/response validation and automatic routing for CRUD operations.
Generate ServerRequest test code using VaporTesting with typed request/response validation and automatic routing for CRUD operations.
Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.
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.
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.
Generate test files for ServerRequest types using VaporTesting infrastructure.
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!) β β β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
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! β β β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// β 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)
// β 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.
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.
FileLocationPurpose{Feature}RequestTests.swiftTests/{Target}Tests/Requests/Test suite for ServerRequestTest YAML (if needed)Tests/{Target}Tests/TestYAML/Localization for test ViewModels
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
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)
func test<R: ServerRequest>( _ request: R, locale: Locale = en, headers: HTTPHeaders = [:], afterResponse: (TestingServerRequestResponse<R>) async throws -> Void ) async throws -> any TestingApplicationTester
Available on TestingApplicationTester: en - English enUS - English (US) enGB - English (UK) es - Spanish
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) } }
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
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.
This skill references conversation context to determine test structure:
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)
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)
From project state: Existing test patterns (similar test files in codebase) Localization setup (YAML fixtures needed) Database requirements (seed data for tests)
Test suite conforming to VaporTesting patterns One @Test function per scenario withTestApp helper for application setup Route registration Request invocations using app.testing().test()
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
@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") } } }
@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) } } }
@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") } } }
@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) } } }
@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) } } }
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.
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) } }
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")) } } }
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.
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
Cause: Controller not registered in test app. Fix: Register the controller before testing: try app.routes.register(collection: MyController())
Cause: JSON decoding failed silently. Fix: Check that ResponseBody type matches server response exactly. Use response.headers to verify Content-Type.
Cause: Locale not passed to encoder. Fix: The test(_:locale:) method handles this automatically. Ensure you're passing the locale parameter.
Cause: YAML localization not loaded. Fix: Initialize localization store in test app setup: try app.initYamlLocalization( bundle: Bundle.module, resourceDirectoryName: "TestYAML" )
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
See reference.md for complete file templates.
FOSMVVMArchitecture.md - Full architecture fosmvvm-serverrequest-generator - Creating ServerRequest types fosmvvm-viewmodel-test-generator - Testing ViewModels (localization only) reference.md - Complete file templates
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
Code helpers, APIs, CLIs, browser automation, testing, and developer operations.
Largest current source with strong distribution and engagement signals.