diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 19c7ea6..79c61cc 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -47,16 +47,14 @@ jobs: PG_PORT: 5432 PG_DATABASE: herbario_test PG_USERNAME: postgres - PG_PASSWORD: testpassword - PG_MIGRATION_USERNAME: postgres - PG_MIGRATION_PASSWORD: testpassword + PG_PASSWORD: secret services: postgres: image: postgis/postgis:18-3.6 env: POSTGRES_DB: herbario_test POSTGRES_USER: postgres - POSTGRES_PASSWORD: testpassword + POSTGRES_PASSWORD: secret ports: - 5432:5432 options: >- @@ -74,9 +72,9 @@ jobs: - name: Install dependencies run: yarn install --frozen-lockfile - name: Apply base schema - run: psql -h $PG_HOST -p $PG_PORT -U $PG_MIGRATION_USERNAME -d $PG_DATABASE -f test/integration/setup/schema.sql + run: psql -h $PG_HOST -p $PG_PORT -U $PG_USERNAME -d $PG_DATABASE -f test/integration/setup/schema.sql env: - PGPASSWORD: ${{ env.PG_MIGRATION_PASSWORD }} + PGPASSWORD: ${{ env.PG_PASSWORD }} - name: Run integration tests run: yarn test:integration diff --git a/compose.integration.yml b/compose.integration.yml index ce3d1f8..ebffc49 100644 --- a/compose.integration.yml +++ b/compose.integration.yml @@ -1,11 +1,11 @@ services: postgres: image: postgis/postgis:18-3.6 - container_name: herbario_postgresql_e2e + container_name: herbario_postgresql_test environment: - POSTGRES_DB: herbario_e2e + POSTGRES_DB: herbario_test POSTGRES_USER: postgres - POSTGRES_PASSWORD: testpassword + POSTGRES_PASSWORD: secret ports: - "5433:5432" volumes: diff --git a/package.json b/package.json index 38a89a2..2f353c7 100644 --- a/package.json +++ b/package.json @@ -19,9 +19,9 @@ "build": "node build.mjs", "test": "vitest --run", "test:unit": "vitest --run --project unit", - "test:unit:watch": "vitest --project unit", + "test:unit:watch": "vitest --project unit", "test:integration": "vitest --run --project integration", - "test:integration:watch": "vitest --project integration", + "test:integration:watch": "vitest --project integration", "test:coverage": "vitest --run --project unit --coverage", "prepare": "husky", "audit": "npm audit --audit-level=moderate", diff --git a/test/integration/README.md b/test/integration/README.md index 37d6634..291d37d 100644 --- a/test/integration/README.md +++ b/test/integration/README.md @@ -1,7 +1,9 @@ # Testes de integração -Os testes de integração rodam contra um banco PostgreSQL real. **Você é responsável por -iniciar o container e aplicar as migrations antes de executar os testes.** +Os testes de integração sobem o Express in-process (via `supertest`) e falam com +um PostgreSQL/PostGIS real. **Você é responsável por iniciar o container antes +de executar os testes.** O schema é aplicado automaticamente na primeira +inicialização do banco (`schema.sql` montado em `docker-entrypoint-initdb.d`). ## Pré-requisitos @@ -16,30 +18,34 @@ iniciar o container e aplicar as migrations antes de executar os testes.** docker compose -f compose.integration.yml up -d ``` -Isso sobe um container PostgreSQL na porta **5433** usando as credenciais do `.env`. +Isso sobe um PostgreSQL na porta **5433** (`herbario_test` / `postgres` / `secret`), +os mesmos valores de `.env.test`. O arquivo Compose já fixa `POSTGRES_*` e a porta; +não depende do `.env` da aplicação. -### 2. Aplique as migrations +### 2. Schema (sem migrations) -```bash -npm run migration:apply -``` +A suíte **não** executa `migration:apply`. Muitos arquivos em +`src/database/migration/` são incrementais, dependem de dados reais ou ainda +usam SQL de MySQL — não dá para replayar o histórico no Postgres de teste. -Isso executa o stack completo de migrations no banco definido no `.env`. Você só precisa -rodar de novo quando novas migrations forem adicionadas. +O contrato é: `schema.sql` é um dump do schema **já migrado**. Quando uma +migration alterar o schema que os testes usam, regenere o dump e commite junto. ## Executando os testes ```bash -npm run test:integration +yarn test:integration ``` -A suíte de testes conecta ao banco já em execução e roda todos os testes -em `test/integration/`. Nenhuma alteração de schema é feita no momento do teste. +A suíte conecta ao banco já em execução e roda os arquivos em +`test/integration/**/*.test.ts`. Nenhuma alteração de schema é feita no momento +do teste. O `globalSetup` apenas verifica a conexão e faz `TRUNCATE` das tabelas +usadas pelos testes. Para o modo watch (reexecuta ao mudar arquivos): ```bash -npm run test:integration:watch +yarn test:integration:watch ``` ## Parando o banco @@ -48,8 +54,8 @@ npm run test:integration:watch docker compose -f compose.integration.yml down ``` -Como o container usa `tmpfs`, todos os dados são perdidos ao parar. Na próxima vez, -comece do zero com `docker compose up -d` seguido de `migration:apply`. +Como o container usa `tmpfs`, todos os dados (e o schema) são perdidos ao parar. +Na próxima vez, comece do zero com `docker compose -f compose.integration.yml up -d`. --- @@ -61,15 +67,6 @@ Cada teste deve ser dono dos seus dados: coluna identificadora (sigla, nome, etc.) que distinga suas linhas das de outros arquivos de teste. - Limpe os dados inseridos em um bloco `finally`, para que a limpeza rode mesmo se a asserção falhar. - **`afterAll`** — chame `knex.destroy()` para liberar o pool de conexões. -- Nunca use `TRUNCATE` — isso apagaria dados de outros arquivos de teste que rodam em paralelo. +- Nunca use `TRUNCATE` no arquivo de teste — isso apagaria dados de outros arquivos que rodam em paralelo. Veja `test/integration/pais/lista-paises.test.ts` para um exemplo concreto. - -### Convenção de namespace - -Use um prefixo curto e único nos identificadores para evitar colisões entre arquivos de teste: - -| Arquivo de teste | Prefixo usado | -|---|---| -| `lista-paises.test.ts` | `XPBR`/`XPAR`/`XPCB` (sigla do país), `XPAI`/`XPCI` (prefixo do nome) | -| `lista-estados.test.ts` | `XEBR` (sigla do país), `XEPR`/`XESP` (sigla do estado) | diff --git a/test/integration/setup/global-setup.ts b/test/integration/setup/global-setup.ts index b184147..b9cb050 100644 --- a/test/integration/setup/global-setup.ts +++ b/test/integration/setup/global-setup.ts @@ -39,10 +39,11 @@ export async function setup(): Promise { try { await knex.raw('SELECT 1') await truncateTables(knex) - } catch { + } catch (error) { + const reason = error instanceof Error ? error.message : String(error) throw new Error( - `Cannot connect to the test database (${PG_HOST}:${PG_PORT}/${PG_DATABASE}). ` - + 'Start the container and apply the schema before running e2e tests — ' + `Cannot prepare the test database (${PG_HOST}:${PG_PORT}/${PG_DATABASE}): ${reason}. ` + + 'Start the container (schema is applied on first boot) — ' + 'see test/integration/README.md' ) } finally { @@ -51,5 +52,5 @@ export async function setup(): Promise { } export async function teardown(): Promise { - // Container lifecycle is managed by the developer — see test/e2e/README.md + // Container lifecycle is managed by the developer — see test/integration/README.md } diff --git a/test/integration/setup/load-env.ts b/test/integration/setup/load-env.ts index b9608d2..a80f021 100644 --- a/test/integration/setup/load-env.ts +++ b/test/integration/setup/load-env.ts @@ -1,4 +1,11 @@ import { mkdirSync } from 'node:fs' import path from 'node:path' +import { loadEnvFile } from 'node:process' + +try { + loadEnvFile('.env.test') +} catch { + // In CI, environment variables are injected directly into the process +} mkdirSync(path.resolve(process.cwd(), 'uploads'), { recursive: true }) diff --git a/test/integration/setup/schema.sql b/test/integration/setup/schema.sql index f6214b4..d0cd624 100644 --- a/test/integration/setup/schema.sql +++ b/test/integration/setup/schema.sql @@ -35,7 +35,7 @@ COMMENT ON SCHEMA public IS ''; -- Name: topology; Type: SCHEMA; Schema: -; Owner: - -- -CREATE SCHEMA topology; +CREATE SCHEMA IF NOT EXISTS topology; --