Ctrl K
ComunidadGuías…

src/mcp_oauth/transition.test.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { describe, expect, it } from "vitest";
import type {
  McpOAuthEvent,
  McpOAuthFlowIdentity,
  McpOAuthInvocationRef,
  McpOAuthState,
} from "./state";
import { transition } from "./transition";

function ref(serverId: number, operationId: string): McpOAuthInvocationRef {
  return {
    kind: "mcp-oauth",
    entityKey: serverId,
    operationId,
  };
}

function identity(serverId: number, operationId: string): McpOAuthFlowIdentity {
  return {
    invocationRef: ref(serverId, operationId),
    rendererMessageId: `message-${operationId}`,
    expectedState: `state-${operationId}`,
    serverId,
  };
}

describe("MCP OAuth transition", () => {
  it("walks binding, callback, exchange, and connected with one ref", () => {
    const flow = identity(1, "one");
    let state: McpOAuthState = { status: "idle" };
    const apply = (event: McpOAuthEvent) => {
      const result = transition(state, event);
      expect(result.kind).toBe("applied");
      state = result.state;
    };

    apply({ type: "CONNECT", ...flow });
    apply({
      type: "BINDS_SETTLED",
      invocationRef: flow.invocationRef,
      boundHosts: ["127.0.0.1"],
      anyInUse: false,
    });
    apply({
      type: "CALLBACK",
      invocationRef: flow.invocationRef,
      state: flow.expectedState,
      code: "code",
    });
    apply({ type: "EXCHANGE_OK", invocationRef: flow.invocationRef });
    expect(state).toMatchObject({
      status: "connected",
      invocationRef: flow.invocationRef,
    });
  });

  it("rejects stale refs across asynchronous boundaries", () => {
    const flow = identity(1, "active");
    const stale = ref(1, "stale");
    const state: McpOAuthState = {
      status: "awaitingCallback",
      ...flow,
    };
    const events: McpOAuthEvent[] = [
      { type: "SOCKETS_CLOSED", invocationRef: stale },
      {
        type: "BINDS_SETTLED",
        invocationRef: stale,
        boundHosts: [],
        anyInUse: false,
      },
      { type: "AUTHORIZED_SILENTLY", invocationRef: stale },
      {
        type: "CALLBACK",
        invocationRef: stale,
        state: flow.expectedState,
        code: "code",
      },
      { type: "TIMEOUT", invocationRef: stale },
      { type: "EXCHANGE_OK", invocationRef: stale },
      {
        type: "EXCHANGE_FAILED",
        invocationRef: stale,
        message: "failure",
      },
    ];
    for (const event of events) {
      expect(transition(state, event)).toEqual({
        kind: "ignored",
        state,
        reason: "stale-invocation",
      });
    }
  });

  it("supersedes an active flow with a distinct invocation", () => {
    const first = identity(1, "first");
    const second = identity(2, "second");
    const result = transition(
      { status: "awaitingCallback", ...first },
      { type: "CONNECT", ...second },
    );
    expect(result).toMatchObject({
      kind: "applied",
      state: {
        status: "superseding",
        closing: first,
        next: second,
      },
    });
  });
});