Skip to content

Commit 6cd0f29

Browse files
committed
test(remote): assert the proxied snapshot's whole wire conversation
#2198 required proof that "the bridge adds no network round trip and transfers only the published response". That was read off the RTT benchmark — identical response bytes, unchanged wall-clock slope — which is inference, not proof, and the raw-result schema carries no round-trip count. Assert it instead: record every request the proxy forwards upstream while one `snapshot -i` runs, and pin the whole conversation rather than just the RPC, so a new call of any kind fails. The bridge contributes nothing to it — no helper, admin or acquisition route appears — and the bytes crossing are the published response. Refs #2198.
1 parent 65ff270 commit 6cd0f29

1 file changed

Lines changed: 95 additions & 2 deletions

File tree

‎test/integration/provider-scenarios/remote-proxy-parity.test.ts‎

Lines changed: 95 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import assert from 'node:assert/strict';
22
import fs from 'node:fs';
3+
import http from 'node:http';
34
import os from 'node:os';
45
import path from 'node:path';
56
import { test } from 'vitest';
@@ -228,7 +229,7 @@ async function runLeg(leg: ScenarioLeg, legName: string): Promise<DaemonResponse
228229
}
229230

230231
async function withProxiedWorld<T>(
231-
run: (context: { world: ParityWorld; proxied: ScenarioLeg }) => Promise<T>,
232+
run: (context: { world: ParityWorld; proxied: ScenarioLeg; upstream: http.Server }) => Promise<T>,
232233
options: ParityWorldOptions = {},
233234
): Promise<T> {
234235
const world = await createParityWorld(options);
@@ -259,7 +260,7 @@ async function withProxiedWorld<T>(
259260
return responseFromClientError(error);
260261
}
261262
};
262-
return await run({ world, proxied });
263+
return await run({ world, proxied, upstream });
263264
} finally {
264265
await closeLoopbackServer(proxy);
265266
await closeLoopbackServer(upstream);
@@ -268,6 +269,39 @@ async function withProxiedWorld<T>(
268269
}
269270
}
270271

272+
/**
273+
* Every request the proxy forwards upstream, in order. Registering a second `request`
274+
* listener records without displacing the server's own handler, so the daemon behaves
275+
* exactly as it does untraced.
276+
*/
277+
function recordUpstreamRequests(upstream: http.Server): { forwarded: ForwardedRequest[] } {
278+
const forwarded: ForwardedRequest[] = [];
279+
upstream.on('request', (req, res) => {
280+
const route = (req.url ?? '').split('?', 1)[0] ?? '';
281+
const entry: ForwardedRequest = { method: req.method ?? '', route, responseBytes: 0 };
282+
const end = res.end.bind(res);
283+
const write = res.write.bind(res);
284+
res.write = ((chunk: unknown, ...rest: unknown[]) => {
285+
entry.responseBytes += byteLengthOf(chunk);
286+
return (write as (...args: unknown[]) => boolean)(chunk, ...rest);
287+
}) as typeof res.write;
288+
res.end = ((chunk?: unknown, ...rest: unknown[]) => {
289+
entry.responseBytes += byteLengthOf(chunk);
290+
return (end as (...args: unknown[]) => http.ServerResponse)(chunk, ...rest);
291+
}) as typeof res.end;
292+
forwarded.push(entry);
293+
});
294+
return { forwarded };
295+
}
296+
297+
type ForwardedRequest = { method: string; route: string; responseBytes: number };
298+
299+
function byteLengthOf(chunk: unknown): number {
300+
if (typeof chunk === 'string') return Buffer.byteLength(chunk);
301+
if (chunk instanceof Uint8Array) return chunk.byteLength;
302+
return 0;
303+
}
304+
271305
type LegRun = { responses: DaemonResponse[]; sessionRoot: string };
272306

273307
function assertPublishedParity(direct: LegRun, proxied: LegRun): void {
@@ -403,6 +437,65 @@ test(
403437
PARALLEL_PROVIDER_SCENARIO_TIMEOUT_MS,
404438
);
405439

440+
/**
441+
* #2198: "the regular-snapshot trace must prove the bridge adds no network round trip and
442+
* transfers only the published response." That was previously read off the RTT benchmark —
443+
* identical response bytes and an unchanged wall-clock slope — which is inference, not proof.
444+
* This asserts it: one `snapshot -i` behind the proxy forwards exactly one upstream request,
445+
* it is the RPC, and the bytes crossing are the published response and nothing else.
446+
*
447+
* The regression it guards is a second forwarded call per snapshot — a bridge, helper or
448+
* admin round trip appearing on the wire, which #2198 forbids outright.
449+
*/
450+
test(
451+
'a proxied snapshot forwards one upstream request and only the published response',
452+
{
453+
timeout: PARALLEL_PROVIDER_SCENARIO_TIMEOUT_MS,
454+
},
455+
async (t) => {
456+
if (await skipWhenLoopbackUnavailable(t)) return;
457+
await withProxiedWorld(async ({ proxied, upstream }) => {
458+
const flags = { platform: 'ios', udid: SIM.id } as const;
459+
await proxied({ session: 'default', command: 'open', positionals: [APP], flags });
460+
461+
// Record only the snapshot: the open above is setup and makes its own calls.
462+
const { forwarded } = recordUpstreamRequests(upstream);
463+
const response = await proxied({
464+
session: 'default',
465+
command: 'snapshot',
466+
positionals: [],
467+
flags: { snapshotInteractiveOnly: true },
468+
});
469+
assert.equal(response.ok, true);
470+
471+
// The WHOLE wire conversation, not just the RPC, so any new call of any kind breaks
472+
// this. `GET /health` is the client's own ADR 0006 protocol-compatibility probe on the
473+
// remote path (`ensureRemoteDaemon`); it is not the bridge, and it predates the bridge.
474+
// The bridge itself contributes nothing: no helper, admin or acquisition route appears.
475+
assert.deepEqual(
476+
forwarded.map((entry) => `${entry.method} ${entry.route}`),
477+
['GET /health', 'POST /rpc'],
478+
'a proxied snapshot must cross the wire only as the compat probe and the RPC',
479+
);
480+
481+
// Only the published response crosses: the proxy adds no envelope of its own, so the
482+
// bytes the daemon wrote are the bytes the client's response accounts for.
483+
const publishedBytes = Buffer.byteLength(JSON.stringify(response));
484+
const forwardedBytes = forwarded.at(-1)?.responseBytes ?? 0;
485+
assert.ok(
486+
forwardedBytes >= publishedBytes,
487+
`upstream sent ${forwardedBytes} bytes, less than the published response (${publishedBytes})`,
488+
);
489+
// The JSON-RPC envelope around the published result is small and fixed; anything that
490+
// carried a second payload (an acquired tree, lineage, a runner quality blob) would not fit.
491+
assert.ok(
492+
forwardedBytes < publishedBytes * 2,
493+
`upstream sent ${forwardedBytes} bytes for a ${publishedBytes}-byte response: more than the published response crossed`,
494+
);
495+
});
496+
},
497+
);
498+
406499
const LEASE_SCOPE = {
407500
tenantId: 'team-a',
408501
runId: 'run-a',

0 commit comments

Comments
 (0)