Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  hooks.before-agent-start.test.ts

  Sprache: JAVA
 

Spracherkennung für: .ts vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

/**
 * Layer 1: Hook Merger Tests for before_agent_start
 *
 * Validates that modelOverride and providerOverride fields are correctly
 * propagated through the hook merger, including priority ordering and
 * backward compatibility.
 */
import { beforeEach, describe, expect, it } from "vitest";
import { createHookRunner } from "./hooks.js";
import { addStaticTestHooks, addTestHook, TEST_PLUGIN_AGENT_CTX } from "./hooks.test-helpers.js";
import { createEmptyPluginRegistry, type PluginRegistry } from "./registry.js";
import type { PluginHookBeforeAgentStartResult, PluginHookRegistration } from "./types.js";

function addBeforeAgentStartHook(
  registry: PluginRegistry,
  pluginId: string,
  handler: () => PluginHookBeforeAgentStartResult | Promise<PluginHookBeforeAgentStartResult>,
  priority?: number,
) {
  addTestHook({
    registry,
    pluginId,
    hookName: "before_agent_start",
    handler: handler as PluginHookRegistration["handler"],
    priority,
  });
}

const stubCtx = TEST_PLUGIN_AGENT_CTX;

describe("before_agent_start hook merger", () => {
  let registry: PluginRegistry;

  beforeEach(() => {
    registry = createEmptyPluginRegistry();
  });

  const runWithSingleHook = async (result: PluginHookBeforeAgentStartResult, priority?: number) => {
    addBeforeAgentStartHook(registry, "plugin-a", () => result, priority);
    const runner = createHookRunner(registry);
    return await runner.runBeforeAgentStart({ prompt: "hello" }, stubCtx);
  };

  const expectSingleModelOverride = async (modelOverride: string) => {
    const result = await runWithSingleHook({ modelOverride });
    expect(result?.modelOverride).toBe(modelOverride);
    return result;
  };

  const expectMergedBeforeAgentStart = async (
    hooks: Array<{
      pluginId: string;
      result: PluginHookBeforeAgentStartResult;
      priority?: number;
    }>,
    expected: Partial<PluginHookBeforeAgentStartResult>,
  ) => {
    const result = await runWithHooks(hooks);
    expect(result).toEqual(expect.objectContaining(expected));
    return result;
  };

  const runWithHooks = async (
    hooks: Array<{
      pluginId: string;
      result: PluginHookBeforeAgentStartResult;
      priority?: number;
    }>,
  ) => {
    addStaticTestHooks(registry, {
      hookName: "before_agent_start",
      hooks,
    });
    const runner = createHookRunner(registry);
    return await runner.runBeforeAgentStart({ prompt: "hello" }, stubCtx);
  };

  it.each([
    [
      "returns modelOverride from a single plugin",
      { modelOverride: "llama3.3:8b" },
      {
        modelOverride: "llama3.3:8b",
      },
    ],
    [
      "returns providerOverride from a single plugin",
      { providerOverride: "ollama" },
      {
        providerOverride: "ollama",
      },
    ],
    [
      "returns both modelOverride and providerOverride together",
      {
        modelOverride: "llama3.3:8b",
        providerOverride: "ollama",
      },
      {
        modelOverride: "llama3.3:8b",
        providerOverride: "ollama",
      },
    ],
    [
      "systemPrompt merges correctly alongside model overrides",
      {
        systemPrompt: "You are a helpful assistant",
        modelOverride: "llama3.3:8b",
        providerOverride: "ollama",
      },
      {
        systemPrompt: "You are a helpful assistant",
        modelOverride: "llama3.3:8b",
        providerOverride: "ollama",
      },
    ],
  ] as const)("%s", async (_name, hookResult, expected) => {
    await expectMergedBeforeAgentStart([{ pluginId: "plugin-a", result: hookResult }], expected);
  });

  it("higher-priority plugin wins for modelOverride", async () => {
    const result = await expectMergedBeforeAgentStart(
      [
        { pluginId: "low-priority", result: { modelOverride: "gpt-5.4" }, priority: 1 },
        { pluginId: "high-priority", result: { modelOverride: "llama3.3:8b" }, priority: 10 },
      ],
      { modelOverride: "llama3.3:8b" },
    );
    expect(result?.modelOverride).toBe("llama3.3:8b");
  });

  it("lower-priority plugin does not overwrite if it returns undefined", async () => {
    const result = await runWithHooks([
      {
        pluginId: "high-priority",
        result: { modelOverride: "llama3.3:8b", providerOverride: "ollama" },
        priority: 10,
      },
      {
        pluginId: "low-priority",
        result: { prependContext: "some context" },
        priority: 1,
      },
    ]);

    // High-priority ran first (priority 10), low-priority ran second (priority 1).
    // Low-priority didn't return modelOverride, so ?? falls back to acc's value.
    expect(result?.modelOverride).toBe("llama3.3:8b");
    expect(result?.providerOverride).toBe("ollama");
    expect(result?.prependContext).toBe("some context");
  });

  it("prependContext still concatenates when modelOverride is present", async () => {
    const result = await runWithHooks([
      {
        pluginId: "plugin-a",
        result: { prependContext: "context A", modelOverride: "llama3.3:8b" },
        priority: 10,
      },
      {
        pluginId: "plugin-b",
        result: { prependContext: "context B" },
        priority: 1,
      },
    ]);

    expect(result?.prependContext).toBe("context A\n\ncontext B");
    expect(result?.modelOverride).toBe("llama3.3:8b");
  });

  it("backward compat: plugin returning only prependContext produces no modelOverride", async () => {
    const result = await runWithSingleHook({ prependContext: "legacy context" });

    expect(result?.prependContext).toBe("legacy context");
    expect(result?.modelOverride).toBeUndefined();
    expect(result?.providerOverride).toBeUndefined();
  });

  it("modelOverride without providerOverride leaves provider undefined", async () => {
    const result = await expectSingleModelOverride("llama3.3:8b");
    expect(result?.providerOverride).toBeUndefined();
  });

  it("returns undefined when no hooks are registered", async () => {
    const runner = createHookRunner(registry);
    const result = await runner.runBeforeAgentStart({ prompt: "hello" }, stubCtx);

    expect(result).toBeUndefined();
  });

  it("passes runId through the agent context to hook handlers", async () => {
    const registry = createEmptyPluginRegistry();
    let capturedCtx: typeof stubCtx | undefined;
    addTestHook({
      registry,
      pluginId: "ctx-spy",
      hookName: "before_agent_start",
      handler: ((_event: unknown, ctx: typeof stubCtx) => {
        capturedCtx = ctx;
        return {};
      }) as PluginHookRegistration["handler"],
    });

    const runner = createHookRunner(registry);
    await runner.runBeforeAgentStart({ prompt: "test" }, stubCtx);

    expect(capturedCtx).toBeDefined();
    expect(capturedCtx?.runId).toBe("test-run-id");
  });
});

¤ Dauer der Verarbeitung: 0.16 Sekunden  (vorverarbeitet am  2026-04-27) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge