test: tighten matrix sync lifecycle assertions

This commit is contained in:
Peter Steinberger
2026-05-11 01:27:53 +01:00
parent 66926b2037
commit 06d025be0f

View File

@@ -36,6 +36,26 @@ function createSyncLifecycleHarness(options?: { withStopping?: boolean }) {
};
}
function statusCalls(setStatus: ReturnType<typeof vi.fn>): Record<string, unknown>[] {
return setStatus.mock.calls.map(([status]) => status as Record<string, unknown>);
}
function lastStatus(setStatus: ReturnType<typeof vi.fn>): Record<string, unknown> {
const status = statusCalls(setStatus).at(-1);
expect(status).toBeDefined();
return status ?? {};
}
function expectLastStatusFields(
setStatus: ReturnType<typeof vi.fn>,
fields: Record<string, unknown>,
): void {
const status = lastStatus(setStatus);
for (const [key, value] of Object.entries(fields)) {
expect(status[key]).toEqual(value);
}
}
describe("createMatrixMonitorSyncLifecycle", () => {
it("rejects the channel wait on unexpected sync errors", async () => {
const { client, lifecycle, setStatus } = createSyncLifecycleHarness();
@@ -44,13 +64,11 @@ describe("createMatrixMonitorSyncLifecycle", () => {
client.emit("sync.unexpected_error", new Error("sync exploded"));
await expect(waitPromise).rejects.toThrow("sync exploded");
expect(setStatus).toHaveBeenCalledWith(
expect.objectContaining({
accountId: "default",
healthState: "error",
lastError: "sync exploded",
}),
);
expectLastStatusFields(setStatus, {
accountId: "default",
healthState: "error",
lastError: "sync exploded",
});
});
it("ignores STOPPED emitted during intentional shutdown", async () => {
@@ -64,12 +82,10 @@ describe("createMatrixMonitorSyncLifecycle", () => {
lifecycle.dispose();
await expect(waitPromise).resolves.toBeUndefined();
expect(setStatus).toHaveBeenCalledWith(
expect.objectContaining({
accountId: "default",
healthState: "stopped",
}),
);
expectLastStatusFields(setStatus, {
accountId: "default",
healthState: "stopped",
});
});
it("marks unexpected STOPPED sync as an error state", async () => {
@@ -79,13 +95,11 @@ describe("createMatrixMonitorSyncLifecycle", () => {
client.emit("sync.state", "STOPPED", "SYNCING", undefined);
await expect(waitPromise).rejects.toThrow("Matrix sync stopped unexpectedly");
expect(setStatus).toHaveBeenCalledWith(
expect.objectContaining({
accountId: "default",
healthState: "error",
lastError: "Matrix sync stopped unexpectedly",
}),
);
expectLastStatusFields(setStatus, {
accountId: "default",
healthState: "error",
lastError: "Matrix sync stopped unexpectedly",
});
});
it("ignores unexpected sync errors emitted during intentional shutdown", async () => {
@@ -99,12 +113,11 @@ describe("createMatrixMonitorSyncLifecycle", () => {
lifecycle.dispose();
await expect(waitPromise).resolves.toBeUndefined();
expect(setStatus).not.toHaveBeenCalledWith(
expect.objectContaining({
accountId: "default",
healthState: "error",
}),
);
expect(
statusCalls(setStatus).some(
(status) => status.accountId === "default" && status.healthState === "error",
),
).toBe(false);
});
it("ignores non-terminal sync states emitted during intentional shutdown", async () => {
@@ -120,13 +133,11 @@ describe("createMatrixMonitorSyncLifecycle", () => {
statusController.markStopped();
await expect(waitPromise).resolves.toBeUndefined();
expect(setStatus).toHaveBeenLastCalledWith(
expect.objectContaining({
accountId: "default",
healthState: "stopped",
lastError: null,
}),
);
expectLastStatusFields(setStatus, {
accountId: "default",
healthState: "stopped",
lastError: null,
});
});
it("only refreshes transport liveness for successful sync responses", async () => {
@@ -137,28 +148,16 @@ describe("createMatrixMonitorSyncLifecycle", () => {
setStatus.mockClear();
client.emit("sync.state", "PREPARED", null, undefined);
expect(setStatus).toHaveBeenLastCalledWith(
expect.not.objectContaining({
lastTransportActivityAt: expect.any(Number),
}),
);
expect(lastStatus(setStatus).lastTransportActivityAt).toBeUndefined();
await vi.advanceTimersByTimeAsync(2_000);
client.emit("sync.state", "SYNCING", "PREPARED", undefined);
const syncAt = Date.now();
expect(setStatus).toHaveBeenLastCalledWith(
expect.objectContaining({
lastTransportActivityAt: syncAt,
}),
);
expect(lastStatus(setStatus).lastTransportActivityAt).toBe(syncAt);
await vi.advanceTimersByTimeAsync(3_000);
client.emit("sync.state", "CATCHUP", "SYNCING", undefined);
expect(setStatus).toHaveBeenLastCalledWith(
expect.objectContaining({
lastTransportActivityAt: syncAt,
}),
);
expect(lastStatus(setStatus).lastTransportActivityAt).toBe(syncAt);
} finally {
lifecycle.dispose();
vi.useRealTimers();
@@ -180,13 +179,11 @@ describe("createMatrixMonitorSyncLifecycle", () => {
lifecycle.dispose();
statusController.markStopped();
expect(setStatus).toHaveBeenLastCalledWith(
expect.objectContaining({
accountId: "default",
healthState: "error",
lastError: "sync exploded",
}),
);
expectLastStatusFields(setStatus, {
accountId: "default",
healthState: "error",
lastError: "sync exploded",
});
});
it("ignores follow-up sync states after a fatal sync error", async () => {
@@ -199,13 +196,11 @@ describe("createMatrixMonitorSyncLifecycle", () => {
client.emit("sync.state", "RECONNECTING", "SYNCING", new Error("late reconnect"));
lifecycle.dispose();
expect(setStatus).toHaveBeenLastCalledWith(
expect.objectContaining({
accountId: "default",
healthState: "error",
lastError: "sync exploded",
}),
);
expectLastStatusFields(setStatus, {
accountId: "default",
healthState: "error",
lastError: "sync exploded",
});
});
it("rejects a second concurrent fatal-stop waiter", async () => {