feat: notification center backend core (PR-1)

- Add notifications table with Alembic migration
- Notification model with user-scoped indexing and partial index on unread
- NotificationService singleton with create/list/count/mark-read/dismiss
- FastAPI router: GET /notifications, GET /unread, PATCH /{id}/read,
  POST /mark-all-read, DELETE /{id}
- Mute categories filtering from UserConfig
- 13 unit tests for NotificationService
- 10 integration tests for API endpoints
- Updated test_models.py with new table registration

Quality gates: pytest 23 new passed, ruff clean
This commit is contained in:
2026-05-29 12:05:31 +02:00
parent 4a0d38384f
commit cbaebcf649
18 changed files with 4161 additions and 9 deletions
+15 -8
View File
@@ -16,7 +16,6 @@ def test_base_metadata_collects_declared_tables() -> None:
@pytest.mark.integration
def test_shared_mixins_define_expected_columns() -> None:
assert "id" in UUIDPrimaryKeyMixin.__dict__
assert "created_at" in TimestampMixin.__dict__
@@ -24,20 +23,26 @@ def test_shared_mixins_define_expected_columns() -> None:
@pytest.mark.integration
def test_expected_tables_are_registered() -> None:
assert set(Base.metadata.tables) == {
"refresh_tokens",
"config_profile_includes",
"config_profiles",
"git_repositories",
"health_checks",
"instance_events",
"notifications",
"projects",
"ssh_keys",
"terminal_sessions",
"tool_definition_manifests",
"tool_instances",
"tool_types",
"user_configs",
"users",
}
@pytest.mark.integration
def test_user_table_has_required_columns() -> None:
columns = User.__table__.columns
@@ -56,7 +61,6 @@ def test_user_table_has_required_columns() -> None:
@pytest.mark.integration
def test_project_relationships_point_to_owner_and_default_ssh_key() -> None:
owner_fk = next(iter(Project.__table__.c.owner_id.foreign_keys))
ssh_fk = next(iter(Project.__table__.c.default_ssh_key_id.foreign_keys))
@@ -68,7 +72,6 @@ def test_project_relationships_point_to_owner_and_default_ssh_key() -> None:
@pytest.mark.integration
def test_repository_and_user_config_relationships_are_registered() -> None:
project_fk = next(iter(GitRepository.__table__.c.project_id.foreign_keys))
owner_fk = next(iter(GitRepository.__table__.c.owner_id.foreign_keys))
@@ -84,9 +87,13 @@ def test_repository_and_user_config_relationships_are_registered() -> None:
@pytest.mark.asyncio
@pytest.mark.integration
async def test_async_session_can_insert_and_load_user(db_session: AsyncSession) -> None:
user = User(email="dev@headquarter.local", name="Dev User", authentik_id="dev-user", avatar_url=None)
user = User(
email="dev@headquarter.local",
name="Dev User",
authentik_id="dev-user",
avatar_url=None,
)
db_session.add(user)
await db_session.commit()