-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbackground-service.test.ts
More file actions
114 lines (101 loc) · 4.29 KB
/
Copy pathbackground-service.test.ts
File metadata and controls
114 lines (101 loc) · 4.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
import { beforeEach, describe, expect, it, vi } from "vitest";
const mocks = vi.hoisted(() => ({
watcher: { on: vi.fn(), close: vi.fn() },
watch: vi.fn(),
ingest: vi.fn(),
correlate: vi.fn(),
extract: vi.fn(),
info: vi.fn(),
debug: vi.fn(),
error: vi.fn(),
dashboard: vi.fn(),
poller: { on: vi.fn(), start: vi.fn(), stop: vi.fn() },
pollerConstructor: vi.fn(),
}));
vi.mock("chokidar", () => ({ watch: mocks.watch }));
vi.mock("../src/history/commit-ingest.js", () => ({ CommitIngester: { ingestLatest: mocks.ingest } }));
vi.mock("../src/history/correlation-engine.js", () => ({ CorrelationEngine: class { correlateAll = mocks.correlate; } }));
vi.mock("../src/history/lesson-extractor.js", () => ({ LessonExtractor: class { extractNewLessons = mocks.extract; } }));
vi.mock("../src/core/logger.js", () => ({ RuntimeLogger: { info: mocks.info, debug: mocks.debug, error: mocks.error } }));
vi.mock("../src/core/dashboard.js", () => ({ CommandCenterDashboard: { log: mocks.dashboard } }));
vi.mock("../src/history/git-poller.js", () => ({
GitPoller: class {
constructor(rootPath: string, interval: number) {
mocks.pollerConstructor(rootPath, interval);
}
on = mocks.poller.on;
start = mocks.poller.start;
stop = mocks.poller.stop;
},
}));
import { BackgroundAutonomyService } from "../src/core/background-service.js";
describe("BackgroundAutonomyService", () => {
beforeEach(() => {
vi.clearAllMocks();
mocks.watch.mockReturnValue(mocks.watcher);
mocks.watcher.on.mockReturnValue(mocks.watcher);
mocks.ingest.mockResolvedValue(2);
mocks.correlate.mockResolvedValue(undefined);
mocks.extract.mockResolvedValue(undefined);
});
it("starts once, runs the initial serialized cycle, and stops the watcher", async () => {
const service = new BackgroundAutonomyService("C:/repo", vi.fn());
service.start();
service.start();
await service.idle();
service.stop();
expect(mocks.watch).toHaveBeenCalledTimes(1);
expect(mocks.ingest).toHaveBeenCalledWith("C:/repo", 100);
expect(mocks.correlate).toHaveBeenCalledBefore(mocks.extract);
expect(mocks.watcher.close).toHaveBeenCalledOnce();
});
it("debounces file changes and logs cycle failures for queue retries", async () => {
vi.useFakeTimers();
mocks.correlate.mockRejectedValue(new Error("correlation failed"));
const service = new BackgroundAutonomyService("C:/repo", vi.fn());
service.start();
const changeHandler = mocks.watcher.on.mock.calls.find(call => call[0] === "all")?.[1];
changeHandler("change", "src/a.ts");
changeHandler("change", "src/b.ts");
await vi.runAllTimersAsync();
await service.idle();
service.stop();
vi.useRealTimers();
expect(mocks.debug).toHaveBeenCalledWith(expect.stringContaining("src/b.ts"));
expect(mocks.error).toHaveBeenCalledWith("Background Autonomy cycle failed", expect.any(Error));
});
it("starts and stops git polling and reacts to discovered commits", async () => {
vi.useFakeTimers();
const service = new BackgroundAutonomyService("C:/repo", vi.fn(), 25);
service.start();
await service.idle();
const commitHandler = mocks.poller.on.mock.calls.find(call => call[0] === "commits")?.[1];
commitHandler();
await vi.advanceTimersByTimeAsync(3000);
await service.idle();
service.stop();
vi.useRealTimers();
expect(mocks.pollerConstructor).toHaveBeenCalledWith("C:/repo", 25);
expect(mocks.poller.start).toHaveBeenCalledOnce();
expect(mocks.poller.stop).toHaveBeenCalledOnce();
expect(mocks.ingest).toHaveBeenCalledTimes(2);
});
it("coalesces a triggered cycle while the initial cycle is pending", async () => {
vi.useFakeTimers();
let release!: () => void;
mocks.ingest.mockReturnValue(new Promise<number>(resolve => {
release = () => resolve(1);
}));
const service = new BackgroundAutonomyService("C:/repo", vi.fn());
service.start();
const changeHandler = mocks.watcher.on.mock.calls.find(call => call[0] === "all")?.[1];
changeHandler("change", "src/a.ts");
await vi.advanceTimersByTimeAsync(3000);
expect(mocks.debug).toHaveBeenCalledWith("Background autonomy cycle coalesced", { rootPath: "C:/repo" });
release();
await service.idle();
service.stop();
service.stop();
vi.useRealTimers();
});
});