Ctrl K
ComunidadGuías…

app/utils/ModelContext.test.ts

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

Archivo original ↗Preguntar a KiBot
import { vi } from "vitest";
import Logger from "~/utils/Logger";
import {
  isModelContextSupported,
  registerModelContextTool,
  type ModelContextTool,
} from "./ModelContext";

const makeTool = (name: string): ModelContextTool => ({
  name,
  description: "A test tool",
  execute: async () => ({ content: [{ type: "text", text: "ok" }] }),
});

describe("ModelContext", () => {
  afterEach(() => {
    delete window.document.modelContext;
  });

  describe("#isModelContextSupported", () => {
    it("should return false when the API is unavailable", () => {
      expect(isModelContextSupported()).toBe(false);
    });

    it("should return true when the API is available", () => {
      window.document.modelContext = { registerTool: vi.fn() };
      expect(isModelContextSupported()).toBe(true);
    });
  });

  describe("#registerModelContextTool", () => {
    it("should return false when the API is unavailable", () => {
      const controller = new AbortController();
      expect(registerModelContextTool(makeTool("one"), controller.signal)).toBe(
        false
      );
    });

    it("should register a tool and skip duplicate names", () => {
      const registerTool = vi.fn();
      window.document.modelContext = { registerTool };
      const controller = new AbortController();

      expect(registerModelContextTool(makeTool("two"), controller.signal)).toBe(
        true
      );
      expect(registerModelContextTool(makeTool("two"), controller.signal)).toBe(
        false
      );
      expect(registerTool).toHaveBeenCalledTimes(1);

      controller.abort();
    });

    it("should allow re-registration after the signal aborts", () => {
      const registerTool = vi.fn();
      window.document.modelContext = { registerTool };

      const first = new AbortController();
      expect(registerModelContextTool(makeTool("three"), first.signal)).toBe(
        true
      );
      first.abort();

      const second = new AbortController();
      expect(registerModelContextTool(makeTool("three"), second.signal)).toBe(
        true
      );
      expect(registerTool).toHaveBeenCalledTimes(2);

      second.abort();
    });

    it("should free the name when async registration rejects", async () => {
      const registerTool = vi.fn().mockRejectedValue(new Error("rejected"));
      window.document.modelContext = { registerTool };

      const first = new AbortController();
      expect(registerModelContextTool(makeTool("five"), first.signal)).toBe(
        true
      );
      await new Promise((resolve) => setTimeout(resolve, 0));

      const second = new AbortController();
      expect(registerModelContextTool(makeTool("five"), second.signal)).toBe(
        true
      );

      second.abort();
    });

    it("should not warn when a pending registration is aborted", async () => {
      const warn = vi.spyOn(Logger, "warn").mockImplementation(() => {});
      const registerTool = vi
        .fn()
        .mockRejectedValue(
          new DOMException("signal is aborted without reason", "AbortError")
        );
      window.document.modelContext = { registerTool };

      const controller = new AbortController();
      expect(registerModelContextTool(makeTool("six"), controller.signal)).toBe(
        true
      );
      controller.abort();
      await new Promise((resolve) => setTimeout(resolve, 0));

      expect(warn).not.toHaveBeenCalled();
      warn.mockRestore();
    });

    it("should not register when the signal is already aborted", () => {
      const registerTool = vi.fn();
      window.document.modelContext = { registerTool };
      const controller = new AbortController();
      controller.abort();

      expect(
        registerModelContextTool(makeTool("four"), controller.signal)
      ).toBe(false);
      expect(registerTool).not.toHaveBeenCalled();
    });
  });
});