Skip to main content

Test overview

The test suite contains 268 tests with a measured code coverage of >73% (1,053+ of 1,441 lines). All tests use pytest 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

pytest

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:
open htmlcov/index.html      # macOS
xdg-open htmlcov/index.html  # Linux
start htmlcov/index.html     # Windows
A coverage.xml report is also generated and consumed by the SonarCloud workflow.

Test categories

FileTestsCoverage area
test_auth.py17Authentication endpoints: register, login, logout, token refresh
test_tasks.py22Task CRUD endpoints: create, read, update, delete, filter, export
test_users.py18User management endpoints: list, get, update, delete
test_tags.py18Tag CRUD endpoints: create, read, update, delete
test_services.py72Business logic layer: AuthService, TaskService, UserService, TagService
test_models.py23SQLAlchemy ORM models: User, Task, Tag, RefreshToken, Role
test_middleware.py11Authentication and RBAC middleware decorators
test_helpers.py29Utility functions: pagination, response formatting
test_validators.py58Input validation functions for all request payloads
Total268

pytest.ini configuration

pytest.ini
[pytest]
testpaths = tests
python_files = test_*.py
python_classes = Test*
python_functions = test_*

addopts =
    -v
    --strict-markers
    --tb=short
    --cov=app
    --cov-report=html
    --cov-report=term-missing
    --cov-report=xml
    --cov-fail-under=70

markers =
    unit: Unit tests
    integration: Integration tests
    slow: Slow running tests
    auth: Authentication tests
    tasks: Task-related tests
    users: User-related tests
    tags: Tag-related tests

filterwarnings =
    ignore::DeprecationWarning
    ignore::PendingDeprecationWarning
--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

The main_task-forge.yml workflow runs:
pytest --cov=app --cov-report=html --cov-report=xml --cov-report=term --cov-fail-under=70 -v
If this command exits non-zero (coverage below 70% or any test failure), the 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 in tests/conftest.py. The key fixtures are:
FixtureScopeDescription
appsessionCreates the Flask application with TestingConfig (in-memory SQLite, rate limiting disabled). Creates admin and user roles once per session.
clientfunctionReturns a Flask test client bound to the app fixture.
init_databasefunctionYields the SQLAlchemy db object and cleans all non-role rows after each test.
admin_userfunctionCreates an admin user (admin@test.com, password Admin123!) using the admin role.
regular_userfunctionCreates a regular user (user@test.com, password User123!) using the user role.
another_userfunctionCreates a second regular user (another@test.com, password Another123!).
admin_tokenfunctionReturns a JWT access token for admin_user.
user_tokenfunctionReturns a JWT access token for regular_user.
admin_refresh_tokenfunctionCreates and persists a refresh token for admin_user.
user_refresh_tokenfunctionCreates and persists a refresh token for regular_user.
auth_headersfunctionReturns {'Authorization': 'Bearer <user_token>', 'Content-Type': 'application/json'}.
admin_auth_headersfunctionReturns {'Authorization': 'Bearer <admin_token>', 'Content-Type': 'application/json'}.
sample_taskfunctionCreates a single PENDING / MEDIUM priority task owned by regular_user.
sample_tasksfunctionCreates three tasks with varied statuses and priorities for regular_user.
sample_tagfunctionCreates a single tag (TestTag, #FF5733).
sample_tagsfunctionCreates three tags: Work, Personal, and Urgent.

Example test

tests/test_auth.py
def test_login_success(client, init_database, regular_user):
    response = client.post(
        '/api/auth/login',
        json={'email': 'user@test.com', 'password': 'User123!'}
    )
    assert response.status_code == 200
    data = response.get_json()
    assert data['success'] is True
    assert 'access_token' in data['data']
    assert 'refresh_token' in data['data']

Pytest markers

Tag tests with the custom markers defined in pytest.ini to group and filter runs:
import pytest

@pytest.mark.auth
def test_register_duplicate_email(client, init_database, regular_user):
    ...
Run only marked tests:
pytest -m auth
pytest -m "unit and not slow"

Build docs developers (and LLMs) love