test: improve app/core module test coverage to 100%
Merge Request
Overview
Improve unit test coverage for app/core/ modules (auth.py, database_triggers.py, logger.py, rbac.py) from 81–90% to 100%, and fix CI pipeline failure caused by missing curl in the python:3.13-slim Docker image.
Linked Issue
This MR is linked to the issue #63 (closed) and now closes it.
What does this MR do and why?
%{first_multiline_commit}
The app/core/ modules had significant test coverage gaps across error handling branches, optional parameter logic, convenience function delegation, and defensive checks. This MR adds targeted unit tests to cover every uncovered code path legitimately — no pragma: no cover or mock bypasses.
Additionally, all 6 CI pipeline jobs were failing because the python:3.13-slim base image does not include curl, which is required to download the uv installer. This MR fixes the CI configuration to install curl before the setup step.
Changes Made
-
tests/test_core/test_auth.py— 4 new tests: customexpires_delta, missingsubclaim, invalid UUID in token, user not found in DB -
tests/test_core/test_logger.py— Tests for all 20+ instance methods, all convenience functions,warningmethod, andlogbullimport failure branch -
tests/test_core/test_rbac.py— 5 new tests: invalid role handling, permission rejection, authentication guard,get_user_role_summary, convenience function callability -
tests/test_core/test_database_triggers.py— 1 new test covering extractedmain()function +__main__guard viarunpy -
app/core/database_triggers.py— Extractedmain()from__main__block for testability (no behavior change) -
.gitlab-ci.yml— Addedapt-get install curl ca-certificatestobefore_script
Technical Details
Test coverage fix:
- Each uncovered line was analysed from
--cov-report=term-missingoutput - Tests exercise real code paths — no
pragma: no cover, no mocking away the logic under test - The
logbullimport failure branch (module-levelexcept ImportError) is covered viaimportlib.reloadwithsys.modulesmanipulation - The
__main__guard is covered viarunpy.run_modulewithrun_name="__main__"
CI fix:
- Root cause:
python:3.13-slimdoes not shipcurl - All 6 jobs (
ruff-lint,ruff-format,type-check,pytest,pyupgrade,schemathesis) failed atcurl -LsSf https://astral.sh/uv/install.sh | sh - Fix: install
curlandca-certificatesviaapt-getbefore theuvinstaller runs
Coverage before → after:
| Module | Before | After |
|---|---|---|
auth.py |
89% | 100% |
database_triggers.py |
90% | 100% |
logger.py |
81% | 100% |
rbac.py |
87% | 100% |
Type of Change
-
🐛 Bug fix (non-breaking change that fixes an issue) -
✨ New feature (non-breaking change that adds functionality) -
💥 Breaking change (fix or feature that would cause existing functionality to change) -
📝 Documentation update -
♻ ️ Refactor (no functional changes) -
⚡ Performance improvement -
🧪 Test update -
🔧 Configuration change -
🚨 Security fix -
🗑 ️ Deprecation (removing deprecated code)
Related Issues / References
N/A
Screenshots or Screen Recordings
N/A — no UI or API changes.
How to Validate Locally
-
Previous behaviour:
uv run pytest tests/test_core/ --cov=app.core.auth --cov=app.core.database_triggers --cov=app.core.logger --cov=app.core.rbac --cov-report=term-missingshowed 81–90% coverage with multiple missing lines -
Changes made: Added targeted tests for every uncovered branch/line, extracted
main()indatabase_triggers.py, fixed.gitlab-ci.yml - New behaviour: Same command now reports 100% coverage across all 4 modules, 70 tests passing, 0 missing lines
Testing Done
-
Unit tests added/updated -
API endpoint tests passing
Test Cases Covered:
| Scenario | Expected Result | Status |
|---|---|---|
auth.py — token with custom expires_delta
|
Token created with correct expiry | |
auth.py — token missing sub claim |
401 HTTPException raised | |
auth.py — token with invalid UUID in sub
|
401 HTTPException raised | |
auth.py — valid token but user not in DB |
401 HTTPException raised | |
database_triggers.py — main() function |
Calls initialize_database_triggers and prints success |
|
database_triggers.py — __main__ guard via runpy |
Executes main() when run as script |
|
logger.py — all instance methods (login, register, vitals, etc.) |
Log entries written without error | |
logger.py — all convenience functions |
Delegate to global multi_logger instance |
|
logger.py — logbull import failure |
LOGBULL_AVAILABLE set to False, fallback to mock |
|
rbac.py — invalid role in get_user_permissions_cached
|
Returns empty tuple, no crash | |
rbac.py — require_any_permission with no match |
403 HTTPException raised | |
rbac.py — require_authentication with falsy user |
401 HTTPException raised | |
rbac.py — get_user_role_summary
|
Returns correct summary dict |
Test Commands Run:
# Run core tests with full coverage
uv run pytest tests/test_core/ --cov=app.core.auth --cov=app.core.database_triggers --cov=app.core.logger --cov=app.core.rbac --cov-report=term-missing -v
# Result: 70 passed, 100% coverage across all 4 modules
Code Quality Checklist Code Standards
- Code follows project conventions (naming, structure, formatting)
- No debug statements or commented-out code left (unless necessary and intended)
- No unused imports, variables, or functions
- No duplicate code (DRY principle followed)
- Type hints are properly defined (no Any unless justified and no mypy type check errors)
Ruff checks pass: ruff check . ruff format . --check
Python & FastAPI Best Practices
- Functions follow single-responsibility principle
- Async/await used correctly (no blocking calls in async functions)
- Dependency injection used appropriately
- Pydantic models used for request/response validation
- SQLAlchemy queries are optimized (no N+1 queries)
- Error handling is comprehensive (try/except with proper logging)
API Design N/A — no API changes in this MR.
Database & Migrations N/A — no database or schema changes in this MR.
Security No sensitive data logged (passwords, tokens, PII) SQL injection prevention verified (ORM used) Input sanitization implemented Authentication tokens handled securely CORS settings appropriate Security scan passes: bandit -r app/
Error Handling Errors are caught and handled gracefully User-friendly error messages returned Errors are logged appropriately HTTP error responses follow API standards
Documentation Code comments explain complex logic (not what, but why)
Known Limitations / Technical Debt None introduced.
Additional Notes The database_triggers.py change (extracting main()) is a minimal refactor with zero behavior change — it simply makes the main entry point testable All pre-commit hooks pass: ruff, ruff-format, bandit, mypy, vulture, pytest coverage The CI fix (apt-get install curl) adds ~2-3 seconds to job startup but is required for python:3.13-slim
MR Acceptance Checklist
Quality & Correctness
- [*] Code works as intended and solves the stated problem
- [*] No bugs introduced (existing functionality not broken)
- [*] Edge cases handled appropriately
Maintainability
- [*] Code is readable and well-organized
- [*] Code is testable and well-tested
- [*] Follows project patterns and conventions
Acceptance Review
-
Reviewed by at least 1 teammate -
Reviewed by product owner