105 lines · 3.3 KB · TypeScript
RawDownload
// ---------------------------------------------------------------------------// Regression test for non-JSON request-body serialization.//// Before the fix, the invoke path only had two branches: JSON, or// `String(bodyValue)` with whatever content-type the spec declared. For an// object body that meant shipping the literal string `[object Object]` with// `Content-Type: application/x-www-form-urlencoded`.// --------------------------------------------------------------------------- import { expect, layer } from "@effect/vitest";import { Effect, Schema } from "effect";import { FetchHttpClient, HttpServerRequest, HttpServerResponse } from "effect/unstable/http";import {  HttpApi,  HttpApiBuilder,  HttpApiEndpoint,  HttpApiGroup,  HttpApiSchema,} from "effect/unstable/httpapi"; import { createExecutor } from "@executor-js/sdk";import {  makeTestWorkspaceLayer,  memoryCredentialsPlugin,  TestWorkspace,} from "@executor-js/sdk/testing";import {  addOpenApiTestConnection,  serveOpenApiHttpApiTestServer,} from "@executor-js/plugin-openapi/testing"; import { openApiPlugin } from "./plugin"; type Captured = {  contentType: string;  body: string;}; const FormPayload = Schema.Struct({  name: Schema.String,  email: Schema.String,}).pipe(HttpApiSchema.asFormUrlEncoded());const Ok = Schema.Struct({ ok: Schema.Boolean }); const FormsGroup = HttpApiGroup.make("forms").add(  HttpApiEndpoint.post("submit", "/submit", {    payload: FormPayload,    success: Ok,  }),); const FormApi = HttpApi.make("formTest").add(FormsGroup); const startEchoServer = () =>  Effect.gen(function* () {    const captured: Captured = { contentType: "", body: "" };    const FormsLive = HttpApiBuilder.group(FormApi, "forms", (handlers) =>      handlers.handleRaw("submit", () =>        Effect.gen(function* () {          const request = yield* HttpServerRequest.HttpServerRequest;          captured.contentType = request.headers["content-type"] ?? "";          captured.body = yield* request.text.pipe(Effect.catch(() => Effect.succeed("")));          return HttpServerResponse.jsonUnsafe({ ok: true });        }),      ),    );    const server = yield* serveOpenApiHttpApiTestServer({      api: FormApi,      handlersLayer: FormsLive,    });    return { server, captured };  }); const plugins = [  openApiPlugin({ httpClientLayer: FetchHttpClient.layer }),  memoryCredentialsPlugin(),] as const; layer(  makeTestWorkspaceLayer({    plugins,  }),  { timeout: "15 seconds" },)("OpenAPI non-JSON request body serialization", (it) => {  it.effect("form-urlencoded object body is properly encoded (no '[object Object]')", () =>    Effect.gen(function* () {      const { server, captured } = yield* startEchoServer();      const { config } = yield* TestWorkspace;      const executor = yield* createExecutor({ ...config, plugins });       const conn = yield* addOpenApiTestConnection(executor, server, { slug: "form" });       yield* executor.execute(conn.address("forms.submit"), {        body: { name: "Acme", email: "a@b.com" },      });       expect(captured.contentType).toBe("application/x-www-form-urlencoded");      expect(captured.body).not.toBe("[object Object]");       const parsed = new URLSearchParams(captured.body);      expect(parsed.get("name")).toBe("Acme");      expect(parsed.get("email")).toBe("a@b.com");    }),  );});