Files
duty-teller/.cursor/rules/testing.mdc
Nikolay Tatarinov 37d4226beb chore: update project documentation and configuration files
- Added AGENTS.md for AI agent documentation and maintainers, outlining project structure and conventions.
- Updated CONTRIBUTING.md to specify that all project documentation must be in English, including README and docstrings.
- Enhanced README.md to reference documentation guidelines and the new AGENTS.md file.
- Cleaned up .gitignore by removing unnecessary entries for cursor-related files.
- Introduced new .cursor rules for backend, frontend, project architecture, and testing to standardize development practices.
2026-03-02 20:02:20 +03:00

104 lines
2.9 KiB
Plaintext

---
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_<module>.py` (e.g. `tests/test_import_service.py`).
- Function naming: `test_<what>_<scenario>` or `test_<what>_<expected_result>`.
- 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 = `
<div id="calendar"></div>
<h2 id="monthTitle"></h2>
<button id="prevBtn"></button>
<button id="nextBtn"></button>
<div id="dutyList"></div>
<div id="dayDetail"></div>
<div id="accessDenied" class="hidden"></div>
<div id="errorBanner" class="hidden"></div>
`;
});
```
### Writing JS tests
- File naming: `webapp/js/<module>.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");
});
});
```