@indigoai-us/hq-cloud 6.15.82 → 6.15.83
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.
|
@@ -4067,6 +4067,238 @@ describe("runRunnerWithLoop — adaptive poll interval (no --poll-remote-ms)", (
|
|
|
4067
4067
|
});
|
|
4068
4068
|
});
|
|
4069
4069
|
describe("runRunnerWithLoop — event-push wiring", () => {
|
|
4070
|
+
it("parallel coordinator activates every event surface after a partial completed full reconcile", async () => {
|
|
4071
|
+
const previousCoordinator = process.env.HQ_SYNC_PARALLEL_COORDINATOR;
|
|
4072
|
+
const previousEventSync = process.env.HQ_SYNC_EVENT_SYNC;
|
|
4073
|
+
const previousFullReconcileMs = process.env.HQ_SYNC_FULL_RECONCILE_MS;
|
|
4074
|
+
process.env.HQ_SYNC_PARALLEL_COORDINATOR = "1";
|
|
4075
|
+
process.env.HQ_SYNC_EVENT_SYNC = "1";
|
|
4076
|
+
process.env.HQ_SYNC_FULL_RECONCILE_MS = "60000";
|
|
4077
|
+
const watcher = makeWatcherStub();
|
|
4078
|
+
const receiver = {
|
|
4079
|
+
connected: false,
|
|
4080
|
+
start: vi.fn().mockResolvedValue(undefined),
|
|
4081
|
+
dispose: vi.fn().mockResolvedValue(undefined),
|
|
4082
|
+
};
|
|
4083
|
+
const scheduler = {
|
|
4084
|
+
signal: vi.fn(),
|
|
4085
|
+
checkHighWater: vi.fn().mockResolvedValue(undefined),
|
|
4086
|
+
dispose: vi.fn().mockResolvedValue(undefined),
|
|
4087
|
+
};
|
|
4088
|
+
const sleep = makeSteppableSleep();
|
|
4089
|
+
let now = 0;
|
|
4090
|
+
let triggerShutdown = () => { };
|
|
4091
|
+
const createWatcher = vi.fn(() => watcher);
|
|
4092
|
+
const createReceiver = vi.fn(() => receiver);
|
|
4093
|
+
const startRealtimeScheduler = vi.fn().mockResolvedValue(scheduler);
|
|
4094
|
+
const startEventSync = vi.fn().mockResolvedValue(null);
|
|
4095
|
+
const runPass = vi.fn().mockResolvedValue(completedPassOutcome(PARTIAL_SYNC_EXIT));
|
|
4096
|
+
const loop = runRunnerWithLoop(["--companies", "--watch", "--event-push", "--hq-root", "/tmp/hq"], {
|
|
4097
|
+
runPass,
|
|
4098
|
+
createWatcher,
|
|
4099
|
+
createReceiver,
|
|
4100
|
+
startRealtimeScheduler,
|
|
4101
|
+
startEventSync,
|
|
4102
|
+
getIdTokenClaims: () => null,
|
|
4103
|
+
getAccessToken: async () => "jwt-test",
|
|
4104
|
+
monotonicNow: () => now,
|
|
4105
|
+
sleep: sleep.sleep,
|
|
4106
|
+
onShutdownSignal: (handler) => {
|
|
4107
|
+
triggerShutdown = handler;
|
|
4108
|
+
return () => { };
|
|
4109
|
+
},
|
|
4110
|
+
});
|
|
4111
|
+
try {
|
|
4112
|
+
await flushLoopMicrotasks(30);
|
|
4113
|
+
expect(createWatcher).toHaveBeenCalledTimes(1);
|
|
4114
|
+
expect(createReceiver).toHaveBeenCalledTimes(1);
|
|
4115
|
+
expect(receiver.start).toHaveBeenCalledTimes(1);
|
|
4116
|
+
expect(startRealtimeScheduler).toHaveBeenCalledTimes(1);
|
|
4117
|
+
expect(startEventSync).toHaveBeenCalledTimes(1);
|
|
4118
|
+
// A partial RunnerPassOutcome is a completed legacy-equivalent pass, so
|
|
4119
|
+
// its completion must restart the configured full-reconcile cadence.
|
|
4120
|
+
now = 1;
|
|
4121
|
+
sleep.tick();
|
|
4122
|
+
await flushLoopMicrotasks();
|
|
4123
|
+
expect(runPass).toHaveBeenCalledTimes(1);
|
|
4124
|
+
}
|
|
4125
|
+
finally {
|
|
4126
|
+
triggerShutdown();
|
|
4127
|
+
await loop;
|
|
4128
|
+
if (previousCoordinator === undefined)
|
|
4129
|
+
delete process.env.HQ_SYNC_PARALLEL_COORDINATOR;
|
|
4130
|
+
else
|
|
4131
|
+
process.env.HQ_SYNC_PARALLEL_COORDINATOR = previousCoordinator;
|
|
4132
|
+
if (previousEventSync === undefined)
|
|
4133
|
+
delete process.env.HQ_SYNC_EVENT_SYNC;
|
|
4134
|
+
else
|
|
4135
|
+
process.env.HQ_SYNC_EVENT_SYNC = previousEventSync;
|
|
4136
|
+
if (previousFullReconcileMs === undefined)
|
|
4137
|
+
delete process.env.HQ_SYNC_FULL_RECONCILE_MS;
|
|
4138
|
+
else
|
|
4139
|
+
process.env.HQ_SYNC_FULL_RECONCILE_MS = previousFullReconcileMs;
|
|
4140
|
+
}
|
|
4141
|
+
});
|
|
4142
|
+
it("parallel coordinator activates event surfaces and advances cadence after a numeric-zero full reconcile", async () => {
|
|
4143
|
+
const previousCoordinator = process.env.HQ_SYNC_PARALLEL_COORDINATOR;
|
|
4144
|
+
const previousEventSync = process.env.HQ_SYNC_EVENT_SYNC;
|
|
4145
|
+
const previousFullReconcileMs = process.env.HQ_SYNC_FULL_RECONCILE_MS;
|
|
4146
|
+
process.env.HQ_SYNC_PARALLEL_COORDINATOR = "1";
|
|
4147
|
+
process.env.HQ_SYNC_EVENT_SYNC = "1";
|
|
4148
|
+
process.env.HQ_SYNC_FULL_RECONCILE_MS = "60000";
|
|
4149
|
+
const watcher = makeWatcherStub();
|
|
4150
|
+
const receiver = {
|
|
4151
|
+
connected: false,
|
|
4152
|
+
start: vi.fn().mockResolvedValue(undefined),
|
|
4153
|
+
dispose: vi.fn().mockResolvedValue(undefined),
|
|
4154
|
+
};
|
|
4155
|
+
const scheduler = {
|
|
4156
|
+
signal: vi.fn(),
|
|
4157
|
+
checkHighWater: vi.fn().mockResolvedValue(undefined),
|
|
4158
|
+
dispose: vi.fn().mockResolvedValue(undefined),
|
|
4159
|
+
};
|
|
4160
|
+
const sleep = makeSteppableSleep();
|
|
4161
|
+
let now = 0;
|
|
4162
|
+
let triggerShutdown = () => { };
|
|
4163
|
+
const createWatcher = vi.fn(() => watcher);
|
|
4164
|
+
const createReceiver = vi.fn(() => receiver);
|
|
4165
|
+
const startRealtimeScheduler = vi.fn().mockResolvedValue(scheduler);
|
|
4166
|
+
const startEventSync = vi.fn().mockResolvedValue(null);
|
|
4167
|
+
const runPass = vi.fn().mockResolvedValue(0);
|
|
4168
|
+
const loop = runRunnerWithLoop(["--companies", "--watch", "--event-push", "--hq-root", "/tmp/hq"], {
|
|
4169
|
+
runPass,
|
|
4170
|
+
createWatcher,
|
|
4171
|
+
createReceiver,
|
|
4172
|
+
startRealtimeScheduler,
|
|
4173
|
+
startEventSync,
|
|
4174
|
+
getIdTokenClaims: () => null,
|
|
4175
|
+
getAccessToken: async () => "jwt-test",
|
|
4176
|
+
monotonicNow: () => now,
|
|
4177
|
+
sleep: sleep.sleep,
|
|
4178
|
+
onShutdownSignal: (handler) => {
|
|
4179
|
+
triggerShutdown = handler;
|
|
4180
|
+
return () => { };
|
|
4181
|
+
},
|
|
4182
|
+
});
|
|
4183
|
+
try {
|
|
4184
|
+
await flushLoopMicrotasks(30);
|
|
4185
|
+
expect(createWatcher).toHaveBeenCalledTimes(1);
|
|
4186
|
+
expect(createReceiver).toHaveBeenCalledTimes(1);
|
|
4187
|
+
expect(receiver.start).toHaveBeenCalledTimes(1);
|
|
4188
|
+
expect(startRealtimeScheduler).toHaveBeenCalledTimes(1);
|
|
4189
|
+
expect(startEventSync).toHaveBeenCalledTimes(1);
|
|
4190
|
+
// The accepted numeric success seam must advance cadence, preventing an
|
|
4191
|
+
// immediate second full reconcile before the configured interval.
|
|
4192
|
+
now = 1;
|
|
4193
|
+
sleep.tick();
|
|
4194
|
+
await flushLoopMicrotasks();
|
|
4195
|
+
expect(runPass).toHaveBeenCalledTimes(1);
|
|
4196
|
+
}
|
|
4197
|
+
finally {
|
|
4198
|
+
triggerShutdown();
|
|
4199
|
+
await loop;
|
|
4200
|
+
if (previousCoordinator === undefined)
|
|
4201
|
+
delete process.env.HQ_SYNC_PARALLEL_COORDINATOR;
|
|
4202
|
+
else
|
|
4203
|
+
process.env.HQ_SYNC_PARALLEL_COORDINATOR = previousCoordinator;
|
|
4204
|
+
if (previousEventSync === undefined)
|
|
4205
|
+
delete process.env.HQ_SYNC_EVENT_SYNC;
|
|
4206
|
+
else
|
|
4207
|
+
process.env.HQ_SYNC_EVENT_SYNC = previousEventSync;
|
|
4208
|
+
if (previousFullReconcileMs === undefined)
|
|
4209
|
+
delete process.env.HQ_SYNC_FULL_RECONCILE_MS;
|
|
4210
|
+
else
|
|
4211
|
+
process.env.HQ_SYNC_FULL_RECONCILE_MS = previousFullReconcileMs;
|
|
4212
|
+
}
|
|
4213
|
+
});
|
|
4214
|
+
it("parallel coordinator keeps event surfaces off for an auth-required full reconcile", async () => {
|
|
4215
|
+
const previousCoordinator = process.env.HQ_SYNC_PARALLEL_COORDINATOR;
|
|
4216
|
+
const previousEventSync = process.env.HQ_SYNC_EVENT_SYNC;
|
|
4217
|
+
process.env.HQ_SYNC_PARALLEL_COORDINATOR = "1";
|
|
4218
|
+
process.env.HQ_SYNC_EVENT_SYNC = "1";
|
|
4219
|
+
const watcher = makeWatcherStub();
|
|
4220
|
+
const createWatcher = vi.fn(() => watcher);
|
|
4221
|
+
const createReceiver = vi.fn();
|
|
4222
|
+
const startRealtimeScheduler = vi.fn();
|
|
4223
|
+
const startEventSync = vi.fn();
|
|
4224
|
+
let triggerShutdown = () => { };
|
|
4225
|
+
const loop = runRunnerWithLoop(["--companies", "--watch", "--event-push", "--hq-root", "/tmp/hq"], {
|
|
4226
|
+
runPass: vi.fn().mockResolvedValue(AUTH_REQUIRED_PASS_EXIT),
|
|
4227
|
+
createWatcher,
|
|
4228
|
+
createReceiver,
|
|
4229
|
+
startRealtimeScheduler,
|
|
4230
|
+
startEventSync,
|
|
4231
|
+
sleep: () => new Promise(() => { }),
|
|
4232
|
+
onShutdownSignal: (handler) => {
|
|
4233
|
+
triggerShutdown = handler;
|
|
4234
|
+
return () => { };
|
|
4235
|
+
},
|
|
4236
|
+
});
|
|
4237
|
+
try {
|
|
4238
|
+
await flushLoopMicrotasks(30);
|
|
4239
|
+
expect(createWatcher).not.toHaveBeenCalled();
|
|
4240
|
+
expect(createReceiver).not.toHaveBeenCalled();
|
|
4241
|
+
expect(startRealtimeScheduler).not.toHaveBeenCalled();
|
|
4242
|
+
expect(startEventSync).not.toHaveBeenCalled();
|
|
4243
|
+
}
|
|
4244
|
+
finally {
|
|
4245
|
+
triggerShutdown();
|
|
4246
|
+
await loop;
|
|
4247
|
+
if (previousCoordinator === undefined)
|
|
4248
|
+
delete process.env.HQ_SYNC_PARALLEL_COORDINATOR;
|
|
4249
|
+
else
|
|
4250
|
+
process.env.HQ_SYNC_PARALLEL_COORDINATOR = previousCoordinator;
|
|
4251
|
+
if (previousEventSync === undefined)
|
|
4252
|
+
delete process.env.HQ_SYNC_EVENT_SYNC;
|
|
4253
|
+
else
|
|
4254
|
+
process.env.HQ_SYNC_EVENT_SYNC = previousEventSync;
|
|
4255
|
+
}
|
|
4256
|
+
});
|
|
4257
|
+
it("parallel coordinator keeps event surfaces off when no RunnerPassOutcome completed", async () => {
|
|
4258
|
+
const previousCoordinator = process.env.HQ_SYNC_PARALLEL_COORDINATOR;
|
|
4259
|
+
const previousEventSync = process.env.HQ_SYNC_EVENT_SYNC;
|
|
4260
|
+
process.env.HQ_SYNC_PARALLEL_COORDINATOR = "1";
|
|
4261
|
+
process.env.HQ_SYNC_EVENT_SYNC = "1";
|
|
4262
|
+
const watcher = makeWatcherStub();
|
|
4263
|
+
const createWatcher = vi.fn(() => watcher);
|
|
4264
|
+
const createReceiver = vi.fn();
|
|
4265
|
+
const startRealtimeScheduler = vi.fn();
|
|
4266
|
+
const startEventSync = vi.fn();
|
|
4267
|
+
let triggerShutdown = () => { };
|
|
4268
|
+
const loop = runRunnerWithLoop(["--companies", "--watch", "--event-push", "--hq-root", "/tmp/hq"], {
|
|
4269
|
+
// A bare nonzero status represents a chore that did not produce the
|
|
4270
|
+
// post-repair runner outcome needed to prove completion.
|
|
4271
|
+
runPass: vi.fn().mockResolvedValue(PARTIAL_SYNC_EXIT),
|
|
4272
|
+
createWatcher,
|
|
4273
|
+
createReceiver,
|
|
4274
|
+
startRealtimeScheduler,
|
|
4275
|
+
startEventSync,
|
|
4276
|
+
sleep: () => new Promise(() => { }),
|
|
4277
|
+
onShutdownSignal: (handler) => {
|
|
4278
|
+
triggerShutdown = handler;
|
|
4279
|
+
return () => { };
|
|
4280
|
+
},
|
|
4281
|
+
});
|
|
4282
|
+
try {
|
|
4283
|
+
await flushLoopMicrotasks(30);
|
|
4284
|
+
expect(createWatcher).not.toHaveBeenCalled();
|
|
4285
|
+
expect(createReceiver).not.toHaveBeenCalled();
|
|
4286
|
+
expect(startRealtimeScheduler).not.toHaveBeenCalled();
|
|
4287
|
+
expect(startEventSync).not.toHaveBeenCalled();
|
|
4288
|
+
}
|
|
4289
|
+
finally {
|
|
4290
|
+
triggerShutdown();
|
|
4291
|
+
await loop;
|
|
4292
|
+
if (previousCoordinator === undefined)
|
|
4293
|
+
delete process.env.HQ_SYNC_PARALLEL_COORDINATOR;
|
|
4294
|
+
else
|
|
4295
|
+
process.env.HQ_SYNC_PARALLEL_COORDINATOR = previousCoordinator;
|
|
4296
|
+
if (previousEventSync === undefined)
|
|
4297
|
+
delete process.env.HQ_SYNC_EVENT_SYNC;
|
|
4298
|
+
else
|
|
4299
|
+
process.env.HQ_SYNC_EVENT_SYNC = previousEventSync;
|
|
4300
|
+
}
|
|
4301
|
+
});
|
|
4070
4302
|
it("keeps a healthy watcher through repeated poll cycles and the periodic full reconcile", async () => {
|
|
4071
4303
|
const sleep = makeSteppableSleep();
|
|
4072
4304
|
const watchers = [];
|