test(e2e): transfers through a gateway keep their promises
メンテナーはふだん 1 日以内に返信
@V3RON がすでに取り組んでいます。
2026年10月14日 から。
評価
この issue はまだ評価されていません。
説明
Part of #552.
Scope
After this PR, #552's measured promises are proven against a real gateway
and worker over the real WebSocket: bytes on the uplink, the 1 s bound
for small calls with uploads and downloads running, a slow reader slowing
only itself, memory per transfer in both directions, 2 GiB both ways with
the download inside agent-device's 90 s, and, on the slow lane, the 1.5×
upload time.
A promise that fails is fixed in this PR, in the module that breaks it.
A fix that would change a value ADR 0028 or #552's Shared values table
sets (credit, unsent mark, message size, channel limit, waits) is not
made: the implementer stops and reports, since those values are the
maintainer's.
The values are in #552's Shared values table.
In short
flowchart LR
T["test"] -->|relay| G["gateway"]
G --- P["counting TCP proxy (test)"] --- W["worker"]
W --> F["fake relay target / real agent-device"]
The tests put a TCP proxy between the worker and the gateway to count
the bytes on the uplink. The timing tests run in their own test project,
alone, so other test files do not compete for the CPU. If this goes
wrong, a promise #552 makes to users is false and nothing shows it.
Technical spec
Facts
| # | Fact | Source |
|---|---|---|
| F1 | startFleet starts a real gateway and worker over the real WebSocket on 127.0.0.1 with a fake relay target; it is local to the file's describe and points the worker's gateway.url straight at the gateway's port |
src/daemon/relay-gateway.e2e.test.ts:124-145, :178, :210 |
| F2 | patternStream and patternDigest make and check large bodies; the pattern is generated byte by byte |
src/daemon/test-relay-support.ts:88-93, :102-110 |
| F3 | The model for timing a small call while a relay runs, with a check that the relay had not finished first | src/daemon/relay-gateway.e2e.test.ts:294-328 |
| F4 | The worker dials gateway.url; the uplink endpoint is derived from it |
src/ports/uplink.ts:52 (resolveUplinkUrl) |
| F5 | The test projects: unit takes src/**/*.test.ts, e2e takes e2e/**/*.test.ts; CI runs pnpm run test:unit |
vitest.config.ts:14-27; package.json:47; .github/workflows/ci.yml:69 |
| F6 | The slow-lane gateway suite starts a gateway and one real-driver worker with one device; its lease tests each open their own lease through the gateway | e2e/slow-agent-device-gateway.test.ts:124-153, :181-200 |
| F7 | A worker answers agent-device calls made with its own agent tokens on its own front | docs/HTTP-API.md:1497-1499 |
| F8 | simlock's upload routes: preflight, direct upload, finalize, each naming the lease in x-agent-device-lease-id |
src/agent-device/shared/routes.ts:20-35; docs/HTTP-API.md:1507-1511 |
| F10 | CI's jobs: unit has a 10-minute limit; quality gates merges and needs static, unit and e2e; pnpm check runs pnpm test, and CI runs what it runs |
.github/workflows/ci.yml:49-54, :105-107; package.json:46, :62; docs/internal/agent-rules/toolchain.md:35 |
| F11 | The command guard lists the scripts agents never run, and toolchain.md's "Checks agents never run" table must match it |
.agents/scripts/tool-guard.mjs:17-28; docs/internal/agent-rules/toolchain.md:18-21 |
| F12 | The mutation config includes every src/**/*.test.ts, and Stryker reruns tests per mutant |
vitest.mutate.config.ts:8; stryker.config.mjs:4-6 |
| F13 | A test pins the lane filters in package.json |
src/test-lanes.test.ts |
| F9 | agent-device's preflight body: sha256, uploadAttemptId, fileName, sizeBytes, artifactType, optional platform and contentType; finalize takes { uploadId } and checks size and hash |
agent-device src/daemon/upload-http.ts:246-256, :197-205; src/daemon/resumable-upload.ts:104-117, at the commit vendor/README.md names |
Modules touched
src/daemon/test-relay-support.ts:startFleetmoves here from
src/daemon/relay-gateway.e2e.test.tswith agatewayUrloption (F1);
a counting TCP proxy helper that pipes each direction with backpressure
and holds no buffer of its own; the pattern helpers derive each byte
from its own offset (a hash in counter mode), so no stretch of the
pattern equals any other, and build it fast enough for 2 GiB (F2).src/daemon/relay-gateway.e2e.test.ts: uses the movedstartFleet.src/daemon/relay-promises.timing.test.ts(new): the tests below.vitest.config.ts,vitest.mutate.config.ts,package.json,
.github/workflows/ci.yml,docs/internal/agent-rules/toolchain.md,
.agents/scripts/tool-guard.mjs,src/test-lanes.test.ts: atiming
project that takessrc/**/*.timing.test.ts, which theunitproject
and the mutation config leave out (F12), runs its files one at a time
with nothing else in parallel, and gives each test 300 s; atest:timingscript that
pnpm test(and sopnpm check) runs (F10); atimingCI job with a
30-minute limit thatqualityneeds; the script intoolchain.md's
"Checks agents never run" table and in the command guard's list (F11),
and a "Tests agents run" row for one timing file:
pnpm vitest run --project timing <file>.e2e/slow-agent-device-gateway.test.ts: the slow-lane test.- A failing promise adds the module that fixes it (Scope).
Contract and event changes
None.
Other code on the same state
None: tests and test tooling. A fix this PR makes names the code it
changes in the PR body.
Rules in play
testing.md: rule 2, every test can fail for the right reason: a
timing test checks its load was still running when it measured.toolchain.md: the new script goes in "Checks agents never run" and
the command guard's list; the slow lane runs through
scripts/slow-e2e.sh.
Tests
Seam: startFleet (F1) with gatewayUrl pointed at the counting TCP
proxy in front of the gateway's port (F4). The proxy counts the bytes it
carries in both directions from a transfer's start to its end. The two
ratio tests (the 1 s bound and the 1.5× slow reader) take the median of 5
runs against a baseline measured the same way in the same test; every
other test is one run. Tests moving 1 GiB or more set a 180 s timeout
and compare by digest while streaming. Model: F3.
- a 100 MiB upload through the gateway puts at most 105 MiB on the uplink
- a 1 GiB upload through the gateway puts at most 1.05 GiB on the uplink, and so does a 1 GiB download, each measured on its own
- while 10 relays each upload 64 MiB and 10 each download 64 MiB to one worker, a forwarded
lease.renewand a relayed request whose target answers at once each answer at most 1 s later than with nothing running, and each of the 20 relays carried at least 1 MiB in the second before and the second after each answer came back - while one relay's caller reads none of its 1 GiB response, another relay's 256 MiB upload to the same worker takes at most 1.5 times as long as with nothing else running
- while the gateway's caller reads none of a 2 GiB response, the target has handed over at most 8 MiB after 5 s; reading it then gets every byte, in order
- while the target reads none of a 2 GiB upload, the gateway has read at most 8 MiB of the caller's body after 5 s; once the target reads, every byte arrives, in order
- a 2 GiB upload through the gateway arrives byte for byte
- a 2 GiB download through the gateway arrives byte for byte and finishes within 90 s
- swapping two chunks of different random sizes (1 byte to 64 KiB) at random offsets of a 2 GiB pattern changes its digest, in each of 100 trials
- the
unitproject and the mutation config leave out*.timing.test.ts, thetimingproject takes them and runs files one at a time,pnpm testrunstest:timing, and CI'squalityjob needs thetimingjob (insrc/test-lanes.test.ts)
Slow lane (F6–F9): in the iOS suite, a test opens a device through the
worker's own front with an agent token minted on the worker, and times a
1 GiB upload with that lease from the preflight to the finalize answer
(F9), three times, then closes it. It then opens a device through the
gateway and times the same upload the same way. The ratio compares the
medians. Both legs run on the same machine, one after the other.
Done when
- Every Tests line passes, and CI runs the
timingproject. - On a Mac with an iOS runtime and the agent-device checks of
toolchain.md: "a 1 GiB upload takes at most 1.5 times as long through the gateway as straight to the worker" passes ine2e/slow-agent-device-gateway.test.ts.
Out of scope
- Any behaviour #558 or #557 owns, except a fix a failing promise needs.
Depends on
- #558
Written by an agent.
- 主要言語
- TypeScript
- スター
- 19
- フォーク
- 1
- 平均マージ
- 6時間 31分
- マージ済み PR(30日)
- 194
環境構築
このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
callstackincubator/simlock のほかの issue
-
Lint does not enforce index.ts boundaries for cleanup/, routing/, warm-pool/ and contract/ helpersオープンbug:new
難易度 2/5 1〜3時間 初心者へのやさしさ 70/100
callstackincubator/simlock#445 ·
メンテナーはふだん 1 日以内に返信
-
bug:new
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
callstackincubator/simlock#424 ·
メンテナーはふだん 1 日以内に返信
-
daemon-lifecycle e2e: the it.fails status test passes for the wrong reason対応中かも @V3RON が 6 日前に担当しました。 オープンbug:triage
難易度 2/5 1〜3時間 初心者へのやさしさ 84/100
callstackincubator/simlock#422 · コメント 1 件 · 担当者 1 名 ·
メンテナーはふだん 1 日以内に返信
-
The console draws each worker's load as gauges on its detail page対応中かも このイシューにリンクされたプルリクエストがオープン中、またはマージ済みです。 オープンtask:draft
難易度 4/5 3〜5日 初心者へのやさしさ 15/100
callstackincubator/simlock#569 ·
メンテナーはふだん 1 日以内に返信
-
feat(gateway): relayed bodies travel on byte channels対応中かも @V3RON が 1 日前に担当しました。 オープンtask:ready
callstackincubator/simlock#558 · 担当者 1 名 ·
メンテナーはふだん 1 日以内に返信
callstackincubator/simlock の issue をすべて見る
似ている issue
-
fix(frontend): correct VITE_IS_DEVELPOMENT typo in shared/app.ts and align dev environment flagsオープン
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
vicharanashala/ajrasakha#1439 ·
メンテナーはふだん 1 日以内に返信
-
Add: TV5Monde Asieオープンapproved check:passed streams:add
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
backnotprop/plannotator#1932 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 半日 初心者へのやさしさ 62/100
ibm-granite/granite.build#504 ·
メンテナーはふだん 2 日以内に返信
-
reproduced
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
anomalyco/opencode#55676 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信