Files
makelore/tests/unit/background-lifecycle.test.ts
2026-09-04 12:14:53 +08:00

103 lines
3.5 KiB
TypeScript

import { afterEach, describe, expect, it, vi } from 'vitest';
import { BackgroundLifecycleController } from '../../electron/main/background-lifecycle';
describe('BackgroundLifecycleController', () => {
afterEach(() => {
vi.useRealTimers();
});
it('stops an idle hidden/inactive desktop after the grace period', async () => {
vi.useFakeTimers();
const onSleep = vi.fn().mockResolvedValue(undefined);
const onStopRuntime = vi.fn().mockResolvedValue(undefined);
const controller = new BackgroundLifecycleController({
idleStopMs: 60_000,
onSleep,
onStopRuntime,
});
controller.setActivity({ visible: false, module: 'painting' });
await vi.advanceTimersByTimeAsync(59_999);
expect(onSleep).not.toHaveBeenCalled();
expect(onStopRuntime).not.toHaveBeenCalled();
await vi.advanceTimersByTimeAsync(1);
expect(onSleep).toHaveBeenCalledTimes(1);
expect(onStopRuntime).toHaveBeenCalledTimes(1);
controller.dispose();
});
it('keeps the runtime alive while an active task lease exists', async () => {
vi.useFakeTimers();
const onSleep = vi.fn().mockResolvedValue(undefined);
const onStopRuntime = vi.fn().mockResolvedValue(undefined);
const controller = new BackgroundLifecycleController({
idleStopMs: 60_000,
onSleep,
onStopRuntime,
});
const release = controller.acquireLease({ id: 'release:1', kind: 'publish' });
controller.setActivity({ visible: false, module: 'programming' });
await vi.advanceTimersByTimeAsync(60_000);
expect(controller.getLeaseCount()).toBe(1);
expect(onStopRuntime).not.toHaveBeenCalled();
release();
await vi.advanceTimersByTimeAsync(60_000);
expect(onSleep).toHaveBeenCalledTimes(1);
expect(onStopRuntime).toHaveBeenCalledTimes(1);
controller.dispose();
});
it('does not stop the runtime when a lease is acquired while sleep preparation is in flight', async () => {
vi.useFakeTimers();
let finishSleepPreparation!: () => void;
const sleepPreparation = new Promise<void>((resolve) => {
finishSleepPreparation = resolve;
});
const onSleep = vi.fn(() => sleepPreparation);
const onStopRuntime = vi.fn().mockResolvedValue(undefined);
const controller = new BackgroundLifecycleController({
idleStopMs: 60_000,
onSleep,
onStopRuntime,
});
controller.setActivity({ visible: false, module: 'painting' });
await vi.advanceTimersByTimeAsync(60_000);
expect(onSleep).toHaveBeenCalledTimes(1);
const release = controller.acquireLease({ id: 'coding-run:1', kind: 'coding-run' });
finishSleepPreparation();
await vi.runAllTicks();
expect(onStopRuntime).not.toHaveBeenCalled();
release();
await vi.advanceTimersByTimeAsync(60_000);
expect(onStopRuntime).toHaveBeenCalledTimes(1);
controller.dispose();
});
it('cancels the idle stop when programming becomes visible again', async () => {
vi.useFakeTimers();
const onSleep = vi.fn();
const onStopRuntime = vi.fn();
const controller = new BackgroundLifecycleController({
idleStopMs: 60_000,
onSleep,
onStopRuntime,
});
controller.setActivity({ visible: false, module: 'robot' });
await vi.advanceTimersByTimeAsync(30_000);
controller.setActivity({ visible: true, module: 'programming' });
await vi.advanceTimersByTimeAsync(90_000);
expect(onSleep).not.toHaveBeenCalled();
expect(onStopRuntime).not.toHaveBeenCalled();
controller.dispose();
});
});