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


Quelle  channel.test.ts

  Sprache: JAVA
 

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

import type { PluginRuntime } from "openclaw/plugin-sdk/core";
import {
  resetPluginRuntimeStateForTest,
  setActivePluginRegistry,
} from "openclaw/plugin-sdk/testing";
import { afterEach, describe, expect, it } from "vitest";
import { extractToolPayload } from "../../../src/infra/outbound/tool-payload.js";
import { createTestRegistry } from "../../../test/helpers/plugins/plugin-registry.js";
import { createStartAccountContext } from "../../../test/helpers/plugins/start-account-context.js";
import { createQaBusState, startQaBusServer } from "../../qa-lab/bus-api.js";
import { qaChannelPlugin, setQaChannelRuntime } from "../api.js";

afterEach(() => {
  resetPluginRuntimeStateForTest();
});

function installQaChannelTestRegistry() {
  setActivePluginRegistry(
    createTestRegistry([{ pluginId: "qa-channel", plugin: qaChannelPlugin, source: "test" }]),
  );
}

function createMockQaRuntime(params?: {
  onDispatch?: (ctx: Record<string, unknown>) => void;
}): PluginRuntime {
  const sessionUpdatedAt = new Map<string, number>();
  return {
    channel: {
      routing: {
        resolveAgentRoute({
          accountId,
          peer,
        }: {
          accountId?: string | null;
          peer?: { kind?: string; id?: string } | null;
        }) {
          return {
            agentId: "qa-agent",
            channel: "qa-channel",
            accountId: accountId ?? "default",
            sessionKey: `qa-agent:${peer?.kind ?? "direct"}:${peer?.id ?? "default"}`,
            mainSessionKey: "qa-agent:main",
            lastRoutePolicy: "session",
            matchedBy: "default",
          };
        },
      },
      session: {
        resolveStorePath(_store: string | undefined, { agentId }: { agentId: string }) {
          return agentId;
        },
        readSessionUpdatedAt({ sessionKey }: { sessionKey: string }) {
          return sessionUpdatedAt.get(sessionKey);
        },
        recordInboundSession({ sessionKey }: { sessionKey: string }) {
          sessionUpdatedAt.set(sessionKey, Date.now());
        },
      },
      reply: {
        resolveEnvelopeFormatOptions() {
          return {};
        },
        formatAgentEnvelope({ body }: { body: string }) {
          return body;
        },
        finalizeInboundContext(ctx: Record<string, unknown>) {
          return ctx as typeof ctx & { CommandAuthorized: boolean };
        },
        async dispatchReplyWithBufferedBlockDispatcher({
          ctx,
          dispatcherOptions,
        }: {
          ctx: { BodyForAgent?: string; Body?: string };
          dispatcherOptions: { deliver: (payload: { text: string }) => Promise<void> };
        }) {
          params?.onDispatch?.(ctx as Record<string, unknown>);
          await dispatcherOptions.deliver({
            text: `qa-echo: ${ctx.BodyForAgent ?? ctx.Body ?? ""}`,
          });
        },
      },
    },
  } as unknown as PluginRuntime;
}

function createQaChannelConfig(params: { baseUrl: string; allowFrom?: string[] }) {
  return {
    channels: {
      "qa-channel": {
        baseUrl: params.baseUrl,
        botUserId: "openclaw",
        botDisplayName: "OpenClaw QA",
        allowFrom: params.allowFrom,
      },
    },
  };
}

async function startQaChannelTestHarness(params?: {
  runtime?: PluginRuntime;
  allowFrom?: string[];
}) {
  installQaChannelTestRegistry();
  const state = createQaBusState();
  const bus = await startQaBusServer({ state });
  setQaChannelRuntime(params?.runtime ?? createMockQaRuntime());
  const cfg = createQaChannelConfig({ baseUrl: bus.baseUrl, allowFrom: params?.allowFrom });
  const account = qaChannelPlugin.config.resolveAccount(cfg, "default");
  const abort = new AbortController();
  const startAccount = qaChannelPlugin.gateway?.startAccount;
  expect(startAccount).toBeDefined();
  const task = startAccount!(
    createStartAccountContext({
      account,
      cfg,
      abortSignal: abort.signal,
    }),
  );
  return {
    state,
    async stop() {
      abort.abort();
      await task;
      await bus.stop();
    },
  };
}

describe("qa-channel plugin", () => {
  it("derives thread-aware outbound session routes from explicit thread targets", async () => {
    const route = await qaChannelPlugin.messaging?.resolveOutboundSessionRoute?.({
      cfg: {},
      agentId: "main",
      accountId: "default",
      target: "thread:qa-room/thread-1",
    });

    expect(route).toMatchObject({
      sessionKey: "agent:main:qa-channel:channel:thread:qa-room/thread-1",
      baseSessionKey: "agent:main:qa-channel:channel:thread:qa-room/thread-1",
    });
    expect(route?.threadId).toBeUndefined();
  });

  it("recovers thread-aware outbound session routes from currentSessionKey", async () => {
    const route = await qaChannelPlugin.messaging?.resolveOutboundSessionRoute?.({
      cfg: {},
      agentId: "main",
      accountId: "default",
      target: "channel:qa-room",
      currentSessionKey: "agent:main:qa-channel:channel:channel:qa-room:thread:thread-1",
    });

    expect(route).toMatchObject({
      sessionKey: "agent:main:qa-channel:channel:channel:qa-room:thread:thread-1",
      baseSessionKey: "agent:main:qa-channel:channel:channel:qa-room",
      threadId: "thread-1",
    });
  });

  it('does not recover currentSessionKey threads for shared dmScope "main" DMs', async () => {
    const route = await qaChannelPlugin.messaging?.resolveOutboundSessionRoute?.({
      cfg: {},
      agentId: "main",
      accountId: "default",
      target: "dm:alice",
      currentSessionKey: "agent:main:main:thread:thread-1",
    });

    expect(route).toMatchObject({
      sessionKey: "agent:main:main",
      baseSessionKey: "agent:main:main",
    });
    expect(route?.threadId).toBeUndefined();
  });

  it("roundtrips inbound DM traffic through the qa bus", { timeout: 20_000 }, async () => {
    const harness = await startQaChannelTestHarness({ allowFrom: ["*"] });

    try {
      harness.state.addInboundMessage({
        conversation: { id: "alice", kind: "direct" },
        senderId: "alice",
        senderName: "Alice",
        text: "hello",
      });

      const outbound = await harness.state.waitFor({
        kind: "message-text",
        textIncludes: "qa-echo: hello",
        direction: "outbound",
        timeoutMs: 15_000,
      });
      expect("text" in outbound && outbound.text).toContain("qa-echo: hello");
    } finally {
      await harness.stop();
    }
  });

  it("stages inbound image attachments into agent media payload", { timeout: 20_000 }, async () => {
    let dispatchedCtx: Record<string, unknown> | null = null;
    const harness = await startQaChannelTestHarness({
      allowFrom: ["*"],
      runtime: createMockQaRuntime({
        onDispatch: (ctx) => {
          dispatchedCtx = ctx;
        },
      }),
    });

    try {
      harness.state.addInboundMessage({
        conversation: { id: "alice", kind: "direct" },
        senderId: "alice",
        senderName: "Alice",
        text: "describe this image",
        attachments: [
          {
            id: "image-1",
            kind: "image",
            mimeType: "image/png",
            fileName: "red-top-blue-bottom.png",
            contentBase64:
              "iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAFElEQVR4nGP4z8Dwn4GBgYGJAQoAHxcCAr7cGDwAAAAASUVORK5CYII=",
          },
        ],
      });

      await harness.state.waitFor({
        kind: "message-text",
        textIncludes: "qa-echo: describe this image",
        direction: "outbound",
        timeoutMs: 15_000,
      });

      expect(dispatchedCtx).not.toBeNull();
      if (!dispatchedCtx) {
        throw new Error("expected dispatched context");
      }
      const mediaCtx: {
        MediaPath?: string;
        MediaPaths?: string[];
        MediaType?: string;
        MediaTypes?: string[];
      } = dispatchedCtx;
      expect(mediaCtx.MediaPath).toEqual(expect.stringContaining("red-top-blue-bottom"));
      expect(mediaCtx.MediaType).toBe("image/png");
      expect(mediaCtx.MediaPaths).toEqual([mediaCtx.MediaPath]);
      expect(mediaCtx.MediaTypes).toEqual(["image/png"]);
    } finally {
      await harness.stop();
    }
  });

  it("exposes thread and message actions against the qa bus", async () => {
    installQaChannelTestRegistry();
    const state = createQaBusState();
    const bus = await startQaBusServer({ state });

    try {
      const cfg = createQaChannelConfig({ baseUrl: bus.baseUrl });

      const handleAction = qaChannelPlugin.actions?.handleAction;
      expect(handleAction).toBeDefined();

      const threadResult = await handleAction!({
        channel: "qa-channel",
        action: "thread-create",
        cfg,
        accountId: "default",
        params: {
          channelId: "qa-room",
          title: "QA thread",
        },
      });
      const threadPayload = extractToolPayload(threadResult) as {
        thread: { id: string };
        target: string;
      };
      expect(threadPayload.thread.id).toBeTruthy();
      expect(threadPayload.target).toContain(threadPayload.thread.id);

      const outbound = state.addOutboundMessage({
        to: threadPayload.target,
        text: "message",
        threadId: threadPayload.thread.id,
      });

      await handleAction!({
        channel: "qa-channel",
        action: "react",
        cfg,
        accountId: "default",
        params: {
          messageId: outbound.id,
          emoji: "white_check_mark",
        },
      });

      await handleAction!({
        channel: "qa-channel",
        action: "edit",
        cfg,
        accountId: "default",
        params: {
          messageId: outbound.id,
          text: "message (edited)",
        },
      });

      const readResult = await handleAction!({
        channel: "qa-channel",
        action: "read",
        cfg,
        accountId: "default",
        params: {
          messageId: outbound.id,
        },
      });
      const readPayload = extractToolPayload(readResult) as { message: { text: string } };
      expect(readPayload.message.text).toContain("(edited)");

      const searchResult = await handleAction!({
        channel: "qa-channel",
        action: "search",
        cfg,
        accountId: "default",
        params: {
          query: "edited",
          channelId: "qa-room",
          threadId: threadPayload.thread.id,
        },
      });
      const searchPayload = extractToolPayload(searchResult) as {
        messages: Array<{ id: string }>;
      };
      expect(searchPayload.messages.some((message) => message.id === outbound.id)).toBe(true);

      await handleAction!({
        channel: "qa-channel",
        action: "delete",
        cfg,
        accountId: "default",
        params: {
          messageId: outbound.id,
        },
      });
      expect(state.readMessage({ messageId: outbound.id }).deleted).toBe(true);
    } finally {
      await bus.stop();
    }
  });

  it("routes the advertised send action to the qa bus", async () => {
    installQaChannelTestRegistry();
    const state = createQaBusState();
    const bus = await startQaBusServer({ state });

    try {
      const cfg = createQaChannelConfig({ baseUrl: bus.baseUrl });

      const sendTarget = qaChannelPlugin.actions?.extractToolSend?.({
        args: {
          action: "send",
          target: "qa-room",
          message: "hello",
        },
      });
      expect(sendTarget).toEqual({ to: "channel:qa-room", threadId: undefined });

      const result = await qaChannelPlugin.actions?.handleAction?.({
        channel: "qa-channel",
        action: "send",
        cfg,
        accountId: "default",
        params: {
          target: "qa-room",
          message: "hello from action",
        },
      });
      const payload = extractToolPayload(result);
      expect(payload).toMatchObject({ message: { text: "hello from action" } });

      const outbound = await state.waitFor({
        kind: "message-text",
        direction: "outbound",
        textIncludes: "hello from action",
        timeoutMs: 5_000,
      });
      expect("conversation" in outbound).toBe(true);
      if (!("conversation" in outbound)) {
        throw new Error("expected outbound message match");
      }
      expect(outbound.conversation).toMatchObject({ id: "qa-room", kind: "channel" });
    } finally {
      await bus.stop();
    }
  });
});

¤ Dauer der Verarbeitung: 0.24 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