refactor(backend): enforce route→service→repo layered architecture

- introduce custom repository exception hierarchy (DuplicateEntryError,
  IntegrityConstraintError, InvalidInputError) replacing raw ValueError
- eliminate all direct repository imports and raw SQL from route layer
- add UserService, SessionService, OrganizationService to service layer
- add get_stats/get_org_distribution service methods replacing admin inline SQL
- fix timing side-channel in authenticate_user via dummy bcrypt check
- replace SHA-256 client secret fallback with explicit InvalidClientError
- replace assert with InvalidGrantError in authorization code exchange
- replace N+1 token revocation loops with bulk UPDATE statements
- rename oauth account token fields (drop misleading 'encrypted' suffix)
- add Alembic migration 0003 for token field column rename
- add 45 new service/repository tests; 975 passing, 94% coverage
This commit is contained in:
2026-02-27 09:32:57 +01:00
parent 0646c96b19
commit 98b455fdc3
62 changed files with 2933 additions and 1728 deletions

View File

@@ -99,7 +99,7 @@ class TestUpdateCurrentUser:
from unittest.mock import patch
with patch(
"app.api.routes.users.user_crud.update", side_effect=Exception("DB error")
"app.api.routes.users.user_service.update_user", side_effect=Exception("DB error")
):
with pytest.raises(Exception):
await client.patch(
@@ -134,7 +134,7 @@ class TestUpdateCurrentUser:
from unittest.mock import patch
with patch(
"app.api.routes.users.user_crud.update",
"app.api.routes.users.user_service.update_user",
side_effect=ValueError("Invalid value"),
):
with pytest.raises(ValueError):
@@ -224,7 +224,7 @@ class TestUpdateUserById:
from unittest.mock import patch
with patch(
"app.api.routes.users.user_crud.update", side_effect=ValueError("Invalid")
"app.api.routes.users.user_service.update_user", side_effect=ValueError("Invalid")
):
with pytest.raises(ValueError):
await client.patch(
@@ -241,7 +241,7 @@ class TestUpdateUserById:
from unittest.mock import patch
with patch(
"app.api.routes.users.user_crud.update", side_effect=Exception("Unexpected")
"app.api.routes.users.user_service.update_user", side_effect=Exception("Unexpected")
):
with pytest.raises(Exception):
await client.patch(
@@ -354,7 +354,7 @@ class TestDeleteUserById:
from unittest.mock import patch
with patch(
"app.api.routes.users.user_crud.soft_delete",
"app.api.routes.users.user_service.soft_delete_user",
side_effect=ValueError("Cannot delete"),
):
with pytest.raises(ValueError):
@@ -371,7 +371,7 @@ class TestDeleteUserById:
from unittest.mock import patch
with patch(
"app.api.routes.users.user_crud.soft_delete",
"app.api.routes.users.user_service.soft_delete_user",
side_effect=Exception("Unexpected"),
):
with pytest.raises(Exception):