Skip to content

Commit e6087f4

Browse files
authored
Merge pull request #44 from TheDeveloperDen/feature/create-python-tests
Created Python Tests for the core functionalities
2 parents 6800ebd + 19f270e commit e6087f4

38 files changed

+1053
-65
lines changed

.github/workflows/test-backend.yml

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
name: Backend Tests
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
pull_request:
8+
branches:
9+
- master
10+
11+
jobs:
12+
test:
13+
runs-on: ubuntu-latest
14+
services:
15+
postgres:
16+
image: postgres:16
17+
env:
18+
POSTGRES_USER: postgres
19+
POSTGRES_PASSWORD: postgres
20+
POSTGRES_DB: devbin_test
21+
ports:
22+
- 5433:5432
23+
options: >-
24+
--health-cmd "pg_isready -U postgres"
25+
--health-interval 5s
26+
--health-timeout 5s
27+
--health-retries 5
28+
29+
steps:
30+
- name: Checkout repository
31+
uses: actions/checkout@v4
32+
33+
- name: Set up Python
34+
uses: actions/setup-python@v5
35+
with:
36+
python-version: '3.13'
37+
38+
- name: Install uv
39+
uses: astral-sh/setup-uv@v4
40+
with:
41+
version: "latest"
42+
43+
- name: Install dependencies
44+
run: uv sync --extra test --extra migration
45+
46+
- name: Run tests
47+
env:
48+
APP_DATABASE_URL: postgresql+asyncpg://postgres:postgres@localhost:5433/devbin_test
49+
APP_BASE_FOLDER_PATH: /tmp/devbin_test_files
50+
APP_DEBUG: "true"
51+
APP_CORS_DOMAINS: '["http://test"]'
52+
APP_ALLOW_CORS_WILDCARD: "false"
53+
run: uv run pytest

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,22 @@ APP_RELOAD=true
140140
APP_SQLALCHEMY_ECHO=true
141141
```
142142

143+
### Tests
144+
145+
### Backend Tests
146+
147+
Run tests with:
148+
149+
```bash
150+
cd backend
151+
uv run pytest
152+
```
153+
154+
> If you modified any of the core API endpoints, make sure to run the tests to ensure they still work as expected. Or if
155+
> you are committing breaking changes, please adjust them and add a note why this breaking change is necessary.
156+
157+
See [Testing](/backend/TESTING.md)
158+
143159
### API Endpoints
144160

145161
- `GET /health` – Health check

Taskfile.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,15 @@ tasks:
1111
env:
1212
APP_DEBUG: true
1313
cmd: docker compose --profile dev_backend up -d --build
14+
dev:down:
15+
cmds:
16+
- docker compose --profile dev_frontend down
17+
- docker compose --profile dev_backend down
18+
- docker compose --profile dev_db down
19+
dev:reset:
20+
cmds:
21+
- docker compose --profile dev_frontend down
22+
- docker compose --profile dev_backend down --volumes
23+
- docker compose --profile dev_db down --volumes
24+
- docker compose --profile dev_frontend up -d --build
25+
- docker compose --profile dev_backend up -d --build

backend/.coverage

52 KB
Binary file not shown.

backend/TESTING.md

Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
# Testing Guide
2+
3+
This guide explains how to run tests locally and in CI/CD.
4+
5+
## Quick Start
6+
7+
### 1. Install Test Dependencies
8+
9+
```bash
10+
uv sync --extra test
11+
```
12+
13+
### 2. Start Test Database
14+
15+
```bash
16+
# Start PostgreSQL test database in Docker
17+
./scripts/setup_test_db.sh
18+
```
19+
20+
This will:
21+
22+
- Start PostgreSQL 16 in Docker on port 5433
23+
- Create the `devbin_test` database
24+
- Wait for the database to be ready
25+
26+
### 3. Run Tests
27+
28+
```bash
29+
# Run all tests
30+
uv run pytest
31+
32+
# Run only unit tests (no database required)
33+
uv run pytest tests/unit/ -v
34+
35+
# Run with coverage report
36+
uv run pytest --cov=app --cov-report=html
37+
38+
# Run in parallel (faster)
39+
uv run pytest -n auto
40+
41+
# Run specific test file
42+
uv run pytest tests/unit/test_token_utils.py -v
43+
```
44+
45+
## Test Structure
46+
47+
```
48+
tests/
49+
├── unit/ # Fast tests, no external dependencies
50+
├── integration/ # Tests with database and file system
51+
├── api/ # Full API endpoint tests
52+
└── security/ # Security-focused tests
53+
```
54+
55+
## Local Development
56+
57+
### Managing Test Database
58+
59+
```bash
60+
# Start test database
61+
docker-compose -f docker-compose.test.yml up -d
62+
63+
# Stop test database
64+
docker-compose -f docker-compose.test.yml down
65+
66+
# Clean up database and volumes
67+
docker-compose -f docker-compose.test.yml down -v
68+
69+
# View logs
70+
docker-compose -f docker-compose.test.yml logs -f
71+
```
72+
73+
### Test Database Connection
74+
75+
- **Host**: localhost
76+
- **Port**: 5433 (to avoid conflicts with dev database on 5432)
77+
- **Database**: devbin_test
78+
- **User**: postgres
79+
- **Password**: postgres
80+
- **Connection String**: `postgresql://postgres:postgres@localhost:5433/devbin_test`
81+
82+
### Environment Variables
83+
84+
Tests use environment variables from `pytest.ini` by default:
85+
86+
```ini
87+
APP_DATABASE_URL = postgresql+asyncpg://postgres:postgres@localhost:5433/devbin_test
88+
APP_BASE_FOLDER_PATH = /tmp/devbin_test_files
89+
APP_DEBUG = true
90+
APP_ALLOW_CORS_WILDCARD = true
91+
```
92+
93+
You can override these by setting environment variables before running tests:
94+
95+
```bash
96+
export APP_DATABASE_URL=postgresql+asyncpg://postgres:postgres@localhost:5432/my_test_db
97+
uv run pytest
98+
```
99+
100+
## CI/CD Setup
101+
102+
### GitHub Actions
103+
104+
The test database is automatically configured in `.github/workflows/test.yml`:
105+
106+
```yaml
107+
services:
108+
postgres:
109+
image: postgres:16
110+
env:
111+
POSTGRES_USER: postgres
112+
POSTGRES_PASSWORD: postgres
113+
POSTGRES_DB: devbin_test
114+
ports:
115+
- 5432:5432
116+
```
117+
118+
In CI, tests use port 5432 (the default PostgreSQL port in the service container).
119+
120+
### Running Tests in CI
121+
122+
```bash
123+
# CI sets APP_DATABASE_URL to use the service container
124+
pytest -v --cov=app --cov-report=xml
125+
coverage report --fail-under=80
126+
```
127+
128+
## Test Categories
129+
130+
### Unit Tests (Fast, Isolated)
131+
132+
```bash
133+
pytest tests/unit/ -m unit
134+
```
135+
136+
- No database or file system
137+
- Mock external dependencies
138+
- < 1ms per test
139+
- Test utilities, validators, pure functions
140+
141+
### Integration Tests
142+
143+
```bash
144+
pytest tests/integration/ -m integration
145+
```
146+
147+
- Real database with transaction rollback
148+
- File system operations (temp directories)
149+
- 10-100ms per test
150+
- Test service layer
151+
152+
### API Tests
153+
154+
```bash
155+
pytest tests/api/
156+
```
157+
158+
- Full HTTP request/response cycle
159+
- All middleware included
160+
- 50-200ms per test
161+
- Test endpoints, rate limiting, caching
162+
163+
## Coverage Reports
164+
165+
### View HTML Coverage Report
166+
167+
```bash
168+
uv run pytest --cov=app --cov-report=html
169+
open htmlcov/index.html # or xdg-open on Linux
170+
```
171+
172+
### Coverage Targets
173+
174+
- Overall: 80%+ (enforced in CI)
175+
- Critical modules: 90%+
176+
- `app/services/paste_service.py`
177+
- `app/utils/token_utils.py`
178+
- `app/api/subroutes/pastes.py`
179+
180+
## Troubleshooting
181+
182+
### Database Connection Errors
183+
184+
**Problem**: `connection refused` or `could not connect to server`
185+
186+
**Solution**:
187+
188+
1. Check if test database is running: `docker ps | grep devbin_test`
189+
2. Start database: `./scripts/setup_test_db.sh`
190+
3. Check database logs: `docker-compose -f docker-compose.test.yml logs`
191+
192+
### Port Already in Use
193+
194+
**Problem**: Port 5433 is already in use
195+
196+
**Solution**:
197+
198+
1. Change port in `docker-compose.test.yml`
199+
2. Update `pytest.ini` to match
200+
3. Restart database
201+
202+
### Tests Fail Randomly
203+
204+
**Problem**: Tests pass sometimes, fail other times (flaky tests)
205+
206+
**Solution**:
207+
208+
1. Check if tests are properly isolated (no shared state)
209+
2. Verify database cleanup between tests
210+
3. Check for timing issues (use `freezegun` for time-based tests)
211+
212+
### Slow Tests
213+
214+
**Problem**: Tests take too long to run
215+
216+
**Solution**:
217+
218+
1. Run tests in parallel: `pytest -n auto`
219+
2. Run only unit tests: `pytest tests/unit/`
220+
3. Run specific test file instead of entire suite
221+
4. Check for N+1 query issues in integration tests
222+
223+
## Best Practices
224+
225+
### Writing New Tests
226+
227+
1. **Use descriptive names**: `test_create_paste_with_valid_data_returns_200`
228+
2. **Test one thing**: Each test should verify one specific behavior
229+
3. **Use fixtures**: Reuse common setup via pytest fixtures
230+
4. **Clean up**: Tests should not leave artifacts (files, DB records)
231+
5. **Mark tests**: Use `@pytest.mark.unit` or `@pytest.mark.integration`
232+
233+
### Test Data
234+
235+
- Use `faker` for realistic test data (IPs, user agents, names)
236+
- Use `sample_paste_data` fixture for consistent paste creation
237+
- Create factory functions for complex test objects
238+
239+
### Mocking
240+
241+
- **Mock external services**: Time, disk usage checks, network calls
242+
- **Use real implementations**: Database, file system (with temp dirs)
243+
- **Mock sparingly**: Real implementations catch more bugs
244+
245+
## Continuous Integration
246+
247+
Tests run automatically on:
248+
249+
- Push to `master` or `develop`
250+
- Pull requests
251+
252+
### CI Requirements
253+
254+
- All tests must pass
255+
- Coverage must not decrease
256+
- Coverage must be >= 80%
257+
- Linting must pass (ruff)
258+
259+
### Viewing CI Results
260+
261+
1. Go to GitHub Actions tab
262+
2. Click on the latest workflow run
263+
3. View test results and coverage report

backend/Taskfile.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,17 @@
33
version: '3'
44

55
tasks:
6+
api:setup_test:
7+
cmds:
8+
- uv sync --extra test
9+
- ./scripts/setup_test_db.sh
610
api:start:
711
env:
812
APP_DEBUG: true
913
cmd: uv run main.py
1014
api:migrate:
1115
cmd: alembic upgrade head
1216
api:downmigrate:
13-
cmd: alembic downgrade -1
17+
cmd: alembic downgrade -1
18+
api:test:
19+
cmd: uv run pytest

backend/alembic/env.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import os
22
from logging.config import fileConfig
33

4+
from alembic import context
45
from sqlalchemy import engine_from_config, pool
56

6-
from alembic import context
7-
from app.db.base import Base
87
from app.db.models import *
98

109
# this is the Alembic Config object, which provides

0 commit comments

Comments
 (0)