mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:00:43 +00:00
fix(regression): avoid sync startup for matrix status reads
This commit is contained in:
@@ -1,20 +1,23 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const withResolvedActionClientMock = vi.fn();
|
||||
const withStartedActionClientMock = vi.fn();
|
||||
|
||||
vi.mock("./client.js", () => ({
|
||||
withResolvedActionClient: (...args: unknown[]) => withResolvedActionClientMock(...args),
|
||||
withStartedActionClient: (...args: unknown[]) => withStartedActionClientMock(...args),
|
||||
}));
|
||||
|
||||
const { listMatrixOwnDevices, pruneMatrixStaleGatewayDevices } = await import("./devices.js");
|
||||
const { getMatrixDeviceHealth, listMatrixOwnDevices, pruneMatrixStaleGatewayDevices } =
|
||||
await import("./devices.js");
|
||||
|
||||
describe("matrix device actions", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("lists own devices on a started client", async () => {
|
||||
withStartedActionClientMock.mockImplementation(async (_opts, run) => {
|
||||
it("lists own devices without starting a sync client", async () => {
|
||||
withResolvedActionClientMock.mockImplementation(async (_opts, run) => {
|
||||
return await run({
|
||||
listOwnDevices: vi.fn(async () => [
|
||||
{
|
||||
@@ -30,10 +33,11 @@ describe("matrix device actions", () => {
|
||||
|
||||
const result = await listMatrixOwnDevices({ accountId: "poe" });
|
||||
|
||||
expect(withStartedActionClientMock).toHaveBeenCalledWith(
|
||||
expect(withResolvedActionClientMock).toHaveBeenCalledWith(
|
||||
{ accountId: "poe" },
|
||||
expect.any(Function),
|
||||
);
|
||||
expect(withStartedActionClientMock).not.toHaveBeenCalled();
|
||||
expect(result).toEqual([
|
||||
expect.objectContaining({
|
||||
deviceId: "A7hWrQ70ea",
|
||||
@@ -42,6 +46,42 @@ describe("matrix device actions", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("computes device health without starting a sync client", async () => {
|
||||
withResolvedActionClientMock.mockImplementation(async (_opts, run) => {
|
||||
return await run({
|
||||
listOwnDevices: vi.fn(async () => [
|
||||
{
|
||||
deviceId: "du314Zpw3A",
|
||||
displayName: "OpenClaw Gateway",
|
||||
lastSeenIp: null,
|
||||
lastSeenTs: null,
|
||||
current: true,
|
||||
},
|
||||
{
|
||||
deviceId: "old123",
|
||||
displayName: "OpenClaw Gateway",
|
||||
lastSeenIp: null,
|
||||
lastSeenTs: null,
|
||||
current: false,
|
||||
},
|
||||
]),
|
||||
});
|
||||
});
|
||||
|
||||
const result = await getMatrixDeviceHealth({ accountId: "poe" });
|
||||
|
||||
expect(result.staleOpenClawDevices).toEqual([
|
||||
expect.objectContaining({
|
||||
deviceId: "old123",
|
||||
}),
|
||||
]);
|
||||
expect(withResolvedActionClientMock).toHaveBeenCalledWith(
|
||||
{ accountId: "poe" },
|
||||
expect.any(Function),
|
||||
);
|
||||
expect(withStartedActionClientMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("prunes stale OpenClaw-managed devices but preserves the current device", async () => {
|
||||
const deleteOwnDevices = vi.fn(async () => ({
|
||||
currentDeviceId: "du314Zpw3A",
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { summarizeMatrixDeviceHealth } from "../device-health.js";
|
||||
import { withStartedActionClient } from "./client.js";
|
||||
import { withResolvedActionClient, withStartedActionClient } from "./client.js";
|
||||
import type { MatrixActionClientOpts } from "./types.js";
|
||||
|
||||
export async function listMatrixOwnDevices(opts: MatrixActionClientOpts = {}) {
|
||||
return await withStartedActionClient(opts, async (client) => await client.listOwnDevices());
|
||||
return await withResolvedActionClient(opts, async (client) => await client.listOwnDevices());
|
||||
}
|
||||
|
||||
export async function pruneMatrixStaleGatewayDevices(opts: MatrixActionClientOpts = {}) {
|
||||
@@ -28,7 +28,7 @@ export async function pruneMatrixStaleGatewayDevices(opts: MatrixActionClientOpt
|
||||
}
|
||||
|
||||
export async function getMatrixDeviceHealth(opts: MatrixActionClientOpts = {}) {
|
||||
return await withStartedActionClient(opts, async (client) =>
|
||||
return await withResolvedActionClient(opts, async (client) =>
|
||||
summarizeMatrixDeviceHealth(await client.listOwnDevices()),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const withResolvedActionClientMock = vi.fn();
|
||||
const withStartedActionClientMock = vi.fn();
|
||||
const loadConfigMock = vi.fn(() => ({
|
||||
channels: {
|
||||
@@ -16,14 +17,23 @@ vi.mock("../../runtime.js", () => ({
|
||||
}));
|
||||
|
||||
vi.mock("./client.js", () => ({
|
||||
withResolvedActionClient: (...args: unknown[]) => withResolvedActionClientMock(...args),
|
||||
withStartedActionClient: (...args: unknown[]) => withStartedActionClientMock(...args),
|
||||
}));
|
||||
|
||||
let listMatrixVerifications: typeof import("./verification.js").listMatrixVerifications;
|
||||
let getMatrixEncryptionStatus: typeof import("./verification.js").getMatrixEncryptionStatus;
|
||||
let getMatrixRoomKeyBackupStatus: typeof import("./verification.js").getMatrixRoomKeyBackupStatus;
|
||||
let getMatrixVerificationStatus: typeof import("./verification.js").getMatrixVerificationStatus;
|
||||
|
||||
describe("matrix verification actions", () => {
|
||||
beforeAll(async () => {
|
||||
({ listMatrixVerifications } = await import("./verification.js"));
|
||||
({
|
||||
getMatrixEncryptionStatus,
|
||||
getMatrixRoomKeyBackupStatus,
|
||||
getMatrixVerificationStatus,
|
||||
listMatrixVerifications,
|
||||
} = await import("./verification.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -102,4 +112,93 @@ describe("matrix verification actions", () => {
|
||||
);
|
||||
expect(loadConfigMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("resolves verification status without starting the Matrix client", async () => {
|
||||
withResolvedActionClientMock.mockImplementation(async (_opts, run) => {
|
||||
return await run({
|
||||
crypto: {
|
||||
listVerifications: vi.fn(async () => []),
|
||||
getRecoveryKey: vi.fn(async () => ({
|
||||
encodedPrivateKey: "rec-key",
|
||||
})),
|
||||
},
|
||||
getOwnDeviceVerificationStatus: vi.fn(async () => ({
|
||||
encryptionEnabled: true,
|
||||
verified: true,
|
||||
userId: "@bot:example.org",
|
||||
deviceId: "DEVICE123",
|
||||
localVerified: true,
|
||||
crossSigningVerified: true,
|
||||
signedByOwner: true,
|
||||
recoveryKeyStored: true,
|
||||
recoveryKeyCreatedAt: null,
|
||||
recoveryKeyId: "SSSS",
|
||||
backupVersion: "11",
|
||||
backup: {
|
||||
serverVersion: "11",
|
||||
activeVersion: "11",
|
||||
trusted: true,
|
||||
matchesDecryptionKey: true,
|
||||
decryptionKeyCached: true,
|
||||
keyLoadAttempted: false,
|
||||
keyLoadError: null,
|
||||
},
|
||||
})),
|
||||
});
|
||||
});
|
||||
|
||||
const status = await getMatrixVerificationStatus({ includeRecoveryKey: true });
|
||||
|
||||
expect(status).toMatchObject({
|
||||
verified: true,
|
||||
pendingVerifications: 0,
|
||||
recoveryKey: "rec-key",
|
||||
});
|
||||
expect(withResolvedActionClientMock).toHaveBeenCalledTimes(1);
|
||||
expect(withStartedActionClientMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("resolves encryption and backup status without starting the Matrix client", async () => {
|
||||
withResolvedActionClientMock
|
||||
.mockImplementationOnce(async (_opts, run) => {
|
||||
return await run({
|
||||
crypto: {
|
||||
getRecoveryKey: vi.fn(async () => ({
|
||||
encodedPrivateKey: "rec-key",
|
||||
createdAt: "2026-01-01T00:00:00.000Z",
|
||||
})),
|
||||
listVerifications: vi.fn(async () => [{ id: "req-1" }]),
|
||||
},
|
||||
});
|
||||
})
|
||||
.mockImplementationOnce(async (_opts, run) => {
|
||||
return await run({
|
||||
getRoomKeyBackupStatus: vi.fn(async () => ({
|
||||
serverVersion: "11",
|
||||
activeVersion: "11",
|
||||
trusted: true,
|
||||
matchesDecryptionKey: true,
|
||||
decryptionKeyCached: true,
|
||||
keyLoadAttempted: false,
|
||||
keyLoadError: null,
|
||||
})),
|
||||
});
|
||||
});
|
||||
|
||||
const encryption = await getMatrixEncryptionStatus({ includeRecoveryKey: true });
|
||||
const backup = await getMatrixRoomKeyBackupStatus();
|
||||
|
||||
expect(encryption).toMatchObject({
|
||||
encryptionEnabled: true,
|
||||
recoveryKeyStored: true,
|
||||
recoveryKey: "rec-key",
|
||||
pendingVerifications: 1,
|
||||
});
|
||||
expect(backup).toMatchObject({
|
||||
serverVersion: "11",
|
||||
trusted: true,
|
||||
});
|
||||
expect(withResolvedActionClientMock).toHaveBeenCalledTimes(2);
|
||||
expect(withStartedActionClientMock).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@ import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
|
||||
import { getMatrixRuntime } from "../../runtime.js";
|
||||
import type { CoreConfig } from "../../types.js";
|
||||
import { formatMatrixEncryptionUnavailableError } from "../encryption-guidance.js";
|
||||
import { withStartedActionClient } from "./client.js";
|
||||
import { withResolvedActionClient, withStartedActionClient } from "./client.js";
|
||||
import type { MatrixActionClientOpts } from "./types.js";
|
||||
|
||||
function requireCrypto(
|
||||
@@ -152,7 +152,7 @@ export async function confirmMatrixVerificationReciprocateQr(
|
||||
export async function getMatrixEncryptionStatus(
|
||||
opts: MatrixActionClientOpts & { includeRecoveryKey?: boolean } = {},
|
||||
) {
|
||||
return await withStartedActionClient(opts, async (client) => {
|
||||
return await withResolvedActionClient(opts, async (client) => {
|
||||
const crypto = requireCrypto(client, opts);
|
||||
const recoveryKey = await crypto.getRecoveryKey();
|
||||
return {
|
||||
@@ -168,7 +168,7 @@ export async function getMatrixEncryptionStatus(
|
||||
export async function getMatrixVerificationStatus(
|
||||
opts: MatrixActionClientOpts & { includeRecoveryKey?: boolean } = {},
|
||||
) {
|
||||
return await withStartedActionClient(opts, async (client) => {
|
||||
return await withResolvedActionClient(opts, async (client) => {
|
||||
const status = await client.getOwnDeviceVerificationStatus();
|
||||
const payload = {
|
||||
...status,
|
||||
@@ -186,7 +186,7 @@ export async function getMatrixVerificationStatus(
|
||||
}
|
||||
|
||||
export async function getMatrixRoomKeyBackupStatus(opts: MatrixActionClientOpts = {}) {
|
||||
return await withStartedActionClient(
|
||||
return await withResolvedActionClient(
|
||||
opts,
|
||||
async (client) => await client.getRoomKeyBackupStatus(),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user