Ctrl K
ComunidadGuías…

lib/capabilities.test.ts

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

Archivo original ↗Preguntar a KiBot
import { describe, expect, it } from "vitest";

import { resolveCapabilities } from "@/lib/capabilities";

const core = {
  MINDDY_PUBLIC_SUPABASE_URL: "http://127.0.0.1:54321",
  MINDDY_PUBLIC_SUPABASE_ANON_KEY: "anon",
  SUPABASE_SERVICE_ROLE_KEY: "service-role",
};

describe("resolveCapabilities", () => {
  it("keeps only Supabase and Storage in the minimal configuration", () => {
    const capabilities = resolveCapabilities(core);

    expect(capabilities.supabase.state).toBe("ready");
    expect(capabilities.storage.state).toBe("ready");
    for (const [id, value] of Object.entries(capabilities)) {
      if (id === "supabase" || id === "storage") continue;
      expect(value.configured, `${id}: ${value.diagnostic}`).toBe(false);
    }
  });

  it("does not infer managed services from present secrets", () => {
    const capabilities = resolveCapabilities({
      ...core,
      OPENROUTER_API_KEY: "operator-key",
      STRIPE_SECRET_KEY: "stripe-key",
    });
    expect(capabilities.managedAi.state).toBe("disabled");
    expect(capabilities.managedBilling.state).toBe("disabled");
  });

  it("diagnoses each partial configuration with its missing variables", () => {
    const capabilities = resolveCapabilities({
      ...core,
      MINDDY_EDITION: "cloud",
      MINDDY_MANAGED_AI: "1",
      MINDDY_MANAGED_BILLING: "1",
      EMAIL_PROVIDER: "resend",
      GITHUB_APP_ID: "1",
      GITLAB_OAUTH_CLIENT_ID: "1",
      MINDDY_PUBLIC_VAPID_PUBLIC_KEY: "public",
      APNS_TEAM_ID: "team",
    });

    for (const id of [
      "managedAi",
      "managedBilling",
      "transactionalEmail",
    ] as const) {
      expect(capabilities[id].state).toBe("incomplete");
      expect(capabilities[id].missing.length).toBeGreaterThan(0);
    }
    for (const id of ["github", "gitlab", "webPush", "apns"] as const) {
      expect(capabilities[id].configured).toBe(false);
      expect(capabilities[id].missing.length).toBeGreaterThan(0);
    }
  });

  it("requires an explicit choice before any Resend delivery", () => {
    const capabilities = resolveCapabilities({
      ...core,
      VERCEL: "1",
      RESEND_API_KEY: "resend-key",
      FEEDBACK_EMAIL_FROM: "feedback@example.test",
      INVITATION_EMAIL_FROM: "invites@example.test",
    });
    expect(capabilities.transactionalEmail.configured).toBe(false);
  });

  it("exposes no code-execution capability, whatever legacy runner settings remain", () => {
    const capabilities = resolveCapabilities({
      ...core,
      AGENT_EXECUTION_BACKEND: "self-hosted",
      AGENT_RUNNER_URL: "http://agent-runner:6464",
      AGENT_RUNNER_SECRET: "runner-secret",
    });
    expect(Object.keys(capabilities)).not.toContain("agentExecution");
    expect(Object.keys(capabilities)).not.toContain("vercelSandbox");
  });

  it("does not activate Cloud providers from deceptive host or Vercel metadata", () => {
    const implicit = resolveCapabilities({
      ...core,
      VERCEL: "1",
      MINDDY_PUBLIC_APP_URL: "https://www.minddy.app",
      VERCEL_PROJECT_PRODUCTION_URL: "minddy.app",
      GITHUB_REF_NAME: "production",
      OPENROUTER_API_KEY: "managed-key",
      RESEND_API_KEY: "resend-key",
      STRIPE_SECRET_KEY: "stripe-key",
      STRIPE_WEBHOOK_SECRET: "webhook-key",
      STRIPE_PRICE_ID_GO: "go",
      STRIPE_PRICE_ID_PRO: "pro",
      STRIPE_PRICE_ID_GO_YEARLY: "go-year",
      STRIPE_PRICE_ID_PRO_YEARLY: "pro-year",
      MINDDY_PUBLIC_VAPID_PUBLIC_KEY: "public",
      VAPID_PRIVATE_KEY: "private",
      APNS_TEAM_ID: "team",
      APNS_KEY_ID: "key",
      APNS_PRIVATE_KEY: "private",
      MINDDY_PUBLIC_POSTHOG_KEY: "posthog-key",
    });

    for (const id of [
      "managedBilling",
      "managedAi",
      "analytics",
      "transactionalEmail",
      "webPush",
      "apns",
    ] as const) {
      expect(implicit[id].configured, `${id}: ${implicit[id].diagnostic}`).toBe(false);
    }
  });

  it("requires the Cloud edition before managed services can start", () => {
    const cloud = resolveCapabilities({
      ...core,
      MINDDY_EDITION: "cloud",
      MINDDY_MANAGED_AI: "1",
      MINDDY_MANAGED_BILLING: "1",
      OPENROUTER_API_KEY: "managed-key",
      STRIPE_SECRET_KEY: "stripe-key",
      STRIPE_WEBHOOK_SECRET: "webhook-key",
      STRIPE_PRICE_ID_GO: "go",
      STRIPE_PRICE_ID_PRO: "pro",
      STRIPE_PRICE_ID_GO_YEARLY: "go-year",
      STRIPE_PRICE_ID_PRO_YEARLY: "pro-year",
      OPENROUTER_MANAGEMENT_KEY: "management-key",
    });
    expect(cloud.managedAi.configured).toBe(true);
    expect(cloud.managedBilling.configured).toBe(true);

    const selfHosted = resolveCapabilities({
      ...core,
      MINDDY_EDITION: "self-hosted",
      MINDDY_MANAGED_AI: "1",
      MINDDY_MANAGED_BILLING: "1",
      OPENROUTER_API_KEY: "managed-key",
      STRIPE_SECRET_KEY: "stripe-key",
    });
    expect(selfHosted.managedAi.configured).toBe(false);
    expect(selfHosted.managedBilling.configured).toBe(false);
  });

  it("reports billed Cloud AI as incomplete without a management key", () => {
    const capabilities = resolveCapabilities({
      ...core,
      MINDDY_EDITION: "cloud",
      MINDDY_MANAGED_AI: "1",
      MINDDY_MANAGED_BILLING: "1",
      OPENROUTER_API_KEY: "managed-key",
      STRIPE_SECRET_KEY: "stripe-key",
      STRIPE_WEBHOOK_SECRET: "webhook-key",
      STRIPE_PRICE_ID_GO: "go",
      STRIPE_PRICE_ID_PRO: "pro",
      STRIPE_PRICE_ID_GO_YEARLY: "go-year",
      STRIPE_PRICE_ID_PRO_YEARLY: "pro-year",
    });
    expect(capabilities.managedAi).toMatchObject({
      state: "incomplete",
      missing: ["OPENROUTER_MANAGEMENT_KEY"],
    });
  });

  it("does not combine two partial PostHog pairs", () => {
    const capabilities = resolveCapabilities({
      ...core,
      POSTHOG_API_KEY: "server-key",
      MINDDY_PUBLIC_POSTHOG_HOST: "https://analytics.example.test",
    });

    expect(capabilities.analytics.state).toBe("disabled");
    expect(capabilities.analytics.configured).toBe(false);
  });

  it("does not declare Web Push ready with an unusable VAPID subject", () => {
    const capabilities = resolveCapabilities({
      ...core,
      MINDDY_PUBLIC_VAPID_PUBLIC_KEY: "public",
      VAPID_PRIVATE_KEY: "private",
      VAPID_SUBJECT: "push@example.test",
    });

    expect(capabilities.webPush.configured).toBe(false);
    expect(capabilities.webPush.missing).toContain("VAPID_SUBJECT (mailto: or https:)");
  });

  it("classifies workable replacements outside Resend and the managed services", () => {
    const capabilities = resolveCapabilities(core);
    expect(capabilities.scheduler.requirement).toBe("replaceable");
    expect(capabilities.transactionalEmail.requirement).toBe("replaceable");
    expect(capabilities.managedAi.requirement).toBe("replaceable");
  });

  it("enables scheduler routes with their authentication secret", () => {
    expect(resolveCapabilities(core).scheduler)
      .toMatchObject({ state: "disabled", configured: false, missing: ["CRON_SECRET"] });
    expect(resolveCapabilities({ ...core, CRON_SECRET: "secret" }).scheduler)
      .toMatchObject({ state: "external", configured: true });
  });

  it("serves git providers through the managed forge relay only when fully configured", () => {
    const relay = {
      MINDDY_FORGE_RELAY_URL: "https://forge-relay.minddy.app",
      MINDDY_FORGE_RELAY_INSTANCE_ID: "instance-id",
      MINDDY_FORGE_RELAY_SECRET: "instance-secret",
      GIT_STATE_SECRET: "state-secret",
      // Relayed tokens are stored encrypted instance-side.
      GIT_TOKEN_ENCRYPTION_SECRET: "x".repeat(32),
    };
    const capabilities = resolveCapabilities({ ...core, ...relay });

    for (const id of ["github", "gitlab"] as const) {
      expect(capabilities[id]).toMatchObject({
        requirement: "replaceable",
        state: "ready",
        configured: true,
      });
      expect(capabilities[id].diagnostic).toContain("managed forge relay");
    }

    // Without usable token encryption neither relay path is ready: GitHub
    // user grants and GitLab connection grants are both stored at handoff.
    const withoutEncryption = resolveCapabilities({
      ...core,
      ...relay,
      GIT_TOKEN_ENCRYPTION_SECRET: undefined,
    });
    for (const id of ["github", "gitlab"] as const) {
      expect(withoutEncryption[id]).toMatchObject({ state: "incomplete" });
      expect(withoutEncryption[id].missing[0]).toContain("GIT_TOKEN_ENCRYPTION_SECRET");
    }

    const weakEncryption = resolveCapabilities({
      ...core,
      ...relay,
      GIT_TOKEN_ENCRYPTION_SECRET: "too-short",
    });
    expect(weakEncryption.github.state).toBe("incomplete");
    expect(weakEncryption.gitlab.state).toBe("incomplete");
  });

  it("keeps the operator-owned app ahead of the relay and provisions automatically by default", () => {
    const relay = {
      MINDDY_FORGE_RELAY_URL: "https://forge-relay.minddy.app",
      MINDDY_FORGE_RELAY_INSTANCE_ID: "instance-id",
      MINDDY_FORGE_RELAY_SECRET: "instance-secret",
      GIT_STATE_SECRET: "state-secret",
      GIT_TOKEN_ENCRYPTION_SECRET: "x".repeat(32),
      GITHUB_APP_ID: "1",
      GITHUB_APP_SLUG: "example-app",
      GITHUB_APP_PRIVATE_KEY: "private-key",
    };
    const local = resolveCapabilities({ ...core, ...relay });
    expect(local.github).toMatchObject({
      requirement: "optional",
      state: "ready",
      diagnostic: expect.stringContaining("operator-owned app"),
    });
    expect(local.gitlab.diagnostic).toContain("managed forge relay");

    // Self-hosted default: no operator-owned app and no relay variables —
    // the relay identity is provisioned automatically on first connect.
    // The instance-side secrets stay required (the installer generates them).
    const automatic = resolveCapabilities({
      ...core,
      GIT_STATE_SECRET: "state-secret",
      GIT_TOKEN_ENCRYPTION_SECRET: "x".repeat(32),
    });
    for (const id of ["github", "gitlab"] as const) {
      expect(automatic[id]).toMatchObject({
        requirement: "replaceable",
        state: "ready",
        configured: true,
      });
      expect(automatic[id].diagnostic).toContain("provisioned automatically");
    }

    // The explicit opt-out (--no-forge-relay) restores the disabled state.
    const optedOut = resolveCapabilities({ ...core, MINDDY_FORGE_RELAY: "0" });
    expect(optedOut.github.state).toBe("disabled");
    expect(optedOut.gitlab.state).toBe("disabled");

    // The cloud edition never self-provisions: it operates the relay instead.
    const cloud = resolveCapabilities({ ...core, MINDDY_EDITION: "cloud" });
    expect(cloud.github.state).toBe("disabled");
    expect(cloud.gitlab.state).toBe("disabled");
  });

  it("falls back to automatic provisioning when relay variables are only partial", () => {
    const capabilities = resolveCapabilities({
      ...core,
      MINDDY_FORGE_RELAY_URL: "https://forge-relay.minddy.app",
      GIT_STATE_SECRET: "state-secret",
      GIT_TOKEN_ENCRYPTION_SECRET: "x".repeat(32),
    });
    // Partial explicit variables do not select a pinned control plane
    // (all three are required); the instance stays on the automatic default.
    expect(capabilities.github).toMatchObject({ state: "ready", configured: true });

    const missingState = resolveCapabilities({
      ...core,
      MINDDY_FORGE_RELAY_URL: "https://forge-relay.minddy.app",
      MINDDY_FORGE_RELAY_INSTANCE_ID: "instance-id",
      MINDDY_FORGE_RELAY_SECRET: "instance-secret",
    });
    expect(missingState.github).toMatchObject({
      state: "incomplete",
      missing: [
        "GIT_STATE_SECRET",
        "GIT_TOKEN_ENCRYPTION_SECRET (at least 32 characters)",
      ],
    });
  });
});