cbaebcf649
- 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
96 lines
5.3 KiB
Markdown
96 lines
5.3 KiB
Markdown
# Apply Progress: PR-1 Backend Core for Notification Center
|
|
|
|
## TDD Cycle Evidence
|
|
|
|
| Cycle | Task | Test File | RED | GREEN | Evidence |
|
|
|-------|------|-----------|-----|-------|----------|
|
|
| 1 | NC-PR1-004 (basic CRUD) | `tests/unit/test_notification_service.py` | 13 tests written against missing service | All 13 pass | `pytest tests/unit/test_notification_service.py` → 13 passed |
|
|
| 2 | NC-PR1-005 (edge cases) | `tests/unit/test_notification_service.py` | Already included in cycle 1 | Added wrong-owner, mute-categories, cross-user isolation | Same 13 tests pass |
|
|
| 3 | NC-PR1-007 (basic endpoints) | `tests/integration/test_notifications_api.py` | 10 tests written against missing router | All 10 pass | `pytest tests/integration/test_notifications_api.py` → 10 passed |
|
|
| 4 | NC-PR1-008 (API edge cases) | `tests/integration/test_notifications_api.py` | Already included in cycle 3 | Pagination, 404 ownership, mute categories at API layer | Same 10 tests pass |
|
|
| 5 | NC-PR1-010 (REFACTOR) | All files | — | ruff clean, no regressions | `ruff check` passes on all new files; existing unit tests 223 passed (4 pre-existing failures unrelated) |
|
|
|
|
## Completed Tasks
|
|
|
|
- [x] NC-PR1-001: Alembic migration for `notifications` table
|
|
- [x] NC-PR1-002: SQLAlchemy `Notification` model (`apps/api/src/models/notification.py`)
|
|
- [x] NC-PR1-003: Export `Notification` in `models/__init__.py`
|
|
- [x] NC-PR1-004: NotificationService unit tests — basic CRUD (RED)
|
|
- [x] NC-PR1-005: Implement `NotificationService` singleton (GREEN)
|
|
- [x] NC-PR1-006: Service edge-case and isolation tests (TRIANGULATE)
|
|
- [x] NC-PR1-007: API integration tests — basic endpoints (RED)
|
|
- [x] NC-PR1-008: Implement FastAPI router + Pydantic schemas (GREEN)
|
|
- [x] NC-PR1-009: API edge-case and ownership tests (TRIANGULATE)
|
|
- [x] NC-PR1-010: Register router in `main.py` + import `Notification` for Alembic
|
|
- [x] NC-PR1-011: Code quality pass — ruff, test regressions, smoke tests (REFACTOR)
|
|
|
|
## Files Changed
|
|
|
|
1. `apps/api/alembic/versions/2026_05_29_add_notifications_table.py` *(new)* — Alembic migration
|
|
2. `apps/api/src/models/notification.py` *(new)* — SQLAlchemy model
|
|
3. `apps/api/src/models/__init__.py` — Export `Notification`
|
|
4. `apps/api/src/services/notification_service.py` *(new)* — `NotificationService` singleton
|
|
5. `apps/api/src/api/notifications.py` *(new)* — FastAPI router + Pydantic schemas
|
|
6. `apps/api/src/api/__init__.py` — Export `notifications_router`
|
|
7. `apps/api/src/main.py` — Register router, import `Notification` for Alembic
|
|
8. `apps/api/tests/unit/test_notification_service.py` *(new)* — 13 unit tests
|
|
9. `apps/api/tests/integration/test_notifications_api.py` *(new)* — 10 integration tests
|
|
10. `apps/api/tests/integration/test_models.py` — Updated expected tables list
|
|
|
|
## Test Commands & Exit Codes
|
|
|
|
```bash
|
|
# Unit tests for NotificationService (13 tests)
|
|
cd apps/api && python -m pytest tests/unit/test_notification_service.py -v
|
|
# Exit: 0 — 13 passed
|
|
|
|
# Integration tests for notifications API (10 tests)
|
|
cd apps/api && python -m pytest tests/integration/test_notifications_api.py -v
|
|
# Exit: 0 — 10 passed
|
|
|
|
# Combined new tests
|
|
cd apps/api && python -m pytest tests/unit/test_notification_service.py tests/integration/test_notifications_api.py -v
|
|
# Exit: 0 — 23 passed
|
|
|
|
# Existing unit tests (no regressions in our code)
|
|
cd apps/api && python -m pytest tests/unit/ -v
|
|
# Exit: 1 — 223 passed, 4 failed (pre-existing failures in test_config.py and test_git_repository_clone_preflight.py)
|
|
|
|
# Ruff linting on all new/modified files
|
|
cd apps/api && python -m ruff check \
|
|
src/models/notification.py \
|
|
src/models/__init__.py \
|
|
src/services/notification_service.py \
|
|
src/api/notifications.py \
|
|
src/api/__init__.py \
|
|
src/main.py \
|
|
alembic/versions/2026_05_29_add_notifications_table.py \
|
|
tests/unit/test_notification_service.py \
|
|
tests/integration/test_notifications_api.py \
|
|
tests/integration/test_models.py
|
|
# Exit: 0 — All checks passed
|
|
|
|
# Smoke tests
|
|
health: 200
|
|
notifications unauth: 401
|
|
```
|
|
|
|
## Deviations from Design
|
|
|
|
- **SQLAlchemy `metadata` column name conflict:** `Base.metadata` is reserved by SQLAlchemy DeclarativeBase. Used `notification_metadata` as the Python attribute name with DB column name `"metadata"`. In the Pydantic response model, used `Field(serialization_alias="metadata")` so the JSON API still exposes `metadata` as specified.
|
|
- **`created_at` type in Pydantic:** Used `datetime` instead of `str` to leverage FastAPI's automatic ISO serialization.
|
|
|
|
## Surprises / Decisions
|
|
|
|
1. **SQLite `func.now()` resolution:** `test_list_notifications_orders_by_created_at_desc` failed because multiple rapid INSERTs got identical timestamps. Fixed by explicitly setting `created_at` offsets in the test after creation.
|
|
2. **Pre-existing integration test failures:** ~40 integration tests fail due to missing `asyncpg` module and direct PostgreSQL connection attempts in their custom setup code. These are unrelated to our changes.
|
|
3. **Pre-existing `test_models.py` outdated:** The `test_expected_tables_are_registered` assertion had a hardcoded set missing many newer tables. Updated it to include all current tables (including `notifications`).
|
|
|
|
## Remaining Tasks
|
|
|
|
None — PR-1 is complete.
|
|
|
|
## PR Boundary
|
|
|
|
This PR covers PR-1 only (NC-PR1-001 through NC-PR1-011). PR-2 (backend integration) and PR-3/PR-4 (frontend) are out of scope.
|