Skip to content

Commit 2a41570

Browse files
committed
feature/update_README
1 parent 95bbcc7 commit 2a41570

2 files changed

Lines changed: 79 additions & 8 deletions

File tree

README.md

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,17 @@ api/
3232
├── schemas/
3333
│ ├── user.py # Pydantic: UserCreate, UserUpdate, UserResponse
3434
│ ├── room.py # Pydantic: RoomCreate, RoomUpdate, RoomResponse
35-
│ └── reservation.py # Pydantic: ReservationCreate, ReservationResponse
35+
│ ├── reservation.py # Pydantic: ReservationCreate, ReservationResponse
36+
│ └── seed.py # Pydantic: Seed* request/response models
3637
├── services/
3738
│ ├── user_service.py # User business rules
3839
│ ├── room_service.py # Room business rules
3940
│ └── reservation_service.py # Reservation business rules
4041
└── routers/
4142
├── users.py # /users endpoints
4243
├── rooms.py # /rooms endpoints
43-
└── reservations.py # /reservations endpoints
44+
├── reservations.py # /reservations endpoints
45+
└── seed.py # /seed endpoints (load-test seeding)
4446
```
4547

4648
### Requirements
@@ -130,9 +132,11 @@ Every push and pull request to `master` runs:
130132

131133
1. **Lint**`ruff check .`
132134
2. **Type check**`mypy api/`
133-
3. **Test with coverage**`pytest tests/ -v --cov=api --cov-report=term-missing` against a PostgreSQL 17 service container
135+
3. **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)
134136
4. **Coverage artifact** uploaded on every run
135137

138+
A separate **Performance workflow** (`.github/workflows/performance.yml`) validates the k6 load scripts: it installs a pinned k6 version and runs `k6 inspect` on every script in `tests/performance/load/` 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.
139+
136140
### Interactive docs
137141

138142
- Swagger: [http://localhost:8000/docs](http://localhost:8000/docs)
@@ -166,15 +170,17 @@ api/
166170
├── schemas/
167171
│ ├── user.py # Pydantic: UserCreate, UserUpdate, UserResponse
168172
│ ├── room.py # Pydantic: RoomCreate, RoomUpdate, RoomResponse
169-
│ └── reservation.py # Pydantic: ReservationCreate, ReservationResponse
173+
│ ├── reservation.py # Pydantic: ReservationCreate, ReservationResponse
174+
│ └── seed.py # Pydantic: modelos Seed* de request/response
170175
├── services/
171176
│ ├── user_service.py # Regras de negócio de usuário
172177
│ ├── room_service.py # Regras de negócio de sala
173178
│ └── reservation_service.py # Regras de negócio de reserva
174179
└── routers/
175180
├── users.py # Endpoints /users
176181
├── rooms.py # Endpoints /rooms
177-
└── reservations.py # Endpoints /reservations
182+
├── reservations.py # Endpoints /reservations
183+
└── seed.py # Endpoints /seed (seeding de testes de carga)
178184
```
179185

180186
### Requisitos
@@ -264,9 +270,11 @@ Cada push e pull request para `master` executa:
264270

265271
1. **Lint**`ruff check .`
266272
2. **Type check**`mypy api/`
267-
3. **Testes com cobertura**`pytest tests/ -v --cov=api --cov-report=term-missing` em container PostgreSQL 17
273+
3. **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)
268274
4. **Artefato de cobertura** enviado a cada execução
269275

276+
Um **workflow de performance** separado (`.github/workflows/performance.yml`) valida os scripts de carga k6: instala uma versão k6 fixada e executa `k6 inspect` em cada script em `tests/performance/load/` 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.
277+
270278
### Documentação interativa
271279

272280
- Swagger: [http://localhost:8000/docs](http://localhost:8000/docs)

tests/README.md

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ tests/
1818
│ ├── conftest.py # TestClient fixture + get_db override
1919
│ ├── test_users_api.py
2020
│ ├── test_rooms_api.py
21-
│ └── test_reservations_api.py
21+
│ ├── test_reservations_api.py
22+
│ └── test_main.py # Real app boot smoke test (lifespan + /openapi.json)
2223
├── performance/ # k6 load tests (not pytest) - see "Performance & Load Tests"
2324
│ ├── helpers/
2425
│ │ ├── config.js # BASE_URL, RUN_ID, SCENARIO, DAY_MS, isoDateFromOffset
@@ -46,6 +47,10 @@ tests/
4647
│ ├── test_user_service.py
4748
│ ├── test_room_service.py
4849
│ └── test_reservation_service.py
50+
├── database/ # DB engine/dependency tests (get_db)
51+
│ └── test_database.py
52+
└── main/ # App assembly + lifespan tests
53+
└── test_main.py
4954
```
5055

5156
## How to run
@@ -115,6 +120,8 @@ Shared fixtures auto-discovered by pytest (available in all subdirectories):
115120
| **TestDeleteUser** | `test_delete_user_success` | User is removed (get returns 404) |
116121
| | `test_delete_user_not_found` | Deleting nonexistent user returns 404 |
117122
| | `test_delete_user_with_active_reservations` | Deleting user with active reservations returns 409 |
123+
| **TestSeedUser** | `test_seed_creates_requested_users` | Bulk-seeds N users, returns distinct ids |
124+
| | `test_seed_zero_quantity` | quantity=0 returns [], creates nothing |
118125

119126
### `services/test_room_service.py`
120127

@@ -136,6 +143,8 @@ Shared fixtures auto-discovered by pytest (available in all subdirectories):
136143
| **TestDeleteRoom** | `test_delete_room_soft_delete` | Deactivation is a soft delete (`is_active=False`) |
137144
| | `test_delete_room_not_found` | Deleting nonexistent room returns 404 |
138145
| | `test_delete_room_with_active_reservations` | Deleting room with active reservations returns 409 |
146+
| **TestSeedRoom** | `test_seed_creates_requested_rooms` | Bulk-seeds N rooms, returns distinct ids |
147+
| | `test_seed_zero_quantity` | quantity=0 returns [], creates nothing |
139148

140149
### `services/test_reservation_service.py`
141150

@@ -162,6 +171,10 @@ Shared fixtures auto-discovered by pytest (available in all subdirectories):
162171
| **TestListRoomReservations** | `test_list_room_reservations` | All reservations for a room returned |
163172
| | `test_list_room_reservations_empty` | Room with no reservations returns empty list |
164173
| | `test_list_room_reservations_room_inactive` | Inactive room returns 404 |
174+
| **TestSeedReservation** | `test_seed_creates_requested_reservations` | Seeds N reservations + 100-user/100-room pool, CONFIRMED |
175+
| | `test_seed_returns_ids_in_insert_order` | Returned ids match DB insert order |
176+
| | `test_seed_zero_quantity` | quantity=0 returns [], creates nothing |
177+
| | `test_seed_beyond_pool_size_cycles_pool` | q>100 cycles users/rooms across future days |
165178

166179
## Integration Tests (`integration/`)
167180

@@ -197,6 +210,12 @@ HTTP-layer tests using FastAPI TestClient. Each test verifies status codes, resp
197210
| **TestListUserReservations** | `test_list_user_reservations` | GET /reservations/user/{id} returns all |
198211
| **TestListRoomReservations** | `test_list_room_reservations` | GET /reservations/room/{id} returns all |
199212

213+
### `integration/test_main.py`
214+
215+
| Class | Test | What it verifies |
216+
| --------- | -------------------------- | ------------------------------------------------------------- |
217+
|| `test_app_boots_and_serves` | Real `api.main` app boots via TestClient; `/openapi.json` returns 200 |
218+
200219
## Schema Tests (`schemas/`)
201220

202221
Pure validation tests — no database required. Each class validates Pydantic field validators.
@@ -297,6 +316,31 @@ ORM model tests — validates defaults, constraints, cascades, and enum values.
297316
| **TestUserCascadeDelete** | `test_delete_user_cascades_to_reservations` | Deleting user cascades to reservations |
298317
| **TestRoomCascadeDelete** | `test_delete_room_cascades_to_reservations` | Deleting room cascades to reservations |
299318

319+
## Database Tests (`database/`)
320+
321+
DB engine/dependency tests — exercise the real `get_db` dependency (yield + close) without opening a database connection.
322+
323+
### `database/test_database.py`
324+
325+
| Class | Test | What it verifies |
326+
| --------- | ----------------------------------- | --------------------------------------------------------- |
327+
|| `test_get_db_yields_a_session` | `get_db()` yields exactly one `Session` and closes it on exhaustion |
328+
|| `test_get_db_as_fastapi_dependency` | `get_db` works end-to-end as a FastAPI dependency |
329+
330+
## App Tests (`main/`)
331+
332+
App assembly and `lifespan` tests for `api/main.py` — no real database required.
333+
334+
### `main/test_main.py`
335+
336+
| Class | Test | What it verifies |
337+
| --------- | ---------------------------------------------- | ------------------------------------------------------- |
338+
|| `test_app_includes_base_routers` | `/users`, `/rooms`, `/reservations` routers registered |
339+
|| `test_seed_router_included_when_enabled` | `/seed` router registered when `ENABLE_LOADTEST_ENDPOINTS=true` |
340+
|| `test_seed_router_excluded_when_disabled` | `/seed` router absent when the flag is disabled |
341+
|| `test_lifespan_creates_tables_on_non_postgres` | `create_all` runs on SQLite (non-postgres branch) |
342+
|| `test_lifespan_runs_postgres_ddl` | `CREATE EXTENSION` + gist `EXCLUDE` constraint emitted on a postgres-dialect engine |
343+
300344
## Performance & Load Tests (`performance/load/`)
301345

302346
Load tests are **k6** scripts (not pytest). They target a running server and are designed to explore the app's concurrency limits — specifically the SQLAlchemy connection pool (`pool_size=5`, `max_overflow=10`, i.e. 15 connections) behind the 40-thread anyio pool used for sync endpoints (see README "Known limitations").
@@ -515,7 +559,8 @@ tests/
515559
│ ├── conftest.py # Rótulos/fixtures do TestClient + override do get_db
516560
│ ├── test_users_api.py
517561
│ ├── test_rooms_api.py
518-
│ └── test_reservations_api.py
562+
│ ├── test_reservations_api.py
563+
│ └── test_main.py # Teste smoke de boot do app real (lifespan + /openapi.json)
519564
├── performance/ # Testes de carga k6 (não pytest) - veja "Testes de Performance & Carga"
520565
│ ├── helpers/
521566
│ │ ├── config.js # BASE_URL, RUN_ID, SCENARIO, DAY_MS, isoDateFromOffset
@@ -543,6 +588,10 @@ tests/
543588
│ ├── test_user_service.py
544589
│ ├── test_room_service.py
545590
│ └── test_reservation_service.py
591+
├── database/ # Testes do engine/dependência (get_db)
592+
│ └── test_database.py
593+
└── main/ # Testes de montagem do app + lifespan
594+
└── test_main.py
546595
```
547596

548597
### Como executar
@@ -612,6 +661,8 @@ Fixtures compartilhadas descobertas automaticamente pelo pytest (disponíveis em
612661
| **TestDeleteUser** | `test_delete_user_success` | Usuário é removido (get retorna 404) |
613662
| | `test_delete_user_not_found` | Excluir usuário inexistente retorna 404 |
614663
| | `test_delete_user_with_active_reservations` | Excluir usuário com reservas ativas retorna 409 |
664+
| **TestSeedUser** | `test_seed_creates_requested_users` | Seed em massa de N usuários, retorna ids distintos |
665+
| | `test_seed_zero_quantity` | quantity=0 retorna [], não cria nada |
615666

616667
### `services/test_room_service.py`
617668

@@ -633,6 +684,8 @@ Fixtures compartilhadas descobertas automaticamente pelo pytest (disponíveis em
633684
| **TestDeleteRoom** | `test_delete_room_soft_delete` | Desativação é um soft-delete (`is_active=False`) |
634685
| | `test_delete_room_not_found` | Excluir sala inexistente retorna 404 |
635686
| | `test_delete_room_with_active_reservations` | Excluir sala com reservas ativas retorna 409 |
687+
| **TestSeedRoom** | `test_seed_creates_requested_rooms` | Seed em massa de N salas, retorna ids distintos |
688+
| | `test_seed_zero_quantity` | quantity=0 retorna [], não cria nada |
636689

637690
### `services/test_reservation_service.py`
638691

@@ -659,6 +712,10 @@ Fixtures compartilhadas descobertas automaticamente pelo pytest (disponíveis em
659712
| **TestListRoomReservations** | `test_list_room_reservations` | Todas as reservas de uma sala retornadas |
660713
| | `test_list_room_reservations_empty` | Sala sem reservas retorna lista vazia |
661714
| | `test_list_room_reservations_room_inactive` | Sala inativa retorna 404 |
715+
| **TestSeedReservation** | `test_seed_creates_requested_reservations` | Seed de N reservas + pool de 100 usuários/salas, CONFIRMED |
716+
| | `test_seed_returns_ids_in_insert_order` | Ids retornados coincidem com a ordem de inserção |
717+
| | `test_seed_zero_quantity` | quantity=0 retorna [], não cria nada |
718+
| | `test_seed_beyond_pool_size_cycles_pool` | q>100 cicla usuários/salas por dias futuros |
662719

663720
## Testes de Integração (`integration/`)
664721

@@ -694,6 +751,12 @@ Testes da camada HTTP usando o TestClient do FastAPI. Cada teste verifica códig
694751
| **TestListUserReservations** | `test_list_user_reservations` | GET /reservations/user/{id} retorna todas |
695752
| **TestListRoomReservations** | `test_list_room_reservations` | GET /reservations/room/{id} retorna todas |
696753

754+
### `integration/test_main.py`
755+
756+
| Classe | Teste | O que verifica |
757+
| ---------- | --------------------------- | ----------------------------------------------------------- |
758+
|| `test_app_boots_and_serves` | App real `api.main` inicia via TestClient; `/openapi.json` retorna 200 |
759+
697760
## Testes de Schema (`schemas/`)
698761

699762
Testes puros de validação — nenhum banco de dados é necessário. Cada classe valida os validadores de campo do Pydantic.

0 commit comments

Comments
 (0)