mirror of https://github.com/openclaw/openclaw.git
Matrix: trim test import breadth
This commit is contained in:
parent
fa2e051bb6
commit
bd957a3a8b
|
|
@ -28,33 +28,6 @@ vi.mock("./credentials-write.runtime.js", () => ({
|
|||
touchMatrixCredentials: touchMatrixCredentialsMock,
|
||||
}));
|
||||
|
||||
let getMatrixScopedEnvVarNames: typeof import("./client/config.js").getMatrixScopedEnvVarNames;
|
||||
let resolveImplicitMatrixAccountId: typeof import("./client/config.js").resolveImplicitMatrixAccountId;
|
||||
let resolveMatrixConfig: typeof import("./client/config.js").resolveMatrixConfig;
|
||||
let resolveMatrixConfigForAccount: typeof import("./client/config.js").resolveMatrixConfigForAccount;
|
||||
let resolveMatrixAuth: typeof import("./client/config.js").resolveMatrixAuth;
|
||||
let resolveMatrixAuthContext: typeof import("./client/config.js").resolveMatrixAuthContext;
|
||||
let resolveValidatedMatrixHomeserverUrl: typeof import("./client/config.js").resolveValidatedMatrixHomeserverUrl;
|
||||
let validateMatrixHomeserverUrl: typeof import("./client/config.js").validateMatrixHomeserverUrl;
|
||||
let credentialsReadModule: typeof import("./credentials-read.js");
|
||||
let sdkModule: typeof import("./sdk.js");
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
({
|
||||
getMatrixScopedEnvVarNames,
|
||||
resolveImplicitMatrixAccountId,
|
||||
resolveMatrixConfig,
|
||||
resolveMatrixConfigForAccount,
|
||||
resolveMatrixAuth,
|
||||
resolveMatrixAuthContext,
|
||||
resolveValidatedMatrixHomeserverUrl,
|
||||
validateMatrixHomeserverUrl,
|
||||
} = await import("./client/config.js"));
|
||||
credentialsReadModule = await import("./credentials-read.js");
|
||||
sdkModule = await import("./sdk.js");
|
||||
});
|
||||
|
||||
vi.mock("matrix-js-sdk", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("matrix-js-sdk")>();
|
||||
return {
|
||||
|
|
@ -66,6 +39,24 @@ vi.mock("matrix-js-sdk", async (importOriginal) => {
|
|||
};
|
||||
});
|
||||
|
||||
const {
|
||||
getMatrixScopedEnvVarNames,
|
||||
resolveImplicitMatrixAccountId,
|
||||
resolveMatrixConfig,
|
||||
resolveMatrixConfigForAccount,
|
||||
resolveMatrixAuth,
|
||||
resolveMatrixAuthContext,
|
||||
resolveValidatedMatrixHomeserverUrl,
|
||||
validateMatrixHomeserverUrl,
|
||||
} = await import("./client/config.js");
|
||||
|
||||
let credentialsReadModule: typeof import("./credentials-read.js") | undefined;
|
||||
let sdkModule: typeof import("./sdk.js") | undefined;
|
||||
|
||||
beforeEach(() => {
|
||||
installMatrixTestRuntime();
|
||||
});
|
||||
|
||||
describe("resolveMatrixConfig", () => {
|
||||
it("prefers config over env", () => {
|
||||
const cfg = {
|
||||
|
|
@ -600,18 +591,30 @@ describe("resolveMatrixConfig", () => {
|
|||
});
|
||||
|
||||
describe("resolveMatrixAuth", () => {
|
||||
beforeEach(async () => {
|
||||
credentialsReadModule ??= await import("./credentials-read.js");
|
||||
sdkModule ??= await import("./sdk.js");
|
||||
vi.mocked(credentialsReadModule.loadMatrixCredentials).mockReset();
|
||||
vi.mocked(credentialsReadModule.loadMatrixCredentials).mockReturnValue(null);
|
||||
vi.mocked(credentialsReadModule.credentialsMatchConfig).mockReset();
|
||||
vi.mocked(credentialsReadModule.credentialsMatchConfig).mockReturnValue(false);
|
||||
saveMatrixCredentialsMock.mockReset();
|
||||
touchMatrixCredentialsMock.mockReset();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
vi.unstubAllGlobals();
|
||||
saveMatrixCredentialsMock.mockReset();
|
||||
});
|
||||
|
||||
it("uses the hardened client request path for password login and persists deviceId", async () => {
|
||||
const doRequestSpy = vi.spyOn(sdkModule.MatrixClient.prototype, "doRequest").mockResolvedValue({
|
||||
access_token: "tok-123",
|
||||
user_id: "@bot:example.org",
|
||||
device_id: "DEVICE123",
|
||||
});
|
||||
const doRequestSpy = vi
|
||||
.spyOn(sdkModule!.MatrixClient.prototype, "doRequest")
|
||||
.mockResolvedValue({
|
||||
access_token: "tok-123",
|
||||
user_id: "@bot:example.org",
|
||||
device_id: "DEVICE123",
|
||||
});
|
||||
|
||||
const cfg = {
|
||||
channels: {
|
||||
|
|
@ -658,7 +661,7 @@ describe("resolveMatrixAuth", () => {
|
|||
});
|
||||
|
||||
it("surfaces password login errors when account credentials are invalid", async () => {
|
||||
const doRequestSpy = vi.spyOn(sdkModule.MatrixClient.prototype, "doRequest");
|
||||
const doRequestSpy = vi.spyOn(sdkModule!.MatrixClient.prototype, "doRequest");
|
||||
doRequestSpy.mockRejectedValueOnce(new Error("Invalid username or password"));
|
||||
|
||||
const cfg = {
|
||||
|
|
@ -690,14 +693,14 @@ describe("resolveMatrixAuth", () => {
|
|||
});
|
||||
|
||||
it("uses cached matching credentials when access token is not configured", async () => {
|
||||
vi.mocked(credentialsReadModule.loadMatrixCredentials).mockReturnValue({
|
||||
vi.mocked(credentialsReadModule!.loadMatrixCredentials).mockReturnValue({
|
||||
homeserver: "https://matrix.example.org",
|
||||
userId: "@bot:example.org",
|
||||
accessToken: "cached-token",
|
||||
deviceId: "CACHEDDEVICE",
|
||||
createdAt: "2026-01-01T00:00:00.000Z",
|
||||
});
|
||||
vi.mocked(credentialsReadModule.credentialsMatchConfig).mockReturnValue(true);
|
||||
vi.mocked(credentialsReadModule!.credentialsMatchConfig).mockReturnValue(true);
|
||||
|
||||
const cfg = {
|
||||
channels: {
|
||||
|
|
@ -740,13 +743,13 @@ describe("resolveMatrixAuth", () => {
|
|||
});
|
||||
|
||||
it("falls back to config deviceId when cached credentials are missing it", async () => {
|
||||
vi.mocked(credentialsReadModule.loadMatrixCredentials).mockReturnValue({
|
||||
vi.mocked(credentialsReadModule!.loadMatrixCredentials).mockReturnValue({
|
||||
homeserver: "https://matrix.example.org",
|
||||
userId: "@bot:example.org",
|
||||
accessToken: "tok-123",
|
||||
createdAt: "2026-01-01T00:00:00.000Z",
|
||||
});
|
||||
vi.mocked(credentialsReadModule.credentialsMatchConfig).mockReturnValue(true);
|
||||
vi.mocked(credentialsReadModule!.credentialsMatchConfig).mockReturnValue(true);
|
||||
|
||||
const cfg = {
|
||||
channels: {
|
||||
|
|
@ -799,10 +802,12 @@ describe("resolveMatrixAuth", () => {
|
|||
});
|
||||
|
||||
it("resolves token-only non-default account userId from whoami instead of inheriting the base user", async () => {
|
||||
const doRequestSpy = vi.spyOn(sdkModule.MatrixClient.prototype, "doRequest").mockResolvedValue({
|
||||
user_id: "@ops:example.org",
|
||||
device_id: "OPSDEVICE",
|
||||
});
|
||||
const doRequestSpy = vi
|
||||
.spyOn(sdkModule!.MatrixClient.prototype, "doRequest")
|
||||
.mockResolvedValue({
|
||||
user_id: "@ops:example.org",
|
||||
device_id: "OPSDEVICE",
|
||||
});
|
||||
|
||||
const cfg = {
|
||||
channels: {
|
||||
|
|
@ -831,13 +836,15 @@ describe("resolveMatrixAuth", () => {
|
|||
});
|
||||
|
||||
it("uses named-account password auth instead of inheriting the base access token", async () => {
|
||||
vi.mocked(credentialsReadModule.loadMatrixCredentials).mockReturnValue(null);
|
||||
vi.mocked(credentialsReadModule.credentialsMatchConfig).mockReturnValue(false);
|
||||
const doRequestSpy = vi.spyOn(sdkModule.MatrixClient.prototype, "doRequest").mockResolvedValue({
|
||||
access_token: "ops-token",
|
||||
user_id: "@ops:example.org",
|
||||
device_id: "OPSDEVICE",
|
||||
});
|
||||
vi.mocked(credentialsReadModule!.loadMatrixCredentials).mockReturnValue(null);
|
||||
vi.mocked(credentialsReadModule!.credentialsMatchConfig).mockReturnValue(false);
|
||||
const doRequestSpy = vi
|
||||
.spyOn(sdkModule!.MatrixClient.prototype, "doRequest")
|
||||
.mockResolvedValue({
|
||||
access_token: "ops-token",
|
||||
user_id: "@ops:example.org",
|
||||
device_id: "OPSDEVICE",
|
||||
});
|
||||
|
||||
const cfg = {
|
||||
channels: {
|
||||
|
|
@ -881,10 +888,12 @@ describe("resolveMatrixAuth", () => {
|
|||
});
|
||||
|
||||
it("resolves missing whoami identity fields for token auth", async () => {
|
||||
const doRequestSpy = vi.spyOn(sdkModule.MatrixClient.prototype, "doRequest").mockResolvedValue({
|
||||
user_id: "@bot:example.org",
|
||||
device_id: "DEVICE123",
|
||||
});
|
||||
const doRequestSpy = vi
|
||||
.spyOn(sdkModule!.MatrixClient.prototype, "doRequest")
|
||||
.mockResolvedValue({
|
||||
user_id: "@bot:example.org",
|
||||
device_id: "DEVICE123",
|
||||
});
|
||||
|
||||
const cfg = {
|
||||
channels: {
|
||||
|
|
@ -918,10 +927,12 @@ describe("resolveMatrixAuth", () => {
|
|||
await fs.writeFile(secretPath, "file-token\n", "utf8");
|
||||
await fs.chmod(secretPath, 0o600);
|
||||
|
||||
const doRequestSpy = vi.spyOn(sdkModule.MatrixClient.prototype, "doRequest").mockResolvedValue({
|
||||
user_id: "@bot:example.org",
|
||||
device_id: "DEVICE123",
|
||||
});
|
||||
const doRequestSpy = vi
|
||||
.spyOn(sdkModule!.MatrixClient.prototype, "doRequest")
|
||||
.mockResolvedValue({
|
||||
user_id: "@bot:example.org",
|
||||
device_id: "DEVICE123",
|
||||
});
|
||||
|
||||
try {
|
||||
const cfg = {
|
||||
|
|
@ -961,10 +972,12 @@ describe("resolveMatrixAuth", () => {
|
|||
});
|
||||
|
||||
it("does not resolve inactive password SecretRefs when scoped token auth wins", async () => {
|
||||
const doRequestSpy = vi.spyOn(sdkModule.MatrixClient.prototype, "doRequest").mockResolvedValue({
|
||||
user_id: "@ops:example.org",
|
||||
device_id: "OPSDEVICE",
|
||||
});
|
||||
const doRequestSpy = vi
|
||||
.spyOn(sdkModule!.MatrixClient.prototype, "doRequest")
|
||||
.mockResolvedValue({
|
||||
user_id: "@ops:example.org",
|
||||
device_id: "OPSDEVICE",
|
||||
});
|
||||
|
||||
const cfg = {
|
||||
channels: {
|
||||
|
|
@ -1006,13 +1019,13 @@ describe("resolveMatrixAuth", () => {
|
|||
});
|
||||
|
||||
it("uses config deviceId with cached credentials when token is loaded from cache", async () => {
|
||||
vi.mocked(credentialsReadModule.loadMatrixCredentials).mockReturnValue({
|
||||
vi.mocked(credentialsReadModule!.loadMatrixCredentials).mockReturnValue({
|
||||
homeserver: "https://matrix.example.org",
|
||||
userId: "@bot:example.org",
|
||||
accessToken: "tok-123",
|
||||
createdAt: "2026-01-01T00:00:00.000Z",
|
||||
});
|
||||
vi.mocked(credentialsReadModule.credentialsMatchConfig).mockReturnValue(true);
|
||||
vi.mocked(credentialsReadModule!.credentialsMatchConfig).mockReturnValue(true);
|
||||
|
||||
const cfg = {
|
||||
channels: {
|
||||
|
|
|
|||
|
|
@ -28,10 +28,25 @@ import {
|
|||
} from "../account-config.js";
|
||||
import { resolveMatrixConfigFieldPath } from "../config-update.js";
|
||||
import { credentialsMatchConfig, loadMatrixCredentials } from "../credentials-read.js";
|
||||
import { MatrixClient } from "../sdk.js";
|
||||
import { ensureMatrixSdkLoggingConfigured } from "./logging.js";
|
||||
import type { MatrixAuth, MatrixResolvedConfig } from "./types.js";
|
||||
|
||||
type MatrixAuthClientDeps = {
|
||||
MatrixClient: typeof import("../sdk.js").MatrixClient;
|
||||
ensureMatrixSdkLoggingConfigured: typeof import("./logging.js").ensureMatrixSdkLoggingConfigured;
|
||||
};
|
||||
|
||||
let matrixAuthClientDepsPromise: Promise<MatrixAuthClientDeps> | undefined;
|
||||
|
||||
async function loadMatrixAuthClientDeps(): Promise<MatrixAuthClientDeps> {
|
||||
matrixAuthClientDepsPromise ??= Promise.all([import("../sdk.js"), import("./logging.js")]).then(
|
||||
([sdkModule, loggingModule]) => ({
|
||||
MatrixClient: sdkModule.MatrixClient,
|
||||
ensureMatrixSdkLoggingConfigured: loggingModule.ensureMatrixSdkLoggingConfigured,
|
||||
}),
|
||||
);
|
||||
return await matrixAuthClientDepsPromise;
|
||||
}
|
||||
|
||||
function readEnvSecretRefFallback(params: {
|
||||
value: unknown;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
|
|
@ -670,6 +685,7 @@ export async function resolveMatrixAuth(params?: {
|
|||
|
||||
if (!userId || !knownDeviceId) {
|
||||
// Fetch whoami when we need to resolve userId and/or deviceId from token auth.
|
||||
const { MatrixClient, ensureMatrixSdkLoggingConfigured } = await loadMatrixAuthClientDeps();
|
||||
ensureMatrixSdkLoggingConfigured();
|
||||
const tempClient = new MatrixClient(homeserver, accessToken, {
|
||||
ssrfPolicy: resolved.ssrfPolicy,
|
||||
|
|
@ -767,6 +783,7 @@ export async function resolveMatrixAuth(params?: {
|
|||
}
|
||||
|
||||
// Login with password using the same hardened request path as other Matrix HTTP calls.
|
||||
const { MatrixClient, ensureMatrixSdkLoggingConfigured } = await loadMatrixAuthClientDeps();
|
||||
ensureMatrixSdkLoggingConfigured();
|
||||
const loginClient = new MatrixClient(homeserver, "", {
|
||||
ssrfPolicy: resolved.ssrfPolicy,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,12 @@
|
|||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { CoreConfig } from "../../types.js";
|
||||
import type { MatrixAccountPatch } from "../config-update.js";
|
||||
import type { MatrixManagedDeviceInfo } from "../device-health.js";
|
||||
import type { MatrixProfileSyncResult } from "../profile.js";
|
||||
import type { MatrixOwnDeviceVerificationStatus } from "../sdk.js";
|
||||
import type { MatrixLegacyCryptoRestoreResult } from "./legacy-crypto-restore.js";
|
||||
import type { MatrixStartupVerificationOutcome } from "./startup-verification.js";
|
||||
import type { MatrixStartupMaintenanceDeps } from "./startup.js";
|
||||
import { runMatrixStartupMaintenance } from "./startup.js";
|
||||
|
||||
function createVerificationStatus(
|
||||
|
|
@ -67,47 +71,32 @@ function createLegacyCryptoRestoreResult(
|
|||
} as MatrixLegacyCryptoRestoreResult;
|
||||
}
|
||||
|
||||
const hoisted = vi.hoisted(() => ({
|
||||
maybeRestoreLegacyMatrixBackup: vi.fn(async () => createLegacyCryptoRestoreResult()),
|
||||
summarizeMatrixDeviceHealth: vi.fn(() => ({
|
||||
staleOpenClawDevices: [] as Array<{ deviceId: string }>,
|
||||
})),
|
||||
syncMatrixOwnProfile: vi.fn(async () => createProfileSyncResult()),
|
||||
ensureMatrixStartupVerification: vi.fn(async () => createStartupVerificationOutcome("verified")),
|
||||
updateMatrixAccountConfig: vi.fn((cfg: unknown) => cfg),
|
||||
}));
|
||||
|
||||
vi.mock("../config-update.js", () => ({
|
||||
updateMatrixAccountConfig: hoisted.updateMatrixAccountConfig,
|
||||
}));
|
||||
|
||||
vi.mock("../device-health.js", () => ({
|
||||
summarizeMatrixDeviceHealth: hoisted.summarizeMatrixDeviceHealth,
|
||||
}));
|
||||
|
||||
vi.mock("../profile.js", () => ({
|
||||
syncMatrixOwnProfile: hoisted.syncMatrixOwnProfile,
|
||||
}));
|
||||
|
||||
vi.mock("./legacy-crypto-restore.js", () => ({
|
||||
maybeRestoreLegacyMatrixBackup: hoisted.maybeRestoreLegacyMatrixBackup,
|
||||
}));
|
||||
|
||||
vi.mock("./startup-verification.js", () => ({
|
||||
ensureMatrixStartupVerification: hoisted.ensureMatrixStartupVerification,
|
||||
}));
|
||||
function createDeps(
|
||||
overrides: Partial<MatrixStartupMaintenanceDeps> = {},
|
||||
): MatrixStartupMaintenanceDeps {
|
||||
return {
|
||||
maybeRestoreLegacyMatrixBackup: vi.fn(async () => createLegacyCryptoRestoreResult()),
|
||||
summarizeMatrixDeviceHealth: vi.fn(() => ({
|
||||
currentDeviceId: null,
|
||||
staleOpenClawDevices: [] as MatrixManagedDeviceInfo[],
|
||||
currentOpenClawDevices: [] as MatrixManagedDeviceInfo[],
|
||||
})),
|
||||
syncMatrixOwnProfile: vi.fn(async () => createProfileSyncResult()),
|
||||
ensureMatrixStartupVerification: vi.fn(async () =>
|
||||
createStartupVerificationOutcome("verified"),
|
||||
),
|
||||
updateMatrixAccountConfig: vi.fn(
|
||||
(cfg: CoreConfig, _accountId: string, _patch: MatrixAccountPatch) => cfg,
|
||||
),
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
describe("runMatrixStartupMaintenance", () => {
|
||||
let deps: MatrixStartupMaintenanceDeps;
|
||||
|
||||
beforeEach(() => {
|
||||
hoisted.maybeRestoreLegacyMatrixBackup
|
||||
.mockClear()
|
||||
.mockResolvedValue(createLegacyCryptoRestoreResult());
|
||||
hoisted.summarizeMatrixDeviceHealth.mockClear().mockReturnValue({ staleOpenClawDevices: [] });
|
||||
hoisted.syncMatrixOwnProfile.mockClear().mockResolvedValue(createProfileSyncResult());
|
||||
hoisted.ensureMatrixStartupVerification
|
||||
.mockClear()
|
||||
.mockResolvedValue(createStartupVerificationOutcome("verified"));
|
||||
hoisted.updateMatrixAccountConfig.mockClear().mockImplementation((cfg: unknown) => cfg);
|
||||
deps = createDeps();
|
||||
});
|
||||
|
||||
function createParams(): Parameters<typeof runMatrixStartupMaintenance>[0] {
|
||||
|
|
@ -151,7 +140,7 @@ describe("runMatrixStartupMaintenance", () => {
|
|||
it("persists converted avatar URLs after profile sync", async () => {
|
||||
const params = createParams();
|
||||
const updatedCfg = { channels: { matrix: { avatarUrl: "mxc://avatar" } } };
|
||||
hoisted.syncMatrixOwnProfile.mockResolvedValue(
|
||||
vi.mocked(deps.syncMatrixOwnProfile).mockResolvedValue(
|
||||
createProfileSyncResult({
|
||||
avatarUpdated: true,
|
||||
resolvedAvatarUrl: "mxc://avatar",
|
||||
|
|
@ -159,18 +148,18 @@ describe("runMatrixStartupMaintenance", () => {
|
|||
convertedAvatarFromHttp: true,
|
||||
}),
|
||||
);
|
||||
hoisted.updateMatrixAccountConfig.mockReturnValue(updatedCfg);
|
||||
vi.mocked(deps.updateMatrixAccountConfig).mockReturnValue(updatedCfg);
|
||||
|
||||
await runMatrixStartupMaintenance(params);
|
||||
await runMatrixStartupMaintenance(params, deps);
|
||||
|
||||
expect(hoisted.syncMatrixOwnProfile).toHaveBeenCalledWith(
|
||||
expect(deps.syncMatrixOwnProfile).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
userId: "@bot:example.org",
|
||||
displayName: "Ops Bot",
|
||||
avatarUrl: "https://example.org/avatar.png",
|
||||
}),
|
||||
);
|
||||
expect(hoisted.updateMatrixAccountConfig).toHaveBeenCalledWith(
|
||||
expect(deps.updateMatrixAccountConfig).toHaveBeenCalledWith(
|
||||
{ channels: { matrix: {} } },
|
||||
"ops",
|
||||
{ avatarUrl: "mxc://avatar" },
|
||||
|
|
@ -184,13 +173,17 @@ describe("runMatrixStartupMaintenance", () => {
|
|||
it("reports stale devices, pending verification, and restored legacy backups", async () => {
|
||||
const params = createParams();
|
||||
params.auth.encryption = true;
|
||||
hoisted.summarizeMatrixDeviceHealth.mockReturnValue({
|
||||
staleOpenClawDevices: [{ deviceId: "DEV123" }],
|
||||
vi.mocked(deps.summarizeMatrixDeviceHealth).mockReturnValue({
|
||||
currentDeviceId: null,
|
||||
staleOpenClawDevices: [
|
||||
{ deviceId: "DEV123", displayName: "OpenClaw Device", current: false },
|
||||
],
|
||||
currentOpenClawDevices: [],
|
||||
});
|
||||
hoisted.ensureMatrixStartupVerification.mockResolvedValue(
|
||||
vi.mocked(deps.ensureMatrixStartupVerification).mockResolvedValue(
|
||||
createStartupVerificationOutcome("pending"),
|
||||
);
|
||||
hoisted.maybeRestoreLegacyMatrixBackup.mockResolvedValue(
|
||||
vi.mocked(deps.maybeRestoreLegacyMatrixBackup).mockResolvedValue(
|
||||
createLegacyCryptoRestoreResult({
|
||||
kind: "restored",
|
||||
imported: 2,
|
||||
|
|
@ -199,7 +192,7 @@ describe("runMatrixStartupMaintenance", () => {
|
|||
}),
|
||||
);
|
||||
|
||||
await runMatrixStartupMaintenance(params);
|
||||
await runMatrixStartupMaintenance(params, deps);
|
||||
|
||||
expect(params.logger.warn).toHaveBeenCalledWith(
|
||||
"matrix: stale OpenClaw devices detected for @bot:example.org: DEV123. Run 'openclaw matrix devices prune-stale --account ops' to keep encrypted-room trust healthy.",
|
||||
|
|
@ -221,21 +214,21 @@ describe("runMatrixStartupMaintenance", () => {
|
|||
it("logs cooldown and request-failure verification outcomes without throwing", async () => {
|
||||
const params = createParams();
|
||||
params.auth.encryption = true;
|
||||
hoisted.ensureMatrixStartupVerification.mockResolvedValueOnce(
|
||||
vi.mocked(deps.ensureMatrixStartupVerification).mockResolvedValueOnce(
|
||||
createStartupVerificationOutcome("cooldown", { retryAfterMs: 321 }),
|
||||
);
|
||||
|
||||
await runMatrixStartupMaintenance(params);
|
||||
await runMatrixStartupMaintenance(params, deps);
|
||||
|
||||
expect(params.logVerboseMessage).toHaveBeenCalledWith(
|
||||
"matrix: skipped startup verification request due to cooldown (retryAfterMs=321)",
|
||||
);
|
||||
|
||||
hoisted.ensureMatrixStartupVerification.mockResolvedValueOnce(
|
||||
vi.mocked(deps.ensureMatrixStartupVerification).mockResolvedValueOnce(
|
||||
createStartupVerificationOutcome("request-failed", { error: "boom" }),
|
||||
);
|
||||
|
||||
await runMatrixStartupMaintenance(params);
|
||||
await runMatrixStartupMaintenance(params, deps);
|
||||
|
||||
expect(params.logger.debug).toHaveBeenCalledWith(
|
||||
"Matrix startup verification request failed (non-fatal)",
|
||||
|
|
|
|||
|
|
@ -1,12 +1,7 @@
|
|||
import type { RuntimeLogger } from "../../runtime-api.js";
|
||||
import type { CoreConfig, MatrixConfig } from "../../types.js";
|
||||
import type { MatrixAuth } from "../client.js";
|
||||
import { updateMatrixAccountConfig } from "../config-update.js";
|
||||
import { summarizeMatrixDeviceHealth } from "../device-health.js";
|
||||
import { syncMatrixOwnProfile } from "../profile.js";
|
||||
import type { MatrixClient } from "../sdk.js";
|
||||
import { maybeRestoreLegacyMatrixBackup } from "./legacy-crypto-restore.js";
|
||||
import { ensureMatrixStartupVerification } from "./startup-verification.js";
|
||||
|
||||
type MatrixStartupClient = Pick<
|
||||
MatrixClient,
|
||||
|
|
@ -20,24 +15,63 @@ type MatrixStartupClient = Pick<
|
|||
| "uploadContent"
|
||||
>;
|
||||
|
||||
export async function runMatrixStartupMaintenance(params: {
|
||||
client: MatrixStartupClient;
|
||||
auth: MatrixAuth;
|
||||
accountId: string;
|
||||
effectiveAccountId: string;
|
||||
accountConfig: MatrixConfig;
|
||||
logger: RuntimeLogger;
|
||||
logVerboseMessage: (message: string) => void;
|
||||
loadConfig: () => CoreConfig;
|
||||
writeConfigFile: (cfg: never) => Promise<void>;
|
||||
loadWebMedia: (
|
||||
url: string,
|
||||
maxBytes: number,
|
||||
) => Promise<{ buffer: Buffer; contentType?: string; fileName?: string }>;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
}): Promise<void> {
|
||||
export type MatrixStartupMaintenanceDeps = {
|
||||
updateMatrixAccountConfig: typeof import("../config-update.js").updateMatrixAccountConfig;
|
||||
summarizeMatrixDeviceHealth: typeof import("../device-health.js").summarizeMatrixDeviceHealth;
|
||||
syncMatrixOwnProfile: typeof import("../profile.js").syncMatrixOwnProfile;
|
||||
maybeRestoreLegacyMatrixBackup: typeof import("./legacy-crypto-restore.js").maybeRestoreLegacyMatrixBackup;
|
||||
ensureMatrixStartupVerification: typeof import("./startup-verification.js").ensureMatrixStartupVerification;
|
||||
};
|
||||
|
||||
let matrixStartupMaintenanceDepsPromise: Promise<MatrixStartupMaintenanceDeps> | undefined;
|
||||
|
||||
async function loadMatrixStartupMaintenanceDeps(): Promise<MatrixStartupMaintenanceDeps> {
|
||||
matrixStartupMaintenanceDepsPromise ??= Promise.all([
|
||||
import("../config-update.js"),
|
||||
import("../device-health.js"),
|
||||
import("../profile.js"),
|
||||
import("./legacy-crypto-restore.js"),
|
||||
import("./startup-verification.js"),
|
||||
]).then(
|
||||
([
|
||||
configUpdateModule,
|
||||
deviceHealthModule,
|
||||
profileModule,
|
||||
legacyCryptoRestoreModule,
|
||||
startupVerificationModule,
|
||||
]) => ({
|
||||
updateMatrixAccountConfig: configUpdateModule.updateMatrixAccountConfig,
|
||||
summarizeMatrixDeviceHealth: deviceHealthModule.summarizeMatrixDeviceHealth,
|
||||
syncMatrixOwnProfile: profileModule.syncMatrixOwnProfile,
|
||||
maybeRestoreLegacyMatrixBackup: legacyCryptoRestoreModule.maybeRestoreLegacyMatrixBackup,
|
||||
ensureMatrixStartupVerification: startupVerificationModule.ensureMatrixStartupVerification,
|
||||
}),
|
||||
);
|
||||
return await matrixStartupMaintenanceDepsPromise;
|
||||
}
|
||||
|
||||
export async function runMatrixStartupMaintenance(
|
||||
params: {
|
||||
client: MatrixStartupClient;
|
||||
auth: MatrixAuth;
|
||||
accountId: string;
|
||||
effectiveAccountId: string;
|
||||
accountConfig: MatrixConfig;
|
||||
logger: RuntimeLogger;
|
||||
logVerboseMessage: (message: string) => void;
|
||||
loadConfig: () => CoreConfig;
|
||||
writeConfigFile: (cfg: never) => Promise<void>;
|
||||
loadWebMedia: (
|
||||
url: string,
|
||||
maxBytes: number,
|
||||
) => Promise<{ buffer: Buffer; contentType?: string; fileName?: string }>;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
},
|
||||
deps?: MatrixStartupMaintenanceDeps,
|
||||
): Promise<void> {
|
||||
const runtimeDeps = deps ?? (await loadMatrixStartupMaintenanceDeps());
|
||||
try {
|
||||
const profileSync = await syncMatrixOwnProfile({
|
||||
const profileSync = await runtimeDeps.syncMatrixOwnProfile({
|
||||
client: params.client,
|
||||
userId: params.auth.userId,
|
||||
displayName: params.accountConfig.name,
|
||||
|
|
@ -56,7 +90,7 @@ export async function runMatrixStartupMaintenance(params: {
|
|||
params.accountConfig.avatarUrl !== profileSync.resolvedAvatarUrl
|
||||
) {
|
||||
const latestCfg = params.loadConfig();
|
||||
const updatedCfg = updateMatrixAccountConfig(latestCfg, params.accountId, {
|
||||
const updatedCfg = runtimeDeps.updateMatrixAccountConfig(latestCfg, params.accountId, {
|
||||
avatarUrl: profileSync.resolvedAvatarUrl,
|
||||
});
|
||||
await params.writeConfigFile(updatedCfg as never);
|
||||
|
|
@ -73,7 +107,9 @@ export async function runMatrixStartupMaintenance(params: {
|
|||
}
|
||||
|
||||
try {
|
||||
const deviceHealth = summarizeMatrixDeviceHealth(await params.client.listOwnDevices());
|
||||
const deviceHealth = runtimeDeps.summarizeMatrixDeviceHealth(
|
||||
await params.client.listOwnDevices(),
|
||||
);
|
||||
if (deviceHealth.staleOpenClawDevices.length > 0) {
|
||||
params.logger.warn(
|
||||
`matrix: stale OpenClaw devices detected for ${params.auth.userId}: ${deviceHealth.staleOpenClawDevices.map((device) => device.deviceId).join(", ")}. Run 'openclaw matrix devices prune-stale --account ${params.effectiveAccountId}' to keep encrypted-room trust healthy.`,
|
||||
|
|
@ -86,7 +122,7 @@ export async function runMatrixStartupMaintenance(params: {
|
|||
}
|
||||
|
||||
try {
|
||||
const startupVerification = await ensureMatrixStartupVerification({
|
||||
const startupVerification = await runtimeDeps.ensureMatrixStartupVerification({
|
||||
client: params.client,
|
||||
auth: params.auth,
|
||||
accountConfig: params.accountConfig,
|
||||
|
|
@ -128,7 +164,7 @@ export async function runMatrixStartupMaintenance(params: {
|
|||
}
|
||||
|
||||
try {
|
||||
const legacyCryptoRestore = await maybeRestoreLegacyMatrixBackup({
|
||||
const legacyCryptoRestore = await runtimeDeps.maybeRestoreLegacyMatrixBackup({
|
||||
client: params.client,
|
||||
auth: params.auth,
|
||||
env: params.env,
|
||||
|
|
|
|||
Loading…
Reference in New Issue