Run the architecture tests
SupaCloud enforces its layer boundaries and file-size cap with a Rust test suite
in server/tests/architecture.rs. These tests run inside the normal cargo test
harness — no external tools required. Run them before every push; they are the
CI gate that prevents regressions from accumulating.
Run the architecture tests
Section titled “Run the architecture tests”cd servercargo test archCargo filters tests by name, so this runs every test whose name contains arch —
currently just the single rust_source_architecture_guards_hold function. The file
also contains helper unit tests (auth_user_handler_scanner_rejects_handlers_without_scope_guard,
cfg_test_module_discovery_rejects_name_only_test_files) and a Docker-context test,
but their names do not contain arch, so they run only under a broader filter such
as plain cargo test. It scans all .rs source files under server/src/ and all
.sql migration files under server/migrations/.
To run the full test suite (which includes the architecture tests):
cargo testWhat the tests check
Section titled “What the tests check”The single entry-point test rust_source_architecture_guards_hold in
server/tests/architecture.rs runs fourteen independent guards and collects
every violation before failing, so you see all problems in one pass.
Dependency direction
Section titled “Dependency direction”The fundamental rule is interface → service → persistence. Four guards enforce it:
| Guard | What it forbids |
|---|---|
| Interfaces bypass services | src/interfaces/** importing crate::db::queries, db::queries, queries::, sqlx::PgPool, &state.db |
| Services call interfaces | src/services/** importing crate::interfaces or any interface adapter alias (api, stripe, telegram, management, discord) |
| Runtime/integrations bypass services | src/runtime/** and src/integrations/** importing the same persistence patterns |
| Auth modules bypass services | src/auth/** importing the same persistence patterns |
Chat adapter isolation
Section titled “Chat adapter isolation”Chat adapters (telegram, discord) must communicate through the neutral
crate::chat surface. Cross-adapter imports are forbidden in either direction,
and src/interfaces/chat must not import either concrete adapter.
HTTP authorisation guard
Section titled “HTTP authorisation guard”Every pub async fn handler in src/interfaces/http that receives an AuthUser
extractor must call at least one authorisation or scope guard (e.g.
resolve_workspace_id(, require_workspace_admin(, authorize_org(, or any
other entry in the guard list). Authenticated handlers that return shared (non-tenant-scoped) metadata and therefore
need no further authz/scope guard have named exceptions registered in the test file
(the HTTP_AUTHZ_AUTHENTICATED_METADATA_EXCEPTIONS list).
Management services stay HTTP-free
Section titled “Management services stay HTTP-free”Files under src/services/management must not construct HTTP responses
(StatusCode::, IntoResponse, .into_response()). Response mapping belongs
in the interface layer.
500-LOC cap
Section titled “500-LOC cap”Every source file under server/src/ must stay below 500 non-blank,
non-comment code lines. The test measures the count after stripping trailing
// comments and blank lines, so documentation volume does not push a file
over the limit.
Additionally, every source file must start with a purpose comment — either
// Purpose: or //! Purpose: — and must not use include!( or
allow(dead_code).
Migration vocabulary (ADR 0040)
Section titled “Migration vocabulary (ADR 0040)”New migration files (number above 168) must not introduce a team_id tenancy
column — use workspace_id — and must not recreate the renamed tables teams,
team_members, or team_invites. Existing sanctioned RENAME COLUMN team_id TO workspace_id lines are exempt.
Localisation hygiene
Section titled “Localisation hygiene”Telegram adapters must use app::i18n (Fluent) rather than inline match
statements on the locale. German copy (umlauts, ß) must live in
server/locales/ .ftl files, not in Rust source.
Reading a failure
Section titled “Reading a failure”When a guard fires, the test output groups violations by rule and prints each
as <path>:<line> followed by the matched pattern in backticks. Fix all of them — the test does not
stop at the first group.
thread 'rust_source_architecture_guards_hold' FAILED
architecture guards failed:interfaces must call services/use-cases instead of persistence directly:src/interfaces/http/example.rs:42 contains `sqlx::PgPool`See also
Section titled “See also”- Architecture explanation — the layer model and dependency direction in prose.
- Add a database migration — how to add a migration without breaking the vocabulary guard.