Test overview
The test suite contains 268 tests with a measured code coverage of >73% (1,053+ of 1,441 lines). All tests usepytest with the pytest-flask and pytest-cov plugins. A coverage gate of 70% is enforced — falling below this threshold fails the GitHub Actions pipeline and blocks deployment.
Running tests
Coverage report
When--cov-report=html is passed, pytest generates an HTML report at htmlcov/index.html. Open it in a browser to inspect per-file and per-line coverage:
coverage.xml report is also generated and consumed by the SonarCloud workflow.
Test categories
| File | Tests | Coverage area |
|---|---|---|
test_auth.py | 17 | Authentication endpoints: register, login, logout, token refresh |
test_tasks.py | 22 | Task CRUD endpoints: create, read, update, delete, filter, export |
test_users.py | 18 | User management endpoints: list, get, update, delete |
test_tags.py | 18 | Tag CRUD endpoints: create, read, update, delete |
test_services.py | 72 | Business logic layer: AuthService, TaskService, UserService, TagService |
test_models.py | 23 | SQLAlchemy ORM models: User, Task, Tag, RefreshToken, Role |
test_middleware.py | 11 | Authentication and RBAC middleware decorators |
test_helpers.py | 29 | Utility functions: pagination, response formatting |
test_validators.py | 58 | Input validation functions for all request payloads |
| Total | 268 |
pytest.ini configuration
pytest.ini
--cov-fail-under=70 causes pytest to exit with a non-zero status code when coverage is below 70%, which fails the test job in the GitHub Actions pipeline and prevents deployment.
CI coverage gate
Themain_task-forge.yml workflow runs:
deploy job does not run. Coverage reports are uploaded as GitHub Actions artifacts (test-results and coverage-report) regardless of outcome.
Writing new tests
Fixtures (conftest.py)
Shared fixtures are defined intests/conftest.py. The key fixtures are:
| Fixture | Scope | Description |
|---|---|---|
app | session | Creates the Flask application with TestingConfig (in-memory SQLite, rate limiting disabled). Creates admin and user roles once per session. |
client | function | Returns a Flask test client bound to the app fixture. |
init_database | function | Yields the SQLAlchemy db object and cleans all non-role rows after each test. |
admin_user | function | Creates an admin user (admin@test.com, password Admin123!) using the admin role. |
regular_user | function | Creates a regular user (user@test.com, password User123!) using the user role. |
another_user | function | Creates a second regular user (another@test.com, password Another123!). |
admin_token | function | Returns a JWT access token for admin_user. |
user_token | function | Returns a JWT access token for regular_user. |
admin_refresh_token | function | Creates and persists a refresh token for admin_user. |
user_refresh_token | function | Creates and persists a refresh token for regular_user. |
auth_headers | function | Returns {'Authorization': 'Bearer <user_token>', 'Content-Type': 'application/json'}. |
admin_auth_headers | function | Returns {'Authorization': 'Bearer <admin_token>', 'Content-Type': 'application/json'}. |
sample_task | function | Creates a single PENDING / MEDIUM priority task owned by regular_user. |
sample_tasks | function | Creates three tasks with varied statuses and priorities for regular_user. |
sample_tag | function | Creates a single tag (TestTag, #FF5733). |
sample_tags | function | Creates three tags: Work, Personal, and Urgent. |
Example test
tests/test_auth.py
Pytest markers
Tag tests with the custom markers defined inpytest.ini to group and filter runs: