Skip to content

feat: profile CRUD + X-User-Id shim (Phase 1.b.4b)#6

Merged
InstaZDLL merged 3 commits into
mainfrom
feat/1.b.4b-profile-crud
May 30, 2026
Merged

feat: profile CRUD + X-User-Id shim (Phase 1.b.4b)#6
InstaZDLL merged 3 commits into
mainfrom
feat/1.b.4b-profile-crud

Conversation

@InstaZDLL
Copy link
Copy Markdown
Owner

@InstaZDLL InstaZDLL commented May 30, 2026

Summary

Phase 1.b.4b — first real data routes on top of the tenant-scoped repository methods landed in waveflow#183. `waveflow-core` git rev bumped to the merge SHA (`24ffa58`).

Schema (2 new migrations)

File What
`20260530000000_users.sql` New `users` table (BIGSERIAL id + epoch-ms `created_at`). 1.d will graft Better Auth metadata onto it.
`20260530000001_profile_user_id.sql` DROP + CREATE `profile` with `user_id` FK (`ON DELETE CASCADE`) + composite `(user_id, last_used_at)` and `user_id`-only indexes. The old migration is immutable; the table was empty everywhere (CRUD wasn't live), so DROP + CREATE beat an ALTER + backfill.

Middleware (`src/middleware.rs`)

`require_user_id` extracts `X-User-Id` off the request, parses it as `i64`, rejects 401 on missing / non-positive / unparseable, injects a typed `UserId(i64)` extension. Phase 1.d swaps the body for JWT verification — the `UserId` shape stays so handlers don't change.

Handlers

Verb Path Notes
`POST` `/api/v1/users` No auth — bootstrap so a client has something to put in `X-User-Id`. Retires in 1.d.
`GET` `/api/v1/profiles` `list_for_user`, MRU-first.
`POST` `/api/v1/profiles` `insert_for_user` + read-back. FK violation (unknown user) → 409.
`GET` `/api/v1/profiles/{id}` `get_for_user`, 404 blurs "missing" and "foreign-owned" so we don't leak existence.
`PATCH` `/api/v1/profiles/{id}` `rename_for_user` + read-back.
`DELETE` `/api/v1/profiles/{id}` `delete_guarded_for_user`, 409 if would leave the user with zero profiles.

Every handler carries a `#[utoipa::path]` declaration with status codes + body schemas, so `/openapi.json` and the Scalar UI at `/reference` stay in lockstep.

Tests (`tests/profiles.rs`, 8 scenarios)

  • `create_user_returns_201_with_id`
  • `profiles_require_x_user_id` (5 verbs × 401)
  • `malformed_x_user_id_rejected` (empty/abc/0/-1)
  • `create_then_list_then_get` — happy round-trip
  • `tenants_are_isolated` — user B sees nothing of user A's profile through list, get OR delete
  • `update_renames_in_place`
  • `delete_blocks_last_profile` (409 + still 1 in list)
  • `delete_succeeds_when_more_than_one` (204 + remaining row visible)
  • `create_with_unknown_user_id_returns_409` (FK violation surface)

Plus CLAUDE.md + README updated with the new endpoints and the tenancy discipline.

Test plan

  • `cargo check --all-targets` ✅
  • `cargo fmt --all --check` ✅
  • `cargo clippy --all-targets --all-features -- -D warnings` ✅
  • CI Linux: full `cargo test` against the Postgres 17 service container (all 11 tests against a per-test database).

Out of scope

  • Library / Track / Playlist CRUD (1.b.5+)
  • Profile color / avatar updates (PATCH today only handles `name`)
  • Real JWT auth (1.d)

Signed-off-by: InstaZDLL github.105mh@8shield.net

Summary by CodeRabbit

  • Nouvelles fonctionnalités
    • Endpoint dev pour créer des utilisateurs (POST /api/v1/users) et CRUD tenant-scopé pour profils (/api/v1/profiles) avec isolation stricte par utilisateur.
  • Documentation
    • Mise à jour Architecture et README : endpoints, comportement du shim dev (X-User-Id → JWT futur) et avertissements.
  • Chores
    • Migrations SQL ajoutées pour tables users et profile (user_id FK, index, contraintes) et nouvelle option d’env via WAVEFLOW_DEV_AUTH.
  • Tests
    • Suite E2E étendue couvrant gate, isolation, flux create/list/get/update/delete et cas d’erreur.

Review Change Stack

Wire the first real data routes on top of the tenant-scoped repository
methods that landed in waveflow#183. Phase 1.b.4 in three commits:
schema, middleware, handlers.

Schema:
- `20260530000000_users.sql` — new `users` table (BIGSERIAL id +
  epoch-ms `created_at`). Phase 1.d will plug Better Auth metadata
  onto the same row, but the FK destination is what matters now.
- `20260530000001_profile_user_id.sql` — DROP + CREATE `profile` with
  `user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE` +
  composite `(user_id, last_used_at DESC)` index (powers
  `list_for_user`) + dedicated `user_id` index (powers the
  delete-time `ORDER BY id FOR UPDATE` lock). The old migration is
  immutable but the table was empty everywhere (CRUD wasn't live yet),
  so DROP + CREATE is simpler than an ALTER with backfill.

Middleware (`src/middleware.rs`):
- `require_user_id` extracts `X-User-Id` off the request, parses it
  as `i64`, rejects 401 if missing / non-positive / unparseable,
  injects a typed `UserId(i64)` extension. Documented as a dev shim
  that 1.d replaces with JWT verification — the `UserId` shape stays
  the same so handlers don't need to change.

Handlers:
- `POST /api/v1/users` — bootstrap: no auth, returns the new id.
  Without this the dev caller has no way to populate `X-User-Id`.
  Retires in 1.d when Better Auth owns user creation.
- `GET /api/v1/profiles` — `list_for_user`, MRU-first.
- `POST /api/v1/profiles` — `insert_for_user` + read-back. FK
  violation (X-User-Id with no matching `users` row) surfaces as 409.
- `GET /api/v1/profiles/{id}` — `get_for_user`, 404 when missing OR
  owned by someone else (the repository deliberately blurs the two
  so we don't leak the existence of other tenants' rows).
- `PATCH /api/v1/profiles/{id}` — `rename_for_user` + read-back.
- `DELETE /api/v1/profiles/{id}` — `delete_guarded_for_user`, 409
  when the row was the user's last remaining profile (same invariant
  the desktop's ProfileSelectorModal enforces client-side).

Each handler carries a `#[utoipa::path]` declaration with status
codes + body schemas, so the OpenAPI doc at `/openapi.json` and the
Scalar UI at `/reference` stay in lockstep.

Tests in `tests/profiles.rs` (8 scenarios):
- `create_user_returns_201_with_id`
- `profiles_require_x_user_id` (5 verbs × 401)
- `malformed_x_user_id_rejected` (empty/abc/0/-1 → 401)
- `create_then_list_then_get` — happy path round-trip
- `tenants_are_isolated` — user B sees nothing of user A's profile,
  not via list nor get nor delete
- `update_renames_in_place`
- `delete_blocks_last_profile` (409 + still 1 in list)
- `delete_succeeds_when_more_than_one` (204 + remaining row visible)
- `create_with_unknown_user_id_returns_409` (FK violation surface)

Also bumps the `waveflow-core` git rev to 24ffa58 (the merge SHA of
the tenant-method PR) and updates CLAUDE.md + README so the next
session sees the new endpoints in the published surface.

Signed-off-by: InstaZDLL <github.105mh@8shield.net>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 30, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 185c15c8-fe4c-422a-81fa-0ada8e41ed36

📥 Commits

Reviewing files that changed from the base of the PR and between bf34cc0 and b9b809a.

📒 Files selected for processing (1)
  • tests/support.rs

📝 Walkthrough

Walkthrough

Cette PR ajoute la table users, un middleware require_user_id lisant X-User-Id, le module DB users::create, un endpoint dev POST /api/v1/users, un CRUD tenant-scoped /api/v1/profiles/* utilisant méthodes *_for_user, migrations, configuration WAVEFLOW_DEV_AUTH pour gating, et une suite E2E couvrant ces comportements.

Changes

Tenant-Scoped Profile Management

Layer / File(s) Summary
Database schema and migrations
migrations/20260530000000_users.sql, migrations/20260530000001_profile_user_id.sql
Création table users (id BIGSERIAL, created_at BIGINT), recréation de profile avec user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE et index profile_user_last_used_idx sur (user_id, last_used_at DESC).
Database users module
src/db.rs
Ajout de pub mod users et pub async fn users::create(pool, created_at) -> Result<i64, sqlx::Error> utilisant INSERT ... RETURNING id.
Authentication middleware and user identification
src/middleware.rs, src/lib.rs
Middleware require_user_id lisant x-user-id, rejetant 401 si absent/malformé/<=0, injectant UserId(i64) dans request.extensions.
Dev user creation API
src/api/users.rs
Endpoint POST /api/v1/users pour insérer un user via db::users::create et retourner 201 { id } (dev shim).
Profile CRUD API with tenant scoping
src/api/profiles.rs
5 endpoints (list/create/get/update/delete) : tous appellent les méthodes *_for_user du repository, create_profile fait read-after-create, erreurs mappées en 200/201/204/404/409/500 selon le cas; FK manquant → 409; suppression du dernier profil → 409.
Router wiring, gating and configuration
src/api/mod.rs, src/config.rs, src/lib.rs
router(state, &Config) construit users et profiles conditionnellement à config.dev_auth_enabled, applique reject_dev_auth_disabled si nécessaire, applique require_user_id en dev sur profiles, et Config::from_env lit WAVEFLOW_DEV_AUTH == "1".
Dependencies, docs and env example
Cargo.toml, CLAUDE.md, README.md, .env.example
Mise à jour waveflow-core rev, ajout chrono = "0.4", renforcement des conventions dans CLAUDE.md (SQL en DB, *_for_user), README vers phase 1.b.4 (endpoints et dev-auth shim), et ajout WAVEFLOW_DEV_AUTH dans .env.example.
Comprehensive end-to-end tests & helpers
tests/profiles.rs, tests/support.rs
Suite E2E testant création user, rejet 401 sans header/valeurs invalides, cycle create→list→get, isolation multi-tenant, PATCH rename, DELETE last profile → 409, DELETE when multiple → 204, FK missing → 409, et gating prod (503). Helpers spawn_app (dev) et spawn_app_prod_gate (prod gate).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • InstaZDLL/waveflow-server#4: Modifie le câblage du routeur OpenAPI dans src/api/mod.rs — recoupe la zone de routing et OpenAPI.
  • InstaZDLL/waveflow-server#3: Établit l'infrastructure Postgres/PgPool et /ready/db::ping ; cette PR construit dessus avec migrations et endpoints tenant-scoped.

Poem

🔐 Profiles cloîtrés par un UserId en tête,
Le middleware veille, le header fait la quête,
Migrations et repos scellent la frontière,
Tests vérifient que nul profil ne s'échappe,
Le dev-shim cédera, JWT prendra la fête.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Le titre est directement lié à la modification principale : ajout du CRUD profile et du shim X-User-Id pour la phase 1.b.4b.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/1.b.4b-profile-crud

Comment @coderabbitai help to get the list of available commands and usage tips.

@InstaZDLL InstaZDLL self-assigned this May 30, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@migrations/20260530000001_profile_user_id.sql`:
- Around line 33-36: Supprimez la création de l'index redondant
profile_user_id_idx : le composite profile_user_last_used_idx (user_id,
last_used_at DESC) couvre déjà les recherches et verrous sur user_id, donc
retirez la ligne CREATE INDEX profile_user_id_idx ON profile (user_id); et, si
nécessaire pour les migrations déjà appliquées, fournissez dans la migration
correspondante une instruction DROP INDEX IF EXISTS profile_user_id_idx pour
supprimer l'index existant afin d'éviter l'amplification d'écriture et
l'utilisation de stockage inutile.

In `@src/api/mod.rs`:
- Around line 42-45: La route profiles::router est montée avec le middleware
auth_middleware::require_user_id qui accepte le header forgeable X-User-Id;
change la branche pour ne pas l’exposer en production: guarder l’ajout du
middleware derrière un flag de configuration (ex.
config.allow_unsafe_user_id_header) ou un env dev/test check, ou remplacer
require_user_id par un middleware de production qui vérifie une authentification
robuste (JWT/session lookup) avant d’injecter UserId; modifyez l’endroit où
.merge(profiles::router(...).layer(middleware::from_fn(auth_middleware::require_user_id)))
est appelé pour appliquer ce contrôle conditionnel.

In `@src/api/profiles.rs`:
- Around line 250-269: Current rename_then_read is non-atomic:
repo.rename_for_user followed by repo.get_for_user can yield a false 404 if a
concurrent DELETE occurs; change repo.rename_for_user to return the updated row
(use an UPDATE ... RETURNING in the repository and have rename_for_user return
Option<Profile> or Result<Profile, _>) or implement rename_for_user to run
UPDATE + SELECT within a DB transaction and return the updated profile, then in
profiles.rs replace the post-rename get_for_user call with the returned Profile
and construct ProfileResponse directly (update any call sites and signatures
such as rename_for_user and usages that expect a bool).
- Around line 92-95: The OpenAPI annotations for the profile handlers are
missing the 500 response the implementations already emit; update each
#[utoipa::path] on the profile endpoints (the attributes covering the blocks at
the commented ranges) to include a (status = 500, description = "Internal Server
Error", body = <error-type>) entry, where <error-type> is the public
serde::Serialize error response struct used by your repository/handlers (or
create one stable public ErrorResponse type if none exists), and ensure the
attribute lists that error type as the body so /openapi.json reflects the real
behavior; apply this change to each affected handler attribute group referenced
in the comment.

In `@src/api/users.rs`:
- Around line 42-44: The OpenAPI responses macro currently lists only (status =
201, description = "User created", body = CreateUserResponse) but the handler
can return INTERNAL_SERVER_ERROR; update the responses(...) invocation to also
declare a 500 response (e.g. (status = 500, description = "Internal Server
Error")) so the generated /openapi.json matches runtime behavior; modify the
same responses(...) block near the handler that returns INTERNAL_SERVER_ERROR
and ensure the 500 entry is added alongside the 201 entry.

In `@tests/profiles.rs`:
- Around line 6-7: Le doc-comment fait référence à un helper inexistant
`expect_user_id`; remplace cette référence par le nom réel du helper `mint_user`
dans le module de tests (mettre à jour toute occurrence de `expect_user_id` dans
les doc-comments en haut de tests/profiles.rs afin qu'elle mentionne
`mint_user`), pour que la documentation reflète correctement la fonction
définie.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 83d4cc29-7371-44bf-a977-02f9f6c29668

📥 Commits

Reviewing files that changed from the base of the PR and between f99ed21 and 765fb94.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock, !Cargo.lock, !*.lock
📒 Files selected for processing (12)
  • CLAUDE.md
  • Cargo.toml
  • README.md
  • migrations/20260530000000_users.sql
  • migrations/20260530000001_profile_user_id.sql
  • src/api/mod.rs
  • src/api/profiles.rs
  • src/api/users.rs
  • src/db.rs
  • src/lib.rs
  • src/middleware.rs
  • tests/profiles.rs

Comment thread migrations/20260530000001_profile_user_id.sql Outdated
Comment thread src/api/mod.rs Outdated
Comment thread src/api/profiles.rs
Comment thread src/api/profiles.rs
Comment thread src/api/users.rs
Comment thread tests/profiles.rs Outdated
Four valid issues applied, one skipped with rationale.

1. APPLIED — Drop the redundant `profile_user_id_idx` index. The
   composite `(user_id, last_used_at DESC)` already serves equality
   filters on its leading column (the `delete_guarded_for_user` lock
   query, the FK cascade lookup) on top of the MRU-ordered list.
   Keeping the dedicated single-column index was pure write
   amplification for no measurable read benefit.

2. APPLIED — Gate the dev-shim middleware behind
   `WAVEFLOW_DEV_AUTH=1`. `Config::dev_auth_enabled` defaults to
   `false`; in that state every `/api/v1/*` request short-circuits to
   503 via a new `reject_dev_auth_disabled` layer (mounted on both
   `users::router` and `profiles::router`). The 503 — rather than
   401 — intentionally hides that the shim is even compiled in, so a
   production probe can't tell whether `X-User-Id` does anything.
   Tests opt in via `Config { dev_auth_enabled: true, .. }` on
   spawn_app, plus a new `dev_auth_gate_returns_503_when_disabled`
   test that verifies the production-default path.

3. APPLIED — Add the `(status = 500, description = ...)` response
   entry to every `#[utoipa::path]` block that has a 500 path. Body
   is documented as plain text (matches what `(StatusCode, &str)`
   produces). Affects all 5 profile handlers + the `users::create`
   handler.

4. APPLIED — Fix the dangling `expect_user_id` reference in
   `tests/profiles.rs`'s doc-comment header. The actual helper is
   `mint_user`; the docstring now matches.

5. SKIPPED — Atomic UPDATE + SELECT for `rename_profile`. The race
   produces a 404 when a concurrent DELETE commits between our
   `UPDATE` and the read-back — which is the same response a
   delete-then-rename request order would naturally produce. A
   proper fix requires changing
   `PostgresProfileRepository::rename_for_user` (and ideally
   `insert_for_user`, `touch_last_used_for_user`) to use
   `UPDATE ... RETURNING *` in waveflow-core. Deferring to a paired
   sweep on those methods rather than a piecemeal hotfix on PR #6.

Validated: cargo check + clippy + fmt + the (offline) workspace
tests pass; the new gate test will exercise the production path on
CI.

Signed-off-by: InstaZDLL <github.105mh@8shield.net>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/support.rs (1)

20-77: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Factoriser les deux helpers quasi identiques.

spawn_app_prod_gate et spawn_app ne diffèrent que par dev_auth_enabled. Cette duplication a déjà dû être maintenue en double (ajout récent du champ) et finira par diverger (timeout, max_connections…). Extraire un helper paramétré.

♻️ Extraction proposée
-#[allow(dead_code)] // some test files don't use this helper
-pub async fn spawn_app_prod_gate(pool: PgPool) -> String {
-    let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
-    let addr = listener.local_addr().unwrap();
-
-    let config = Config {
-        bind_addr: addr,
-        request_timeout_secs: 5,
-        database_url: "<test>".into(),
-        db_max_connections: 1,
-        dev_auth_enabled: false,
-    };
-
-    let state = AppState { db: pool };
-    tokio::spawn(async move {
-        axum::serve(
-            listener,
-            app(config, state).into_make_service_with_connect_info::<SocketAddr>(),
-        )
-        .await
-        .unwrap();
-    });
-
-    format!("http://{addr}")
-}
-
-/// Spawn the app in the background and return its base URL.
-pub async fn spawn_app(pool: PgPool) -> String {
-    let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
-    let addr = listener.local_addr().unwrap();
-
-    let config = Config {
-        bind_addr: addr,
-        request_timeout_secs: 5,
-        // `database_url` is unused by `app()` once the pool is built —
-        // it lives in the config purely so `Config::from_env` is the
-        // single source of truth for env reading. Placeholder string
-        // is fine inside the test.
-        database_url: "<test>".into(),
-        db_max_connections: 1,
-        // Integration tests exercise the dev `X-User-Id` shim — the
-        // production gate is what we want to verify *separately*
-        // (see `dev_auth_gate_returns_503_when_disabled`), here we
-        // just want the routes reachable.
-        dev_auth_enabled: true,
-    };
-
-    let state = AppState { db: pool };
-    tokio::spawn(async move {
-        axum::serve(
-            listener,
-            app(config, state).into_make_service_with_connect_info::<SocketAddr>(),
-        )
-        .await
-        .unwrap();
-    });
-
-    format!("http://{addr}")
-}
+async fn spawn_with(pool: PgPool, dev_auth_enabled: bool) -> String {
+    let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
+    let addr = listener.local_addr().unwrap();
+
+    let config = Config {
+        bind_addr: addr,
+        request_timeout_secs: 5,
+        // `database_url` is unused by `app()` once the pool is built —
+        // placeholder is fine inside the test.
+        database_url: "<test>".into(),
+        db_max_connections: 1,
+        dev_auth_enabled,
+    };
+
+    let state = AppState { db: pool };
+    tokio::spawn(async move {
+        axum::serve(
+            listener,
+            app(config, state).into_make_service_with_connect_info::<SocketAddr>(),
+        )
+        .await
+        .unwrap();
+    });
+
+    format!("http://{addr}")
+}
+
+/// Spawn the app with the production-default config — dev auth
+/// **disabled**. Verifies the 503 gate when `WAVEFLOW_DEV_AUTH != "1"`.
+#[allow(dead_code)] // some test files don't use this helper
+pub async fn spawn_app_prod_gate(pool: PgPool) -> String {
+    spawn_with(pool, false).await
+}
+
+/// Spawn the app in the background (dev shim enabled) and return its base URL.
+pub async fn spawn_app(pool: PgPool) -> String {
+    spawn_with(pool, true).await
+}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/support.rs` around lines 20 - 77, Les deux helpers spawn_app_prod_gate
et spawn_app sont dupliqués; refactorise en extrayant une fonction unique (par
ex. spawn_app_with_dev_auth(pool: PgPool, dev_auth_enabled: bool) -> String) qui
construit le Config (même request_timeout_secs, database_url,
db_max_connections) et lance l'app via axum::serve/app(...). Remplace
spawn_app_prod_gate et spawn_app par de simples wrappers qui appellent
spawn_app_with_dev_auth(..., false) et spawn_app_with_dev_auth(..., true)
respectivement, et mets à jour les appels de tests pour utiliser ces wrappers.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@tests/support.rs`:
- Around line 20-77: Les deux helpers spawn_app_prod_gate et spawn_app sont
dupliqués; refactorise en extrayant une fonction unique (par ex.
spawn_app_with_dev_auth(pool: PgPool, dev_auth_enabled: bool) -> String) qui
construit le Config (même request_timeout_secs, database_url,
db_max_connections) et lance l'app via axum::serve/app(...). Remplace
spawn_app_prod_gate et spawn_app par de simples wrappers qui appellent
spawn_app_with_dev_auth(..., false) et spawn_app_with_dev_auth(..., true)
respectivement, et mets à jour les appels de tests pour utiliser ces wrappers.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 81f41650-b14b-4fd6-8047-127ebe480533

📥 Commits

Reviewing files that changed from the base of the PR and between 765fb94 and bf34cc0.

📒 Files selected for processing (10)
  • .env.example
  • README.md
  • migrations/20260530000001_profile_user_id.sql
  • src/api/mod.rs
  • src/api/profiles.rs
  • src/api/users.rs
  • src/config.rs
  • src/lib.rs
  • tests/profiles.rs
  • tests/support.rs

CodeRabbit caught the duplication: spawn_app and spawn_app_prod_gate
were copies that differed only in the `dev_auth_enabled` bool. Extract
the common boot path into `spawn_app_with_dev_auth(pool,
dev_auth_enabled)` and reduce the two public helpers to one-liner
wrappers. Test call sites are unchanged.

Signed-off-by: InstaZDLL <github.105mh@8shield.net>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant