Room reservation management / Gerenciamento de reservas de salas
REST API for room, user and reservation management. Built with FastAPI, SQLAlchemy and PostgreSQL. This project provides a deliberately simple REST API that serves as the system under test for a dedicated performance-testing suite built with k6.
- Rooms — Full CRUD with soft-delete
- Users — Full CRUD with email and phone validation
- Reservations — Create with date conflict check, cancel, list by user and by room
api/
├── main.py # FastAPI app, lifespan with create_all
├── config.py # Config (DB_* env vars)
├── database.py # SQLAlchemy engine, session, declarative base
├── models/
│ ├── user.py # User model
│ ├── room.py # Room model
│ └── reservation.py # Reservation model + ReservationStatus enum
├── schemas/
│ ├── user.py # Pydantic: UserCreate, UserUpdate, UserResponse
│ ├── room.py # Pydantic: RoomCreate, RoomUpdate, RoomResponse
│ ├── reservation.py # Pydantic: ReservationCreate, ReservationResponse
│ └── seed.py # Pydantic: Seed* request/response models
├── services/
│ ├── user_service.py # User business rules
│ ├── room_service.py # Room business rules
│ └── reservation_service.py # Reservation business rules
└── routers/
├── users.py # /users endpoints
├── rooms.py # /rooms endpoints
├── reservations.py # /reservations endpoints
└── seed.py # /seed endpoints (load-test seeding)
- Python >= 3.14
- Running PostgreSQL
- Managed with uv
- Clone the repository.
- Copy the environment file and edit with your credentials:
cp .env.example .env
- Create the database in PostgreSQL:
CREATE DATABASE performancelab;- Install dependencies:
uv sync
- Run:
uv run uvicorn api.main:app --reload --timeout-keep-alive 30
Tables are created automatically on startup via Base.metadata.create_all.
| Method | Route | Description |
|---|---|---|
POST |
/users/ |
Create user |
GET |
/users/ |
List users |
GET |
/users/{id} |
Get user |
PUT |
/users/{id} |
Update user |
DELETE |
/users/{id} |
Delete user |
POST |
/rooms/ |
Create room |
GET |
/rooms/ |
List rooms |
GET |
/rooms/{id} |
Get room |
PUT |
/rooms/{id} |
Update room |
DELETE |
/rooms/{id} |
Deactivate room |
POST |
/reservations/ |
Create reservation |
PATCH |
/reservations/{id}/cancel |
Cancel reservation |
GET |
/reservations/user/{user_id} |
List user reservations |
GET |
/reservations/room/{room_id} |
List room reservations |
- User email must be unique.
- Room name must be unique.
- Capacity must be >= 1.
- Price must be > 0.
- check_in must be today or in the future.
- check_out must be after check_in.
- A room cannot have overlapping reservations (status = confirmed).
- A reservation can only be cancelled if its status is confirmed.
- A user can only be deleted if they have no active reservations.
- A room can only be deactivated if it has no active reservations.
- Known performance constraint (intentional). The API uses SQLAlchemy's default connection pool (
pool_size=5,max_overflow=10) and syncdefendpoints, so FastAPI runs them on anyio's threadpool (capped at 40 threads). Under concurrent load (e.g. 50 VUs), requests queue on both the threadpool and the connection pool, causing latency to climb regardless of endpoint. This constraint is deliberately left in place so that the load tests intests/performance/load/can observe how the system behaves as concurrency approaches and exceeds the available database and thread capacity. Fixes to consider: raisepool_size/max_overflow(ideally env-driven), enablepool_pre_ping, or migrate to an async engine andasync defendpoints.
Functional tests are in tests/ (unit tests in tests/unit/ and integration tests in tests/integration/). Performance tests are maintained separately under tests/performance/ and use k6 against a running API. Run all with:
uv run pytest tests/ -v
See tests/README.md for a full description of every test.
Every push and pull request to master runs:
- Lint —
ruff check . - Type check —
mypy api/ - Test with coverage —
pytest tests/ -v --cov=api --cov-report=term-missing(the suite runs against in-memory SQLite; a PostgreSQL 17 service container is provisioned in the job) - Coverage artifact uploaded on every run
A separate Performance workflow (.github/workflows/performance.yml) validates the k6 load scripts on every push and pull request. A manual smoke run against a live API can be triggered via workflow_dispatch, with tunable p95-ms and error-rate thresholds. See tests/README.md for details.
- Swagger: http://localhost:8000/docs
- ReDoc: http://localhost:8000/redoc
API REST para gerenciamento de salas, usuários e reservas. Desenvolvida com FastAPI, SQLAlchemy e PostgreSQL. Este projeto fornece uma API REST deliberadamente simples que funciona como sistema sob teste para uma suíte dedicada de testes de performance construída com k6.
- Salas — CRUD completo com soft-delete (desativação)
- Usuários — CRUD completo com validação de e-mail e telefone
- Reservas — Criação com verificação de conflito de datas, cancelamento, listagem por usuário e por sala
api/
├── main.py # App FastAPI, lifespan com create_all
├── config.py # Config (DB_* via variáveis de ambiente)
├── database.py # Engine SQLAlchemy, Sessão, Base declarativa
├── models/
│ ├── user.py # Modelo User
│ ├── room.py # Modelo Room
│ └── reservation.py # Modelo Reservation + enum ReservationStatus
├── schemas/
│ ├── user.py # Pydantic: UserCreate, UserUpdate, UserResponse
│ ├── room.py # Pydantic: RoomCreate, RoomUpdate, RoomResponse
│ ├── reservation.py # Pydantic: ReservationCreate, ReservationResponse
│ └── seed.py # Pydantic: modelos Seed* de request/response
├── services/
│ ├── user_service.py # Regras de negócio de usuário
│ ├── room_service.py # Regras de negócio de sala
│ └── reservation_service.py # Regras de negócio de reserva
└── routers/
├── users.py # Endpoints /users
├── rooms.py # Endpoints /rooms
├── reservations.py # Endpoints /reservations
└── seed.py # Endpoints /seed (seeding de testes de carga)
- Python >= 3.14
- PostgreSQL rodando
- Gerenciado com uv
- Clone o repositório.
- Copie o arquivo de ambiente e edite com suas credenciais:
cp .env.example .env
- Crie o banco de dados no PostgreSQL:
CREATE DATABASE performancelab;- Instale as dependências:
uv sync
- Execute:
uv run uvicorn api.main:app --reload --timeout-keep-alive 30
As tabelas são criadas automaticamente na inicialização via Base.metadata.create_all.
| Método | Rota | Descrição |
|---|---|---|
POST |
/users/ |
Criar usuário |
GET |
/users/ |
Listar usuários |
GET |
/users/{id} |
Obter usuário |
PUT |
/users/{id} |
Atualizar usuário |
DELETE |
/users/{id} |
Excluir usuário |
POST |
/rooms/ |
Criar sala |
GET |
/rooms/ |
Listar salas |
GET |
/rooms/{id} |
Obter sala |
PUT |
/rooms/{id} |
Atualizar sala |
DELETE |
/rooms/{id} |
Desativar sala |
POST |
/reservations/ |
Criar reserva |
PATCH |
/reservations/{id}/cancel |
Cancelar reserva |
GET |
/reservations/user/{user_id} |
Reservas de um usuário |
GET |
/reservations/room/{room_id} |
Reservas de uma sala |
- E-mail do usuário deve ser único.
- Nome da sala deve ser único.
- Capacidade da sala deve ser >= 1.
- Preço da sala deve ser > 0.
- check_in deve ser hoje ou no futuro.
- check_out deve ser posterior a check_in.
- Uma sala não pode ter reservas com datas sobrepostas (status = confirmed).
- Uma reserva só pode ser cancelada se estiver com status confirmed.
- Um usuário só pode ser excluído se não tiver reservas ativas.
- Uma sala só pode ser desativada se não tiver reservas ativas.
- Restrição de performance conhecida (intencional). A API usa o pool de conexões padrão do SQLAlchemy (
pool_size=5,max_overflow=10) e endpointsdefsíncronos, então o FastAPI os executa no threadpool do anyio (limitado a 40 threads). Sob carga concorrente (ex.: 50 VUs), as requisições ficam enfileiradas tanto no threadpool quanto no pool de conexões, fazendo a latência aumentar independentemente do endpoint. Essa restrição foi deixada de forma intencional para que os testes de carga emtests/performance/load/observem como o sistema se comporta quando a concorrência se aproxima e excede a capacidade disponível de banco de dados e threads. Correções a considerar: aumentarpool_size/max_overflow(idealmente via variáveis de ambiente), habilitarpool_pre_ping, ou migrar para engine assíncrono e endpointsasync def.
Os testes funcionais estão em tests/ (testes unitários em tests/unit/ e testes de integração em tests/integration/). Os testes de performance ficam separados em tests/performance/ e usam k6 contra uma API em execução. Execute todos com:
uv run pytest tests/ -v
Consulte tests/README.md para a descrição completa de cada teste.
Cada push e pull request para master executa:
- Lint —
ruff check . - Type check —
mypy api/ - Testes com cobertura —
pytest tests/ -v --cov=api --cov-report=term-missing(a suíte roda sobre SQLite em memória; um container de serviço PostgreSQL 17 é provisionado no job) - Artefato de cobertura enviado a cada execução
Um workflow de performance separado (.github/workflows/performance.yml) valida os scripts de carga k6 a cada push e pull request. Uma execução smoke manual contra uma API ativa pode ser disparada via workflow_dispatch, com limites p95-ms e error-rate configuráveis. Veja tests/README.md para detalhes.
- Swagger: http://localhost:8000/docs
- ReDoc: http://localhost:8000/redoc