--- description: Rules for writing and running tests globs: - tests/** - webapp/js/**/*.test.js --- # Testing ## Python tests (pytest) ### Configuration - Config in `pyproject.toml` under `[tool.pytest.ini_options]`. - `asyncio_mode = "auto"` — async test functions are detected automatically, no decorator needed. - Coverage: `--cov=duty_teller --cov-fail-under=80`. - Run: `pytest tests/ -v` from project root. ### File layout ``` tests/ ├── conftest.py # Shared fixtures (in-memory DB, sessions, bot app) ├── helpers.py # Test helper functions └── test_*.py # Test modules ``` ### Key fixtures (conftest.py) - `conftest.py` sets `BOT_TOKEN=test-token-for-pytest` if the env var is missing, so tests run without a real token. - Database fixtures use in-memory SQLite for isolation. ### Writing Python tests - File naming: `tests/test_.py` (e.g. `tests/test_import_service.py`). - Function naming: `test__` or `test__`. - Use `session_scope` with the test database URL for DB tests. - Async tests: just use `async def test_...` — `asyncio_mode = "auto"` handles it. - Mock external dependencies (Telegram API, HTTP calls) with `unittest.mock` or `pytest-mock`. ### Example structure ```python import pytest from duty_teller.db.session import session_scope def test_get_or_create_user_creates_new(test_db_url): with session_scope(test_db_url) as session: user = get_or_create_user(session, telegram_user_id=123, full_name="Test") assert user.telegram_user_id == 123 ``` ## JavaScript tests (Vitest) ### Configuration - Config: `webapp/vitest.config.js`. - Environment: `happy-dom` (lightweight DOM implementation). - Test files: `webapp/js/**/*.test.js`. - Run: `npm run test` (from `webapp/`). ### DOM setup Modules that import from `dom.js` expect DOM elements to exist at import time. Use `beforeAll` to set up the required HTML structure before the first import: ```js import { beforeAll, describe, it, expect } from "vitest"; beforeAll(() => { document.body.innerHTML = `

`; }); ``` ### Writing JS tests - File naming: `webapp/js/.test.js` (co-located with the source module). - Test pure functions first (`dateUtils`, `i18n`, `utils`); mock DOM for render tests. - Use `describe` blocks to group by function, `it` blocks for individual cases. ### Example structure ```js import { describe, it, expect } from "vitest"; import { localDateString } from "./dateUtils.js"; describe("localDateString", () => { it("formats date as YYYY-MM-DD", () => { const d = new Date(2025, 0, 15); expect(localDateString(d)).toBe("2025-01-15"); }); }); ```