mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-27 09:02:15 +00:00
refactor: share readiness test harness
This commit is contained in:
@@ -46,172 +46,188 @@ function createHealthyDiscordManager(startedAt: number, lastEventAt: number): Ch
|
||||
);
|
||||
}
|
||||
|
||||
function withReadinessClock(run: () => void) {
|
||||
vi.useFakeTimers();
|
||||
vi.setSystemTime(new Date("2026-03-06T12:00:00Z"));
|
||||
try {
|
||||
run();
|
||||
} finally {
|
||||
vi.useRealTimers();
|
||||
}
|
||||
}
|
||||
|
||||
function createReadinessHarness(params: {
|
||||
startedAgoMs: number;
|
||||
accounts: Record<string, Partial<ChannelAccountSnapshot>>;
|
||||
cacheTtlMs?: number;
|
||||
}) {
|
||||
const startedAt = Date.now() - params.startedAgoMs;
|
||||
const manager = createManager(snapshotWith(params.accounts));
|
||||
return {
|
||||
manager,
|
||||
readiness: createReadinessChecker({
|
||||
channelManager: manager,
|
||||
startedAt,
|
||||
cacheTtlMs: params.cacheTtlMs,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
describe("createReadinessChecker", () => {
|
||||
it("reports ready when all managed channels are healthy", () => {
|
||||
vi.useFakeTimers();
|
||||
vi.setSystemTime(new Date("2026-03-06T12:00:00Z"));
|
||||
const startedAt = Date.now() - 5 * 60_000;
|
||||
const manager = createHealthyDiscordManager(startedAt, Date.now() - 1_000);
|
||||
withReadinessClock(() => {
|
||||
const startedAt = Date.now() - 5 * 60_000;
|
||||
const manager = createHealthyDiscordManager(startedAt, Date.now() - 1_000);
|
||||
|
||||
const readiness = createReadinessChecker({ channelManager: manager, startedAt });
|
||||
expect(readiness()).toEqual({ ready: true, failing: [], uptimeMs: 300_000 });
|
||||
vi.useRealTimers();
|
||||
const readiness = createReadinessChecker({ channelManager: manager, startedAt });
|
||||
expect(readiness()).toEqual({ ready: true, failing: [], uptimeMs: 300_000 });
|
||||
});
|
||||
});
|
||||
|
||||
it("ignores disabled and unconfigured channels", () => {
|
||||
vi.useFakeTimers();
|
||||
vi.setSystemTime(new Date("2026-03-06T12:00:00Z"));
|
||||
const startedAt = Date.now() - 5 * 60_000;
|
||||
const manager = createManager(
|
||||
snapshotWith({
|
||||
discord: {
|
||||
running: false,
|
||||
enabled: false,
|
||||
configured: true,
|
||||
lastStartAt: startedAt,
|
||||
withReadinessClock(() => {
|
||||
const { readiness } = createReadinessHarness({
|
||||
startedAgoMs: 5 * 60_000,
|
||||
accounts: {
|
||||
discord: {
|
||||
running: false,
|
||||
enabled: false,
|
||||
configured: true,
|
||||
lastStartAt: Date.now() - 5 * 60_000,
|
||||
},
|
||||
telegram: {
|
||||
running: false,
|
||||
enabled: true,
|
||||
configured: false,
|
||||
lastStartAt: Date.now() - 5 * 60_000,
|
||||
},
|
||||
},
|
||||
telegram: {
|
||||
running: false,
|
||||
enabled: true,
|
||||
configured: false,
|
||||
lastStartAt: startedAt,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const readiness = createReadinessChecker({ channelManager: manager, startedAt });
|
||||
expect(readiness()).toEqual({ ready: true, failing: [], uptimeMs: 300_000 });
|
||||
vi.useRealTimers();
|
||||
});
|
||||
expect(readiness()).toEqual({ ready: true, failing: [], uptimeMs: 300_000 });
|
||||
});
|
||||
});
|
||||
|
||||
it("uses startup grace before marking disconnected channels not ready", () => {
|
||||
vi.useFakeTimers();
|
||||
vi.setSystemTime(new Date("2026-03-06T12:00:00Z"));
|
||||
const startedAt = Date.now() - 30_000;
|
||||
const manager = createManager(
|
||||
snapshotWith({
|
||||
discord: {
|
||||
running: true,
|
||||
connected: false,
|
||||
enabled: true,
|
||||
configured: true,
|
||||
lastStartAt: startedAt,
|
||||
withReadinessClock(() => {
|
||||
const { readiness } = createReadinessHarness({
|
||||
startedAgoMs: 30_000,
|
||||
accounts: {
|
||||
discord: {
|
||||
running: true,
|
||||
connected: false,
|
||||
enabled: true,
|
||||
configured: true,
|
||||
lastStartAt: Date.now() - 30_000,
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const readiness = createReadinessChecker({ channelManager: manager, startedAt });
|
||||
expect(readiness()).toEqual({ ready: true, failing: [], uptimeMs: 30_000 });
|
||||
vi.useRealTimers();
|
||||
});
|
||||
expect(readiness()).toEqual({ ready: true, failing: [], uptimeMs: 30_000 });
|
||||
});
|
||||
});
|
||||
|
||||
it("reports disconnected managed channels after startup grace", () => {
|
||||
vi.useFakeTimers();
|
||||
vi.setSystemTime(new Date("2026-03-06T12:00:00Z"));
|
||||
const startedAt = Date.now() - 5 * 60_000;
|
||||
const manager = createManager(
|
||||
snapshotWith({
|
||||
discord: {
|
||||
running: true,
|
||||
connected: false,
|
||||
enabled: true,
|
||||
configured: true,
|
||||
lastStartAt: startedAt,
|
||||
withReadinessClock(() => {
|
||||
const { readiness } = createReadinessHarness({
|
||||
startedAgoMs: 5 * 60_000,
|
||||
accounts: {
|
||||
discord: {
|
||||
running: true,
|
||||
connected: false,
|
||||
enabled: true,
|
||||
configured: true,
|
||||
lastStartAt: Date.now() - 5 * 60_000,
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const readiness = createReadinessChecker({ channelManager: manager, startedAt });
|
||||
expect(readiness()).toEqual({ ready: false, failing: ["discord"], uptimeMs: 300_000 });
|
||||
vi.useRealTimers();
|
||||
});
|
||||
expect(readiness()).toEqual({ ready: false, failing: ["discord"], uptimeMs: 300_000 });
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps restart-pending channels ready during reconnect backoff", () => {
|
||||
vi.useFakeTimers();
|
||||
vi.setSystemTime(new Date("2026-03-06T12:00:00Z"));
|
||||
const startedAt = Date.now() - 5 * 60_000;
|
||||
const manager = createManager(
|
||||
snapshotWith({
|
||||
discord: {
|
||||
running: false,
|
||||
restartPending: true,
|
||||
reconnectAttempts: 3,
|
||||
enabled: true,
|
||||
configured: true,
|
||||
lastStartAt: startedAt - 30_000,
|
||||
lastStopAt: Date.now() - 5_000,
|
||||
withReadinessClock(() => {
|
||||
const startedAt = Date.now() - 5 * 60_000;
|
||||
const { readiness } = createReadinessHarness({
|
||||
startedAgoMs: 5 * 60_000,
|
||||
accounts: {
|
||||
discord: {
|
||||
running: false,
|
||||
restartPending: true,
|
||||
reconnectAttempts: 3,
|
||||
enabled: true,
|
||||
configured: true,
|
||||
lastStartAt: startedAt - 30_000,
|
||||
lastStopAt: Date.now() - 5_000,
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const readiness = createReadinessChecker({ channelManager: manager, startedAt });
|
||||
expect(readiness()).toEqual({ ready: true, failing: [], uptimeMs: 300_000 });
|
||||
vi.useRealTimers();
|
||||
});
|
||||
expect(readiness()).toEqual({ ready: true, failing: [], uptimeMs: 300_000 });
|
||||
});
|
||||
});
|
||||
|
||||
it("treats stale-socket channels as ready to avoid pulling healthy idle pods", () => {
|
||||
vi.useFakeTimers();
|
||||
vi.setSystemTime(new Date("2026-03-06T12:00:00Z"));
|
||||
const startedAt = Date.now() - 31 * 60_000;
|
||||
const manager = createManager(
|
||||
snapshotWith({
|
||||
discord: {
|
||||
running: true,
|
||||
connected: true,
|
||||
enabled: true,
|
||||
configured: true,
|
||||
lastStartAt: startedAt,
|
||||
lastEventAt: Date.now() - 31 * 60_000,
|
||||
withReadinessClock(() => {
|
||||
const startedAt = Date.now() - 31 * 60_000;
|
||||
const { readiness } = createReadinessHarness({
|
||||
startedAgoMs: 31 * 60_000,
|
||||
accounts: {
|
||||
discord: {
|
||||
running: true,
|
||||
connected: true,
|
||||
enabled: true,
|
||||
configured: true,
|
||||
lastStartAt: startedAt,
|
||||
lastEventAt: Date.now() - 31 * 60_000,
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const readiness = createReadinessChecker({ channelManager: manager, startedAt });
|
||||
expect(readiness()).toEqual({ ready: true, failing: [], uptimeMs: 1_860_000 });
|
||||
vi.useRealTimers();
|
||||
});
|
||||
expect(readiness()).toEqual({ ready: true, failing: [], uptimeMs: 1_860_000 });
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps telegram long-polling channels ready without stale-socket classification", () => {
|
||||
vi.useFakeTimers();
|
||||
vi.setSystemTime(new Date("2026-03-06T12:00:00Z"));
|
||||
const startedAt = Date.now() - 31 * 60_000;
|
||||
const manager = createManager(
|
||||
snapshotWith({
|
||||
telegram: {
|
||||
running: true,
|
||||
connected: true,
|
||||
enabled: true,
|
||||
configured: true,
|
||||
lastStartAt: startedAt,
|
||||
lastEventAt: null,
|
||||
withReadinessClock(() => {
|
||||
const startedAt = Date.now() - 31 * 60_000;
|
||||
const { readiness } = createReadinessHarness({
|
||||
startedAgoMs: 31 * 60_000,
|
||||
accounts: {
|
||||
telegram: {
|
||||
running: true,
|
||||
connected: true,
|
||||
enabled: true,
|
||||
configured: true,
|
||||
lastStartAt: startedAt,
|
||||
lastEventAt: null,
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const readiness = createReadinessChecker({ channelManager: manager, startedAt });
|
||||
expect(readiness()).toEqual({ ready: true, failing: [], uptimeMs: 1_860_000 });
|
||||
vi.useRealTimers();
|
||||
});
|
||||
expect(readiness()).toEqual({ ready: true, failing: [], uptimeMs: 1_860_000 });
|
||||
});
|
||||
});
|
||||
|
||||
it("caches readiness snapshots briefly to keep repeated probes cheap", () => {
|
||||
vi.useFakeTimers();
|
||||
vi.setSystemTime(new Date("2026-03-06T12:00:00Z"));
|
||||
const startedAt = Date.now() - 5 * 60_000;
|
||||
const manager = createHealthyDiscordManager(startedAt, Date.now() - 1_000);
|
||||
withReadinessClock(() => {
|
||||
const { manager, readiness } = createReadinessHarness({
|
||||
startedAgoMs: 5 * 60_000,
|
||||
accounts: {
|
||||
discord: {
|
||||
running: true,
|
||||
connected: true,
|
||||
enabled: true,
|
||||
configured: true,
|
||||
lastStartAt: Date.now() - 5 * 60_000,
|
||||
lastEventAt: Date.now() - 1_000,
|
||||
},
|
||||
},
|
||||
cacheTtlMs: 1_000,
|
||||
});
|
||||
expect(readiness()).toEqual({ ready: true, failing: [], uptimeMs: 300_000 });
|
||||
vi.advanceTimersByTime(500);
|
||||
expect(readiness()).toEqual({ ready: true, failing: [], uptimeMs: 300_500 });
|
||||
expect(manager.getRuntimeSnapshot).toHaveBeenCalledTimes(1);
|
||||
|
||||
const readiness = createReadinessChecker({
|
||||
channelManager: manager,
|
||||
startedAt,
|
||||
cacheTtlMs: 1_000,
|
||||
vi.advanceTimersByTime(600);
|
||||
expect(readiness()).toEqual({ ready: true, failing: [], uptimeMs: 301_100 });
|
||||
expect(manager.getRuntimeSnapshot).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
expect(readiness()).toEqual({ ready: true, failing: [], uptimeMs: 300_000 });
|
||||
vi.advanceTimersByTime(500);
|
||||
expect(readiness()).toEqual({ ready: true, failing: [], uptimeMs: 300_500 });
|
||||
expect(manager.getRuntimeSnapshot).toHaveBeenCalledTimes(1);
|
||||
|
||||
vi.advanceTimersByTime(600);
|
||||
expect(readiness()).toEqual({ ready: true, failing: [], uptimeMs: 301_100 });
|
||||
expect(manager.getRuntimeSnapshot).toHaveBeenCalledTimes(2);
|
||||
vi.useRealTimers();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user