

























@@ -36,6 +36,26 @@ function createSyncLifecycleHarness(options?: { withStopping?: boolean }) {
3636};
3737}
383839+function statusCalls(setStatus: ReturnType<typeof vi.fn>): Record<string, unknown>[] {
40+return setStatus.mock.calls.map(([status]) => status as Record<string, unknown>);
41+}
42+43+function lastStatus(setStatus: ReturnType<typeof vi.fn>): Record<string, unknown> {
44+const status = statusCalls(setStatus).at(-1);
45+expect(status).toBeDefined();
46+return status ?? {};
47+}
48+49+function expectLastStatusFields(
50+setStatus: ReturnType<typeof vi.fn>,
51+fields: Record<string, unknown>,
52+): void {
53+const status = lastStatus(setStatus);
54+for (const [key, value] of Object.entries(fields)) {
55+expect(status[key]).toEqual(value);
56+}
57+}
58+3959describe("createMatrixMonitorSyncLifecycle", () => {
4060it("rejects the channel wait on unexpected sync errors", async () => {
4161const { client, lifecycle, setStatus } = createSyncLifecycleHarness();
@@ -44,13 +64,11 @@ describe("createMatrixMonitorSyncLifecycle", () => {
4464client.emit("sync.unexpected_error", new Error("sync exploded"));
45654666await expect(waitPromise).rejects.toThrow("sync exploded");
47-expect(setStatus).toHaveBeenCalledWith(
48-expect.objectContaining({
49-accountId: "default",
50-healthState: "error",
51-lastError: "sync exploded",
52-}),
53-);
67+expectLastStatusFields(setStatus, {
68+accountId: "default",
69+healthState: "error",
70+lastError: "sync exploded",
71+});
5472});
55735674it("ignores STOPPED emitted during intentional shutdown", async () => {
@@ -64,12 +82,10 @@ describe("createMatrixMonitorSyncLifecycle", () => {
6482lifecycle.dispose();
65836684await expect(waitPromise).resolves.toBeUndefined();
67-expect(setStatus).toHaveBeenCalledWith(
68-expect.objectContaining({
69-accountId: "default",
70-healthState: "stopped",
71-}),
72-);
85+expectLastStatusFields(setStatus, {
86+accountId: "default",
87+healthState: "stopped",
88+});
7389});
74907591it("marks unexpected STOPPED sync as an error state", async () => {
@@ -79,13 +95,11 @@ describe("createMatrixMonitorSyncLifecycle", () => {
7995client.emit("sync.state", "STOPPED", "SYNCING", undefined);
80968197await expect(waitPromise).rejects.toThrow("Matrix sync stopped unexpectedly");
82-expect(setStatus).toHaveBeenCalledWith(
83-expect.objectContaining({
84-accountId: "default",
85-healthState: "error",
86-lastError: "Matrix sync stopped unexpectedly",
87-}),
88-);
98+expectLastStatusFields(setStatus, {
99+accountId: "default",
100+healthState: "error",
101+lastError: "Matrix sync stopped unexpectedly",
102+});
89103});
9010491105it("ignores unexpected sync errors emitted during intentional shutdown", async () => {
@@ -99,12 +113,11 @@ describe("createMatrixMonitorSyncLifecycle", () => {
99113lifecycle.dispose();
100114101115await expect(waitPromise).resolves.toBeUndefined();
102-expect(setStatus).not.toHaveBeenCalledWith(
103-expect.objectContaining({
104-accountId: "default",
105-healthState: "error",
106-}),
107-);
116+expect(
117+statusCalls(setStatus).some(
118+(status) => status.accountId === "default" && status.healthState === "error",
119+),
120+).toBe(false);
108121});
109122110123it("ignores non-terminal sync states emitted during intentional shutdown", async () => {
@@ -120,13 +133,11 @@ describe("createMatrixMonitorSyncLifecycle", () => {
120133statusController.markStopped();
121134122135await expect(waitPromise).resolves.toBeUndefined();
123-expect(setStatus).toHaveBeenLastCalledWith(
124-expect.objectContaining({
125-accountId: "default",
126-healthState: "stopped",
127-lastError: null,
128-}),
129-);
136+expectLastStatusFields(setStatus, {
137+accountId: "default",
138+healthState: "stopped",
139+lastError: null,
140+});
130141});
131142132143it("only refreshes transport liveness for successful sync responses", async () => {
@@ -137,28 +148,16 @@ describe("createMatrixMonitorSyncLifecycle", () => {
137148setStatus.mockClear();
138149139150client.emit("sync.state", "PREPARED", null, undefined);
140-expect(setStatus).toHaveBeenLastCalledWith(
141-expect.not.objectContaining({
142-lastTransportActivityAt: expect.any(Number),
143-}),
144-);
151+expect(lastStatus(setStatus).lastTransportActivityAt).toBeUndefined();
145152146153await vi.advanceTimersByTimeAsync(2_000);
147154client.emit("sync.state", "SYNCING", "PREPARED", undefined);
148155const syncAt = Date.now();
149-expect(setStatus).toHaveBeenLastCalledWith(
150-expect.objectContaining({
151-lastTransportActivityAt: syncAt,
152-}),
153-);
156+expect(lastStatus(setStatus).lastTransportActivityAt).toBe(syncAt);
154157155158await vi.advanceTimersByTimeAsync(3_000);
156159client.emit("sync.state", "CATCHUP", "SYNCING", undefined);
157-expect(setStatus).toHaveBeenLastCalledWith(
158-expect.objectContaining({
159-lastTransportActivityAt: syncAt,
160-}),
161-);
160+expect(lastStatus(setStatus).lastTransportActivityAt).toBe(syncAt);
162161} finally {
163162lifecycle.dispose();
164163vi.useRealTimers();
@@ -180,13 +179,11 @@ describe("createMatrixMonitorSyncLifecycle", () => {
180179lifecycle.dispose();
181180statusController.markStopped();
182181183-expect(setStatus).toHaveBeenLastCalledWith(
184-expect.objectContaining({
185-accountId: "default",
186-healthState: "error",
187-lastError: "sync exploded",
188-}),
189-);
182+expectLastStatusFields(setStatus, {
183+accountId: "default",
184+healthState: "error",
185+lastError: "sync exploded",
186+});
190187});
191188192189it("ignores follow-up sync states after a fatal sync error", async () => {
@@ -199,13 +196,11 @@ describe("createMatrixMonitorSyncLifecycle", () => {
199196client.emit("sync.state", "RECONNECTING", "SYNCING", new Error("late reconnect"));
200197lifecycle.dispose();
201198202-expect(setStatus).toHaveBeenLastCalledWith(
203-expect.objectContaining({
204-accountId: "default",
205-healthState: "error",
206-lastError: "sync exploded",
207-}),
208-);
199+expectLastStatusFields(setStatus, {
200+accountId: "default",
201+healthState: "error",
202+lastError: "sync exploded",
203+});
209204});
210205211206it("rejects a second concurrent fatal-stop waiter", async () => {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。