mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-19 22:10:51 +00:00
Matrix: make onboarding status runtime-safe (#49995)
* Matrix: make onboarding status runtime-safe * Matrix tests: mock reply dispatch in BodyForAgent coverage * changelog Signed-off-by: joshavant <830519+joshavant@users.noreply.github.com> --------- Signed-off-by: joshavant <830519+joshavant@users.noreply.github.com>
This commit is contained in:
@@ -152,6 +152,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Signal/runtime API: re-export `SignalAccountConfig` so Signal account resolution type-checks again. (#49470) Thanks @scoootscooob.
|
||||
- Google Chat/runtime API: thin the private runtime barrel onto the curated public SDK surface while keeping public Google Chat exports intact. (#49504) Thanks @scoootscooob.
|
||||
- WhatsApp: stabilize inbound monitor and setup tests (#50007) Thanks @joshavant.
|
||||
- Matrix: make onboarding status runtime-safe (#49995) Thanks @joshavant.
|
||||
|
||||
### Breaking
|
||||
|
||||
|
||||
73
extensions/matrix/src/matrix/credentials.test.ts
Normal file
73
extensions/matrix/src/matrix/credentials.test.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import { clearMatrixRuntime, setMatrixRuntime } from "../runtime.js";
|
||||
import { loadMatrixCredentials, resolveMatrixCredentialsDir } from "./credentials.js";
|
||||
|
||||
describe("matrix credentials paths", () => {
|
||||
const previousStateDir = process.env.OPENCLAW_STATE_DIR;
|
||||
|
||||
beforeEach(() => {
|
||||
clearMatrixRuntime();
|
||||
delete process.env.OPENCLAW_STATE_DIR;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
clearMatrixRuntime();
|
||||
if (previousStateDir === undefined) {
|
||||
delete process.env.OPENCLAW_STATE_DIR;
|
||||
} else {
|
||||
process.env.OPENCLAW_STATE_DIR = previousStateDir;
|
||||
}
|
||||
});
|
||||
|
||||
it("falls back to OPENCLAW_STATE_DIR when runtime is not initialized", () => {
|
||||
const stateDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-matrix-creds-"));
|
||||
process.env.OPENCLAW_STATE_DIR = stateDir;
|
||||
|
||||
expect(resolveMatrixCredentialsDir(process.env)).toBe(
|
||||
path.join(stateDir, "credentials", "matrix"),
|
||||
);
|
||||
});
|
||||
|
||||
it("prefers runtime state dir when runtime is initialized", () => {
|
||||
const runtimeStateDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-matrix-runtime-"));
|
||||
const envStateDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-matrix-env-"));
|
||||
process.env.OPENCLAW_STATE_DIR = envStateDir;
|
||||
|
||||
setMatrixRuntime({
|
||||
state: {
|
||||
resolveStateDir: () => runtimeStateDir,
|
||||
},
|
||||
} as never);
|
||||
|
||||
expect(resolveMatrixCredentialsDir(process.env)).toBe(
|
||||
path.join(runtimeStateDir, "credentials", "matrix"),
|
||||
);
|
||||
});
|
||||
|
||||
it("prefers explicit stateDir argument over runtime/env", () => {
|
||||
const explicitStateDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-matrix-explicit-"));
|
||||
const runtimeStateDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-matrix-runtime-"));
|
||||
process.env.OPENCLAW_STATE_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-matrix-env-"));
|
||||
|
||||
setMatrixRuntime({
|
||||
state: {
|
||||
resolveStateDir: () => runtimeStateDir,
|
||||
},
|
||||
} as never);
|
||||
|
||||
expect(resolveMatrixCredentialsDir(process.env, explicitStateDir)).toBe(
|
||||
path.join(explicitStateDir, "credentials", "matrix"),
|
||||
);
|
||||
});
|
||||
|
||||
it("returns null without throwing when credentials are missing and runtime is absent", () => {
|
||||
const stateDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-matrix-creds-missing-"));
|
||||
process.env.OPENCLAW_STATE_DIR = stateDir;
|
||||
|
||||
expect(() => loadMatrixCredentials(process.env)).not.toThrow();
|
||||
expect(loadMatrixCredentials(process.env)).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -2,7 +2,8 @@ import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/account-id";
|
||||
import { getMatrixRuntime } from "../runtime.js";
|
||||
import { resolveStateDir } from "openclaw/plugin-sdk/state-paths";
|
||||
import { tryGetMatrixRuntime } from "../runtime.js";
|
||||
|
||||
export type MatrixStoredCredentials = {
|
||||
homeserver: string;
|
||||
@@ -27,7 +28,9 @@ export function resolveMatrixCredentialsDir(
|
||||
env: NodeJS.ProcessEnv = process.env,
|
||||
stateDir?: string,
|
||||
): string {
|
||||
const resolvedStateDir = stateDir ?? getMatrixRuntime().state.resolveStateDir(env, os.homedir);
|
||||
const runtime = tryGetMatrixRuntime();
|
||||
const resolvedStateDir =
|
||||
stateDir ?? runtime?.state.resolveStateDir(env, os.homedir) ?? resolveStateDir(env, os.homedir);
|
||||
return path.join(resolvedStateDir, "credentials", "matrix");
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,22 @@ import {
|
||||
} from "./handler.js";
|
||||
import { EventType, type MatrixRawEvent } from "./types.js";
|
||||
|
||||
const dispatchReplyFromConfigWithSettledDispatcherMock = vi.hoisted(() =>
|
||||
vi.fn().mockResolvedValue({
|
||||
queuedFinal: false,
|
||||
counts: { final: 0, partial: 0, tool: 0 },
|
||||
}),
|
||||
);
|
||||
|
||||
vi.mock("../../../runtime-api.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("../../../runtime-api.js")>();
|
||||
return {
|
||||
...actual,
|
||||
dispatchReplyFromConfigWithSettledDispatcher: (...args: unknown[]) =>
|
||||
dispatchReplyFromConfigWithSettledDispatcherMock(...args),
|
||||
};
|
||||
});
|
||||
|
||||
describe("createMatrixRoomMessageHandler BodyForAgent sender label", () => {
|
||||
it("stores sender-labeled BodyForAgent for group thread messages", async () => {
|
||||
const recordInboundSession = vi.fn().mockResolvedValue(undefined);
|
||||
@@ -149,6 +165,7 @@ describe("createMatrixRoomMessageHandler BodyForAgent sender label", () => {
|
||||
}),
|
||||
}),
|
||||
);
|
||||
expect(dispatchReplyFromConfigWithSettledDispatcherMock).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("uses room-scoped session keys for DM rooms matched via parentPeer binding", () => {
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
|
||||
import type { PluginRuntime } from "../runtime-api.js";
|
||||
|
||||
const { setRuntime: setMatrixRuntime, getRuntime: getMatrixRuntime } =
|
||||
createPluginRuntimeStore<PluginRuntime>("Matrix runtime not initialized");
|
||||
export { getMatrixRuntime, setMatrixRuntime };
|
||||
const {
|
||||
setRuntime: setMatrixRuntime,
|
||||
clearRuntime: clearMatrixRuntime,
|
||||
tryGetRuntime: tryGetMatrixRuntime,
|
||||
getRuntime: getMatrixRuntime,
|
||||
} = createPluginRuntimeStore<PluginRuntime>("Matrix runtime not initialized");
|
||||
export { clearMatrixRuntime, getMatrixRuntime, setMatrixRuntime, tryGetMatrixRuntime };
|
||||
|
||||
@@ -303,6 +303,32 @@ describe("setupChannels", () => {
|
||||
expect(multiselect).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("renders the QuickStart channel picker without requiring the Matrix runtime", async () => {
|
||||
const select = vi.fn(async ({ message }: { message: string }) => {
|
||||
if (message === "Select channel (QuickStart)") {
|
||||
return "__skip__";
|
||||
}
|
||||
return "__done__";
|
||||
});
|
||||
const { multiselect, text } = createUnexpectedPromptGuards();
|
||||
const prompter = createPrompter({
|
||||
select: select as unknown as WizardPrompter["select"],
|
||||
multiselect,
|
||||
text,
|
||||
});
|
||||
|
||||
await expect(
|
||||
runSetupChannels({} as OpenClawConfig, prompter, {
|
||||
quickstartDefaults: true,
|
||||
}),
|
||||
).resolves.toEqual({} as OpenClawConfig);
|
||||
|
||||
expect(select).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ message: "Select channel (QuickStart)" }),
|
||||
);
|
||||
expect(multiselect).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("continues Telegram setup when the plugin registry is empty", async () => {
|
||||
// Simulate missing registry entries (the scenario reported in #25545).
|
||||
setActivePluginRegistry(createEmptyPluginRegistry());
|
||||
|
||||
Reference in New Issue
Block a user