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 — see the dependency policy in Architecture. -
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. Two fault-injection markers in a file’s name provoke server behaviour that is otherwise hard to stage:.fail-once(the firstPUTanswers 500) and.no-etag*(PUT/MOVEanswer without anETag). 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 every server-facing engine test lives.
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 Credential storage in Architecture for how the keyring’s failure modes are covered without a keyring at all. |
-
Real FUSE mount (Linux,
/dev/fuse).mise run fuse-testmounts the full stack — engine + FUSE — against the in-processwusel-mockand drives it through the kernel:ls,cat,stat,statfs, then unmount. It needs a Linux container, so it is not part of the nativemise run test; run it via podman (see below). 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 below. The local run uses one server — override it withNC_IMAGE=nextcloud:33-apache mise run e2e-localto reproduce a matrix leg.The responsiveness checks throttle the link with
tc, which needs NET_ADMIN and an interface whose root qdisc can be displaced. The dev container has both; a CI runner has neither, so there those steps are skipped — announced as loudly as a failure would be, and counted in the closing line. They are a local tier by design, not a gap that crept in. Anything the script still cannot cover — livenotify_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.