How Wusel is tested
Wusel talks to a server over the network and to a kernel through FUSE. Both are expensive to stand up and easy to fake badly, so the question of what a green run actually proves is not a detail here — it is most of the design.
The answer is a pyramid. We do not boot a Nextcloud for every run, and we do not pretend a mock proves what only a real server can.
The tiers
A pyramid — we do not boot a Nextcloud for every run.
-
Unit tests (no server). Pure logic: PROPFIND parsing, SQLite state, sync-state decisions, three-way merge, path handling. Fast, deterministic, run everywhere.
-
Purpose-built fake servers (no Nextcloud, no mocking crate). Protocols that are not WebDAV get a few lines of hand-rolled
tokioserver in the test itself: thenotify_pushwebsocket and the self-signed-TLS handshake. No HTTP-mocking dependency is pulled in for this: the project keeps third-party dependencies to as few as possible. -
Standalone WebDAV mock (
wusel-mock). A tiny binary crate that serves a real directory as a Nextcloud-style user root (PROPFIND Depth 0/1, range GET, and the write verbs). It is pure Rust with a single dependency (tokio) — deliberately no Python and no mocking framework, to keep the project to one language and few dependencies. Because it mirrors a directory on disk, exercising change detection is justecho/rmin that directory, and fixtures are staged by creating files rather than by writing XML. Fault-injection markers in a file’s name provoke server behaviour that is otherwise hard to stage:-
*.fail-once— the firstPUTanswers 500, then it succeeds; -
*.fail-perm— everyPUTanswers 403, a permanent refusal; -
.no-etag—PUT/MOVEanswer without anETag; -
.lost-once.— the firstPUTlands, then is answered 504, as if the answer were lost on the way back; -
*.relist-fails(a directory) — after the firstPUTinto it, its next listing answers 503, once; -
.proxy-403— a chunked upload assembles, but the finalMOVEis answered 403, like a proxy mangling it.
Environment variables add latency:
WUSEL_MOCK_GET_DELAY_MS,WUSEL_MOCK_PUT_DELAY_MSandWUSEL_MOCK_PROPFIND_DELAY_MSdelay every answer of that kind;WUSEL_MOCK_GROUP_FOLDERS(comma-separated) marks directories as group folders. Start it withmise run mock(--root DIR), point a mount at it, or let the integration tests undercrates/wusel-mock/tests/drive the real client against it — that is where most server-facing engine tests live; the socket frontend’s tests (crates/wusel-ipc/tests/) and the mount tests (crates/wusel-fuse/tests/) run against the same mock. -
All three tiers run under mise run test — they are ordinary cargo test cases
(the standalone mock via an integration test; wusel-mock is also a library, so
tests can drive it in-process).
|
On Linux It buys two things. The credential tests answer the same on an unlocked GNOME desktop and in a bare container, instead of depending on whether the machine happens to have a Secret Service. And no test can touch your own login keyring — the same account key the product uses. See How that fallback is tested in Credentials and trust for how the keyring’s failure modes are covered without a keyring at all. |
-
Real FUSE mount (Linux,
/dev/fuse).mise run fuse-test, run on the host, builds the podman dev container and runs the Linux-only gate inside it: thewusel-fusetest suites, which mount the full stack — engine + FUSE — against the in-processwusel-mockand drive it through the kernel (reads, writes, renames, directory streams, attributes,statfs, concurrent reads, unmount); thewusel-desktoptests, including the notify hook with no session bus; clippy on both crates; andscripts/check-network-home.sh, which proves the network-home relocation in a mount namespace. It needs/dev/fuseand a privileged container, so it is not part of the nativemise run test, and CI does not run it. This is the automated counterpart to the manual mount check. -
Real Nextcloud — automated, nightly.
scripts/e2e-nextcloud.shdrives a FUSE-mountedwuselagainst a real Nextcloud container: read/hydration, write/upload, pin, and the opt-in 3-way merge on a genuine 412 conflict. This is the tier a mock cannot stand in for (ETag propagation, chunked-upload NG, the server’s own conditional-request behaviour). Run it locally withmise run e2e-local(podman); in CI it is the E2E workflow, see What CI will run on your change. The local run uses one server — override it withNC_IMAGE=nextcloud:33-apache mise run e2e-localto reproduce a matrix leg. Locally, a Caddy reverse proxy with its own CA sits in front of the server by default, so the run goes through TLS,[tls] ca_certand HTTP/2; a final gate reads the proxy’s access log and fails unless every Wusel request used HTTP/2.E2E_TLS=0talks plain HTTP to the server directly, as CI does;E2E_HTTP1=1keeps the proxy but sets[tls] http1_only, the baseline to hold HTTP/2’s timings against.The responsiveness checks throttle the link with
tc(netem), which needs NET_ADMIN and an interface whose root qdisc can be displaced. The dev container has both; a CI runner has neither — see The rule underneath all of it below for how that is handled.Anything the script still cannot cover — live
notify_push, the desktop integration in a real session — stays a manual check against our own instance (see the GNOME tutorial).
The rule underneath all of it
A test that only runs when someone remembers to ask for it does not run. Nothing
is #[ignore], and nothing is skipped-when-absent either: a green run has to
mean the thing was checked, so when a tier’s tooling is missing the suite stops
and names the package to install.
The one exception is announced as loudly as a failure would be. The responsiveness checks need to throttle the link, which requires privileges a CI runner does not have; there those steps are skipped, said out loud, and counted in the closing line. They are a local tier by design, not a gap that crept in.
The same reasoning drives the adverse conditions the concurrency tests inject rather than hope for — see Concurrency.
What no test covers
Live notify_push against a real server, and the desktop integration in a real
session. Both stay manual checks against our own instance. There is no honest
way to automate a notification appearing on somebody’s screen.
The macOS frontend’s Swift code — the File Provider extension and the agent —
has no automated tests. The socket it speaks is covered from the Rust side, by
the wusel-ipc tests against the mock.