Robustness Guide
This document covers the testing strategy, CI gates, error-handling conventions, and overall reliability posture of the project.
Test Suite
Tests live in tests/ and are run via pytest.
pytest tests/ -v
Test Structure
| Module | Coverage |
|---|---|
test_api.py |
Canvas API client and HTTP layer |
test_cli.py |
CLI argument parsing and entry point |
test_config.py |
Settings persistence, keybindings, theme/layout |
test_courses.py |
Course data normalization |
test_deadline.py |
Deadline detection and notification scheduling |
test_filtering.py |
Assignment/event filtering logic |
test_grades.py |
Grade normalization and display |
test_ics.py |
ICS calendar export |
test_models.py |
Pydantic/data model validation |
test_notifications.py |
Notification formatting and dispatch |
test_plots.py |
Chart/plot rendering |
test_reranker.py |
GemmaReranker scoring |
test_state.py |
App state transitions and persistence |
test_theme.py |
Theme application |
test_utils.py |
General utility functions |
rmp/ |
RateMyProfessor matching (separate integration) |
Running Specific Tests
# Single module
pytest tests/test_api.py -v
# By keyword
pytest -k "config" -v
# With coverage
pytest tests/ --cov=src/canvas_tui --cov-report=term-missing
CI/CD Gates
All PRs run through the full CI matrix before merge. See .github/workflows/ for the canonical definitions.
Required Checks
- Branch name policy — enforced via
super-linteror equivalent - Python compatibility — tested on 3.11, 3.12, 3.13
- Test suite — must pass on all supported Python versions
- Type checking — if
pyrightormypyis configured, must be clean
Release Workflow
The release workflow runs require-ci as a polling job (~2–3 minutes). It is triggered by version tags matching v*.*.*.
# Always tag with the v prefix
git tag v1.2.3 && git push origin v1.2.3
See release-checklist.md for the full step-by-step release procedure.
Error Handling
API Layer (src/canvas_tui/api.py)
- Network errors are caught and re-raised as typed
CanvasErrorexceptions - HTTP 4xx responses are treated as client errors (non-retryable)
- HTTP 5xx responses trigger exponential backoff retry (up to 3 attempts)
- All API calls log at
DEBUGlevel with method + endpoint on entry
Cache Layer (src/canvas_tui/cache.py)
- Cache misses fall back gracefully to live API calls
- Corrupt cache entries are logged and purged; they do not crash the app
- TTL expiry is enforced on read, not write (writes always succeed)
Config Layer (src/canvas_tui/config.py)
- Missing config keys use safe defaults rather than crashing
- Schema validation errors are surfaced clearly before the app starts
- Sensitive fields (tokens) are never written to logs
Offline-First Behavior
The app is designed to work offline with a warm cache:
- On first launch, seed the cache from Canvas API responses
- On subsequent launches, serve from cache immediately and refresh in background
- If API is unreachable, fall back to cache and show a stale-data indicator
Dependency Management
- All runtime dependencies are pinned with lower bounds in
pyproject.toml - The
.venvis regenerated frompyproject.tomlon CI; norequirements.txtdrift - New dependencies require a test pass and a human review step
Monitoring & Alerts
| Signal | How it's handled |
|---|---|
| Test failures on PR | CI blocks merge |
| Release workflow failure | GitHub Actions alerts repo owner |
| Stale PR (>14 days) | stale.yml workflow applies close label |
| Cache corruption | Logged and purged; app continues with live API |