@agentteams/runner 0.0.84-dev.208 → 0.0.85
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api-client.d.ts +2 -2
- package/dist/api-client.js +66 -66
- package/dist/api-client.js.map +1 -1
- package/dist/api-client.test.js +54 -63
- package/dist/api-client.test.js.map +1 -1
- package/dist/autostart.js +81 -81
- package/dist/autostart.js.map +1 -1
- package/dist/autostart.test.js +35 -35
- package/dist/autostart.test.js.map +1 -1
- package/dist/commands/cleanup.js +4 -4
- package/dist/commands/init.js +14 -14
- package/dist/commands/restart.js +3 -3
- package/dist/commands/start.js +10 -10
- package/dist/commands/start.js.map +1 -1
- package/dist/commands/status.js +7 -7
- package/dist/commands/stop.js +7 -7
- package/dist/commands/uninstall.js +6 -6
- package/dist/commands/update.d.ts +4 -4
- package/dist/commands/update.js +22 -20
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/update.test.js +42 -38
- package/dist/commands/update.test.js.map +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.js +12 -12
- package/dist/config.test.js +61 -61
- package/dist/config.test.js.map +1 -1
- package/dist/daemon-control.d.ts +5 -5
- package/dist/daemon-control.js +22 -21
- package/dist/daemon-control.js.map +1 -1
- package/dist/daemon-control.test.js +29 -29
- package/dist/executable.d.ts +3 -3
- package/dist/executable.js +49 -46
- package/dist/executable.js.map +1 -1
- package/dist/executable.test.js +62 -62
- package/dist/executable.test.js.map +1 -1
- package/dist/handlers/trigger-handler.d.ts +6 -6
- package/dist/handlers/trigger-handler.js +124 -118
- package/dist/handlers/trigger-handler.js.map +1 -1
- package/dist/handlers/trigger-handler.test.js +365 -373
- package/dist/handlers/trigger-handler.test.js.map +1 -1
- package/dist/index.js +25 -25
- package/dist/logger.js +1 -1
- package/dist/pid.js +6 -6
- package/dist/poller.d.ts +7 -7
- package/dist/poller.js +53 -54
- package/dist/poller.js.map +1 -1
- package/dist/poller.test.js +181 -209
- package/dist/poller.test.js.map +1 -1
- package/dist/runners/amp.d.ts +1 -1
- package/dist/runners/amp.js +81 -74
- package/dist/runners/amp.js.map +1 -1
- package/dist/runners/antigravity.d.ts +3 -3
- package/dist/runners/antigravity.js +127 -116
- package/dist/runners/antigravity.js.map +1 -1
- package/dist/runners/antigravity.replay.test.js +25 -25
- package/dist/runners/antigravity.replay.test.js.map +1 -1
- package/dist/runners/antigravity.test.js +98 -98
- package/dist/runners/antigravity.test.js.map +1 -1
- package/dist/runners/claude-code.d.ts +1 -1
- package/dist/runners/claude-code.js +84 -85
- package/dist/runners/claude-code.js.map +1 -1
- package/dist/runners/claude-code.test.js +40 -37
- package/dist/runners/claude-code.test.js.map +1 -1
- package/dist/runners/codex.d.ts +1 -1
- package/dist/runners/codex.js +96 -87
- package/dist/runners/codex.js.map +1 -1
- package/dist/runners/codex.test.js +68 -68
- package/dist/runners/codex.test.js.map +1 -1
- package/dist/runners/index.d.ts +1 -1
- package/dist/runners/index.js +10 -10
- package/dist/runners/index.test.js +16 -16
- package/dist/runners/log-reporter.d.ts +2 -2
- package/dist/runners/log-reporter.js +10 -10
- package/dist/runners/log-reporter.test.js +70 -70
- package/dist/runners/log-reporter.test.js.map +1 -1
- package/dist/runners/opencode.d.ts +1 -1
- package/dist/runners/opencode.js +75 -68
- package/dist/runners/opencode.js.map +1 -1
- package/dist/runners/process-control.d.ts +1 -1
- package/dist/runners/process-control.js +11 -13
- package/dist/runners/process-control.js.map +1 -1
- package/dist/runners/stream-json-parser.d.ts +1 -1
- package/dist/runners/stream-json-parser.js +67 -70
- package/dist/runners/stream-json-parser.js.map +1 -1
- package/dist/runners/stream-json-parser.replay.test.js +17 -17
- package/dist/runners/stream-json-parser.replay.test.js.map +1 -1
- package/dist/runners/stream-json-parser.test.js +78 -76
- package/dist/runners/stream-json-parser.test.js.map +1 -1
- package/dist/types.d.ts +4 -4
- package/dist/utils/auth-path-store.js +7 -7
- package/dist/utils/auth-path-store.test.js +29 -24
- package/dist/utils/auth-path-store.test.js.map +1 -1
- package/dist/utils/auto-update.d.ts +4 -4
- package/dist/utils/auto-update.js +28 -30
- package/dist/utils/auto-update.js.map +1 -1
- package/dist/utils/auto-update.test.js +30 -30
- package/dist/utils/convention-evaluator.d.ts +1 -1
- package/dist/utils/convention-evaluator.js +21 -22
- package/dist/utils/convention-evaluator.js.map +1 -1
- package/dist/utils/convention-evaluator.test.js +110 -120
- package/dist/utils/convention-evaluator.test.js.map +1 -1
- package/dist/utils/convention-sync.d.ts +3 -3
- package/dist/utils/convention-sync.js +10 -10
- package/dist/utils/convention-sync.test.js +25 -39
- package/dist/utils/convention-sync.test.js.map +1 -1
- package/dist/utils/git-worktree.js +18 -20
- package/dist/utils/git-worktree.js.map +1 -1
- package/dist/utils/git-worktree.test.js +98 -98
- package/dist/utils/git-worktree.test.js.map +1 -1
- package/dist/utils/origin-issue-safeguard.js +30 -35
- package/dist/utils/origin-issue-safeguard.js.map +1 -1
- package/dist/utils/power-save-blocker.d.ts +4 -4
- package/dist/utils/power-save-blocker.js +33 -33
- package/dist/utils/power-save-blocker.test.js +37 -37
- package/dist/utils/power-save-blocker.test.js.map +1 -1
- package/dist/utils/runner-cleanup.d.ts +3 -3
- package/dist/utils/runner-cleanup.js +13 -13
- package/dist/utils/runner-cleanup.test.js +35 -35
- package/dist/utils/runner-history.js +3 -3
- package/dist/utils/runner-history.test.js +17 -17
- package/package.json +1 -4
- package/readme.md +10 -10
package/dist/poller.test.js
CHANGED
|
@@ -1,34 +1,34 @@
|
|
|
1
|
-
import assert from
|
|
2
|
-
import * as fsModule from
|
|
3
|
-
import os from
|
|
4
|
-
import path from
|
|
5
|
-
import test, { mock } from
|
|
6
|
-
import { logger } from
|
|
7
|
-
import { startPolling } from
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import * as fsModule from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import test, { mock } from "node:test";
|
|
6
|
+
import { logger } from "./logger.js";
|
|
7
|
+
import { startPolling } from "./poller.js";
|
|
8
8
|
const config = {
|
|
9
|
-
daemonToken:
|
|
10
|
-
apiUrl:
|
|
9
|
+
daemonToken: "daemon-token",
|
|
10
|
+
apiUrl: "https://api.example",
|
|
11
11
|
pollingIntervalMs: 5000,
|
|
12
12
|
timeoutMs: 1000,
|
|
13
13
|
idleTimeoutMs: 500,
|
|
14
|
-
runnerCmd:
|
|
15
|
-
preventSleepWhileBusy: false
|
|
14
|
+
runnerCmd: "opencode",
|
|
15
|
+
preventSleepWhileBusy: false
|
|
16
16
|
};
|
|
17
17
|
const trigger = {
|
|
18
|
-
id:
|
|
19
|
-
prompt:
|
|
20
|
-
runnerType:
|
|
21
|
-
model:
|
|
18
|
+
id: "trigger-1",
|
|
19
|
+
prompt: "hello",
|
|
20
|
+
runnerType: "CODEX",
|
|
21
|
+
model: "o4-mini",
|
|
22
22
|
fastMode: false,
|
|
23
|
-
status:
|
|
24
|
-
agentConfigId:
|
|
23
|
+
status: "PENDING",
|
|
24
|
+
agentConfigId: "agent-1",
|
|
25
25
|
startedAt: null,
|
|
26
26
|
errorMessage: null,
|
|
27
27
|
worktreeError: null,
|
|
28
28
|
lastHeartbeatAt: null,
|
|
29
29
|
conversationId: null,
|
|
30
30
|
parentTriggerId: null,
|
|
31
|
-
createdByMemberId:
|
|
31
|
+
createdByMemberId: "member-1",
|
|
32
32
|
planMode: false,
|
|
33
33
|
targetDaemonId: null,
|
|
34
34
|
claimedByDaemonId: null,
|
|
@@ -36,8 +36,8 @@ const trigger = {
|
|
|
36
36
|
baseBranch: null,
|
|
37
37
|
worktreeId: null,
|
|
38
38
|
worktreeStatus: null,
|
|
39
|
-
createdAt:
|
|
40
|
-
updatedAt:
|
|
39
|
+
createdAt: "2026-01-01T00:00:00.000Z",
|
|
40
|
+
updatedAt: "2026-01-01T00:00:00.000Z"
|
|
41
41
|
};
|
|
42
42
|
test.afterEach(() => {
|
|
43
43
|
mock.restoreAll();
|
|
@@ -48,15 +48,15 @@ const createRecordingBlocker = () => {
|
|
|
48
48
|
events,
|
|
49
49
|
blocker: {
|
|
50
50
|
acquire: (label) => {
|
|
51
|
-
events.push(`acquire:${label ??
|
|
52
|
-
return () => events.push(`release:${label ??
|
|
53
|
-
}
|
|
54
|
-
}
|
|
51
|
+
events.push(`acquire:${label ?? ""}`);
|
|
52
|
+
return () => events.push(`release:${label ?? ""}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
55
|
};
|
|
56
56
|
};
|
|
57
|
-
test(
|
|
57
|
+
test("startPolling handles a claimed trigger, registers auth paths, and runs scheduled cleanup", async () => {
|
|
58
58
|
const infos = [];
|
|
59
|
-
mock.method(logger,
|
|
59
|
+
mock.method(logger, "info", (message, meta) => {
|
|
60
60
|
infos.push({ message, meta });
|
|
61
61
|
});
|
|
62
62
|
const signals = new Map();
|
|
@@ -74,10 +74,10 @@ test('startPolling handles a claimed trigger, registers auth paths, and runs sch
|
|
|
74
74
|
fetchPendingWorktreeRemovals: async () => [],
|
|
75
75
|
reportWorktreeStatus: async () => undefined,
|
|
76
76
|
notifyUpdate: async () => undefined,
|
|
77
|
-
ackRestartRequest: async () => undefined
|
|
77
|
+
ackRestartRequest: async () => undefined
|
|
78
78
|
};
|
|
79
79
|
const createHandler = (onAuthPathDiscovered) => {
|
|
80
|
-
onAuthPathDiscovered(
|
|
80
|
+
onAuthPathDiscovered("/auth/path");
|
|
81
81
|
return async (value) => {
|
|
82
82
|
handledTriggers.push(value.id);
|
|
83
83
|
};
|
|
@@ -97,40 +97,39 @@ test('startPolling handles a claimed trigger, registers auth paths, and runs sch
|
|
|
97
97
|
signals.set(event, listener);
|
|
98
98
|
}),
|
|
99
99
|
processExit: (() => {
|
|
100
|
-
throw new Error(
|
|
100
|
+
throw new Error("should not exit");
|
|
101
101
|
}),
|
|
102
102
|
now: () => nowValue,
|
|
103
103
|
loadAuthPaths: () => [],
|
|
104
104
|
saveAuthPath: (authPath) => {
|
|
105
105
|
savedAuthPaths.push(authPath);
|
|
106
|
-
return
|
|
106
|
+
return "/tmp/auth-paths.json";
|
|
107
107
|
},
|
|
108
108
|
keepAlive: () => new Promise((resolve) => {
|
|
109
109
|
keepAliveResolve = resolve;
|
|
110
|
-
})
|
|
110
|
+
})
|
|
111
111
|
});
|
|
112
112
|
await new Promise((resolve) => setImmediate(resolve));
|
|
113
|
-
assert.deepEqual(handledTriggers, [
|
|
113
|
+
assert.deepEqual(handledTriggers, ["trigger-1"]);
|
|
114
114
|
assert.deepEqual(cleanupCalls, []);
|
|
115
115
|
assert.equal(intervalCallbacks.length, 1);
|
|
116
116
|
nowValue = 24 * 60 * 60 * 1000 + 1;
|
|
117
117
|
await intervalCallbacks[0]?.();
|
|
118
118
|
await new Promise((resolve) => setImmediate(resolve));
|
|
119
|
-
assert.deepEqual(cleanupCalls, [
|
|
120
|
-
assert.deepEqual(savedAuthPaths, [
|
|
121
|
-
assert.equal(signals.has(
|
|
122
|
-
assert.equal(signals.has(
|
|
123
|
-
assert.equal(infos.some((entry) => entry.message ===
|
|
124
|
-
const resolveKeepAlive = keepAliveResolve ??
|
|
125
|
-
(
|
|
126
|
-
|
|
127
|
-
});
|
|
119
|
+
assert.deepEqual(cleanupCalls, ["/auth/path"]);
|
|
120
|
+
assert.deepEqual(savedAuthPaths, ["/auth/path"]);
|
|
121
|
+
assert.equal(signals.has("SIGINT"), true);
|
|
122
|
+
assert.equal(signals.has("SIGTERM"), true);
|
|
123
|
+
assert.equal(infos.some((entry) => entry.message === "Daemon polling started"), true);
|
|
124
|
+
const resolveKeepAlive = keepAliveResolve ?? (() => {
|
|
125
|
+
throw new Error("keepAlive resolver was not registered");
|
|
126
|
+
});
|
|
128
127
|
resolveKeepAlive();
|
|
129
128
|
await pollingPromise;
|
|
130
129
|
});
|
|
131
|
-
test(
|
|
130
|
+
test("startPolling logs conflicts and suppresses overlapping polling cycles", async () => {
|
|
132
131
|
const infos = [];
|
|
133
|
-
mock.method(logger,
|
|
132
|
+
mock.method(logger, "info", (message, meta) => {
|
|
134
133
|
infos.push({ message, meta });
|
|
135
134
|
});
|
|
136
135
|
let releaseFetch = null;
|
|
@@ -144,12 +143,12 @@ test('startPolling logs conflicts and suppresses overlapping polling cycles', as
|
|
|
144
143
|
fetchPendingWorktreeRemovals: async () => [],
|
|
145
144
|
reportWorktreeStatus: async () => undefined,
|
|
146
145
|
notifyUpdate: async () => undefined,
|
|
147
|
-
ackRestartRequest: async () => undefined
|
|
146
|
+
ackRestartRequest: async () => undefined
|
|
148
147
|
};
|
|
149
148
|
const intervalCallbacks = [];
|
|
150
149
|
let keepAliveResolve = null;
|
|
151
150
|
const pollingPromise = startPolling(config, () => async () => {
|
|
152
|
-
throw new Error(
|
|
151
|
+
throw new Error("handler should not run");
|
|
153
152
|
}, {
|
|
154
153
|
createClient: () => client,
|
|
155
154
|
runCleanup: async () => undefined,
|
|
@@ -160,32 +159,30 @@ test('startPolling logs conflicts and suppresses overlapping polling cycles', as
|
|
|
160
159
|
clearInterval: (() => undefined),
|
|
161
160
|
processOn: (() => undefined),
|
|
162
161
|
processExit: (() => {
|
|
163
|
-
throw new Error(
|
|
162
|
+
throw new Error("should not exit");
|
|
164
163
|
}),
|
|
165
164
|
now: () => 0,
|
|
166
165
|
loadAuthPaths: () => [],
|
|
167
|
-
saveAuthPath: () =>
|
|
166
|
+
saveAuthPath: () => "/tmp/auth-paths.json",
|
|
168
167
|
keepAlive: () => new Promise((resolve) => {
|
|
169
168
|
keepAliveResolve = resolve;
|
|
170
|
-
})
|
|
169
|
+
})
|
|
171
170
|
});
|
|
172
171
|
await new Promise((resolve) => setImmediate(resolve));
|
|
173
172
|
await intervalCallbacks[0]?.();
|
|
174
|
-
const releasePendingFetch = releaseFetch ??
|
|
175
|
-
(
|
|
176
|
-
|
|
177
|
-
});
|
|
173
|
+
const releasePendingFetch = releaseFetch ?? (() => {
|
|
174
|
+
throw new Error("fetch release was not registered");
|
|
175
|
+
});
|
|
178
176
|
releasePendingFetch();
|
|
179
177
|
await new Promise((resolve) => setImmediate(resolve));
|
|
180
|
-
assert.equal(infos.some((entry) => entry.message ===
|
|
181
|
-
const resolveKeepAlive = keepAliveResolve ??
|
|
182
|
-
(
|
|
183
|
-
|
|
184
|
-
});
|
|
178
|
+
assert.equal(infos.some((entry) => entry.message === "Trigger already claimed by another daemon"), true);
|
|
179
|
+
const resolveKeepAlive = keepAliveResolve ?? (() => {
|
|
180
|
+
throw new Error("keepAlive resolver was not registered");
|
|
181
|
+
});
|
|
185
182
|
resolveKeepAlive();
|
|
186
183
|
await pollingPromise;
|
|
187
184
|
});
|
|
188
|
-
test(
|
|
185
|
+
test("startPolling clears the interval and exits on shutdown signals", async () => {
|
|
189
186
|
const signals = new Map();
|
|
190
187
|
const cleared = [];
|
|
191
188
|
let keepAliveResolve = null;
|
|
@@ -200,7 +197,7 @@ test('startPolling clears the interval and exits on shutdown signals', async ()
|
|
|
200
197
|
fetchPendingWorktreeRemovals: async () => [],
|
|
201
198
|
reportWorktreeStatus: async () => undefined,
|
|
202
199
|
notifyUpdate: async () => undefined,
|
|
203
|
-
ackRestartRequest: async () => undefined
|
|
200
|
+
ackRestartRequest: async () => undefined
|
|
204
201
|
}),
|
|
205
202
|
runCleanup: async () => undefined,
|
|
206
203
|
setInterval: (() => intervalHandle),
|
|
@@ -216,37 +213,36 @@ test('startPolling clears the interval and exits on shutdown signals', async ()
|
|
|
216
213
|
}),
|
|
217
214
|
now: () => 0,
|
|
218
215
|
loadAuthPaths: () => [],
|
|
219
|
-
saveAuthPath: () =>
|
|
216
|
+
saveAuthPath: () => "/tmp/auth-paths.json",
|
|
220
217
|
keepAlive: () => new Promise((resolve) => {
|
|
221
218
|
keepAliveResolve = resolve;
|
|
222
|
-
})
|
|
219
|
+
})
|
|
223
220
|
}).catch((error) => {
|
|
224
|
-
if (error.message !==
|
|
221
|
+
if (error.message !== "exit") {
|
|
225
222
|
throw error;
|
|
226
223
|
}
|
|
227
224
|
});
|
|
228
225
|
await new Promise((resolve) => setImmediate(resolve));
|
|
229
|
-
assert.ok(signals.get(
|
|
230
|
-
const sigterm = signals.get(
|
|
226
|
+
assert.ok(signals.get("SIGTERM"));
|
|
227
|
+
const sigterm = signals.get("SIGTERM");
|
|
231
228
|
assert.ok(sigterm);
|
|
232
229
|
sigterm();
|
|
233
230
|
assert.equal(exitCode, 0);
|
|
234
231
|
assert.deepEqual(cleared, [intervalHandle]);
|
|
235
|
-
const resolveKeepAlive = keepAliveResolve ??
|
|
236
|
-
(
|
|
237
|
-
|
|
238
|
-
});
|
|
232
|
+
const resolveKeepAlive = keepAliveResolve ?? (() => {
|
|
233
|
+
throw new Error("keepAlive resolver was not registered");
|
|
234
|
+
});
|
|
239
235
|
resolveKeepAlive();
|
|
240
236
|
await pollingPromise;
|
|
241
237
|
});
|
|
242
|
-
test(
|
|
238
|
+
test("startPolling delegates auto-update via injected dependency", async () => {
|
|
243
239
|
let autoUpdateCallCount = 0;
|
|
244
240
|
let keepAliveResolve = null;
|
|
245
241
|
const pollingPromise = startPolling(config, () => async () => undefined, {
|
|
246
242
|
createClient: () => ({
|
|
247
243
|
fetchPendingTrigger: async () => ({
|
|
248
244
|
data: null,
|
|
249
|
-
meta: { cliLatestVersion: null, runnerLatestVersion:
|
|
245
|
+
meta: { cliLatestVersion: null, runnerLatestVersion: "99.0.0" }
|
|
250
246
|
}),
|
|
251
247
|
claimTrigger: async () => ({ ok: true, conflict: false }),
|
|
252
248
|
fetchOrphanedCancelRequested: async () => [],
|
|
@@ -254,7 +250,7 @@ test('startPolling delegates auto-update via injected dependency', async () => {
|
|
|
254
250
|
fetchPendingWorktreeRemovals: async () => [],
|
|
255
251
|
reportWorktreeStatus: async () => undefined,
|
|
256
252
|
notifyUpdate: async () => undefined,
|
|
257
|
-
ackRestartRequest: async () => undefined
|
|
253
|
+
ackRestartRequest: async () => undefined
|
|
258
254
|
}),
|
|
259
255
|
runCleanup: async () => undefined,
|
|
260
256
|
maybeAutoUpdate: async () => {
|
|
@@ -267,34 +263,33 @@ test('startPolling delegates auto-update via injected dependency', async () => {
|
|
|
267
263
|
processExit: (() => undefined),
|
|
268
264
|
now: () => 0,
|
|
269
265
|
loadAuthPaths: () => [],
|
|
270
|
-
saveAuthPath: () =>
|
|
266
|
+
saveAuthPath: () => "/tmp/auth-paths.json",
|
|
271
267
|
keepAlive: () => new Promise((resolve) => {
|
|
272
268
|
keepAliveResolve = resolve;
|
|
273
|
-
})
|
|
269
|
+
})
|
|
274
270
|
});
|
|
275
271
|
await new Promise((resolve) => setImmediate(resolve));
|
|
276
|
-
assert.equal(autoUpdateCallCount, 1,
|
|
277
|
-
const resolveKeepAlive = keepAliveResolve ??
|
|
278
|
-
(
|
|
279
|
-
|
|
280
|
-
});
|
|
272
|
+
assert.equal(autoUpdateCallCount, 1, "should call injected maybeAutoUpdate on idle poll");
|
|
273
|
+
const resolveKeepAlive = keepAliveResolve ?? (() => {
|
|
274
|
+
throw new Error("keepAlive resolver was not registered");
|
|
275
|
+
});
|
|
281
276
|
resolveKeepAlive();
|
|
282
277
|
await pollingPromise;
|
|
283
278
|
});
|
|
284
|
-
test(
|
|
279
|
+
test("startPolling restores persisted auth paths for worktree removals after restart", async () => {
|
|
285
280
|
const removedWorktrees = [];
|
|
286
281
|
const reportedStatuses = [];
|
|
287
|
-
const tempRoot = fsModule.mkdtempSync(path.join(os.tmpdir(),
|
|
288
|
-
const authPath = path.join(tempRoot,
|
|
289
|
-
const worktreePath = path.join(tempRoot,
|
|
282
|
+
const tempRoot = fsModule.mkdtempSync(path.join(os.tmpdir(), "agentrunner-poller-"));
|
|
283
|
+
const authPath = path.join(tempRoot, "path");
|
|
284
|
+
const worktreePath = path.join(tempRoot, ".path-worktrees", "wt-worktree-1");
|
|
290
285
|
fsModule.mkdirSync(authPath, { recursive: true });
|
|
291
286
|
fsModule.mkdirSync(worktreePath, { recursive: true });
|
|
292
287
|
const worktreeRemovalTrigger = {
|
|
293
288
|
...trigger,
|
|
294
|
-
id:
|
|
289
|
+
id: "trigger-remove",
|
|
295
290
|
useWorktree: true,
|
|
296
|
-
worktreeId:
|
|
297
|
-
worktreeStatus:
|
|
291
|
+
worktreeId: "worktree-1",
|
|
292
|
+
worktreeStatus: "REMOVE_REQUESTED"
|
|
298
293
|
};
|
|
299
294
|
let keepAliveResolve = null;
|
|
300
295
|
const pollingPromise = startPolling(config, () => async () => undefined, {
|
|
@@ -308,7 +303,7 @@ test('startPolling restores persisted auth paths for worktree removals after res
|
|
|
308
303
|
reportedStatuses.push({ triggerId, status, worktreeError });
|
|
309
304
|
},
|
|
310
305
|
notifyUpdate: async () => undefined,
|
|
311
|
-
ackRestartRequest: async () => undefined
|
|
306
|
+
ackRestartRequest: async () => undefined
|
|
312
307
|
}),
|
|
313
308
|
runCleanup: async () => undefined,
|
|
314
309
|
removeWorktree: (authPath, worktreePath, worktreeId) => {
|
|
@@ -318,40 +313,37 @@ test('startPolling restores persisted auth paths for worktree removals after res
|
|
|
318
313
|
clearInterval: (() => undefined),
|
|
319
314
|
processOn: (() => undefined),
|
|
320
315
|
processExit: (() => {
|
|
321
|
-
throw new Error(
|
|
316
|
+
throw new Error("should not exit");
|
|
322
317
|
}),
|
|
323
318
|
now: () => 0,
|
|
324
319
|
loadAuthPaths: () => [authPath],
|
|
325
|
-
saveAuthPath: () =>
|
|
320
|
+
saveAuthPath: () => "/tmp/auth-paths.json",
|
|
326
321
|
keepAlive: () => new Promise((resolve) => {
|
|
327
322
|
keepAliveResolve = resolve;
|
|
328
|
-
})
|
|
323
|
+
})
|
|
329
324
|
});
|
|
330
325
|
await new Promise((resolve) => setImmediate(resolve));
|
|
331
|
-
assert.deepEqual(removedWorktrees, [
|
|
332
|
-
{
|
|
326
|
+
assert.deepEqual(removedWorktrees, [{
|
|
333
327
|
authPath,
|
|
334
328
|
worktreePath,
|
|
335
|
-
worktreeId:
|
|
336
|
-
}
|
|
337
|
-
]);
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
throw new Error('keepAlive resolver was not registered');
|
|
342
|
-
});
|
|
329
|
+
worktreeId: "worktree-1"
|
|
330
|
+
}]);
|
|
331
|
+
assert.deepEqual(reportedStatuses, [{ triggerId: "trigger-remove", status: "REMOVED", worktreeError: undefined }]);
|
|
332
|
+
const resolveKeepAlive = keepAliveResolve ?? (() => {
|
|
333
|
+
throw new Error("keepAlive resolver was not registered");
|
|
334
|
+
});
|
|
343
335
|
resolveKeepAlive();
|
|
344
336
|
await pollingPromise;
|
|
345
337
|
fsModule.rmSync(tempRoot, { recursive: true, force: true });
|
|
346
338
|
});
|
|
347
|
-
test(
|
|
339
|
+
test("startPolling reports FAILED when no persisted auth path matches the worktree", async () => {
|
|
348
340
|
const reportedStatuses = [];
|
|
349
341
|
const worktreeRemovalTrigger = {
|
|
350
342
|
...trigger,
|
|
351
|
-
id:
|
|
343
|
+
id: "trigger-remove-missing",
|
|
352
344
|
useWorktree: true,
|
|
353
|
-
worktreeId:
|
|
354
|
-
worktreeStatus:
|
|
345
|
+
worktreeId: "worktree-missing",
|
|
346
|
+
worktreeStatus: "REMOVE_REQUESTED"
|
|
355
347
|
};
|
|
356
348
|
let keepAliveResolve = null;
|
|
357
349
|
const pollingPromise = startPolling(config, () => async () => undefined, {
|
|
@@ -365,53 +357,50 @@ test('startPolling reports FAILED when no persisted auth path matches the worktr
|
|
|
365
357
|
reportedStatuses.push({ triggerId, status, worktreeError });
|
|
366
358
|
},
|
|
367
359
|
notifyUpdate: async () => undefined,
|
|
368
|
-
ackRestartRequest: async () => undefined
|
|
360
|
+
ackRestartRequest: async () => undefined
|
|
369
361
|
}),
|
|
370
362
|
runCleanup: async () => undefined,
|
|
371
363
|
removeWorktree: () => {
|
|
372
|
-
throw new Error(
|
|
364
|
+
throw new Error("removeWorktree should not be called");
|
|
373
365
|
},
|
|
374
366
|
setInterval: (() => ({ ref() { }, unref() { } })),
|
|
375
367
|
clearInterval: (() => undefined),
|
|
376
368
|
processOn: (() => undefined),
|
|
377
369
|
processExit: (() => {
|
|
378
|
-
throw new Error(
|
|
370
|
+
throw new Error("should not exit");
|
|
379
371
|
}),
|
|
380
372
|
now: () => 0,
|
|
381
|
-
loadAuthPaths: () => [
|
|
382
|
-
saveAuthPath: () =>
|
|
373
|
+
loadAuthPaths: () => ["/persisted/auth/path"],
|
|
374
|
+
saveAuthPath: () => "/tmp/auth-paths.json",
|
|
383
375
|
keepAlive: () => new Promise((resolve) => {
|
|
384
376
|
keepAliveResolve = resolve;
|
|
385
|
-
})
|
|
377
|
+
})
|
|
386
378
|
});
|
|
387
379
|
await new Promise((resolve) => setImmediate(resolve));
|
|
388
|
-
assert.deepEqual(reportedStatuses, [
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
(() => {
|
|
397
|
-
throw new Error('keepAlive resolver was not registered');
|
|
398
|
-
});
|
|
380
|
+
assert.deepEqual(reportedStatuses, [{
|
|
381
|
+
triggerId: "trigger-remove-missing",
|
|
382
|
+
status: "FAILED",
|
|
383
|
+
worktreeError: "Failed to remove RunnerBox: worktree path was not found for worktree-missing"
|
|
384
|
+
}]);
|
|
385
|
+
const resolveKeepAlive = keepAliveResolve ?? (() => {
|
|
386
|
+
throw new Error("keepAlive resolver was not registered");
|
|
387
|
+
});
|
|
399
388
|
resolveKeepAlive();
|
|
400
389
|
await pollingPromise;
|
|
401
390
|
});
|
|
402
|
-
test(
|
|
391
|
+
test("startPolling reports FAILED when worktree removal throws after matching a path", async () => {
|
|
403
392
|
const reportedStatuses = [];
|
|
404
|
-
const tempRoot = fsModule.mkdtempSync(path.join(os.tmpdir(),
|
|
405
|
-
const authPath = path.join(tempRoot,
|
|
406
|
-
const worktreePath = path.join(tempRoot,
|
|
393
|
+
const tempRoot = fsModule.mkdtempSync(path.join(os.tmpdir(), "agentrunner-poller-"));
|
|
394
|
+
const authPath = path.join(tempRoot, "path");
|
|
395
|
+
const worktreePath = path.join(tempRoot, ".path-worktrees", "wt-worktree-failed");
|
|
407
396
|
fsModule.mkdirSync(authPath, { recursive: true });
|
|
408
397
|
fsModule.mkdirSync(worktreePath, { recursive: true });
|
|
409
398
|
const worktreeRemovalTrigger = {
|
|
410
399
|
...trigger,
|
|
411
|
-
id:
|
|
400
|
+
id: "trigger-remove-failed",
|
|
412
401
|
useWorktree: true,
|
|
413
|
-
worktreeId:
|
|
414
|
-
worktreeStatus:
|
|
402
|
+
worktreeId: "worktree-failed",
|
|
403
|
+
worktreeStatus: "REMOVE_REQUESTED"
|
|
415
404
|
};
|
|
416
405
|
let keepAliveResolve = null;
|
|
417
406
|
const pollingPromise = startPolling(config, () => async () => undefined, {
|
|
@@ -425,51 +414,45 @@ test('startPolling reports FAILED when worktree removal throws after matching a
|
|
|
425
414
|
reportedStatuses.push({ triggerId, status, worktreeError });
|
|
426
415
|
},
|
|
427
416
|
notifyUpdate: async () => undefined,
|
|
428
|
-
ackRestartRequest: async () => undefined
|
|
417
|
+
ackRestartRequest: async () => undefined
|
|
429
418
|
}),
|
|
430
419
|
runCleanup: async () => undefined,
|
|
431
420
|
removeWorktree: () => {
|
|
432
|
-
throw new Error(
|
|
421
|
+
throw new Error("permission denied");
|
|
433
422
|
},
|
|
434
423
|
setInterval: (() => ({ ref() { }, unref() { } })),
|
|
435
424
|
clearInterval: (() => undefined),
|
|
436
425
|
processOn: (() => undefined),
|
|
437
426
|
processExit: (() => {
|
|
438
|
-
throw new Error(
|
|
427
|
+
throw new Error("should not exit");
|
|
439
428
|
}),
|
|
440
429
|
now: () => 0,
|
|
441
430
|
loadAuthPaths: () => [authPath],
|
|
442
|
-
saveAuthPath: () =>
|
|
431
|
+
saveAuthPath: () => "/tmp/auth-paths.json",
|
|
443
432
|
keepAlive: () => new Promise((resolve) => {
|
|
444
433
|
keepAliveResolve = resolve;
|
|
445
|
-
})
|
|
434
|
+
})
|
|
446
435
|
});
|
|
447
436
|
await new Promise((resolve) => setImmediate(resolve));
|
|
448
|
-
assert.deepEqual(reportedStatuses, [
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
(() => {
|
|
457
|
-
throw new Error('keepAlive resolver was not registered');
|
|
458
|
-
});
|
|
437
|
+
assert.deepEqual(reportedStatuses, [{
|
|
438
|
+
triggerId: "trigger-remove-failed",
|
|
439
|
+
status: "FAILED",
|
|
440
|
+
worktreeError: "permission denied"
|
|
441
|
+
}]);
|
|
442
|
+
const resolveKeepAlive = keepAliveResolve ?? (() => {
|
|
443
|
+
throw new Error("keepAlive resolver was not registered");
|
|
444
|
+
});
|
|
459
445
|
resolveKeepAlive();
|
|
460
446
|
await pollingPromise;
|
|
461
447
|
fsModule.rmSync(tempRoot, { recursive: true, force: true });
|
|
462
448
|
});
|
|
463
|
-
test(
|
|
449
|
+
test("startPolling acks and runs restart when meta.restartRequested is true with no pending trigger", async () => {
|
|
464
450
|
let ackCalled = 0;
|
|
465
451
|
let restartCalled = 0;
|
|
466
452
|
let keepAliveResolve = null;
|
|
467
453
|
const pollingPromise = startPolling(config, () => async () => undefined, {
|
|
468
454
|
createClient: () => ({
|
|
469
|
-
fetchPendingTrigger: async () => ({
|
|
470
|
-
data: null,
|
|
471
|
-
meta: { cliLatestVersion: null, runnerLatestVersion: null, restartRequested: true },
|
|
472
|
-
}),
|
|
455
|
+
fetchPendingTrigger: async () => ({ data: null, meta: { cliLatestVersion: null, runnerLatestVersion: null, restartRequested: true } }),
|
|
473
456
|
claimTrigger: async () => ({ ok: true, conflict: false }),
|
|
474
457
|
fetchOrphanedCancelRequested: async () => [],
|
|
475
458
|
updateTriggerStatus: async () => undefined,
|
|
@@ -478,11 +461,11 @@ test('startPolling acks and runs restart when meta.restartRequested is true with
|
|
|
478
461
|
notifyUpdate: async () => undefined,
|
|
479
462
|
ackRestartRequest: async () => {
|
|
480
463
|
ackCalled += 1;
|
|
481
|
-
}
|
|
464
|
+
}
|
|
482
465
|
}),
|
|
483
466
|
runCleanup: async () => undefined,
|
|
484
467
|
maybeAutoUpdate: async () => {
|
|
485
|
-
throw new Error(
|
|
468
|
+
throw new Error("auto-update must be skipped while restarting");
|
|
486
469
|
},
|
|
487
470
|
executeRestartRequest: () => {
|
|
488
471
|
restartCalled += 1;
|
|
@@ -493,34 +476,30 @@ test('startPolling acks and runs restart when meta.restartRequested is true with
|
|
|
493
476
|
processExit: (() => undefined),
|
|
494
477
|
now: () => 0,
|
|
495
478
|
loadAuthPaths: () => [],
|
|
496
|
-
saveAuthPath: () =>
|
|
479
|
+
saveAuthPath: () => "/tmp/auth-paths.json",
|
|
497
480
|
keepAlive: () => new Promise((resolve) => {
|
|
498
481
|
keepAliveResolve = resolve;
|
|
499
|
-
})
|
|
482
|
+
})
|
|
500
483
|
});
|
|
501
484
|
await new Promise((resolve) => setImmediate(resolve));
|
|
502
|
-
assert.equal(ackCalled, 1,
|
|
503
|
-
assert.equal(restartCalled, 1,
|
|
504
|
-
const resolveKeepAlive = keepAliveResolve ??
|
|
505
|
-
(
|
|
506
|
-
|
|
507
|
-
});
|
|
485
|
+
assert.equal(ackCalled, 1, "ackRestartRequest should be called exactly once");
|
|
486
|
+
assert.equal(restartCalled, 1, "executeRestartRequest should be called exactly once");
|
|
487
|
+
const resolveKeepAlive = keepAliveResolve ?? (() => {
|
|
488
|
+
throw new Error("keepAlive resolver was not registered");
|
|
489
|
+
});
|
|
508
490
|
resolveKeepAlive();
|
|
509
491
|
await pollingPromise;
|
|
510
492
|
});
|
|
511
|
-
test(
|
|
493
|
+
test("startPolling restarts even when a pending trigger is present, ignoring the trigger", async () => {
|
|
512
494
|
let ackCalled = 0;
|
|
513
495
|
let restartCalled = 0;
|
|
514
496
|
let claimCalled = 0;
|
|
515
497
|
let keepAliveResolve = null;
|
|
516
498
|
const pollingPromise = startPolling(config, () => async () => {
|
|
517
|
-
throw new Error(
|
|
499
|
+
throw new Error("trigger handler must not run when restart wins");
|
|
518
500
|
}, {
|
|
519
501
|
createClient: () => ({
|
|
520
|
-
fetchPendingTrigger: async () => ({
|
|
521
|
-
data: trigger,
|
|
522
|
-
meta: { cliLatestVersion: null, runnerLatestVersion: null, restartRequested: true },
|
|
523
|
-
}),
|
|
502
|
+
fetchPendingTrigger: async () => ({ data: trigger, meta: { cliLatestVersion: null, runnerLatestVersion: null, restartRequested: true } }),
|
|
524
503
|
claimTrigger: async () => {
|
|
525
504
|
claimCalled += 1;
|
|
526
505
|
return { ok: true, conflict: false };
|
|
@@ -532,7 +511,7 @@ test('startPolling restarts even when a pending trigger is present, ignoring the
|
|
|
532
511
|
notifyUpdate: async () => undefined,
|
|
533
512
|
ackRestartRequest: async () => {
|
|
534
513
|
ackCalled += 1;
|
|
535
|
-
}
|
|
514
|
+
}
|
|
536
515
|
}),
|
|
537
516
|
runCleanup: async () => undefined,
|
|
538
517
|
executeRestartRequest: () => {
|
|
@@ -544,31 +523,27 @@ test('startPolling restarts even when a pending trigger is present, ignoring the
|
|
|
544
523
|
processExit: (() => undefined),
|
|
545
524
|
now: () => 0,
|
|
546
525
|
loadAuthPaths: () => [],
|
|
547
|
-
saveAuthPath: () =>
|
|
526
|
+
saveAuthPath: () => "/tmp/auth-paths.json",
|
|
548
527
|
keepAlive: () => new Promise((resolve) => {
|
|
549
528
|
keepAliveResolve = resolve;
|
|
550
|
-
})
|
|
529
|
+
})
|
|
551
530
|
});
|
|
552
531
|
await new Promise((resolve) => setImmediate(resolve));
|
|
553
|
-
assert.equal(ackCalled, 1,
|
|
554
|
-
assert.equal(restartCalled, 1,
|
|
555
|
-
assert.equal(claimCalled, 0,
|
|
556
|
-
const resolveKeepAlive = keepAliveResolve ??
|
|
557
|
-
(
|
|
558
|
-
|
|
559
|
-
});
|
|
532
|
+
assert.equal(ackCalled, 1, "ack should run before restart");
|
|
533
|
+
assert.equal(restartCalled, 1, "restart should run even with a pending trigger");
|
|
534
|
+
assert.equal(claimCalled, 0, "trigger must not be claimed while restarting");
|
|
535
|
+
const resolveKeepAlive = keepAliveResolve ?? (() => {
|
|
536
|
+
throw new Error("keepAlive resolver was not registered");
|
|
537
|
+
});
|
|
560
538
|
resolveKeepAlive();
|
|
561
539
|
await pollingPromise;
|
|
562
540
|
});
|
|
563
|
-
test(
|
|
541
|
+
test("startPolling skips restart and leaves the flag for retry when ack fails", async () => {
|
|
564
542
|
let restartCalled = 0;
|
|
565
543
|
let keepAliveResolve = null;
|
|
566
544
|
const pollingPromise = startPolling(config, () => async () => undefined, {
|
|
567
545
|
createClient: () => ({
|
|
568
|
-
fetchPendingTrigger: async () => ({
|
|
569
|
-
data: null,
|
|
570
|
-
meta: { cliLatestVersion: null, runnerLatestVersion: null, restartRequested: true },
|
|
571
|
-
}),
|
|
546
|
+
fetchPendingTrigger: async () => ({ data: null, meta: { cliLatestVersion: null, runnerLatestVersion: null, restartRequested: true } }),
|
|
572
547
|
claimTrigger: async () => ({ ok: true, conflict: false }),
|
|
573
548
|
fetchOrphanedCancelRequested: async () => [],
|
|
574
549
|
updateTriggerStatus: async () => undefined,
|
|
@@ -576,8 +551,8 @@ test('startPolling skips restart and leaves the flag for retry when ack fails',
|
|
|
576
551
|
reportWorktreeStatus: async () => undefined,
|
|
577
552
|
notifyUpdate: async () => undefined,
|
|
578
553
|
ackRestartRequest: async () => {
|
|
579
|
-
throw new Error(
|
|
580
|
-
}
|
|
554
|
+
throw new Error("network down");
|
|
555
|
+
}
|
|
581
556
|
}),
|
|
582
557
|
runCleanup: async () => undefined,
|
|
583
558
|
executeRestartRequest: () => {
|
|
@@ -589,21 +564,20 @@ test('startPolling skips restart and leaves the flag for retry when ack fails',
|
|
|
589
564
|
processExit: (() => undefined),
|
|
590
565
|
now: () => 0,
|
|
591
566
|
loadAuthPaths: () => [],
|
|
592
|
-
saveAuthPath: () =>
|
|
567
|
+
saveAuthPath: () => "/tmp/auth-paths.json",
|
|
593
568
|
keepAlive: () => new Promise((resolve) => {
|
|
594
569
|
keepAliveResolve = resolve;
|
|
595
|
-
})
|
|
570
|
+
})
|
|
596
571
|
});
|
|
597
572
|
await new Promise((resolve) => setImmediate(resolve));
|
|
598
|
-
assert.equal(restartCalled, 0,
|
|
599
|
-
const resolveKeepAlive = keepAliveResolve ??
|
|
600
|
-
(
|
|
601
|
-
|
|
602
|
-
});
|
|
573
|
+
assert.equal(restartCalled, 0, "restart must not run when ack fails");
|
|
574
|
+
const resolveKeepAlive = keepAliveResolve ?? (() => {
|
|
575
|
+
throw new Error("keepAlive resolver was not registered");
|
|
576
|
+
});
|
|
603
577
|
resolveKeepAlive();
|
|
604
578
|
await pollingPromise;
|
|
605
579
|
});
|
|
606
|
-
test(
|
|
580
|
+
test("startPolling acquires the power save blocker on start and releases it on shutdown", async () => {
|
|
607
581
|
const { events, blocker } = createRecordingBlocker();
|
|
608
582
|
const signals = new Map();
|
|
609
583
|
let keepAliveResolve = null;
|
|
@@ -616,7 +590,7 @@ test('startPolling acquires the power save blocker on start and releases it on s
|
|
|
616
590
|
fetchPendingWorktreeRemovals: async () => [],
|
|
617
591
|
reportWorktreeStatus: async () => undefined,
|
|
618
592
|
notifyUpdate: async () => undefined,
|
|
619
|
-
ackRestartRequest: async () => undefined
|
|
593
|
+
ackRestartRequest: async () => undefined
|
|
620
594
|
}),
|
|
621
595
|
runCleanup: async () => undefined,
|
|
622
596
|
setInterval: (() => ({})),
|
|
@@ -627,26 +601,25 @@ test('startPolling acquires the power save blocker on start and releases it on s
|
|
|
627
601
|
processExit: (() => undefined),
|
|
628
602
|
now: () => 0,
|
|
629
603
|
loadAuthPaths: () => [],
|
|
630
|
-
saveAuthPath: () =>
|
|
604
|
+
saveAuthPath: () => "/tmp/auth-paths.json",
|
|
631
605
|
powerSaveBlocker: blocker,
|
|
632
606
|
keepAlive: () => new Promise((resolve) => {
|
|
633
607
|
keepAliveResolve = resolve;
|
|
634
|
-
})
|
|
608
|
+
})
|
|
635
609
|
});
|
|
636
610
|
await new Promise((resolve) => setImmediate(resolve));
|
|
637
|
-
assert.deepEqual(events, [
|
|
638
|
-
const sigterm = signals.get(
|
|
611
|
+
assert.deepEqual(events, ["acquire:daemon-polling"], "blocker should be acquired exactly once on start");
|
|
612
|
+
const sigterm = signals.get("SIGTERM");
|
|
639
613
|
assert.ok(sigterm);
|
|
640
614
|
sigterm();
|
|
641
|
-
assert.deepEqual(events, [
|
|
642
|
-
const resolveKeepAlive = keepAliveResolve ??
|
|
643
|
-
(
|
|
644
|
-
|
|
645
|
-
});
|
|
615
|
+
assert.deepEqual(events, ["acquire:daemon-polling", "release:daemon-polling"], "blocker should be released on shutdown");
|
|
616
|
+
const resolveKeepAlive = keepAliveResolve ?? (() => {
|
|
617
|
+
throw new Error("keepAlive resolver was not registered");
|
|
618
|
+
});
|
|
646
619
|
resolveKeepAlive();
|
|
647
620
|
await pollingPromise;
|
|
648
621
|
});
|
|
649
|
-
test(
|
|
622
|
+
test("startPolling releases the power save blocker when keepAlive resolves", async () => {
|
|
650
623
|
const { events, blocker } = createRecordingBlocker();
|
|
651
624
|
let keepAliveResolve = null;
|
|
652
625
|
const pollingPromise = startPolling(config, () => async () => undefined, {
|
|
@@ -658,7 +631,7 @@ test('startPolling releases the power save blocker when keepAlive resolves', asy
|
|
|
658
631
|
fetchPendingWorktreeRemovals: async () => [],
|
|
659
632
|
reportWorktreeStatus: async () => undefined,
|
|
660
633
|
notifyUpdate: async () => undefined,
|
|
661
|
-
ackRestartRequest: async () => undefined
|
|
634
|
+
ackRestartRequest: async () => undefined
|
|
662
635
|
}),
|
|
663
636
|
runCleanup: async () => undefined,
|
|
664
637
|
setInterval: (() => ({})),
|
|
@@ -667,20 +640,19 @@ test('startPolling releases the power save blocker when keepAlive resolves', asy
|
|
|
667
640
|
processExit: (() => undefined),
|
|
668
641
|
now: () => 0,
|
|
669
642
|
loadAuthPaths: () => [],
|
|
670
|
-
saveAuthPath: () =>
|
|
643
|
+
saveAuthPath: () => "/tmp/auth-paths.json",
|
|
671
644
|
powerSaveBlocker: blocker,
|
|
672
645
|
keepAlive: () => new Promise((resolve) => {
|
|
673
646
|
keepAliveResolve = resolve;
|
|
674
|
-
})
|
|
647
|
+
})
|
|
675
648
|
});
|
|
676
649
|
await new Promise((resolve) => setImmediate(resolve));
|
|
677
|
-
assert.deepEqual(events, [
|
|
678
|
-
const resolveKeepAlive = keepAliveResolve ??
|
|
679
|
-
(
|
|
680
|
-
|
|
681
|
-
});
|
|
650
|
+
assert.deepEqual(events, ["acquire:daemon-polling"]);
|
|
651
|
+
const resolveKeepAlive = keepAliveResolve ?? (() => {
|
|
652
|
+
throw new Error("keepAlive resolver was not registered");
|
|
653
|
+
});
|
|
682
654
|
resolveKeepAlive();
|
|
683
655
|
await pollingPromise;
|
|
684
|
-
assert.deepEqual(events, [
|
|
656
|
+
assert.deepEqual(events, ["acquire:daemon-polling", "release:daemon-polling"], "blocker should be released after keepAlive resolves");
|
|
685
657
|
});
|
|
686
658
|
//# sourceMappingURL=poller.test.js.map
|