// ---------------------------------------------------------------------------// OpenAPI plugin — `configure` (add custom auth method) coverage.//// `configure` appends an `APIKeyAuthentication` template to an existing// integration's opaque config. These tests exercise the extension method// directly (the same path the HTTP `configure` handler calls):// - round-trip: add a custom apiKey method → `getConfig` shows it with the// correct header/query placements,// - slug generation: a method with no slug is assigned `custom_<id>`,// - slug uniqueness/dedupe: a method whose slug matches an existing entry// replaces it rather than duplicating, and two slug-less methods in one call// get distinct generated slugs,// - the merged template renders against a connection value at invoke time.// --------------------------------------------------------------------------- import { describe, expect, it } from "@effect/vitest";import { Effect, Schema } from "effect";import { FetchHttpClient, HttpServerRequest } from "effect/unstable/http";import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup } from "effect/unstable/httpapi"; import { createExecutor, AuthTemplateSlug, ConnectionName, IntegrationSlug, ToolAddress,} from "@executor-js/sdk";import { makeTestConfig, memoryCredentialsPlugin } from "@executor-js/sdk/testing"; import { openApiPlugin } from "./plugin";import type { APIKeyAuthentication, Authentication, AuthenticationInput } from "./types";import { variable } from "@executor-js/sdk/http-auth";import { makeOpenApiHttpApiTestSourceConfig, serveOpenApiHttpApiTestServer, unwrapInvocation,} from "../testing"; const testPlugins = (httpClientLayer = FetchHttpClient.layer) => [openApiPlugin({ httpClientLayer }), memoryCredentialsPlugin()] as const; // A tiny echo-headers spec so a rendered template can be observed end-to-end.const EchoHeaders = Schema.Struct({ authorization: Schema.optional(Schema.String), "x-api-key": Schema.optional(Schema.String),}); const EchoGroup = HttpApiGroup.make("items").add( HttpApiEndpoint.get("echoHeaders", "/echo-headers", { success: EchoHeaders }),);const TestApi = HttpApi.make("testApi").add(EchoGroup); const EchoGroupLive = HttpApiBuilder.group(TestApi, "items", (handlers) => handlers.handle("echoHeaders", () => Effect.gen(function* () { const req = yield* HttpServerRequest.HttpServerRequest; return EchoHeaders.make({ authorization: req.headers["authorization"], "x-api-key": req.headers["x-api-key"], }); }), ),); const servePluginTestApi = () => serveOpenApiHttpApiTestServer({ api: TestApi, handlersLayer: EchoGroupLive }); const specText = () => { const spec = makeOpenApiHttpApiTestSourceConfig(TestApi, {}).spec; if (spec.kind === "blob") return spec.value; if (spec.kind === "googleDiscoveryBundle") return spec.urls[0] ?? ""; return spec.url;}; // A custom apiKey method that places the connection value into `x-api-key`,// authored request-shaped (the one apikey input dialect).const customApiKey: AuthenticationInput = { slug: "my_custom", type: "apiKey", headers: { "x-api-key": [variable("token")] },}; // Build an apiKey method WITHOUT a slug — simulating an untyped JSON caller that// omits it, which `configure` should backfill with a generated `custom_<id>`.// The cast is confined to this one boundary helper.const sluglessApiKey = ( template: Omit<AuthenticationInput & { type: "apiKey" }, "slug" | "type">,): AuthenticationInput => ({ type: "apiKey", ...template }); describe("OpenAPI Plugin — configure (custom auth method)", () => { it.effect("adds a custom apiKey method and getConfig reflects it", () => Effect.scoped( Effect.gen(function* () { const executor = yield* createExecutor(makeTestConfig({ plugins: testPlugins() })); yield* executor.openapi.addSpec({ spec: { kind: "blob", value: specText() }, slug: "cfg_api", }); const merged = yield* executor.openapi.configure("cfg_api", { authenticationTemplate: [customApiKey], }); expect(merged).toHaveLength(1); expect(String(merged[0]!.slug)).toBe("my_custom"); const config = yield* executor.openapi.getConfig("cfg_api"); const template = (config?.authenticationTemplate ?? []) as readonly Authentication[]; expect(template).toHaveLength(1); const entry = template[0] as APIKeyAuthentication; expect(entry.kind).toBe("apikey"); expect(String(entry.slug)).toBe("my_custom"); // The placement preserves the canonical `token` input (stored absent). expect(entry.placements).toEqual([{ carrier: "header", name: "x-api-key" }]); }), ), ); it.effect("appends to an existing spec-derived template without dropping entries", () => Effect.scoped( Effect.gen(function* () { const executor = yield* createExecutor(makeTestConfig({ plugins: testPlugins() })); const seedTemplate: AuthenticationInput = { slug: "seed", type: "apiKey", headers: { authorization: ["Bearer ", variable("token")] }, }; yield* executor.openapi.addSpec({ spec: { kind: "blob", value: specText() }, slug: "cfg_append", authenticationTemplate: [seedTemplate], }); const merged = yield* executor.openapi.configure("cfg_append", { authenticationTemplate: [customApiKey], }); expect(merged.map((m: Authentication) => String(m.slug))).toEqual(["seed", "my_custom"]); }), ), ); it.effect("generates a custom_<id> slug for a method submitted without one", () => Effect.scoped( Effect.gen(function* () { const executor = yield* createExecutor(makeTestConfig({ plugins: testPlugins() })); yield* executor.openapi.addSpec({ spec: { kind: "blob", value: specText() }, slug: "cfg_genslug", }); const slugless = sluglessApiKey({ headers: { "x-api-key": [variable("token")] } }); const merged = yield* executor.openapi.configure("cfg_genslug", { authenticationTemplate: [slugless], }); expect(merged).toHaveLength(1); expect(String(merged[0]!.slug)).toMatch(/^custom_[a-z0-9]+$/); }), ), ); it.effect("dedupes: a matching slug replaces in place; two slugless get distinct slugs", () => Effect.scoped( Effect.gen(function* () { const executor = yield* createExecutor(makeTestConfig({ plugins: testPlugins() })); yield* executor.openapi.addSpec({ spec: { kind: "blob", value: specText() }, slug: "cfg_dedupe", authenticationTemplate: [customApiKey], }); // Re-submit the same slug with a different placement → replace in place. const replacement: AuthenticationInput = { slug: "my_custom", type: "apiKey", headers: { "x-other": [variable("token")] }, }; const slugless = sluglessApiKey({ queryParams: { api_key: [variable("token")] } }); const slugless2 = sluglessApiKey({ queryParams: { token: [variable("token")] } }); const merged = yield* executor.openapi.configure("cfg_dedupe", { authenticationTemplate: [replacement, slugless, slugless2], }); // my_custom replaced (still one), plus two generated → three total. const slugs = merged.map((m: Authentication) => String(m.slug)); expect(slugs.filter((s: string) => s === "my_custom")).toHaveLength(1); expect(merged).toHaveLength(3); const generated = slugs.filter((s: string) => s.startsWith("custom_")); expect(generated).toHaveLength(2); // Generated slugs are distinct. expect(new Set(generated).size).toBe(2); // The replacement took effect (new header placement). const replaced = merged.find( (m: Authentication) => String(m.slug) === "my_custom", ) as APIKeyAuthentication; expect(replaced.placements).toEqual([{ carrier: "header", name: "x-other" }]); }), ), ); it.effect("replace mode overwrites the auth template array", () => Effect.scoped( Effect.gen(function* () { const executor = yield* createExecutor(makeTestConfig({ plugins: testPlugins() })); const seedTemplate: AuthenticationInput = { slug: "seed", type: "apiKey", headers: { authorization: ["Bearer ", variable("token")] }, }; yield* executor.openapi.addSpec({ spec: { kind: "blob", value: specText() }, slug: "cfg_replace", authenticationTemplate: [seedTemplate, customApiKey], }); const merged = yield* executor.openapi.configure("cfg_replace", { authenticationTemplate: [seedTemplate], mode: "replace", }); expect(merged.map((m: Authentication) => String(m.slug))).toEqual(["seed"]); }), ), ); it.effect("a configured custom method renders against a connection value at invoke time", () => Effect.scoped( Effect.gen(function* () { const server = yield* servePluginTestApi(); const executor = yield* createExecutor(makeTestConfig({ plugins: testPlugins() })); yield* executor.openapi.addSpec({ spec: { kind: "blob", value: server.specJson }, slug: "cfg_invoke", baseUrl: server.baseUrl, }); yield* executor.openapi.configure("cfg_invoke", { authenticationTemplate: [customApiKey], }); yield* executor.connections.create({ owner: "org", name: ConnectionName.make("main"), integration: IntegrationSlug.make("cfg_invoke"), template: AuthTemplateSlug.make("my_custom"), value: "configured-secret", }); const result = unwrapInvocation( yield* executor.execute( ToolAddress.make("tools.cfg_invoke.org.main.items.echoHeaders"), {}, ), ).data as { "x-api-key"?: string }; expect(result["x-api-key"]).toBe("configured-secret"); }), ), ); it.effect("the request-shaped dialect configures a method and renders on the wire", () => Effect.scoped( Effect.gen(function* () { const server = yield* servePluginTestApi(); const executor = yield* createExecutor(makeTestConfig({ plugins: testPlugins() })); yield* executor.openapi.addSpec({ spec: { kind: "blob", value: server.specJson }, slug: "cfg_authored", baseUrl: server.baseUrl, }); // Authored request-shaped; stored canonical. yield* executor.openapi.configure("cfg_authored", { authenticationTemplate: [ { slug: "bearer", type: "apiKey", headers: { authorization: ["Bearer ", variable("token")] }, }, ], }); const config = yield* executor.openapi.getConfig("cfg_authored"); expect(config?.authenticationTemplate?.[0]).toEqual({ slug: "bearer", kind: "apikey", placements: [{ carrier: "header", name: "authorization", prefix: "Bearer " }], }); yield* executor.connections.create({ owner: "org", name: ConnectionName.make("main"), integration: IntegrationSlug.make("cfg_authored"), template: AuthTemplateSlug.make("bearer"), value: "authored-secret", }); const result = unwrapInvocation( yield* executor.execute( ToolAddress.make("tools.cfg_authored.org.main.items.echoHeaders"), {}, ), ).data as { authorization?: string }; expect(result.authorization).toBe("Bearer authored-secret"); }), ), ); it.effect("getConfig returns null for an unknown integration", () => Effect.scoped( Effect.gen(function* () { const executor = yield* createExecutor(makeTestConfig({ plugins: testPlugins() })); expect(yield* executor.openapi.getConfig("nope")).toBeNull(); }), ), );});
// ---------------------------------------------------------------------------
// OpenAPI plugin — `configure` (add custom auth method) coverage.
//
// `configure` appends an `APIKeyAuthentication` template to an existing
// integration's opaque config. These tests exercise the extension method
// directly (the same path the HTTP `configure` handler calls):
// - round-trip: add a custom apiKey method → `getConfig` shows it with the
// correct header/query placements,
// - slug generation: a method with no slug is assigned `custom_<id>`,
// - slug uniqueness/dedupe: a method whose slug matches an existing entry
// replaces it rather than duplicating, and two slug-less methods in one call
// get distinct generated slugs,
// - the merged template renders against a connection value at invoke time.
// ---------------------------------------------------------------------------
import { describe, expect, it } from "@effect/vitest";
import { Effect, Schema } from "effect";
import { FetchHttpClient, HttpServerRequest } from "effect/unstable/http";
import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup } from "effect/unstable/httpapi";
import {
createExecutor,
AuthTemplateSlug,
ConnectionName,
IntegrationSlug,
ToolAddress,
} from "@executor-js/sdk";
import { makeTestConfig, memoryCredentialsPlugin } from "@executor-js/sdk/testing";
import { openApiPlugin } from "./plugin";
import type { APIKeyAuthentication, Authentication, AuthenticationInput } from "./types";
import { variable } from "@executor-js/sdk/http-auth";
import {
makeOpenApiHttpApiTestSourceConfig,
serveOpenApiHttpApiTestServer,
unwrapInvocation,
} from "../testing";
const testPlugins = (httpClientLayer = FetchHttpClient.layer) =>
[openApiPlugin({ httpClientLayer }), memoryCredentialsPlugin()] as const;
// A tiny echo-headers spec so a rendered template can be observed end-to-end.
const EchoHeaders = Schema.Struct({
authorization: Schema.optional(Schema.String),
"x-api-key": Schema.optional(Schema.String),
});
const EchoGroup = HttpApiGroup.make("items").add(
HttpApiEndpoint.get("echoHeaders", "/echo-headers", { success: EchoHeaders }),
);
const TestApi = HttpApi.make("testApi").add(EchoGroup);
const EchoGroupLive = HttpApiBuilder.group(TestApi, "items", (handlers) =>
handlers.handle("echoHeaders", () =>
Effect.gen(function* () {
const req = yield* HttpServerRequest.HttpServerRequest;
return EchoHeaders.make({
authorization: req.headers["authorization"],
"x-api-key": req.headers["x-api-key"],
});
}),
),
);
const servePluginTestApi = () =>
serveOpenApiHttpApiTestServer({ api: TestApi, handlersLayer: EchoGroupLive });
const specText = () => {
const spec = makeOpenApiHttpApiTestSourceConfig(TestApi, {}).spec;
if (spec.kind === "blob") return spec.value;
if (spec.kind === "googleDiscoveryBundle") return spec.urls[0] ?? "";
return spec.url;
};
// A custom apiKey method that places the connection value into `x-api-key`,
// authored request-shaped (the one apikey input dialect).
const customApiKey: AuthenticationInput = {
slug: "my_custom",
type: "apiKey",
headers: { "x-api-key": [variable("token")] },
};
// Build an apiKey method WITHOUT a slug — simulating an untyped JSON caller that
// omits it, which `configure` should backfill with a generated `custom_<id>`.
// The cast is confined to this one boundary helper.
const sluglessApiKey = (
template: Omit<AuthenticationInput & { type: "apiKey" }, "slug" | "type">,
): AuthenticationInput => ({ type: "apiKey", ...template });
describe("OpenAPI Plugin — configure (custom auth method)", () => {
it.effect("adds a custom apiKey method and getConfig reflects it", () =>
Effect.scoped(
Effect.gen(function* () {
const executor = yield* createExecutor(makeTestConfig({ plugins: testPlugins() }));
yield* executor.openapi.addSpec({
spec: { kind: "blob", value: specText() },
slug: "cfg_api",
});
const merged = yield* executor.openapi.configure("cfg_api", {
authenticationTemplate: [customApiKey],
});
expect(merged).toHaveLength(1);
expect(String(merged[0]!.slug)).toBe("my_custom");
const config = yield* executor.openapi.getConfig("cfg_api");
const template = (config?.authenticationTemplate ?? []) as readonly Authentication[];
expect(template).toHaveLength(1);
const entry = template[0] as APIKeyAuthentication;
expect(entry.kind).toBe("apikey");
expect(String(entry.slug)).toBe("my_custom");
// The placement preserves the canonical `token` input (stored absent).
expect(entry.placements).toEqual([{ carrier: "header", name: "x-api-key" }]);
}),
),
);
it.effect("appends to an existing spec-derived template without dropping entries", () =>
Effect.scoped(
Effect.gen(function* () {
const executor = yield* createExecutor(makeTestConfig({ plugins: testPlugins() }));
const seedTemplate: AuthenticationInput = {
slug: "seed",
type: "apiKey",
headers: { authorization: ["Bearer ", variable("token")] },
};
yield* executor.openapi.addSpec({
spec: { kind: "blob", value: specText() },
slug: "cfg_append",
authenticationTemplate: [seedTemplate],
});
const merged = yield* executor.openapi.configure("cfg_append", {
authenticationTemplate: [customApiKey],
});
expect(merged.map((m: Authentication) => String(m.slug))).toEqual(["seed", "my_custom"]);
}),
),
);
it.effect("generates a custom_<id> slug for a method submitted without one", () =>
Effect.scoped(
Effect.gen(function* () {
const executor = yield* createExecutor(makeTestConfig({ plugins: testPlugins() }));
yield* executor.openapi.addSpec({
spec: { kind: "blob", value: specText() },
slug: "cfg_genslug",
});
const slugless = sluglessApiKey({ headers: { "x-api-key": [variable("token")] } });
const merged = yield* executor.openapi.configure("cfg_genslug", {
authenticationTemplate: [slugless],
});
expect(merged).toHaveLength(1);
expect(String(merged[0]!.slug)).toMatch(/^custom_[a-z0-9]+$/);
}),
),
);
it.effect("dedupes: a matching slug replaces in place; two slugless get distinct slugs", () =>
Effect.scoped(
Effect.gen(function* () {
const executor = yield* createExecutor(makeTestConfig({ plugins: testPlugins() }));
yield* executor.openapi.addSpec({
spec: { kind: "blob", value: specText() },
slug: "cfg_dedupe",
authenticationTemplate: [customApiKey],
});
// Re-submit the same slug with a different placement → replace in place.
const replacement: AuthenticationInput = {
slug: "my_custom",
type: "apiKey",
headers: { "x-other": [variable("token")] },
};
const slugless = sluglessApiKey({ queryParams: { api_key: [variable("token")] } });
const slugless2 = sluglessApiKey({ queryParams: { token: [variable("token")] } });
const merged = yield* executor.openapi.configure("cfg_dedupe", {
authenticationTemplate: [replacement, slugless, slugless2],
});
// my_custom replaced (still one), plus two generated → three total.
const slugs = merged.map((m: Authentication) => String(m.slug));
expect(slugs.filter((s: string) => s === "my_custom")).toHaveLength(1);
expect(merged).toHaveLength(3);
const generated = slugs.filter((s: string) => s.startsWith("custom_"));
expect(generated).toHaveLength(2);
// Generated slugs are distinct.
expect(new Set(generated).size).toBe(2);
// The replacement took effect (new header placement).
const replaced = merged.find(
(m: Authentication) => String(m.slug) === "my_custom",
) as APIKeyAuthentication;
expect(replaced.placements).toEqual([{ carrier: "header", name: "x-other" }]);
}),
),
);
it.effect("replace mode overwrites the auth template array", () =>
Effect.scoped(
Effect.gen(function* () {
const executor = yield* createExecutor(makeTestConfig({ plugins: testPlugins() }));
const seedTemplate: AuthenticationInput = {
slug: "seed",
type: "apiKey",
headers: { authorization: ["Bearer ", variable("token")] },
};
yield* executor.openapi.addSpec({
spec: { kind: "blob", value: specText() },
slug: "cfg_replace",
authenticationTemplate: [seedTemplate, customApiKey],
});
const merged = yield* executor.openapi.configure("cfg_replace", {
authenticationTemplate: [seedTemplate],
mode: "replace",
});
expect(merged.map((m: Authentication) => String(m.slug))).toEqual(["seed"]);
}),
),
);
it.effect("a configured custom method renders against a connection value at invoke time", () =>
Effect.scoped(
Effect.gen(function* () {
const server = yield* servePluginTestApi();
const executor = yield* createExecutor(makeTestConfig({ plugins: testPlugins() }));
yield* executor.openapi.addSpec({
spec: { kind: "blob", value: server.specJson },
slug: "cfg_invoke",
baseUrl: server.baseUrl,
});
yield* executor.openapi.configure("cfg_invoke", {
authenticationTemplate: [customApiKey],
});
yield* executor.connections.create({
owner: "org",
name: ConnectionName.make("main"),
integration: IntegrationSlug.make("cfg_invoke"),
template: AuthTemplateSlug.make("my_custom"),
value: "configured-secret",
});
const result = unwrapInvocation(
yield* executor.execute(
ToolAddress.make("tools.cfg_invoke.org.main.items.echoHeaders"),
{},
),
).data as { "x-api-key"?: string };
expect(result["x-api-key"]).toBe("configured-secret");
}),
),
);
it.effect("the request-shaped dialect configures a method and renders on the wire", () =>
Effect.scoped(
Effect.gen(function* () {
const server = yield* servePluginTestApi();
const executor = yield* createExecutor(makeTestConfig({ plugins: testPlugins() }));
yield* executor.openapi.addSpec({
spec: { kind: "blob", value: server.specJson },
slug: "cfg_authored",
baseUrl: server.baseUrl,
});
// Authored request-shaped; stored canonical.
yield* executor.openapi.configure("cfg_authored", {
authenticationTemplate: [
{
slug: "bearer",
type: "apiKey",
headers: { authorization: ["Bearer ", variable("token")] },
},
],
});
const config = yield* executor.openapi.getConfig("cfg_authored");
expect(config?.authenticationTemplate?.[0]).toEqual({
slug: "bearer",
kind: "apikey",
placements: [{ carrier: "header", name: "authorization", prefix: "Bearer " }],
});
yield* executor.connections.create({
owner: "org",
name: ConnectionName.make("main"),
integration: IntegrationSlug.make("cfg_authored"),
template: AuthTemplateSlug.make("bearer"),
value: "authored-secret",
});
const result = unwrapInvocation(
yield* executor.execute(
ToolAddress.make("tools.cfg_authored.org.main.items.echoHeaders"),
{},
),
).data as { authorization?: string };
expect(result.authorization).toBe("Bearer authored-secret");
}),
),
);
it.effect("getConfig returns null for an unknown integration", () =>
Effect.scoped(
Effect.gen(function* () {
const executor = yield* createExecutor(makeTestConfig({ plugins: testPlugins() }));
expect(yield* executor.openapi.getConfig("nope")).toBeNull();
}),
),
);
});