Zum Inhalt springen
Farbschema wählenSprache wählen

Architekturtests ausführen

SupaCloud erzwingt seine Schichtgrenzen und die Dateigrößen-Obergrenze mit einer Rust-Testsuite in server/tests/architecture.rs. Diese Tests laufen innerhalb des normalen cargo test-Frameworks — keine externen Werkzeuge erforderlich. Führe sie vor jedem Push aus; sie sind das CI-Gate, das verhindert, dass sich Regressionen ansammeln.

Terminal window
cd server
cargo test arch

Cargo filtert Tests nach Namen, daher werden alle Tests ausgeführt, deren Name arch enthält — derzeit ausschließlich die einzelne Funktion rust_source_architecture_guards_hold. Die Datei enthält außerdem Hilfs-Unit-Tests (auth_user_handler_scanner_rejects_handlers_without_scope_guard, cfg_test_module_discovery_rejects_name_only_test_files) sowie einen Docker-Kontext-Test, deren Namen jedoch kein arch enthalten — sie werden daher nur bei einem breiteren Filter wie dem einfachen cargo test ausgeführt. Der Test durchsucht alle .rs-Quelldateien unter server/src/ und alle .sql-Migrationsdateien unter server/migrations/.

Um die vollständige Testsuite auszuführen (die auch die Architekturtests enthält):

Terminal window
cargo test

Der einzelne Einstiegspunkt-Test rust_source_architecture_guards_hold in server/tests/architecture.rs führt vierzehn unabhängige Guards aus und sammelt alle Verstöße, bevor er fehlschlägt — du siehst also alle Probleme in einem Durchlauf.

Die grundlegende Regel lautet Interface → Service → Persistence. Vier Guards erzwingen sie:

Guard Was er verbietet
Interfaces umgehen Services src/interfaces/** importiert crate::db::queries, db::queries, queries::, sqlx::PgPool, &state.db
Services rufen Interfaces auf src/services/** importiert crate::interfaces oder einen Interface-Adapter-Alias (api, stripe, telegram, management, discord)
Runtime/Integrationen umgehen Services src/runtime/** und src/integrations/** importieren dieselben Persistence-Muster
Auth-Module umgehen Services src/auth/** importiert dieselben Persistence-Muster

Chat-Adapter (telegram, discord) müssen über die neutrale crate::chat-Oberfläche kommunizieren. Adapter-übergreifende Imports sind in beide Richtungen verboten, und src/interfaces/chat darf keinen der konkreten Adapter importieren.

Jede pub async fn-Handler-Funktion in src/interfaces/http, die einen AuthUser-Extraktor empfängt, muss mindestens einen Autorisierungs- oder Scope-Guard aufrufen (z. B. resolve_workspace_id(, require_workspace_admin(, authorize_org( oder einen anderen Eintrag in der Guard-Liste). Authentifizierte Handler, die gemeinsame (nicht mandantenspezifische) Metadaten zurückgeben und daher keinen weiteren Authz-/Scope-Guard benötigen, haben benannte Ausnahmen, die in der Testdatei registriert sind (die Liste HTTP_AUTHZ_AUTHENTICATED_METADATA_EXCEPTIONS).

Dateien unter src/services/management dürfen keine HTTP-Antworten konstruieren (StatusCode::, IntoResponse, .into_response()). Das Antwort-Mapping gehört in die Interface-Schicht.

Jede Quelldatei unter server/src/ muss unter 500 nicht-leeren, nicht-kommentierten Code-Zeilen bleiben. Der Test misst die Anzahl nach dem Entfernen nachgestellter //-Kommentare und Leerzeilen, sodass Dokumentationsvolumen eine Datei nicht über die Grenze treibt.

Außerdem muss jede Quelldatei mit einem Zweckkommentar beginnen — entweder // Purpose: oder //! Purpose: — und darf weder include!( noch allow(dead_code) verwenden.

Neue Migrationsdateien (Nummer über 168) dürfen keine team_id-Tenancy-Spalte einführen — verwende stattdessen workspace_id — und dürfen die umbenannten Tabellen teams, team_members oder team_invites nicht neu erstellen. Bestehende, erlaubte RENAME COLUMN team_id TO workspace_id-Zeilen sind ausgenommen.

Telegram-Adapter müssen app::i18n (Fluent) verwenden statt inline match-Anweisungen für das Locale. Deutscher Text (Umlaute, ß) muss in .ftl-Dateien unter server/locales/ stehen, nicht im Rust-Quellcode.

Wenn ein Guard ausgelöst wird, gruppiert die Testausgabe Verstöße nach Regel und gibt jeden als <Pfad>:<Zeile> gefolgt vom passenden Muster in Backticks aus. Behebe sie alle — der Test hält nicht bei der ersten Gruppe an.

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`