Skip to content

Commit b236e8b

Browse files
only21milcodex
andcommitted
test(clients): isolate outdated-host progress recovery
Preserve the existing bare-socket update test and add a separate progress-disconnect case. Co-Authored-By: GPT-6.1 Sol <[email protected]>
1 parent 408a437 commit b236e8b

1 file changed

Lines changed: 111 additions & 20 deletions

File tree

‎packages/client-runtime/src/connection/outdatedHostUpdate.test.ts‎

Lines changed: 111 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -166,20 +166,13 @@ class OutdatedHostSocket {
166166
}
167167

168168
describe("updateOutdatedHost", () => {
169-
it.effect.each([false, true])("updates host, progress loss=%s", (progressDisconnect) =>
169+
it.effect("updates a protocol-1 host over a bare socket, then switches it back on", () =>
170170
Effect.gen(function* () {
171171
const blocked = orchestrationProtocolCompatibilityError(descriptor(undefined, "0.0.45"));
172172
expect(blocked).toMatchObject({ serverUpdateRequired: true });
173173

174174
const sockets: Array<OutdatedHostSocket> = [];
175-
let served: ExecutionEnvironmentDescriptor = {
176-
...descriptor(undefined, "0.0.45"),
177-
capabilities: {
178-
repositoryIdentity: true,
179-
serverSelfUpdate: "boot-service",
180-
serverSelfUpdateProgress: progressDisconnect,
181-
},
182-
};
175+
let served: ExecutionEnvironmentDescriptor = descriptor(undefined, "0.0.45");
183176
const entries = yield* SubscriptionRef.make<
184177
ReadonlyMap<EnvironmentId, ConnectionCatalogEntry>
185178
>(
@@ -227,16 +220,10 @@ describe("updateOutdatedHost", () => {
227220
),
228221
);
229222

230-
const stages: Array<string> = [];
231223
const result = yield* updateOutdatedHost(
232224
TARGET.environmentId,
233225
{ targetVersion: "0.0.46" },
234-
(stage) =>
235-
Effect.sync(() => {
236-
stages.push(stage);
237-
// Lose the transport only after the consumer has observed progress.
238-
if (stage === "downloading") sockets[0]?.close();
239-
}),
226+
() => Effect.void,
240227
).pipe(
241228
Effect.provide(
242229
Layer.mergeAll(
@@ -265,11 +252,8 @@ describe("updateOutdatedHost", () => {
265252

266253
expect(sockets[0]?.url).not.toContain("orchestrationProtocol");
267254
expect(sockets[0]?.requests.map((request) => request.tag)).toEqual([
268-
progressDisconnect
269-
? WS_METHODS.serverUpdateServerWithProgress
270-
: WS_METHODS.serverUpdateServer,
255+
WS_METHODS.serverUpdateServer,
271256
]);
272-
expect(stages).toEqual(progressDisconnect ? ["downloading", "resuming"] : ["resuming"]);
273257
expect(result.targetVersion).toBe("0.0.46");
274258
expect(calls).toEqual(["compatibility:clear", "enabled:true"]);
275259
}),
@@ -344,4 +328,111 @@ describe("updateOutdatedHost", () => {
344328
expect(opened).toBe(false);
345329
}),
346330
);
331+
332+
it.effect("resumes an outdated host update after progress transport loss", () =>
333+
Effect.gen(function* () {
334+
const blocked = orchestrationProtocolCompatibilityError(descriptor(undefined, "0.0.45"));
335+
expect(blocked).toMatchObject({ serverUpdateRequired: true });
336+
337+
const sockets: Array<OutdatedHostSocket> = [];
338+
let served: ExecutionEnvironmentDescriptor = {
339+
...descriptor(undefined, "0.0.45"),
340+
capabilities: {
341+
repositoryIdentity: true,
342+
serverSelfUpdate: "boot-service",
343+
serverSelfUpdateProgress: true,
344+
},
345+
};
346+
const entries = yield* SubscriptionRef.make<
347+
ReadonlyMap<EnvironmentId, ConnectionCatalogEntry>
348+
>(
349+
new Map([
350+
[
351+
TARGET.environmentId,
352+
{
353+
target: TARGET,
354+
profile: Option.none(),
355+
enabled: false,
356+
unsupportedReason: blocked?.message ?? "",
357+
serverUpdateRequired: true,
358+
},
359+
],
360+
]),
361+
);
362+
const calls: Array<string> = [];
363+
const registry = EnvironmentRegistry.EnvironmentRegistry.of({
364+
entries,
365+
setCompatibility: (_environmentId: EnvironmentId, error: unknown) =>
366+
Effect.sync(() => calls.push(`compatibility:${error === null ? "clear" : "block"}`)),
367+
setEnabled: (_environmentId: EnvironmentId, enabled: boolean) =>
368+
Effect.sync(() => calls.push(`enabled:${enabled}`)),
369+
} as unknown as EnvironmentRegistry.EnvironmentRegistry["Service"]);
370+
const resolver = ConnectionResolver.ConnectionResolver.of({
371+
prepare: () => Effect.die(new Error("The update must bypass the protocol gate.")),
372+
prepareForUpdate: () =>
373+
Effect.sync(() => served).pipe(
374+
Effect.map((current) => ({
375+
descriptor: current,
376+
prepared: {
377+
environmentId: TARGET.environmentId,
378+
label: TARGET.label,
379+
httpBaseUrl: TARGET.httpBaseUrl,
380+
socketUrl: "wss://build.example.test/ws?wsTicket=ticket",
381+
httpAuthorization: null,
382+
target: TARGET,
383+
},
384+
})),
385+
),
386+
});
387+
const httpClient = HttpClient.make((request) =>
388+
Effect.sync(() =>
389+
HttpClientResponse.fromWeb(request, new Response(encodeDescriptor(served))),
390+
),
391+
);
392+
393+
const stages: Array<string> = [];
394+
const result = yield* updateOutdatedHost(
395+
TARGET.environmentId,
396+
{ targetVersion: "0.0.46" },
397+
(stage) =>
398+
Effect.sync(() => {
399+
stages.push(stage);
400+
// Lose the transport only after the consumer has observed progress.
401+
if (stage === "downloading") sockets[0]?.close();
402+
}),
403+
).pipe(
404+
Effect.provide(
405+
Layer.mergeAll(
406+
Layer.succeed(EnvironmentRegistry.EnvironmentRegistry, registry),
407+
Layer.succeed(ConnectionResolver.ConnectionResolver, resolver),
408+
Layer.succeed(
409+
RelayEnvironmentDiscovery.RelayEnvironmentDiscovery,
410+
RelayEnvironmentDiscovery.RelayEnvironmentDiscovery.of({
411+
state: yield* SubscriptionRef.make(
412+
RelayEnvironmentDiscovery.EMPTY_RELAY_ENVIRONMENT_DISCOVERY_STATE,
413+
),
414+
refresh: Effect.void,
415+
}),
416+
),
417+
Layer.succeed(HttpClient.HttpClient, httpClient),
418+
Layer.succeed(Socket.WebSocketConstructor, (url) => {
419+
const socket = new OutdatedHostSocket(url, () => {
420+
served = descriptor(ORCHESTRATION_PROTOCOL_VERSION, "0.0.46");
421+
});
422+
sockets.push(socket);
423+
return socket as unknown as globalThis.WebSocket;
424+
}),
425+
),
426+
),
427+
);
428+
429+
expect(sockets[0]?.url).not.toContain("orchestrationProtocol");
430+
expect(sockets[0]?.requests.map((request) => request.tag)).toEqual([
431+
WS_METHODS.serverUpdateServerWithProgress,
432+
]);
433+
expect(stages).toEqual(["downloading", "resuming"]);
434+
expect(result.targetVersion).toBe("0.0.46");
435+
expect(calls).toEqual(["compatibility:clear", "enabled:true"]);
436+
}),
437+
);
347438
});

0 commit comments

Comments
 (0)