800 lines · 26.8 KB · TypeScript
RawDownload
// Source endpoints — CRUD through HttpApiClient. Complements tenant// isolation tests by exercising add → get → update → remove flows and// the error paths (remove non-existent, remove static, etc.) within a// single org. import { describe, expect, it } from "@effect/vitest";import { Effect, Result, Schema } from "effect";import { HttpApi, HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi";import { readFileSync } from "node:fs";import { resolve } from "node:path"; import { ScopeId, SecretId } from "@executor-js/sdk";import {  serveGraphqlTestServer,  makeGreetingGraphqlSchema,} from "@executor-js/plugin-graphql/testing";import { makeGreetingMcpServer, serveMcpServer } from "@executor-js/plugin-mcp/testing";import {  makeOpenApiHttpApiTestAddSpecPayload,  makeOpenApiHttpApiTestSpecPayload,  serveOpenApiEchoTestServer,} from "@executor-js/plugin-openapi/testing";import { secretsForCredentialTarget } from "@executor-js/react/plugins/secret-header-auth"; import { asOrg, asUser, testUserOrgScopeId } from "./__test-harness__/api-harness"; const isJsonObject = (value: unknown): value is Record<string, unknown> =>  typeof value === "object" && value !== null && !Array.isArray(value); const PingGroup = HttpApiGroup.make("default", { topLevel: true }).add(  HttpApiEndpoint.get("ping", "/ping", { success: Schema.Unknown }),); const MinimalSourceApi = HttpApi.make("sourcesApiTest")  .add(PingGroup)  .annotateMerge(OpenApi.annotations({ title: "Sources API Test", version: "1.0.0" })); const makeMinimalOpenApiSourcePayload = (  namespace: string,  options: Omit<Parameters<typeof makeOpenApiHttpApiTestAddSpecPayload>[1], "namespace"> = {},) =>  makeOpenApiHttpApiTestAddSpecPayload(MinimalSourceApi, {    namespace,    ...options,  }); const makeMinimalOpenApiPreviewPayload = () => makeOpenApiHttpApiTestSpecPayload(MinimalSourceApi); // The Cloudflare OpenAPI spec is the biggest real spec we care about:// 16MB, 2700+ operations, thousands of shared schemas. Exercising// addSpec end-to-end on it through the real Drizzle/FumaDB path is the// load-bearing check that any storage regression (per-row `createMany`,// accidental N+1 reads, transaction snapshots that copy too much) will show up// as a test failure instead of a prod incident.const CLOUDFLARE_SPEC_PATH = resolve(  __dirname,  "../../../../packages/plugins/openapi/fixtures/cloudflare.json",);const CLOUDFLARE_SPEC = readFileSync(CLOUDFLARE_SPEC_PATH, "utf-8"); describe("sources api (HTTP)", () => {  it.effect("addSpec → sources.list includes the new namespace", () =>    Effect.gen(function* () {      const org = `org_${crypto.randomUUID()}`;      const namespace = `ns_${crypto.randomUUID().replace(/-/g, "_")}`;       yield* asOrg(org, (client) =>        Effect.gen(function* () {          const result = yield* client.openapi.addSpec({            params: { scopeId: ScopeId.make(org) },            payload: makeMinimalOpenApiSourcePayload(namespace),          });          expect(result.namespace).toBe(namespace);          expect(result.toolCount).toBeGreaterThan(0);        }),      );       const sources = yield* asOrg(org, (client) =>        client.sources.list({ params: { scopeId: ScopeId.make(org) } }),      );      expect(sources.map((s) => s.id)).toContain(namespace);    }),  );   it.effect("openapi.getSource returns the stored source after addSpec", () =>    Effect.gen(function* () {      const org = `org_${crypto.randomUUID()}`;      const namespace = `ns_${crypto.randomUUID().replace(/-/g, "_")}`;       yield* asOrg(org, (client) =>        client.openapi.addSpec({          params: { scopeId: ScopeId.make(org) },          payload: makeMinimalOpenApiSourcePayload(namespace),        }),      );       const fetched = yield* asOrg(org, (client) =>        client.openapi.getSource({ params: { scopeId: ScopeId.make(org), namespace } }),      );      expect(fetched).not.toBeNull();      expect(fetched?.namespace).toBe(namespace);    }),  );   it.effect("openapi.previewSpec returns class-backed preview metadata over HTTP", () =>    Effect.gen(function* () {      const org = `org_${crypto.randomUUID()}`;      const preview = yield* asOrg(org, (client) =>        client.openapi.previewSpec({          params: { scopeId: ScopeId.make(org) },          payload: makeMinimalOpenApiPreviewPayload(),        }),      );       expect(preview).toMatchObject({        operationCount: 1,        operations: [          expect.objectContaining({            operationId: "ping",            method: "get",            path: "/ping",          }),        ],      });    }),  );   it.effect("openapi.addSpec accepts a public HTTP baseUrl in local mode", () =>    Effect.gen(function* () {      const org = `org_${crypto.randomUUID()}`;       const result = yield* asOrg(org, (client) =>        client.openapi.addSpec({          params: { scopeId: ScopeId.make(org) },          payload: makeMinimalOpenApiSourcePayload(`ns_${crypto.randomUUID().replace(/-/g, "_")}`, {            baseUrl: "http://example.com",          }),        }),      );       expect(result.toolCount).toBe(1);    }),  );   it.effect("added OpenAPI source can be listed, inspected, and invoked through execution", () =>    Effect.gen(function* () {      const server = yield* serveOpenApiEchoTestServer({        transformSpec: (spec) => ({          ...spec,          info: { title: "Invocable Source API", version: "1.0.0" },          paths: {            "/echo/{message}": isJsonObject(spec.paths) ? spec.paths["/echo/{message}"] : {},          },        }),      });      const org = `org_${crypto.randomUUID()}`;      const namespace = `ns_${crypto.randomUUID().replace(/-/g, "_")}`;      const scopeId = ScopeId.make(org);       const addResult = yield* asOrg(org, (client) =>        client.openapi.addSpec({          params: { scopeId },          payload: {            spec: { kind: "blob", value: server.specJson },            name: "Invocable Source API",            baseUrl: server.baseUrl,            namespace,          },        }),      );      expect(addResult).toEqual({ namespace, toolCount: 1 });       const fetched = yield* asOrg(org, (client) =>        client.openapi.getSource({ params: { scopeId, namespace } }),      );      expect(fetched).toMatchObject({        namespace,        name: "Invocable Source API",        config: { baseUrl: server.baseUrl },      });       const tools = yield* asOrg(org, (client) =>        client.sources.tools({ params: { scopeId, sourceId: namespace } }),      );      const toolId = `${namespace}.echo.echoMessage`;      expect(tools.map((tool) => tool.id)).toContain(toolId);       const execution = yield* asOrg(org, (client) =>        client.executions.execute({          payload: {            code: [              `const result = await tools.${namespace}.echo.echoMessage({ message: "hello", suffix: "world" });`,              "return result;",            ].join("\n"),          },        }),      );       expect(execution.status).toBe("completed");      if (execution.status !== "completed") return;      expect(execution.isError).toBe(false);      expect(execution.structured).toMatchObject({        status: "completed",        result: {          ok: true,          data: {            status: 200,            data: {              message: "hello",              suffix: "world",              path: "/echo/hello",            },          },        },        logs: [],      });      expect(yield* server.requests).toContainEqual(        expect.objectContaining({ path: "/echo/hello" }),      );    }),  );   it.effect("mcp.getSource returns a persisted source even when discovery failed", () =>    Effect.gen(function* () {      const org = `org_${crypto.randomUUID()}`;      const namespace = `mcp_${crypto.randomUUID().replace(/-/g, "_")}`;      const scopeId = ScopeId.make(org);       const addResult = yield* asOrg(org, (client) =>        client.mcp          .addSource({            params: { scopeId },            payload: {              transport: "remote",              name: "Broken MCP",              endpoint: "http://127.0.0.1:1/mcp",              remoteTransport: "auto",              namespace,            },          })          .pipe(Effect.result),      );      expect(Result.isFailure(addResult)).toBe(true);       const fetched = yield* asOrg(org, (client) =>        client.mcp.getSource({ params: { scopeId, namespace } }),      );      expect(fetched).toMatchObject({        namespace,        name: "Broken MCP",        config: {          transport: "remote",          endpoint: "http://127.0.0.1:1/mcp",          remoteTransport: "auto",        },      });       const tools = yield* asOrg(org, (client) =>        client.sources.tools({ params: { scopeId, sourceId: namespace } }),      );      expect(tools).toEqual([]);    }),  );   it.effect("added GraphQL source can be inspected and invoked through execution", () =>    Effect.gen(function* () {      const server = yield* serveGraphqlTestServer({        schema: makeGreetingGraphqlSchema({ includeMutation: false }),      });      const org = `org_${crypto.randomUUID()}`;      const namespace = `gql_${crypto.randomUUID().replace(/-/g, "_")}`;      const scopeId = ScopeId.make(org);       const added = yield* asOrg(org, (client) =>        client.graphql.addSource({          params: { scopeId },          payload: {            endpoint: server.endpoint,            namespace,            name: "Cloud GraphQL",          },        }),      );      expect(added).toEqual({ namespace, toolCount: 1 });       const fetched = yield* asOrg(org, (client) =>        client.graphql.getSource({ params: { scopeId, namespace } }),      );      expect(fetched).toMatchObject({        namespace,        name: "Cloud GraphQL",        endpoint: server.endpoint,      });       const tools = yield* asOrg(org, (client) =>        client.sources.tools({ params: { scopeId, sourceId: namespace } }),      );      const toolId = `${namespace}.query.hello`;      expect(tools.map((tool) => tool.id)).toContain(toolId);       const execution = yield* asOrg(org, (client) =>        client.executions.execute({          payload: {            code: [              `const result = await tools.${namespace}.query.hello({ name: "Ada" });`,              "return result;",            ].join("\n"),          },        }),      );       expect(execution.status).toBe("completed");      if (execution.status !== "completed") return;      expect(execution.isError).toBe(false);      expect(execution.structured).toMatchObject({        status: "completed",        result: { ok: true, data: { hello: "Hello Ada" } },      });      const requests = yield* server.requests;      expect(requests.some((request) => request.payload.query?.includes("__schema"))).toBe(true);      expect(requests).toContainEqual(        expect.objectContaining({          payload: expect.objectContaining({ variables: { name: "Ada" } }),        }),      );    }),  );   it.effect(    "GraphQL add accepts a user-scoped bearer credential for org source introspection",    () =>      Effect.gen(function* () {        const server = yield* serveGraphqlTestServer({          schema: makeGreetingGraphqlSchema({ includeMutation: false }),          auth: {            validateAuthorization: (authorization) =>              Effect.succeed(authorization === "Bearer github-token"),          },        });        const orgId = `org_${crypto.randomUUID()}`;        const userId = `user_${crypto.randomUUID()}`;        const userScope = testUserOrgScopeId(userId, orgId);        const namespace = `github_graphql_${crypto.randomUUID().replace(/-/g, "_")}`;         yield* asUser(userId, orgId, (client) =>          client.secrets.set({            params: { scopeId: ScopeId.make(userScope) },            payload: {              id: SecretId.make("github-graphql-authorization"),              name: "Github GraphQL Authorization",              value: "github-token",            },          }),        );         const added = yield* asUser(userId, orgId, (client) =>          client.graphql.addSource({            params: { scopeId: ScopeId.make(orgId) },            payload: {              endpoint: server.endpoint,              namespace,              name: "Github GraphQL",              headers: {                Authorization: { kind: "secret", prefix: "Bearer " },              },              credentials: {                scope: ScopeId.make(userScope),                headers: {                  Authorization: {                    kind: "secret",                    secretId: "github-graphql-authorization",                    secretScope: userScope,                    prefix: "Bearer ",                  },                },              },            },          }),        );         expect(added).toEqual({ namespace, toolCount: 1 });        const requests = yield* server.requests;        expect(          requests.some((request) => request.headers.authorization === "Bearer github-token"),        ).toBe(true);      }),  );   it.effect("added MCP source can be inspected and invoked through execution", () =>    Effect.gen(function* () {      const server = yield* serveMcpServer(() =>        makeGreetingMcpServer({          name: "cloud-e2e-mcp",          toolDescription: "Echoes from the cloud e2e MCP server",          text: "cloud-mcp-ok",        }),      );      const org = `org_${crypto.randomUUID()}`;      const namespace = `mcp_${crypto.randomUUID().replace(/-/g, "_")}`;      const scopeId = ScopeId.make(org);       const added = yield* asOrg(org, (client) =>        client.mcp.addSource({          params: { scopeId },          payload: {            transport: "remote",            name: "Cloud MCP",            endpoint: server.endpoint,            remoteTransport: "streamable-http",            namespace,          },        }),      );      expect(added).toEqual({ namespace, toolCount: 1 });       const fetched = yield* asOrg(org, (client) =>        client.mcp.getSource({ params: { scopeId, namespace } }),      );      expect(fetched).toMatchObject({        namespace,        name: "Cloud MCP",        config: {          transport: "remote",          endpoint: server.endpoint,          remoteTransport: "streamable-http",        },      });       const tools = yield* asOrg(org, (client) =>        client.sources.tools({ params: { scopeId, sourceId: namespace } }),      );      const toolId = `${namespace}.simple_echo`;      expect(tools.map((tool) => tool.id)).toContain(toolId);       const execution = yield* asOrg(org, (client) =>        client.executions.execute({          payload: {            code: [              `const result = await tools.${namespace}.simple_echo({});`,              "return result;",            ].join("\n"),          },        }),      );       expect(execution.status).toBe("completed");      if (execution.status !== "completed") return;      expect(execution.isError).toBe(false);      expect(execution.structured).toMatchObject({        status: "completed",        result: {          ok: true,          data: { content: [{ type: "text", text: "cloud-mcp-ok" }] },        },      });      expect((yield* server.requests).length).toBeGreaterThanOrEqual(2);    }),  );   it.effect("sources.remove deletes the source and it drops off sources.list", () =>    Effect.gen(function* () {      const org = `org_${crypto.randomUUID()}`;      const namespace = `ns_${crypto.randomUUID().replace(/-/g, "_")}`;       yield* asOrg(org, (client) =>        Effect.gen(function* () {          yield* client.openapi.addSpec({            params: { scopeId: ScopeId.make(org) },            payload: makeMinimalOpenApiSourcePayload(namespace),          });          yield* client.sources.remove({            params: { scopeId: ScopeId.make(org), sourceId: namespace },          });        }),      );       const after = yield* asOrg(org, (client) =>        client.sources.list({ params: { scopeId: ScopeId.make(org) } }),      );      expect(after.map((s) => s.id)).not.toContain(namespace);    }),  );   it.effect("sources.remove on a non-existent sourceId is a no-op (idempotent)", () =>    Effect.gen(function* () {      const org = `org_${crypto.randomUUID()}`;      const ghost = `missing_${crypto.randomUUID().slice(0, 8)}`;       const result = yield* asOrg(org, (client) =>        client.sources          .remove({ params: { scopeId: ScopeId.make(org), sourceId: ghost } })          .pipe(Effect.result),      );      expect(Result.isSuccess(result)).toBe(true);    }),  );   it.effect("sources.remove on a static source is rejected", () =>    Effect.gen(function* () {      // `canRemove: false` is reserved for static (plugin-declared)

Lines 1–500 of 800.