Add integration tests based on VM

This commit is contained in:
2026-10-26 22:34:00 +02:00
parent f4a6540df9
commit 7d0f006601
22 changed files with 3826 additions and 69 deletions
+305 -15
View File
@@ -108,17 +108,17 @@ Actual systemd/D-Bus acceptance runs in a disposable VM. Never use production to
Each row is a delivery milestone, not a completion claim. Expand its record before execution; retain its original ID and acceptance scope.
| ID | Milestone | Prerequisites | State |
| --- | -------------------------------------------------------- | ----------------------------------------- | -------- |
| 1 | Rust foundation, settings, and domain states | None | accepted |
| 2 | Server configuration editing with byte preservation | 1 | accepted |
| 3 | Client keys, encrypted vault, and existing-client import | 2 | accepted |
| 4 | Backups, safe Commit, and full restoration | 3 | accepted |
| 5 | Runtime observation and systemd restart adapter | 4 | accepted |
| 6 | Client configuration delivery and reissue lifecycle | 5 | accepted |
| 7 | Complete terminal management workflow | 6 | accepted |
| 8 | Alpine Testcontainers integration | 7; suitable disposable Docker environment | accepted |
| 9 | Real systemd acceptance and release readiness | 8; disposable systemd VM | pending |
| ID | Milestone | Prerequisites | State |
| --- | -------------------------------------------------------- | ----------------------------------------- | ----------- |
| 1 | Rust foundation, settings, and domain states | None | accepted |
| 2 | Server configuration editing with byte preservation | 1 | accepted |
| 3 | Client keys, encrypted vault, and existing-client import | 2 | accepted |
| 4 | Backups, safe Commit, and full restoration | 3 | accepted |
| 5 | Runtime observation and systemd restart adapter | 4 | accepted |
| 6 | Client configuration delivery and reissue lifecycle | 5 | accepted |
| 7 | Complete terminal management workflow | 6 | accepted |
| 8 | Alpine Testcontainers integration | 7; suitable disposable Docker environment | accepted |
| 9 | Real systemd acceptance and release readiness | 8; disposable systemd VM | in_progress |
### Milestone 1 — Rust foundation, settings, and domain states
@@ -2981,13 +2981,302 @@ Milestone 9 remains pending.
### Milestone 9 — Real systemd acceptance and release readiness
Prepare a repeatable disposable VM procedure and run the actual application/systemd adapter against synthetic WireGuard state. Finish operating/recovery documentation, safe settings examples, native target release build, and review of executable runtime dependencies.
Status: paused (2026-10-26T20:30:15Z; user explicitly stopped Milestone 9 as
over-engineering). No VM acceptance result exists.
Prerequisites: milestones 1–8 accepted; explicitly selected disposable Linux VM
with its own kernel, systemd as PID 1, system D-Bus, WireGuard and synthetic data;
compatible pinned-toolchain artifacts and a bounded management/console channel.
Missing VM access or required capabilities is a blocker, not passing acceptance.
Acceptance: real D-Bus job handling, separate restart verification, address release, and manual backup recovery pass in the VM. All specification acceptance criteria have linked evidence. Required default checks and Alpine tests pass on the integrated revision; unresolved defects or unavailable required VM checks prevent final acceptance. Production is never the test environment.
#### Outcome and scope
Verify the real production `Application::restart` -> private zbus systemd job
adapter -> real Rustix WireGuard observation -> journaled verification sequence.
Complete operating/recovery instructions, an acceptance evidence map, and a
reproducible release candidate. Acceptance authorizes no production experiment,
installation, publication, deployment, Git commit or tag.
Read SPEC.md, IMPLEMENTATION.md, CONTEXT.md, ADRs 0001–0003, RUNTIME.md,
PERSISTENCE.md, BACKUP_FORMAT.md, DELIVERY.md, ENVIRONMENT.md, TESTING.md,
DEPENDENCIES.md and EXECUTION_RECORDS.md. Read only the relevant final M8 evidence
and focused diagnostics when needed; do not reconstruct every historical attempt.
M8 was accepted for user-authorized IPv4-only Alpine coverage. Preserve that
acceptance and do not silently reintroduce excluded IPv6 tests as a prerequisite
for the VM. Production dual-stack requirements remain unchanged, so the final
acceptance matrix/release notes must explicitly retain the unverified real IPv6
limitation. Do not claim complete dual-stack production validation from IPv4
VM/container or synthetic tests. Any additional IPv6 validation or requirement
change needs an explicit scope decision; it is not a reason to block independent
systemd/IPv4 work or conceal the remaining release qualification.
Out of scope: replacing Alpine coverage, new product features, another service
manager, global toolchain/package installation, production access, automatic VPN
rollback, new unapproved direct dependencies, and a general-purpose VM framework.
Choose the smallest reproducible VM procedure using an available approved runtime;
if none exists, record the prerequisite instead of installing one silently.
#### Current implementation and critical boundaries
- M7 provides actual startup, root checks, executable-relative settings, TUI and
serialized ApplicationWorker. Help/version remain unprivileged. Use real root
management only inside the selected VM, never on the development/production host.
- M8's `src/container_driver.rs` is cfg(test) and uses an explicitly fake fixture
service apply plus real observation/persistence. It does not exercise zbus.
Do not reuse that callback as M9 service evidence. Reuse safe fixture generation
and redacted protocol patterns only where their semantics actually match.
- Application has preview/confirmation, pre-restart backup, attempt/generation
checks, bounded service/runtime operations and durable state marking. Public
`Application::restart` is the required VM entry point. Test-only VM code may
inspect private results but must not substitute its service/read/install adapters.
- The pinned zbus 5.19.0 adapter subscribes before submission, resolves the loaded
unit's primary ID, follows the returned job and accepts only `done`. Verify this
against actual systemd, including fast completion and an alias resolving to a
primary unit. Do not demand a daemon PID from an active/exited oneshot service.
- A service job completing does not prove peer correspondence; matching peers do
not prove verification was persisted. Check both stages and address release.
Historical markers remain historical after reopening. Retain M4 journal recovery
and M5 timeout/partial-runtime-change reporting; no rollback/retry is automatic.
- A release executable is distinct from a cfg(test) harness. Capture each artifact's
source identity/target and prove the actual executable starts in the VM as well
as exercising library operations through a test driver.
#### Tasks and ownership
Execute sequentially with one writer, GPT-6-Sol/medium coordination and
GPT-6-Luna/high implementer, independent reviewer and repairer. Provide bounded
file ownership and acceptance IDs; the coordinator maintains this current snapshot.
| Task | Owner | Deliverable and boundary | State |
| --- | --- | --- | --- |
| M9.1 | implementer | `docs/VM_ACCEPTANCE.md` and `tests/vm/` contract: explicit disposable target selection, pinned base/provisioning, driver, prerequisites and cleanup; no production endpoints | paused by user: guarded first start ran; Debian installer progress was observed, then the exact disposable VM was powered off on stop request; guest completion/provisioning/reset/cleanup unverified |
| M9.2 | implementer | Minimal VM fixture/opt-in driver and build recipe: real systemd/WireGuard service, private synthetic settings/data, compatible test and release artifacts | pending M9.1 guest/guard |
| M9.3 | implementer | Execute actual successful/failed/timeout/verification VM scenarios below; repair narrowly if real integration exposes defects | pending M9.2 fixture |
| M9.4 | implementer | README/operations/recovery and release documentation, requirements-to-evidence matrix, release-candidate identity/runtime dependency assessment | complete for host/documentation scope: independently reviewed operator/recovery docs, qualified matrix and native GNU artifact assessment; guest startup remains blocked |
| M9.5 | reviewer, repairer | Fresh independent review of VM targeting, actual production adapter use, failures/recovery, evidence and remaining release limitations; repair/re-review | partial document reviews closed; fresh VM implementation review pending after M9.1–M9.3 |
| M9.6 | coordinator | Integrated default/affected Alpine/VM gates, concise final evidence and qualified acceptance decision; no publishing or deployment | prior host gates passed; integrated VM gates and acceptance pending |
M9.4 review finding: **M9.4-R1 (P2, closed)** — `docs/RUNTIME.md` described
accepted M7 TUI/startup and M8 real IPv4 Rustix validation as future work.
Targeted repair corrected those claims while retaining the systemd/D-Bus and
real IPv6 limits; independent re-review closed it. Guest/systemd validation
remains unavailable.
M9.1-R1–R8 (P2, closed on independent re-review) covered bounded guest marker
capture, strict inventory, accurate host access status, occupied-path preflight,
the observed NAT baseline, exact SATA controller inventory, target-folder symlink
escape, and actual VBox machine-readable recording/storage syntax. Repairs and
regressions passed the integrated synthetic VM suite (26/26). The create run
registered only the pinned target UUID
`b14c850b-a21e-4a56-acca-785590f0c4ac`; the UUID-pinned resume configured
its VDI, ISO, host marker and manifest. The actual read-only guard then passed
on the powered-off target. Neither create nor resume may be rerun on this occupied
state. Diagnostic attempts are in [milestone-9 diagnostics](docs/diagnostics/milestone-9.md).
M9.1-R9/R10 (P2, closed on independent re-review) corrected unattended command
options and accepted VirtualBox's empty SATA port rows while rejecting extra
media. The integrated synthetic VM suite passed 33/33 before actual preparation.
#### VM setup and execution contract
Use a disposable server VM and a controlled synthetic client in a separate VM or
isolated namespace inside the lab. Test traffic must reach tunnel-only addresses,
not succeed via the management network. Do not attach physical production networks,
forward production credentials, mount host D-Bus/system paths, or expose host
service control. Keep management reachable independently of the VPN being restarted.
Document VM image/release, kernel, architecture, systemd, D-Bus and WireGuard-tools
versions, fixture units/drop-ins and exact provision/build/run/reset commands.
Cache immutable image/build inputs; reset mutable state between scenarios. Require
explicit target selection and lab marker validation before privileged test actions.
A remote target being reachable or running Linux is not sufficient authorization.
Refuse missing identity markers, ambiguous endpoints and accidental host execution.
Use bounded waits/transport, owned-resource cleanup and redacted reports. Secrets
never travel in argv, public logs, committed images/snapshots or evidence files.
Provision wg-quick/service prerequisites in the VM only, with a loaded/enabled
fixture service, permissions compatible with SavedStore, and stable invariant
paths. Set up fixtures without unsafe SaveConfig rewriting; test preflight refusal
of such a configuration rather than modifying administrator settings automatically.
Existing fixture hooks, if needed, are fixed reviewed lab code, never imported text.
Network/kernel prerequisite changes and snapshots affect the disposable guest only.
Do not use a naked ignored test that can restart the current host's service when
someone runs --ignored. Put target guards at the earliest privileged boundary.
Use an explicitly selected VM launcher/driver and document exact commands after
its files exist. All normal cargo tests remain unprivileged and bus/network-free.
No production environment switch or fake-systemd mode is added to the binary.
#### Required scenarios
1. **Real successful apply.** Establish the existing client's IPv4 tunnel. Through
Application create/Commit/deliver a new client and show saved/runtime divergence.
Preview/cancel preserves draft and sends no service request; confirmed restart
with the real adapter has a completed pre-restart backup, matching terminal job,
actual interface/peer comparison and durable state write. New traffic succeeds,
verification history is recorded, and reopening treats it as history, not a new
live verification. Repeat a fast real job/loaded alias case without losing its
completion signal. Do not use log inference in place of application outcomes.
2. **Deletion and reservations.** Commit deletion of a working client: access and
reservation remain until the real successful restart/verification path. After
it, the peer is absent, fresh tunnel-only traffic fails, durable reservation is
released and old backups remain. Establish positive traffic first and bound
negative checks so unrelated fixture failure cannot pass the scenario.
3. **Failures and deadline.** Use fixed guest-only fixture unit/drop-ins to cause
a terminal job failure and a submitted job exceeding the configured deadline.
Check bounded return, honest submitted/may-still-complete classification, no
automatic retry/rollback, no false Verified state or IP release. Wait/reset the
guest explicitly after a late completion; timeout is not cancellation. Also
cover an unresolved configured unit before submission and backup failure that
prevents any restart. Use focused synthetic tests for exhaustive bus races,
not destructive daemon manipulation merely to duplicate unit coverage.
4. **Successful job, failed verification.** A dedicated fixture service completes
but leaves the interface missing or a mismatched peer set. The real adapter must
report the appropriate verification failure and retain reservations; no client
handshake is required to prove correct idle peers. A separate controlled saved-
state write failure after actual restart must report changed runtime without
falsely recording durable verification. RecoveryRequired must prevent unsafe
subsequent mutations; use established M4 fault seams only for persistence, never
replace the actual service/runtime calls to claim this scenario passed.
5. **Manual restore/recovery.** After a deliberately bad saved/apply state, select
a known-good archive through preview/confirm without supplying a vault password.
Check precautionary backup, restored settings/config/vault and unchanged runtime
until an explicit real restart; then verify the restored peer set and traffic.
Follow the documented manual procedure, not a hidden automatic rollback. Keep
transport access independent and validate file permissions/reopen behavior.
6. **Actual executable smoke.** In the guest's console/PTY, start the release binary
from a dedicated root-only directory outside PATH, with settings alongside it.
Check list/current statistics and one controlled Commit/restart workflow through
the real worker path; exit/cancel restores terminal state. Help/version work as
non-root without settings; non-root management is rejected. Reuse M7's detailed
synthetic terminal tests rather than repeating every keyboard/form edge case.
#### Release preparation and evidence
Document installation prerequisites and binary placement, 0600/0700 permissions,
setup/import with untouched originals, initial vault password and unrecoverable
password loss, first/reissue file/QR behavior, Commit versus restart, timeout and
partial-write outcomes, and manual recovery. Explain backup plaintext server-secret
exposure and that backups beside the binary are not protection against disk loss.
Do not add automatic retention, encryption, or remote backup features.
Produce a native release candidate under the pinned compiler and locked dependency
set. Record source/dirty identity, artifact identity/target and actual dynamic-library
requirements. Test the artifact in the chosen guest; distinguish its supported
baseline from the tested development host. Existing musl artifacts may be reused
only with captured compatible inputs. Do not claim reproducibility from merely
running cargo build twice or claim support for untested architectures/distributions.
Create a specification-to-evidence matrix (including preservation, secrets, saved/
applied separation, backups, delivery and terminal behavior) using existing valid
M1–M8 evidence plus M9 results. Reuse M8's current evidence where relevant inputs
are unchanged; rebuild/rerun affected Alpine behavior when M9 changes invalidate
it. Never carry forward a pass from an unidentified or older dirty artifact.
List actual IPv4, synthetic dual-stack, unverified real IPv6, QR/device and systemd
scopes honestly rather than labelling the entire product universally verified.
#### Acceptance and validation
- [ ] M9-A1: documented guarded VM procedure/compatible artifacts execute in the
explicitly selected disposable guest; default tests cannot target host services.
- [ ] M9-A2: real zbus submission/job completion and Rustix verification succeed
through Application::restart, including fast completion/primary-unit alias;
actual release-worker startup is exercised separately from the test harness.
- [ ] M9-A3: completed pre-restart backup precedes service mutation; Commit/restore
never apply changes; canceled draft/restart actions have no unintended effects.
- [ ] M9-A4: real successful deletion/apply releases durable reservations only after
full verification; fresh traffic and reopened state support the result.
- [ ] M9-A5: unit/job/deadline/backup/missing-interface/mismatch and post-restart
persistence failures have accurate staged outcomes, retained reservations,
deterministic manual recovery and no automatic restart retry/VPN rollback.
- [ ] M9-A6: previewed password-free restore plus explicit real restart recovers the
selected known-good state/traffic; the VM procedure matches operator instructions.
- [ ] M9-A7: root/non-root executable-relative startup, limited real terminal flow,
artifact compatibility and private output/backup permissions are verified in VM.
- [ ] M9-A8: evidence matrix, release artifact identity, runtime dependencies,
operator/recovery docs and unresolved coverage qualifications are accurate.
M8's IPv6 exclusion is preserved; no unsupported full dual-stack claim is made.
- [ ] M9-A9: current default gates and affected Alpine/VM scenarios pass on the
identified integrated revision; independent review closes actionable findings.
Unavailable required VM checks prevent acceptance, rather than becoming a mock pass.
#### Validation and execution records
From repository root, run these existing commands at the integrated gate, not
on every diagnostic retry:
```sh
cargo fmt --all -- --check
cargo clippy --locked --all-targets -- -D warnings
cargo test --locked
cargo build --locked --release
./target/release/wireguard-admin --help
./target/release/wireguard-admin --version
git diff --check
```
Before VM execution, document exact newly implemented build/provision/run/cleanup
commands in `docs/VM_ACCEPTANCE.md` and the existing selected-daemon Alpine command
in TESTING.md. No VM target/command is claimed available yet. Record actual results,
counts, selected VM/base identity, source/artifact identity, cleanup and review
mapping in `docs/evidence/milestone-9.md` when runs occur. Keep only latest applicable
evidence/status in this plan. Investigations belong in `docs/diagnostics/milestone-9.md`
when needed; after two failed repairs reassess/minimize before another expensive
full run per EXECUTION_RECORDS.md. Do not create placeholder reports claiming tests.
#### Review, evidence and handoff
M9.1 initial non-runnable contract passed read-only review. After the user
authorized local VirtualBox, approved outside-sandbox inventory confirmed
`VBoxManage` 7.2.8 works and `/dev/vboxdrv` is visible there; only Win7/Win11
are registered and neither is a test target. The official Debian 13.7 amd64
netinst ISO in the private lab root has SHA256
`a7ef94ac2fb9a7fec454552abd629b7cc9d5155c886165a45649f5ce6167e355`,
matching the downloaded official SHA256SUMS. The new target UUID
`b14c850b-a21e-4a56-acca-785590f0c4ac` is configured and powered off with
the private manifest/marker, exact VDI and pinned ISO; actual read-only guard
`check` exited 0. The integrated synthetic VM suite passed 26/26 after
independent review/repair; no existing Windows VM was modified. An exact-target
VirtualBox unattended install dry run with private synthetic password files,
Guest Additions ISO, and redacted output exited 0. The reviewed preparation
command then exited 0; read-only `showvminfo` found the expected VDI and a
private generated auxiliary `.viso` at SATA ports 0/1. It did not boot the
guest. M9.4 [operator instructions](docs/OPERATIONS.md),
[qualified evidence matrix](docs/RELEASE_READINESS.md), and updated README,
environment and runtime guidance passed independent review after M9.4-R1 repair.
The native GNU artifact SHA256 is
`d52d43cd02332352ffbd09c97e3b8095a710359128260c1286006e1c76c1b283`;
host `--help` and `--version` exited 0. On the integrated unchanged Rust source,
`cargo fmt --all -- --check`, `cargo clippy --locked --all-targets -- -D warnings`,
`cargo test --locked`, `cargo build --locked --release`, and `git diff --check`
exited 0. M8's source-input fingerprint remains
`b28dc6e4cb92aa6edf384d966ba5952a8d6d0efe4c22d6a804b67fb2c0297da2`;
no Rust/Alpine input changed, so M8 evidence was not rerun. No VM scenario,
production access or key read occurred. Guest compatibility and actual zbus/
systemd behavior remain unverified. The prior blocker audit was valid before
VirtualBox was made accessible outside the sandbox; its no-VM conclusion is
superseded by this authorized prerequisite path. M9.4 has no open finding.
M8 is accepted for real IPv4 Alpine application/transport coverage; its test-only
service callback is not systemd evidence. Carry forward that distinction. Initial
M9 evidence is unavailable until actual guest execution; record missing historical
inputs as unavailable rather than reconstructing them.
Next action: none. User stopped Milestone 9. Do not restart the VM or resume
M9.1–M9.3 without a new explicit request. No M9-A1–A9 check is complete.
#### Outcome
Paused at the user's request. The separate disposable Debian VM
`b14c850b-a21e-4a56-acca-785590f0c4ac` was powered off; a read-only
running-VM inventory was empty afterward. M9.1 remains incomplete, M9.2–M9.3
have not run, and M9-A1–A9 remain unchecked. Prior host gates and M9.4
documentation passed; no release acceptance or deployment is claimed.
Real IPv6 remains separately unverified by the previously accepted test scope.
## Current progress and resume point
- Milestones 1–8 are accepted; milestone 9 remains pending.
- Bounded TUI maintenance (2026-10-26): repaired list arrows opening client details. Up/Down now only move the highlight; Enter opens details and Esc returns. Initial navigation follows the renderer's first-row highlight. Independent review closed NAV-001 with no remaining scoped findings; validation is recorded in [navigation repair evidence](docs/evidence/tui-navigation.md). This does not advance M9 or supply VM/Alpine acceptance evidence; prior artifact identities remain historical after this Rust change.
- Milestones 1–8 are accepted; milestone 9 is paused by the user and unaccepted.
- The approved dependency list is in [DEPENDENCIES.md](docs/DEPENDENCIES.md); container testing methodology is in [TESTING.md](docs/TESTING.md).
- Milestone 1 is expanded and the Rust environment is pinned.
- Milestone 2 delivered a lossless in-memory WireGuard configuration editor, versioned metadata, and behavioral preservation tests.
@@ -2999,7 +3288,8 @@ Acceptance: real D-Bus job handling, separate restart verification, address rele
- Milestone 7 delivered the native terminal workflow, safe startup/session projection, bounded single-owner worker, setup/import/draft/Commit/restart/delivery/backup/restore forms, synthetic PTY and workflow tests, and documentation. Independent R2 closed its list-status finding; all planned coordinator gates passed. Real WireGuard and systemd remain M8/M9.
- Milestone 8 delivered reviewed opt-in Alpine Testcontainers coverage for real IPv4 WireGuard peers, application lifecycle, saved/runtime separation, restoration, import/reissue, backup failures and the real Rustix reader. Host gates and three fresh real-container runs passed on one stable source fingerprint; M8-A1–A10 accepted. Real IPv6 behavior is unverified by user-authorized scope.
- Next action: stop after M8. Milestone 9 remains pending for disposable-VM systemd/D-Bus acceptance; no production access, commit or deployment is authorized by M8 acceptance.
- Milestone 9 is paused and unaccepted: the reviewed disposable VM preparation and guarded first start did not establish Debian/systemd acceptance. The exact VM was powered off after the user stopped work. M9.2–M9.3 were not started; M9-A1–A9 are unchecked.
- Next action: none unless the user explicitly requests more Milestone 9 work. Do not restart the VM, access production, commit, or deploy.
## Plan maintenance