223 lines · 8.5 KB · TypeScript
RawDownload
import { describe, expect, it } from "@effect/vitest";import { Effect } from "effect";import { FetchHttpClient } from "effect/unstable/http"; import { createExecutor } from "../packages/core/sdk/src/index";import { makeTestConfig } from "../packages/core/sdk/src/testing";import { openApiPlugin } from "../packages/plugins/openapi/src/sdk/plugin";import { parse, resolveSpecText } from "../packages/plugins/openapi/src/sdk/parse";import {  convertGoogleDiscoveryToOpenApi,  fetchGoogleDiscoveryDocument,  isGoogleDiscoveryUrl,} from "../packages/plugins/openapi/src/providers/google/discovery";import { mcpPlugin } from "../packages/plugins/mcp/src/sdk/plugin";import { graphqlPlugin } from "../packages/plugins/graphql/src/sdk/plugin";import { introspect } from "../packages/plugins/graphql/src/sdk/introspect"; import { openApiPresets } from "../packages/plugins/openapi/src/sdk/presets";import { mcpPresets } from "../packages/plugins/mcp/src/sdk/presets";import { graphqlPresets } from "../packages/plugins/graphql/src/sdk/presets"; // ---------------------------------------------------------------------------// All presets with plugin metadata// --------------------------------------------------------------------------- const allPresets = [  ...openApiPresets.map((p) => ({ ...p, plugin: "openapi" as const })),  ...mcpPresets.map((p) => ({ ...p, plugin: "mcp" as const })),  ...graphqlPresets.map((p) => ({ ...p, plugin: "graphql" as const })),]; // ---------------------------------------------------------------------------// OpenAPI presets — parse the spec through the SDK// --------------------------------------------------------------------------- describe("openapi presets parse as valid specs", () => {  for (const preset of openApiPresets) {    it.effect(      preset.name,      () =>        Effect.gen(function* () {          const specText = isGoogleDiscoveryUrl(preset.url)            ? yield* fetchGoogleDiscoveryDocument(preset.url).pipe(                Effect.provide(FetchHttpClient.layer),                Effect.flatMap((documentText) =>                  convertGoogleDiscoveryToOpenApi({                    discoveryUrl: preset.url,                    documentText,                  }),                ),                Effect.map((conversion) => conversion.specText),              )            : yield* resolveSpecText(preset.url).pipe(Effect.provide(FetchHttpClient.layer));          const doc = yield* parse(specText);          expect(doc).toBeDefined();          expect(doc.openapi).toBeDefined();        }),      { timeout: 30_000 },    );  }}); // ---------------------------------------------------------------------------// GraphQL presets — introspect the endpoint (auth-required = 401 is ok)// --------------------------------------------------------------------------- describe("graphql presets are reachable endpoints", () => {  for (const preset of graphqlPresets) {    it.effect(      preset.name,      () =>        Effect.gen(function* () {          const result = yield* introspect(preset.url).pipe(            Effect.provide(FetchHttpClient.layer),            Effect.map((r) => ({ ok: true as const, schema: r })),            Effect.catchTag("GraphqlIntrospectionError", (err) =>              Effect.succeed({                ok: false as const,                // oxlint-disable-next-line executor/no-unknown-error-message -- boundary: catchTag narrows to GraphqlIntrospectionError whose public contract includes message                message: err.message,              }),            ),          );           const authFailureMessage = result.ok ? null : result.message;          let isReachable =            result.ok || /401|403|Unauthorized|Forbidden|auth/i.test(authFailureMessage ?? "");          let failureDetails = authFailureMessage ?? "";          if (!isReachable) {            const response = yield* Effect.tryPromise(() =>              fetch(preset.url, {                method: "POST",                signal: AbortSignal.timeout(10_000),                headers: { "Content-Type": "application/json" },                body: JSON.stringify({ query: "{ __typename }" }),                redirect: "follow",              }),            );            isReachable = response.ok;            failureDetails = `${failureDetails}; probe returned ${response.status}`;          }           expect(            isReachable,            `${preset.name} should expose introspection, fail with auth, or answer a GraphQL probe; ${failureDetails}`,          ).toBe(true);          if (!result.ok) return;          expect(result.schema.__schema).toBeDefined();          expect(result.schema.__schema.types.length).toBeGreaterThan(0);        }),      { timeout: 15_000 },    );  }}); // ---------------------------------------------------------------------------// MCP presets — probe the endpoint (POST to verify it's alive)// --------------------------------------------------------------------------- const remoteMcpPresets = mcpPresets.filter((p) => !("transport" in p && p.transport === "stdio")); describe("mcp presets are reachable endpoints", () => {  for (const preset of remoteMcpPresets) {    it.effect(      preset.name,      () =>        Effect.gen(function* () {          // Simple POST probe — MCP endpoints reject malformed requests          // but return non-404 status codes proving the service is up          const response = yield* Effect.tryPromise(() =>            fetch(preset.url, {              method: "POST",              signal: AbortSignal.timeout(10_000),              headers: { "Content-Type": "application/json" },              body: "{}",              redirect: "follow",            }),          );           expect(            response.status !== 404 && response.status !== 502 && response.status !== 503,            `${preset.name} returned ${response.status} — endpoint appears down`,          ).toBe(true);        }),      { timeout: 15_000 },    );  }}); // ---------------------------------------------------------------------------// Detection — full executor pipeline, only for presets that don't need auth// --------------------------------------------------------------------------- const publicPresets = allPresets.filter(  (p) =>    // Skip auth-required endpoints that won't pass detection without credentials    !["github-graphql", "linear", "monday", "stripe"].includes(p.id) &&    // Skip stdio presets (not HTTP-reachable)    !("transport" in p && (p as Record<string, unknown>).transport === "stdio") &&    // Skip endpoints where detection is flaky due to timeout or misdetection    // (these are detect() implementation issues, not preset issues)    !["digitalocean", "firecrawl", "gitlab", "openai"].includes(p.id),); describe("public preset URLs are detected by the correct plugin", () => {  const makeExecutor = () =>    createExecutor(      makeTestConfig({        plugins: [openApiPlugin(), mcpPlugin(), graphqlPlugin()] as const,      }),    );   for (const preset of publicPresets) {    it.effect(      `[${preset.plugin}] ${preset.name}`,      () =>        Effect.gen(function* () {          const executor = yield* makeExecutor();          const results = yield* executor.integrations.detect(preset.url);           expect(            results.length,            `No detection results for ${preset.name} (${preset.url})`,          ).toBeGreaterThan(0);           const expectedKinds: Record<string, string> = {            openapi: "openapi",            mcp: "mcp",            graphql: "graphql",          };          const best = results[0]!;          expect(best.kind).toBe(expectedKinds[preset.plugin]);        }),      { timeout: 30_000 },    );  }}); // ---------------------------------------------------------------------------// Icons// --------------------------------------------------------------------------- describe("preset icons are reachable", () => {  const presetsWithIcons = allPresets.filter((p) => p.icon);  for (const preset of presetsWithIcons) {    it.effect(      `[${preset.plugin}] ${preset.name} icon`,      () =>        Effect.gen(function* () {          const response = yield* Effect.tryPromise(() =>            fetch(preset.icon!, {              method: "GET",              signal: AbortSignal.timeout(10_000),              headers: { "User-Agent": "executor-preset-test" },              redirect: "follow",            }),          );          expect(response.ok, `${preset.name} icon returned ${response.status}`).toBe(true);        }),      { timeout: 15_000 },    );  }});