Skip to content

test(cloud-lab): Docker-based integration test environment - #362

Merged
c03rad0r merged 4 commits into
OpenTollGate:mainfrom
Amperstrand:consolidation/test-cloud-lab
Aug 27, 2026
Merged

c03rad0r merged 4 commits into
OpenTollGate:mainfrom
Amperstrand:consolidation/test-cloud-lab

Conversation

@Amperstrand

Copy link
Copy Markdown
Collaborator

From net4sats/main; Dockerized 2-router + mint test environment (smoke payment, autopay, mint-failure scenarios). Part of the net4sats->OpenTollGate consolidation.

Rebase of OpenTollGate#362 onto rewritten main — carries only the tests/cloud-lab/
Docker environment (2-router + mint topology: smoke payment, two-router
autopay, mint-failure scenarios, fake-ndsctl shim).

Dropped: deploy-backup-20260730/ (leaked secrets, incident #364),
DEPENDS packaging change, go.mod churn, token-recovery binary.

Original-PR: OpenTollGate#362
@Amperstrand

Copy link
Copy Markdown
Collaborator Author

⚠️ Branch cleaned up (force-push) — please review the new head.

Same incident cleanup as #360/#361: the branch carried deploy-backup-20260730/ (leaked merchant key / wallet.db, purged from main via history rewrite — incident #364).

New head 2081c4e carries only the Docker test environment: tests/cloud-lab/ (docker-compose with cdk-mintd FakeWallet mint + TollGate containers + client, smoke-payment / two-router-autopay / mint-failure suites, fake-ndsctl shim). Verified: py-compile clean, shell syntax clean. A full local docker compose run of the smoke suite is in progress on this exact tree, and a standard cloud-lab e2e run on the built ipk is in flight — results will follow.

Dropped: secrets directory, DEPENDS packaging change, go.mod churn, token-recovery binary, pre-commit config.

Dockerfile.tollgate pinned golang:1.24-bookworm while src/go.mod declares
go 1.25.0 — the in-container build fails (exit 1). Align the base image.
@felixfelix-bot

Copy link
Copy Markdown
Contributor

Thanks for this — a Docker-based integration lab with a FakeWallet mint is a big step up from hardware-only testing, and the README does a good job delineating logic-level coverage vs QEMU/hardware-only. Review findings below, in priority order:

  1. [BLOCK] Token minted while the mint is down — tests/cloud-lab/test_mint_failure.py:107: test_payment_fails_when_mint_down stops tg-mint (line 90) and only then calls create_cashu_token() (line 107). cdk-cli send performs a swap against the mint, so it fails with RuntimeError("cdk-cli send failed") before the payment POST at line 111 is ever attempted — the test cannot exercise the path it exists to test. Create the token before docker stop tg-mint (the wallet can spend pre-swapped proofs offline only if the exact denomination already exists; simplest fix is to create the token first).

  2. [BLOCK] The Docker-dependent tests can never run as wired — tests/cloud-lab/test_mint_failure.py:79 uses @pytest.mark.requires_docker and shells out to docker (lines 90, 121), but tests/cloud-lab/Dockerfile.client installs no docker CLI and the client service in tests/cloud-lab/docker-compose.yml:96-113 does not mount /var/run/docker.sock. So subprocess.run(["docker", ...]) always raises FileNotFoundError → pytest.skip, i.e. the mint-kill scenario silently never runs. Also requires_docker is not a registered marker (no pytest.ini/setup.cfg in the dir), producing unknown-mark warnings. Either mount the socket + install the CLI in the client image, or drive the kill/restart from the host (make target or compose command between pytest phases) so the test doesn't need in-container Docker at all.

  3. [RISK] Base64 padding bug in _expand_keyset_ids — tests/cloud-lab/conftest.py:83: payload += "=" * (4 - len(payload) % 4) appends four = characters when len(payload) % 4 == 0, which makes urlsafe_b64decode raise binascii.Error: Invalid padding (Python ≥3.11 is strict about excess padding). Any already-aligned token payload — roughly 1 in 4 — will blow up create_cashu_token() intermittently. Use "=" * (-len(payload) % 4) after payload = payload.rstrip("=").

  4. [RISK] Same nsec for upstream and reseller — tests/cloud-lab/configs/upstream-identities.json:6 and tests/cloud-lab/configs/reseller-identities.json:6 contain the identical privatekey, and it looks like a typed placeholder (…3qj6q3qj6q3qj… repeating) rather than a real key. If it fails bech32 parsing, the service may error at startup or on first sign; if it parses, both TollGates in two-router mode share one merchant identity, conflating p-tag routing and profit-share accounting between upstream and reseller. Generate two distinct throwaway keys (nak key generate) and note in the README that they are test-only.

  5. [RISK] Unpinned toolchain versions — tests/cloud-lab/Dockerfile.mint:20 (cargo install cdk-mintd with no version), tests/cloud-lab/Dockerfile.client:9 (cdk-cli --locked, unversioned) and tests/cloud-lab/Dockerfile.client:14 (nak@latest) make the lab non-reproducible; a crates.io/GitHub release can break CI overnight (the Dockerfile.mint header comment already anticipates cdk-mintd disappearing from crates.io). Pin --version x.y.z / @vx.y.z.

  6. [NIT] requirements.txt is dead code — tests/cloud-lab/Dockerfile.client:26 runs pip install pytest requests directly instead of pip install -r requirements.txt, so the file and the image can drift apart.

  7. [NIT] Vacuous assertions — tests/cloud-lab/test_smoke_payment.py:139 accepts status_code in (200, 400) and tests/cloud-lab/test_two_router_autopay.py:90 accepts (200, 400, 500), so nearly any non-crash outcome passes. Consider pinning the response event kind (1022 session vs 21023 notice) to make the expected behavior explicit. Related: test_gate_was_opened (tests/cloud-lab/test_smoke_payment.py:97) never checks the gate — mounting a shared volume for /tmp/ndsctl.log and asserting an AUTH line for the client MAC would make the test name true.

Nice work overall — the fake-ndsctl logging design and documenting the keyset-ID expansion workaround (modulo the padding bug above) are good touches. No approval/merge from me; just review.

@Amperstrand
Amperstrand force-pushed the consolidation/test-cloud-lab branch from f178b45 to 80897bb Compare August 27, 2026 16:45
@felixfelix-bot

Copy link
Copy Markdown
Contributor

Thanks — re-reviewed the new head 80897bb (delta since last review: 083afcc golang:1.25 base fix + 80897bb CHANGELOG entry).

Delta review — no new findings. Both changes are correct:

  • tests/cloud-lab/Dockerfile.tollgate: golang:1.24-bookworm → golang:1.25-bookworm is the right fix — it matches the go 1.25.0 requirement in src/go.mod and the golang:1.25-bookworm builder already used in Dockerfile.client, so the in-container build now has a consistent toolchain.
  • CHANGELOG.md: entry is accurate in scope and placed under the existing "Added" section.

Status of prior findings: the seven findings from my earlier comment (2 BLOCK / 3 RISK / 2 NIT) are still unaddressed at this head — in particular both BLOCKs (tests/cloud-lab/test_mint_failure.py:106 token created after the mint is stopped; tests/cloud-lab/test_mint_failure.py:78 Docker-dependent tests can never run as wired — no docker CLI/socket in the client container) and the conftest.py:83 base64 padding bug are all still present verbatim. Not re-explaining them here; see the previous comment for details and suggested fixes.


E2E / verification note (test-infra PR — hardware video run not applicable, per project gate rules): no UI files touched, so no physical-router video evidence is required for this change. Mechanical verification performed at 80897bb:

Check Result
python3 -m py_compile — conftest.py, test_smoke_payment.py, test_mint_failure.py, test_two_router_autopay.py ✅ all 4 clean
sh -n fake-ndsctl.sh ✅ syntax OK
JSON parse — configs/{install,reseller-config,reseller-identities,upstream-config,upstream-identities}.json ✅ 5/5 valid
docker compose -f docker-compose.yml config -q ✅ validates

Cold-review verdict: infra wiring is syntactically sound and the compose topology is well-formed, but from a review standpoint this is not merge-ready while the two BLOCK findings stand — as wired, the mint-failure suite cannot actually exercise its scenarios (it will pytest.skip silently), which is the exact behavior the lab exists to catch. Fixing BLOCKs 1–2 plus the conftest.py:83 padding bug would make the suite meaningfully green. No approval/merge from me; just review.

@c03rad0r
c03rad0r self-requested a review August 27, 2026 17:48
@c03rad0r
c03rad0r merged commit aa919c0 into OpenTollGate:main Aug 27, 2026
@Amperstrand
Amperstrand deleted the consolidation/test-cloud-lab branch August 27, 2026 19:57
@Amperstrand

Copy link
Copy Markdown
Collaborator Author

E2E results (follow-up to review): deployed head 0a79659 as ci-pr-362.199.0a79659 (CI-built ipk) on the OpenWrt x86_64 QEMU lab:

Suite Result
api/test_quote_persistence 4/4 passed
api/test_lightning_backoff 3/3 passed

This PR's own environment exercised in full (docker compose on this exact tree):

tests/cloud-lab suite Result
test_smoke_payment (mint reachable → wallet balance → payment → session event → gate open → balance) 6/6 passed
test_mint_failure (healthy payment, mint-down behavior, recovery after mint restart) 4/5 passed, 1 skipped
test_two_router_autopay (reseller profile, client pays reseller) 3/3 passed

Two content fixes were applied to the branch during review:

  1. Dockerfile.tollgate: golang:1.24 → golang:1.25 (src/go.mod declares go 1.25.0 — the image build failed with 1.24). Without this the compose environment cannot build at all.
  2. CHANGELOG entry added.

With those, the environment builds cleanly from scratch and all three suites run green — the payment e2e inside this environment is actually the strongest payment evidence of the whole batch, since the shared lab's payment path had environment issues today (see final note). Ready for approval/merge from our side; noting for reviewers that the fake-ndsctl shim means gate operations are simulated — all payment/session/merchant logic is real.

Amperstrand pushed a commit that referenced this pull request Sep 14, 2026
Two lab repairs found by running the suite for the gonuts-bump e2e:

- Dockerfile.mint pinned cdk-mintd to 0.17.6. The unpinned cargo install
  now fetches 0.18.0 (released 2026-09-02), which replaced env-var
  startup with a mandatory 'config init --file <toml>' flow — the mint
  container exited immediately ('mintd configuration is not initialized')
  and the whole lab was unrunnable. 0.17.6 (2026-08-26) is the version
  the lab was green on when it landed in #362 (2026-08-27).
- README quick-start used 'compose run --rm client pytest -sv …', but the
  client service's entrypoint IS pytest, so 'pytest' arrived as a test
  filename: 'ERROR: file or directory not found: pytest'. Drop the
  prefix.

With these, the full suite passes at this branch's head: smoke 6/6,
mint-failure 3 passed + 1 skipped, two-router 3/3.
c03rad0r added a commit that referenced this pull request Sep 14, 2026
…TokenAlreadySpent reachable (#392)

* fix(wallet): bump gonuts to the #23/#24/#25 integration ref; make ErrTokenAlreadySpent reachable

Pins gonuts-tollgate to 717c886 (main + #23 + #25 + #24, branch
tmp/release-integration on the gonuts repo; re-pin to the tagged
release when it exists) across every consuming module (root, merchant,
cli, tollwallet).

With the bump, three wallet-layer fixes ride into tollgate: the
empty-proofs panic becomes a normal error, mint swap rejections
surface verbatim, and the below-fee swap fails fast. The surfaced
rejections are what finally make the ErrTokenAlreadySpent sentinel
reachable: tollwallet matched only the exact string 'Token already
spent', while the mint's rejection (previously swallowed entirely by
gonuts) says 'inputs have already been spent'. The match is broadened
(case-insensitive 'already spent' / 'already been spent') and pinned
two ways: a phrasing table, and a two-layer test driving a mock mint's
rejection through gonuts into the sentinel via errors.Is — which fails
if either layer regresses to swallowing.

Verified: gofmt/vet/build clean; root module -race testenv green
(15.3s); merchant (185s), cli, tollwallet modules green.

* test(cloud-lab): pin cdk-mintd 0.17.6; fix quick-start pytest invocation

Two lab repairs found by running the suite for the gonuts-bump e2e:

- Dockerfile.mint pinned cdk-mintd to 0.17.6. The unpinned cargo install
  now fetches 0.18.0 (released 2026-09-02), which replaced env-var
  startup with a mandatory 'config init --file <toml>' flow — the mint
  container exited immediately ('mintd configuration is not initialized')
  and the whole lab was unrunnable. 0.17.6 (2026-08-26) is the version
  the lab was green on when it landed in #362 (2026-08-27).
- README quick-start used 'compose run --rm client pytest -sv …', but the
  client service's entrypoint IS pytest, so 'pytest' arrived as a test
  filename: 'ERROR: file or directory not found: pytest'. Drop the
  prefix.

With these, the full suite passes at this branch's head: smoke 6/6,
mint-failure 3 passed + 1 skipped, two-router 3/3.

---------

Co-authored-by: Amperstrand <amperstrand@localhost>
Co-authored-by: c03rad0r <1100745+c03rad0r@users.noreply.github.com>
felixfelix-bot pushed a commit to felixfelix-bot/tollgate-module-basic-go that referenced this pull request Sep 20, 2026
…ate#362)

* test(cloud-lab): Docker-based integration test environment

Rebase of OpenTollGate#362 onto rewritten main — carries only the tests/cloud-lab/
Docker environment (2-router + mint topology: smoke payment, two-router
autopay, mint-failure scenarios, fake-ndsctl shim).

Dropped: deploy-backup-20260730/ (leaked secrets, incident #364),
DEPENDS packaging change, go.mod churn, token-recovery binary.

Original-PR: OpenTollGate#362

* fix(cloud-lab): golang:1.25 base — go.mod requires go 1.25.0

Dockerfile.tollgate pinned golang:1.24-bookworm while src/go.mod declares
go 1.25.0 — the in-container build fails (exit 1). Align the base image.

* docs: changelog entry for docker cloud-lab test environment

---------

Co-authored-by: Amperstrand <amperstrand@users.noreply.github.com>
Co-authored-by: c03rad0r <1100745+c03rad0r@users.noreply.github.com>
@felixfelix-bot

Copy link
Copy Markdown
Contributor

SEC-AUDIT T4 Review Evidence

Cold cross-family review (Gate 2.5) — dispatched to Kimi family reviewer (kimi-k3, moonshot family ≠ author's deepseek/glm lane). Verdict: APPROVED (0 blocking).

{
  "verdict": "APPROVED",
  "reviewer_model": "kimi-k3",
  "blocking_findings": "0",
  "summary": "Docker cloud-lab integration test suite is well-structured and safe to merge. All 18 files add a self-contained test-only environment; no production code is touched. The two hardcoded nsec keys are identical across upstream and reseller identity files, but they are (a) bech32-encoded test patterns, (b) not actually used at runtime because docker-compose.yml does NOT mount *-identities.json into the containers — the code generates a fresh random hex key via nostr.GeneratePrivateKey(). The checked-in nsec files are effectively dead fixtures. Test logic is mostly meaningful... Compose file passes docker compose config validation with and without profiles."
}

Non-blocking findings (low): dead nsec fixtures not mounted; weak 200/400/500 assertions in test_two_router_autopay.py/test_balance_check; unpinned go install nak@latest / cargo install --locked; no .github/workflows file (README documents a snippet only).

Docker compose validation (Gate 5 verification) — docker compose -f tests/cloud-lab/docker-compose.yml config exits 0 on both default and --profile two-router --profile test invocations.

Full build (executed) — docker compose build mint upstream succeeded cleanly (13m56s): cloud-lab-mint Built, cloud-lab-upstream Built. The golang:1.25 base aligns with src/go.mod's go 1.25.0.

Runtime note (honest) — at the as-merged commit, cargo install cdk-mintd --locked resolves to cdk-mintd 0.18.x, which requires cdk-mintd config init and no longer boots from env alone → tg-mint exits(1) as merged. This is already fixed on current main by follow-up commit 7cd1882f (#392) which pins --version 0.17.6; with that pin the mint boots from the compose env. Flagging so future runs use the pinned build.

Merge status — PR already merged via squash aa919c07 on 2026-08-27 (reviewDecision APPROVED). CHANGELOG entry included (Gate 3). Branch not behind main; no reverts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants