Skip to content

Commit 0ddc806

Browse files
committed
tests: add integration tests
1 parent c052cfd commit 0ddc806

14 files changed

Lines changed: 224 additions & 16 deletions

File tree

CLAUDE.md

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ The application is organized in a modular monolith with clear separation:
1212
- `app.db.session`: Database session management
1313
- `app.modules.orders`: Complete order module with models, schemas, repository, service, routes
1414
- `alembic`: Database migration tool
15-
- `tests`: Unit tests (SQLite in-memory)
15+
- `tests/unit`: Unit tests (SQLite in-memory)
16+
- `tests/integration`: Integration tests (PostgreSQL)
1617

1718
## Key Development Commands
1819

@@ -24,10 +25,21 @@ source .venv/bin/activate # Windows: .venv\Scripts\Activate.ps1
2425

2526
# Install dependencies
2627
pip install --upgrade pip
27-
pip install -e .[dev]
28-
29-
# Run API
30-
uvicorn app.main:app --reload
28+
pip install -e '.[dev]'
29+
30+
# Run fresh database
31+
docker stop postgres-dev || true
32+
docker rm postgres-dev || true
33+
docker run -d \
34+
--name postgres-dev \
35+
-e POSTGRES_USER=postgres \
36+
-e POSTGRES_PASSWORD=postgres \
37+
-e POSTGRES_DB=app \
38+
-p 5432:5432 \
39+
postgres:15
40+
41+
# Run API in background
42+
uvicorn app.main:app --reload &
3143
```
3244

3345
**Testing**

README.md

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Alembic for migrations, and SQLite for fast unit tests.
1313
│ ├── api/
1414
│ │ └── router.py
1515
│ ├── core/
16+
│ │ └── config.py
1617
│ ├── db/
1718
│ │ └── session.py
1819
│ └── modules/
@@ -28,10 +29,10 @@ Alembic for migrations, and SQLite for fast unit tests.
2829
│ ├── repository.py
2930
│ ├── service.py
3031
│ └── routes.py
31-
3232
├── alembic/
3333
├── tests/
34-
│ └── unit/
34+
│ ├── unit/
35+
│ └── integration/
3536
├── pyproject.toml
3637
└── README.md
3738
```
@@ -69,9 +70,11 @@ pip install -e '.[dev]'
6970

7071
## 🐘 Running PostgreSQL (Docker)
7172

72-
Start a local PostgreSQL instance:
73+
Start a fresh local PostgreSQL instance:
7374

7475
```bash
76+
docker stop postgres-dev || true
77+
docker rm postgres-dev || true
7578
docker run -d \
7679
--name postgres-dev \
7780
-e POSTGRES_USER=postgres \
@@ -123,10 +126,20 @@ http://localhost:8000/docs
123126

124127
## 🧪 Running Tests
125128

126-
Tests use **SQLite in-memory**, so no database setup is required.
129+
### Unit Tests
130+
131+
> Uses SQLite in-memory, so no database setup is required.
132+
133+
```bash
134+
pytest tests/unit
135+
```
136+
137+
### Integration Tests
138+
139+
> Fresh PostgreSQL instance with migrations applied is required.
127140
128141
```bash
129-
pytest
142+
pytest tests/integration
130143
```
131144

132145
## 🔌 Example API Usage

alembic/env.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,25 @@
11
from logging.config import fileConfig
2-
import os
3-
42
from sqlalchemy import engine_from_config, pool
53
from alembic import context
6-
74
from app.db.session import Base
8-
from app.modules.orders import models # important
5+
import os
6+
import pkgutil
7+
import importlib
8+
9+
# Dynamically import all 'models' submodules in 'app.modules'
10+
def import_all_models():
11+
import app.modules as modules_pkg
12+
# walk_packages looks into subdirectories of app.modules
13+
for loader, module_name, is_pkg in pkgutil.walk_packages(
14+
modules_pkg.__path__, modules_pkg.__name__ + "."
15+
):
16+
# Only import if the submodule ends with '.models'
17+
if module_name.endswith(".models"):
18+
importlib.import_module(module_name)
19+
import_all_models()
920

1021
config = context.config
11-
1222
fileConfig(config.config_file_name)
13-
1423
target_metadata = Base.metadata
1524

1625

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
"""add users
2+
3+
Revision ID: 084d9bd91fca
4+
Revises: 91a829a056ca
5+
Create Date: 2026-04-24 09:32:08.216154
6+
7+
"""
8+
from typing import Sequence, Union
9+
10+
from alembic import op
11+
import sqlalchemy as sa
12+
13+
14+
# revision identifiers, used by Alembic.
15+
revision: str = '084d9bd91fca'
16+
down_revision: Union[str, Sequence[str], None] = '91a829a056ca'
17+
branch_labels: Union[str, Sequence[str], None] = None
18+
depends_on: Union[str, Sequence[str], None] = None
19+
20+
21+
def upgrade() -> None:
22+
"""Upgrade schema."""
23+
# ### commands auto generated by Alembic - please adjust! ###
24+
op.create_table('users',
25+
sa.Column('id', sa.Integer(), nullable=False),
26+
sa.Column('username', sa.String(), nullable=False),
27+
sa.Column('hashed_password', sa.String(), nullable=False),
28+
sa.PrimaryKeyConstraint('id')
29+
)
30+
op.create_index(op.f('ix_users_id'), 'users', ['id'], unique=False)
31+
op.create_index(op.f('ix_users_username'), 'users', ['username'], unique=True)
32+
# ### end Alembic commands ###
33+
34+
35+
def downgrade() -> None:
36+
"""Downgrade schema."""
37+
# ### commands auto generated by Alembic - please adjust! ###
38+
op.drop_index(op.f('ix_users_username'), table_name='users')
39+
op.drop_index(op.f('ix_users_id'), table_name='users')
40+
op.drop_table('users')
41+
# ### end Alembic commands ###

app/__init__.py

Whitespace-only changes.

app/api/__init__.py

Whitespace-only changes.

app/core/__init__.py

Whitespace-only changes.

app/db/__init__.py

Whitespace-only changes.

app/modules/__init__.py

Whitespace-only changes.

app/modules/orders/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)