Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/Java/Openclaw/extensions/matrix/src/matrix/monitor/   (KI Agentensystem Version 22©)  Datei vom 26.3.2026 mit Größe 31 kB image not shown  

Quelle  index.test.ts

  Sprache: JAVA
 

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

import { z } from "openclaw/plugin-sdk/zod";
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import type { MatrixRoomInfo } from "./room-info.js";

type DirectRoomTrackerOptions = {
  canPromoteRecentInvite?: (roomId: string) => boolean | Promise<boolean>;
  shouldKeepLocallyPromotedDirectRoom?:
    | ((roomId: string) => boolean | undefined | Promise<boolean | undefined>)
    | undefined;
};

const hoisted = vi.hoisted(() => {
  const createEmitter = () => {
    const listeners = new Map<string, Set<(...args: unknown[]) => void>>();
    return {
      on(event: string, listener: (...args: unknown[]) => void) {
        let bucket = listeners.get(event);
        if (!bucket) {
          bucket = new Set();
          listeners.set(event, bucket);
        }
        bucket.add(listener);
        return this;
      },
      off(event: string, listener: (...args: unknown[]) => void) {
        listeners.get(event)?.delete(listener);
        return this;
      },
      emit(event: string, ...args: unknown[]) {
        for (const listener of listeners.get(event) ?? []) {
          listener(...args);
        }
        return true;
      },
      removeAllListeners() {
        listeners.clear();
        return this;
      },
    };
  };
  const callOrder: string[] = [];
  const state = {
    startClientError: null as Error | null,
  };
  const accountConfig = {
    dm: {},
  };
  const inboundDeduper = {
    claimEvent: vi.fn(() => true),
    commitEvent: vi.fn(async () => undefined),
    releaseEvent: vi.fn(),
    flush: vi.fn(async () => undefined),
    stop: vi.fn(async () => undefined),
  };
  const createMatrixInboundEventDeduper = vi.fn(async () => inboundDeduper);
  const client = Object.assign(createEmitter(), {
    id: "matrix-client",
    hasPersistedSyncState: vi.fn(() => false),
    stopSyncWithoutPersist: vi.fn(),
    drainPendingDecryptions: vi.fn(async () => undefined),
  });
  const createMatrixRoomMessageHandler = vi.fn(() => vi.fn());
  const createDirectRoomTracker = vi.fn((_client: unknown, _opts?: DirectRoomTrackerOptions) => ({
    isDirectMessage: vi.fn(async () => false),
  }));
  const getRoomInfo = vi.fn<
    (roomId: string, opts?: { includeAliases?: boolean }) => Promise<MatrixRoomInfo>
  >(async () => ({
    altAliases: [],
    nameResolved: true,
    aliasesResolved: true,
  }));
  const getMemberDisplayName = vi.fn(async () => "Bot");
  const resolveTextChunkLimit = vi.fn<
    (cfg: unknown, channel: unknown, accountId?: unknown) => number
  >(() => 4000);
  const logger = {
    info: vi.fn(),
    warn: vi.fn(),
    error: vi.fn(),
    debug: vi.fn(),
  };
  const stopThreadBindingManager = vi.fn();
  const releaseSharedClientInstance = vi.fn(async () => true);
  const resolveSharedMatrixClient = vi.fn(async (params: { startClient?: boolean }) => {
    if (params.startClient === false) {
      callOrder.push("prepare-client");
      return client;
    }
    if (!callOrder.includes("create-manager")) {
      throw new Error("Matrix client started before thread bindings were registered");
    }
    if (state.startClientError) {
      throw state.startClientError;
    }
    callOrder.push("start-client");
    return client;
  });
  const setActiveMatrixClient = vi.fn();
  const setMatrixRuntime = vi.fn();
  const backfillMatrixAuthDeviceIdAfterStartup = vi.fn(async () => undefined);
  const runMatrixStartupMaintenance = vi.fn<
    (params: { abortSignal?: AbortSignal }) => Promise<void>
  >(async () => undefined);
  const setStatus = vi.fn();
  return {
    backfillMatrixAuthDeviceIdAfterStartup,
    callOrder,
    accountConfig,
    client,
    createDirectRoomTracker,
    createMatrixInboundEventDeduper,
    createMatrixRoomMessageHandler,
    getMemberDisplayName,
    getRoomInfo,
    inboundDeduper,
    logger,
    registeredOnRoomMessage: null as null | ((roomId: string, event: unknown) => Promise<void>),
    releaseSharedClientInstance,
    resolveSharedMatrixClient,
    resolveTextChunkLimit,
    runMatrixStartupMaintenance,
    registeredHealthySyncGetter: undefined as undefined | (() => number | undefined),
    setActiveMatrixClient,
    setMatrixRuntime,
    setStatus,
    state,
    stopThreadBindingManager,
  };
});

vi.mock("../../runtime-api.js", () => {
  const normalizeAccountId = (value: string | null | undefined) => value?.trim() || "default";
  return {
    DEFAULT_ACCOUNT_ID: "default",
    GROUP_POLICY_BLOCKED_LABEL: {
      room: "room",
    },
    MarkdownConfigSchema: z.any().optional(),
    PAIRING_APPROVED_MESSAGE: "paired",
    ToolPolicySchema: z.any().optional(),
    addAllowlistUserEntriesFromConfigEntry: vi.fn(),
    buildChannelConfigSchema: (schema: unknown) => schema,
    buildChannelKeyCandidates: () => [],
    buildProbeChannelStatusSummary: (
      snapshot: Record<string, unknown>,
      extra?: Record<string, unknown>,
    ) => ({
      ...snapshot,
      ...extra,
    }),
    buildSecretInputSchema: () => z.string(),
    chunkTextForOutbound: vi.fn((text: string) => [text]),
    collectStatusIssuesFromLastError: () => [],
    createActionGate: () => () => true,
    createReplyPrefixOptions: () => ({}),
    createTypingCallbacks: () => ({}),
    formatDocsLink: (input: string) => input,
    formatZonedTimestamp: () => "2026-03-27T00:00:00.000Z",
    getAgentScopedMediaLocalRoots: () => [],
    getSessionBindingService: () => ({}),
    hasConfiguredSecretInput: (value: unknown) => Boolean(value),
    mergeAllowlist: ({ existing, additions }: { existing: string[]; additions: string[] }) => [
      ...existing,
      ...additions,
    ],
    normalizeAccountId,
    normalizeOptionalAccountId: normalizeAccountId,
    resolveThreadBindingIdleTimeoutMsForChannel: () => 24 * 60 * 60 * 1000,
    resolveThreadBindingMaxAgeMsForChannel: () => 0,
    resolveAllowlistProviderRuntimeGroupPolicy: () => ({
      groupPolicy: "allowlist",
      providerMissingFallbackApplied: false,
    }),
    resolveChannelEntryMatch: ({
      entries,
      keys,
      wildcardKey,
    }: {
      entries: Record<string, unknown>;
      keys: string[];
      wildcardKey: string;
    }) => {
      for (const key of keys) {
        if (Object.prototype.hasOwnProperty.call(entries, key)) {
          return {
            entry: entries[key],
            key,
            wildcardEntry: Object.prototype.hasOwnProperty.call(entries, wildcardKey)
              ? entries[wildcardKey]
              : undefined,
            wildcardKey: Object.prototype.hasOwnProperty.call(entries, wildcardKey)
              ? wildcardKey
              : undefined,
          };
        }
      }
      return {
        entry: undefined,
        key: undefined,
        wildcardEntry: Object.prototype.hasOwnProperty.call(entries, wildcardKey)
          ? entries[wildcardKey]
          : undefined,
        wildcardKey: Object.prototype.hasOwnProperty.call(entries, wildcardKey)
          ? wildcardKey
          : undefined,
      };
    },
    resolveDefaultGroupPolicy: () => "allowlist",
    resolveOutboundSendDep: () => null,
    resolveThreadBindingFarewellText: () => null,
    resolveAckReaction: () => null,
    readJsonFileWithFallback: vi.fn(),
    readNumberParam: vi.fn(),
    readReactionParams: vi.fn(),
    readStringArrayParam: vi.fn(),
    readStringParam: vi.fn(),
    summarizeMapping: vi.fn(),
    warnMissingProviderGroupPolicyFallbackOnce: vi.fn(),
  };
});

vi.mock("../../resolve-targets.js", () => ({
  resolveMatrixTargets: vi.fn(async () => []),
}));

vi.mock("../../runtime.js", () => ({
  getMatrixRuntime: () => ({
    config: {
      loadConfig: () => ({
        channels: {
          matrix: hoisted.accountConfig,
        },
      }),
      writeConfigFile: vi.fn(),
    },
    logging: {
      getChildLogger: () => hoisted.logger,
      shouldLogVerbose: () => false,
    },
    channel: {
      mentions: {
        buildMentionRegexes: () => [],
      },
      text: {
        resolveTextChunkLimit: (cfg: unknown, channel: unknown, accountId?: unknown) =>
          hoisted.resolveTextChunkLimit(cfg, channel, accountId),
      },
    },
    system: {
      formatNativeDependencyHint: () => "",
    },
    media: {
      loadWebMedia: vi.fn(),
    },
  }),
  setMatrixRuntime: hoisted.setMatrixRuntime,
}));

vi.mock("../accounts.js", async () => {
  const actual = await vi.importActual<typeof import("../accounts.js")>("../accounts.js");
  return {
    ...actual,
    resolveConfiguredMatrixBotUserIds: vi.fn(() => new Set<string>()),
    resolveMatrixAccount: () => ({
      accountId: "default",
      config: hoisted.accountConfig,
    }),
  };
});

vi.mock("../active-client.js", () => ({
  setActiveMatrixClient: hoisted.setActiveMatrixClient,
}));

vi.mock("../client.js", () => ({
  backfillMatrixAuthDeviceIdAfterStartup: hoisted.backfillMatrixAuthDeviceIdAfterStartup,
  isBunRuntime: () => false,
  resolveMatrixAuth: vi.fn(async () => ({
    accountId: "default",
    homeserver: "https://matrix.example.org",
    userId: "@bot:example.org",
    accessToken: "token",
    initialSyncLimit: 20,
    encryption: false,
  })),
  resolveMatrixAuthContext: vi.fn(() => ({
    accountId: "default",
  })),
  resolveSharedMatrixClient: hoisted.resolveSharedMatrixClient,
}));

vi.mock("../client/shared.js", () => ({
  releaseSharedClientInstance: hoisted.releaseSharedClientInstance,
}));

vi.mock("../config-update.js", () => ({
  updateMatrixAccountConfig: vi.fn((cfg: unknown) => cfg),
}));

vi.mock("../device-health.js", () => ({
  summarizeMatrixDeviceHealth: vi.fn(() => ({
    staleOpenClawDevices: [],
  })),
}));

vi.mock("../profile.js", () => ({
  syncMatrixOwnProfile: vi.fn(async () => ({
    displayNameUpdated: false,
    avatarUpdated: false,
    convertedAvatarFromHttp: false,
    resolvedAvatarUrl: undefined,
  })),
}));

vi.mock("../thread-bindings.js", () => ({
  createMatrixThreadBindingManager: vi.fn(async () => {
    hoisted.callOrder.push("create-manager");
    return {
      accountId: "default",
      stop: hoisted.stopThreadBindingManager,
    };
  }),
}));

vi.mock("./allowlist.js", () => ({
  normalizeMatrixUserId: (value: string) => value,
}));

vi.mock("./auto-join.js", () => ({
  registerMatrixAutoJoin: vi.fn(),
}));

vi.mock("./direct.js", () => ({
  createDirectRoomTracker: hoisted.createDirectRoomTracker,
}));

vi.mock("./events.js", () => ({
  registerMatrixMonitorEvents: vi.fn(
    (params: {
      getHealthySyncSinceMs?: () => number | undefined;
      onRoomMessage: (roomId: string, event: unknown) => Promise<void>;
      runDetachedTask?: (label: string, task: () => Promise<void>) => Promise<void>;
    }) => {
      hoisted.callOrder.push("register-events");
      hoisted.registeredHealthySyncGetter = params.getHealthySyncSinceMs;
      hoisted.registeredOnRoomMessage = (roomId: string, event: unknown) =>
        params.runDetachedTask
          ? params.runDetachedTask("test room message", async () => {
              await params.onRoomMessage(roomId, event);
            })
          : params.onRoomMessage(roomId, event);
    },
  ),
}));

vi.mock("./handler.js", () => ({
  createMatrixRoomMessageHandler: hoisted.createMatrixRoomMessageHandler,
}));

vi.mock("./inbound-dedupe.js", () => ({
  createMatrixInboundEventDeduper: hoisted.createMatrixInboundEventDeduper,
}));

vi.mock("./legacy-crypto-restore.js", () => ({
  maybeRestoreLegacyMatrixBackup: vi.fn(),
}));

vi.mock("./room-info.js", () => ({
  createMatrixRoomInfoResolver: vi.fn(() => ({
    getRoomInfo: hoisted.getRoomInfo,
    getMemberDisplayName: hoisted.getMemberDisplayName,
  })),
}));

vi.mock("./startup-verification.js", () => ({
  ensureMatrixStartupVerification: vi.fn(),
}));

vi.mock("./startup.js", () => ({
  runMatrixStartupMaintenance: hoisted.runMatrixStartupMaintenance,
}));

let monitorMatrixProvider: typeof import("./index.js").monitorMatrixProvider;

describe("monitorMatrixProvider", () => {
  beforeAll(async () => {
    ({ monitorMatrixProvider } = await import("./index.js"));
  });

  async function flushUntil(predicate: () => boolean, message: string): Promise<void> {
    for (let i = 0; i < 20; i++) {
      if (predicate()) {
        return;
      }
      await Promise.resolve();
    }
    throw new Error(message);
  }

  async function waitForCallOrderEntry(entry: string): Promise<void> {
    await flushUntil(
      () => hoisted.callOrder.includes(entry),
      `expected call order to include ${entry}`,
    );
  }

  async function startMonitorAndAbortAfterStartup(): Promise<void> {
    const abortController = new AbortController();
    const monitorPromise = monitorMatrixProvider({ abortSignal: abortController.signal });
    await waitForCallOrderEntry("start-client");
    abortController.abort();
    await monitorPromise;
  }
  beforeEach(() => {
    hoisted.callOrder.length = 0;
    hoisted.state.startClientError = null;
    hoisted.accountConfig.dm = {};
    delete (hoisted.accountConfig as { rooms?: Record<string, unknown> }).rooms;
    hoisted.resolveTextChunkLimit.mockReset().mockReturnValue(4000);
    hoisted.releaseSharedClientInstance.mockReset().mockResolvedValue(true);
    hoisted.resolveSharedMatrixClient
      .mockReset()
      .mockImplementation(async (params: { startClient?: boolean }) => {
        if (params.startClient === false) {
          hoisted.callOrder.push("prepare-client");
          return hoisted.client;
        }
        if (!hoisted.callOrder.includes("create-manager")) {
          throw new Error("Matrix client started before thread bindings were registered");
        }
        if (hoisted.state.startClientError) {
          throw hoisted.state.startClientError;
        }
        hoisted.callOrder.push("start-client");
        return hoisted.client;
      });
    hoisted.createDirectRoomTracker.mockReset().mockReturnValue({
      isDirectMessage: vi.fn(async () => false),
    });
    hoisted.getRoomInfo.mockReset().mockResolvedValue({
      altAliases: [],
      nameResolved: true,
      aliasesResolved: true,
    });
    hoisted.getMemberDisplayName.mockReset().mockResolvedValue("Bot");
    hoisted.registeredOnRoomMessage = null;
    hoisted.registeredHealthySyncGetter = undefined;
    hoisted.setActiveMatrixClient.mockReset();
    hoisted.stopThreadBindingManager.mockReset();
    hoisted.client.removeAllListeners();
    hoisted.client.hasPersistedSyncState.mockReset().mockReturnValue(false);
    hoisted.client.stopSyncWithoutPersist.mockReset();
    hoisted.client.drainPendingDecryptions.mockReset().mockResolvedValue(undefined);
    hoisted.inboundDeduper.claimEvent.mockReset().mockReturnValue(true);
    hoisted.inboundDeduper.commitEvent.mockReset().mockResolvedValue(undefined);
    hoisted.inboundDeduper.releaseEvent.mockReset();
    hoisted.inboundDeduper.flush.mockReset().mockResolvedValue(undefined);
    hoisted.inboundDeduper.stop.mockReset().mockResolvedValue(undefined);
    hoisted.createMatrixInboundEventDeduper.mockReset().mockResolvedValue(hoisted.inboundDeduper);
    hoisted.backfillMatrixAuthDeviceIdAfterStartup.mockReset().mockResolvedValue(undefined);
    hoisted.runMatrixStartupMaintenance.mockReset().mockResolvedValue(undefined);
    hoisted.createMatrixRoomMessageHandler.mockReset().mockReturnValue(vi.fn());
    hoisted.setStatus.mockReset();
    Object.values(hoisted.logger).forEach((mock) => mock.mockReset());
  });

  it("returns immediately when the abort signal is already canceled", async () => {
    const abortController = new AbortController();
    abortController.abort();

    await monitorMatrixProvider({ abortSignal: abortController.signal });

    expect(hoisted.callOrder).toEqual([]);
    expect(hoisted.resolveTextChunkLimit).not.toHaveBeenCalled();
    expect(hoisted.createMatrixRoomMessageHandler).not.toHaveBeenCalled();
    expect(hoisted.setActiveMatrixClient).not.toHaveBeenCalled();
  });

  it("publishes disconnected startup status and connected sync status without failing the monitor", async () => {
    const abortController = new AbortController();
    const monitorPromise = monitorMatrixProvider({
      abortSignal: abortController.signal,
      setStatus: hoisted.setStatus,
    });

    await waitForCallOrderEntry("start-client");

    expect(hoisted.setStatus).toHaveBeenCalledWith(
      expect.objectContaining({
        accountId: "default",
        baseUrl: "https://matrix.example.org",
        connected: false,
        healthState: "starting",
      }),
    );

    hoisted.client.emit("sync.state", "SYNCING", "RECONNECTING", undefined);

    expect(hoisted.setStatus).toHaveBeenCalledWith(
      expect.objectContaining({
        accountId: "default",
        connected: true,
        healthState: "healthy",
        lastError: null,
      }),
    );

    abortController.abort();
    await expect(monitorPromise).resolves.toBeUndefined();
  });

  it("re-arms the healthy-sync milestone across reconnect transitions", async () => {
    vi.useFakeTimers();
    vi.setSystemTime(new Date("2026-04-10T16:21:00.000Z"));
    const abortController = new AbortController();
    try {
      const monitorPromise = monitorMatrixProvider({
        abortSignal: abortController.signal,
        setStatus: hoisted.setStatus,
      });

      await waitForCallOrderEntry("start-client");

      const getHealthySyncSinceMs = hoisted.registeredHealthySyncGetter;
      if (!getHealthySyncSinceMs) {
        throw new Error("expected healthy sync getter to be registered");
      }

      expect(getHealthySyncSinceMs()).toBeUndefined();

      hoisted.client.emit("sync.state", "SYNCING", "RECONNECTING", undefined);
      const firstHealthySyncSinceMs = Date.now();
      expect(getHealthySyncSinceMs()).toBe(firstHealthySyncSinceMs);

      await vi.advanceTimersByTimeAsync(3_000);
      hoisted.client.emit("sync.state", "CATCHUP", "SYNCING", undefined);
      expect(getHealthySyncSinceMs()).toBe(firstHealthySyncSinceMs);

      await vi.advanceTimersByTimeAsync(2_000);
      hoisted.client.emit("sync.state", "PREPARED", "CATCHUP", undefined);
      expect(getHealthySyncSinceMs()).toBe(firstHealthySyncSinceMs);

      await vi.advanceTimersByTimeAsync(5_000);
      hoisted.client.emit("sync.state", "RECONNECTING", "SYNCING", new Error("network flap"));
      expect(getHealthySyncSinceMs()).toBeUndefined();

      await vi.advanceTimersByTimeAsync(7_000);
      hoisted.client.emit("sync.state", "SYNCING", "RECONNECTING", undefined);
      const rearmedHealthySyncSinceMs = Date.now();
      expect(getHealthySyncSinceMs()).toBe(rearmedHealthySyncSinceMs);

      abortController.abort();
      await expect(monitorPromise).resolves.toBeUndefined();

      hoisted.client.emit("sync.state", "RECONNECTING", "SYNCING", new Error("late noise"));
      expect(getHealthySyncSinceMs()).toBe(rearmedHealthySyncSinceMs);
    } finally {
      vi.useRealTimers();
    }
  });

  it("contains room-message handler rejections inside monitor task tracking", async () => {
    const abortController = new AbortController();
    const unhandled: unknown[] = [];
    const onUnhandled = (reason: unknown) => {
      unhandled.push(reason);
    };

    hoisted.createMatrixRoomMessageHandler.mockReturnValue(
      vi.fn(async () => {
        throw new Error("room handler exploded");
      }),
    );

    process.on("unhandledRejection", onUnhandled);
    try {
      const monitorPromise = monitorMatrixProvider({ abortSignal: abortController.signal });
      await waitForCallOrderEntry("start-client");

      const onRoomMessage = hoisted.registeredOnRoomMessage;
      if (!onRoomMessage) {
        throw new Error("expected room message handler to be registered");
      }

      await onRoomMessage("!room:example.org", { event_id: "$event" });
      await Promise.resolve();

      expect(unhandled).toHaveLength(0);
      expect(hoisted.logger.warn).toHaveBeenCalledWith(
        "matrix background task failed",
        expect.objectContaining({
          task: "test room message",
          error: "Error: room handler exploded",
        }),
      );

      abortController.abort();
      await monitorPromise;
    } finally {
      process.off("unhandledRejection", onUnhandled);
    }
  });

  it("fails the channel task when Matrix sync emits an unexpected fatal error", async () => {
    const abortController = new AbortController();
    const monitorPromise = monitorMatrixProvider({
      abortSignal: abortController.signal,
      setStatus: hoisted.setStatus,
    });

    await waitForCallOrderEntry("start-client");

    hoisted.client.emit("sync.unexpected_error", new Error("sync exploded"));

    await expect(monitorPromise).rejects.toThrow("sync exploded");
    expect(hoisted.releaseSharedClientInstance).toHaveBeenCalledWith(hoisted.client, "persist");
    expect(hoisted.setStatus).toHaveBeenCalledWith(
      expect.objectContaining({
        accountId: "default",
        connected: false,
        healthState: "error",
        lastError: "sync exploded",
      }),
    );
  });

  it("marks early startup failures as error before the monitor loop starts", async () => {
    hoisted.resolveSharedMatrixClient.mockImplementation(
      async (params: { startClient?: boolean }) => {
        if (params.startClient === false) {
          throw new Error("prepare failed");
        }
        hoisted.callOrder.push("start-client");
        return hoisted.client;
      },
    );

    await expect(
      monitorMatrixProvider({
        setStatus: hoisted.setStatus,
      }),
    ).rejects.toThrow("prepare failed");

    expect(hoisted.releaseSharedClientInstance).not.toHaveBeenCalled();
    expect(hoisted.setStatus).toHaveBeenLastCalledWith(
      expect.objectContaining({
        accountId: "default",
        connected: false,
        healthState: "error",
        lastError: "prepare failed",
      }),
    );
  });

  it("releases the prepared client when startup fails before later resources exist", async () => {
    hoisted.createMatrixInboundEventDeduper.mockRejectedValue(new Error("deduper failed"));

    await expect(
      monitorMatrixProvider({
        setStatus: hoisted.setStatus,
      }),
    ).rejects.toThrow("deduper failed");

    expect(hoisted.releaseSharedClientInstance).toHaveBeenCalledWith(hoisted.client, "persist");
    expect(hoisted.inboundDeduper.stop).not.toHaveBeenCalled();
    expect(hoisted.setStatus).toHaveBeenLastCalledWith(
      expect.objectContaining({
        accountId: "default",
        connected: false,
        healthState: "error",
        lastError: "deduper failed",
      }),
    );
  });

  it("aborts stalled startup promptly and releases the shared client without persist", async () => {
    const abortController = new AbortController();
    hoisted.resolveSharedMatrixClient.mockImplementation(
      async (params: { startClient?: boolean; abortSignal?: AbortSignal }) => {
        if (params.startClient === false) {
          hoisted.callOrder.push("prepare-client");
          return hoisted.client;
        }
        hoisted.callOrder.push("start-client");
        return await new Promise<typeof hoisted.client>((_resolve, reject) => {
          params.abortSignal?.addEventListener(
            "abort",
            () => {
              const error = new Error("Matrix startup aborted");
              error.name = "AbortError";
              reject(error);
            },
            { once: true },
          );
        });
      },
    );

    const monitorPromise = monitorMatrixProvider({ abortSignal: abortController.signal });

    await waitForCallOrderEntry("start-client");

    abortController.abort();

    await expect(monitorPromise).resolves.toBeUndefined();
    expect(hoisted.releaseSharedClientInstance).toHaveBeenCalledWith(hoisted.client, "stop");
    expect(hoisted.client.drainPendingDecryptions).not.toHaveBeenCalled();
  });

  it("aborts during startup maintenance and releases the shared client without persist", async () => {
    const abortController = new AbortController();
    hoisted.runMatrixStartupMaintenance.mockImplementation(
      async (params: { abortSignal?: AbortSignal }) =>
        await new Promise<void>((_resolve, reject) => {
          params.abortSignal?.addEventListener(
            "abort",
            () => {
              const error = new Error("Matrix startup aborted");
              error.name = "AbortError";
              reject(error);
            },
            { once: true },
          );
        }),
    );

    const monitorPromise = monitorMatrixProvider({ abortSignal: abortController.signal });

    await flushUntil(
      () => hoisted.runMatrixStartupMaintenance.mock.calls.length === 1,
      "expected startup maintenance to run",
    );

    abortController.abort();

    await expect(monitorPromise).resolves.toBeUndefined();
    expect(hoisted.releaseSharedClientInstance).toHaveBeenCalledWith(hoisted.client, "stop");
    expect(hoisted.client.drainPendingDecryptions).not.toHaveBeenCalled();
  });

  it("registers Matrix thread bindings before starting the client", async () => {
    await startMonitorAndAbortAfterStartup();

    expect(hoisted.callOrder).toEqual([
      "prepare-client",
      "create-manager",
      "register-events",
      "start-client",
    ]);
    expect(hoisted.stopThreadBindingManager).toHaveBeenCalledTimes(1);
  });

  it("resolves text chunk limit for the effective Matrix account", async () => {
    await startMonitorAndAbortAfterStartup();

    expect(hoisted.resolveTextChunkLimit).toHaveBeenCalledWith(
      expect.anything(),
      "matrix",
      "default",
    );
  });

  it("starts monitoring without waiting for best-effort deviceId backfill", async () => {
    hoisted.backfillMatrixAuthDeviceIdAfterStartup.mockImplementation(
      () => new Promise<undefined>(() => {}),
    );

    const abortController = new AbortController();
    const monitorPromise = monitorMatrixProvider({ abortSignal: abortController.signal });

    await waitForCallOrderEntry("start-client");
    expect(hoisted.backfillMatrixAuthDeviceIdAfterStartup).toHaveBeenCalledTimes(1);
    expect(hoisted.backfillMatrixAuthDeviceIdAfterStartup).toHaveBeenCalledWith(
      expect.objectContaining({
        abortSignal: abortController.signal,
      }),
    );

    abortController.abort();
    await expect(monitorPromise).resolves.toBeUndefined();
  });

  it("cleans up thread bindings and shared clients when startup fails", async () => {
    hoisted.state.startClientError = new Error("start failed");

    await expect(monitorMatrixProvider()).rejects.toThrow("start failed");

    expect(hoisted.stopThreadBindingManager).toHaveBeenCalledTimes(1);
    expect(hoisted.releaseSharedClientInstance).toHaveBeenCalledTimes(1);
    expect(hoisted.releaseSharedClientInstance).toHaveBeenCalledWith(hoisted.client, "persist");
    expect(hoisted.setActiveMatrixClient).toHaveBeenNthCalledWith(1, hoisted.client, "default");
    expect(hoisted.setActiveMatrixClient).toHaveBeenNthCalledWith(2, null, "default");
  });

  it("disables cold-start backlog dropping only when sync state is cleanly persisted", async () => {
    hoisted.client.hasPersistedSyncState.mockReturnValue(true);
    await startMonitorAndAbortAfterStartup();

    expect(hoisted.createMatrixRoomMessageHandler).toHaveBeenCalledWith(
      expect.objectContaining({
        dropPreStartupMessages: false,
      }),
    );
  });

  it("stops sync, drains decryptions, then waits for in-flight handlers before persisting", async () => {
    const abortController = new AbortController();
    let resolveHandler: (() => void) | null = null;

    hoisted.createMatrixRoomMessageHandler.mockReturnValue(
      vi.fn(() => {
        hoisted.callOrder.push("handler-start");
        return new Promise<void>((resolve) => {
          resolveHandler = () => {
            hoisted.callOrder.push("handler-done");
            resolve();
          };
        });
      }),
    );
    hoisted.client.stopSyncWithoutPersist.mockImplementation(() => {
      hoisted.callOrder.push("pause-client");
    });
    hoisted.client.drainPendingDecryptions.mockImplementation(async () => {
      hoisted.callOrder.push("drain-decrypts");
    });
    hoisted.stopThreadBindingManager.mockImplementation(() => {
      hoisted.callOrder.push("stop-manager");
    });
    hoisted.releaseSharedClientInstance.mockImplementation(async () => {
      hoisted.callOrder.push("release-client");
      return true;
    });
    hoisted.inboundDeduper.stop.mockImplementation(async () => {
      hoisted.callOrder.push("stop-deduper");
    });

    const monitorPromise = monitorMatrixProvider({ abortSignal: abortController.signal });
    await waitForCallOrderEntry("start-client");
    const onRoomMessage = hoisted.registeredOnRoomMessage;
    if (!onRoomMessage) {
      throw new Error("expected room message handler to be registered");
    }

    const roomMessagePromise = onRoomMessage("!room:example.org", { event_id: "$event" });
    abortController.abort();
    await waitForCallOrderEntry("pause-client");
    expect(hoisted.callOrder).not.toContain("stop-deduper");

    if (resolveHandler === null) {
      throw new Error("expected in-flight handler to be pending");
    }
    (resolveHandler as () => void)();
    await roomMessagePromise;
    await monitorPromise;

    expect(hoisted.callOrder.indexOf("pause-client")).toBeLessThan(
      hoisted.callOrder.indexOf("drain-decrypts"),
    );
    expect(hoisted.callOrder.indexOf("drain-decrypts")).toBeLessThan(
      hoisted.callOrder.indexOf("handler-done"),
    );
    expect(hoisted.callOrder.indexOf("handler-done")).toBeLessThan(
      hoisted.callOrder.indexOf("stop-manager"),
    );
    expect(hoisted.callOrder.indexOf("stop-manager")).toBeLessThan(
      hoisted.callOrder.indexOf("stop-deduper"),
    );
    expect(hoisted.callOrder.indexOf("stop-deduper")).toBeLessThan(
      hoisted.callOrder.indexOf("release-client"),
    );
  });

  it("wires recent-invite promotion to fail closed when room metadata is unresolved", async () => {
    await startMonitorAndAbortAfterStartup();

    const trackerOpts = hoisted.createDirectRoomTracker.mock.calls[0]?.[1];
    if (!trackerOpts?.canPromoteRecentInvite) {
      throw new Error("recent invite promotion callback was not wired");
    }

    hoisted.getRoomInfo.mockResolvedValueOnce({
      altAliases: [],
      nameResolved: false,
      aliasesResolved: false,
    });

    await expect(trackerOpts.canPromoteRecentInvite("!room:example.org")).resolves.toBe(false);
  });

  it("wires recent-invite promotion to reject named rooms", async () => {
    await startMonitorAndAbortAfterStartup();

    const trackerOpts = hoisted.createDirectRoomTracker.mock.calls[0]?.[1];
    if (!trackerOpts?.canPromoteRecentInvite) {
      throw new Error("recent invite promotion callback was not wired");
    }

    hoisted.getRoomInfo.mockResolvedValueOnce({
      name: "Ops Room",
      altAliases: [],
      nameResolved: true,
      aliasesResolved: true,
    });

    await expect(trackerOpts.canPromoteRecentInvite("!room:example.org")).resolves.toBe(false);
  });

  it("wires recent-invite promotion to reject wildcard-configured rooms", async () => {
    (hoisted.accountConfig as { rooms?: Record<string, unknown> }).rooms = {
      "*": { enabled: false },
    };

    await startMonitorAndAbortAfterStartup();

    const trackerOpts = hoisted.createDirectRoomTracker.mock.calls[0]?.[1];
    if (!trackerOpts?.canPromoteRecentInvite) {
      throw new Error("recent invite promotion callback was not wired");
    }

    hoisted.getRoomInfo.mockResolvedValueOnce({
      altAliases: [],
      nameResolved: true,
      aliasesResolved: true,
    });

    await expect(trackerOpts.canPromoteRecentInvite("!room:example.org")).resolves.toBe(false);
  });

  it("treats unresolved room metadata as indeterminate for local promotion revalidation", async () => {
    await startMonitorAndAbortAfterStartup();

    const trackerOpts = hoisted.createDirectRoomTracker.mock.calls[0]?.[1];
    if (!trackerOpts?.shouldKeepLocallyPromotedDirectRoom) {
      throw new Error("local promotion revalidation callback was not wired");
    }

    hoisted.getRoomInfo.mockResolvedValueOnce({
      altAliases: [],
      nameResolved: false,
      aliasesResolved: false,
    });

    await expect(
      trackerOpts.shouldKeepLocallyPromotedDirectRoom("!room:example.org"),
    ).resolves.toBeUndefined();
  });
});

¤ Dauer der Verarbeitung: 0.27 Sekunden  (vorverarbeitet am  2026-04-28) ¤

*© 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.