test: make consolidated workspace self-contained

This commit is contained in:
gti
2026-09-29 15:14:54 -03:00
parent d21a9d1140
commit 016bfc756c
4 changed files with 203 additions and 23 deletions
+17 -22
View File
@@ -1,5 +1,4 @@
"""Missão 6 — Bloco A: Testes de Higiene (Migrations e Inventário dos 9 Datasets).""" """Missão 6 — Bloco A: Testes de Higiene (Migrations e Inventário dos 9 Datasets)."""
import subprocess
from pathlib import Path from pathlib import Path
from alembic.config import Config from alembic.config import Config
from alembic.script import ScriptDirectory from alembic.script import ScriptDirectory
@@ -15,28 +14,24 @@ def test_alembic_master_head_is_single():
assert heads == ["0035_cfop_ippt_write_mode"], f"Esperado head única 0035, obtido: {heads}" assert heads == ["0035_cfop_ippt_write_mode"], f"Esperado head única 0035, obtido: {heads}"
def test_feature_branch_migration_chains_to_0034(): def test_migration_0035_chains_to_0034_and_0033_is_gone():
"""Garante que na branch feature/cfop-ippt-write a migração 0035 encadeia exatamente em 0034.""" """Garante que 0035 encadeia exatamente em 0034 e que a numeração antiga (0033) não existe mais.
# Obter conteúdo de 0035 na branch feature/cfop-ippt-write via git show
result = subprocess.run(
["git", "show", "feature/cfop-ippt-write:central/alembic/versions/0035_cfop_ippt_write_mode.py"],
capture_output=True,
text=True,
cwd=str(ROOT),
)
assert result.returncode == 0, f"Arquivo 0035_cfop_ippt_write_mode.py deve existir na branch feature/cfop-ippt-write: {result.stderr}"
content = result.stdout
assert 'revision = "0035_cfop_ippt_write_mode"' in content
assert 'down_revision = "0034_reference_data_registry"' in content
# Verifica que 0033 não existe mais na branch Historicamente (Missão 8) este teste lia o arquivo via `git show
res_old = subprocess.run( feature/cfop-ippt-write:...` para confirmar a correção *antes* do merge
["git", "show", "feature/cfop-ippt-write:central/alembic/versions/0033_cfop_ippt_write_mode.py"], daquela branch em master. A branch foi mesclada integralmente (0
capture_output=True, commits exclusivos — confirmado na auditoria de consolidação do
text=True, monorepo) e master já carrega o resultado correto diretamente em
cwd=str(ROOT), `central/alembic/versions/`, então a checagem passou a ler o estado
) versionado atual em vez de depender de uma ref Git externa — necessário
assert res_old.returncode != 0, "Migração 0033 não deve mais existir na branch feature" para o teste funcionar também num clone que não tenha essa branch (como
o monorepo). A invariante validada é a mesma de antes.
"""
versions_dir = ROOT / "central" / "alembic" / "versions"
current = (versions_dir / "0035_cfop_ippt_write_mode.py").read_text(encoding="utf-8")
assert 'revision = "0035_cfop_ippt_write_mode"' in current
assert 'down_revision = "0034_reference_data_registry"' in current
assert not (versions_dir / "0033_cfop_ippt_write_mode.py").exists(), "Migração 0033 não deve mais existir"
def test_inventory_9_datasets_coverage(): def test_inventory_9_datasets_coverage():
+30 -1
View File
@@ -61,7 +61,36 @@ a tabela ficar vazia. Login checa primeiro o admin do `.env`, depois
pytest -v pytest -v
``` ```
Os testes usam defaults definidos em `tests/conftest.py` (equivalentes ao `.env.example`) e incluem testes de integração que precisam do container MySQL de dev (`licenciamento-mysql-dev`, `127.0.0.1:3307`) rodando. Os testes usam defaults definidos em `tests/conftest.py` (equivalentes ao `.env.example`) e incluem testes de integração que precisam de um MySQL em `127.0.0.1:3307`.
### Ambiente de teste local (MySQL descartável)
Fora do repositório original (`BLOQUEIO SGV`), este subtree não tem acesso ao container
`licenciamento-mysql-dev` nem ao dump real de clientes — nem deveria, dado que este monorepo
não importa dado real de nenhum cliente. Para rodar a suíte de forma autocontida, este
diretório traz um MySQL de teste **descartável**, separado do `docker-compose.yml` de
produção:
```
docker compose -f docker-compose.test.yml up -d
```
Isso sobe um `mysql:5.7` só com a estrutura das tabelas (`CLIENTE`, `CLIENTE_CONTATO`,
`Clientes_Chaves`, `FUNCIONARIO_PAINEL`, `LOG_PAINEL`) a partir de `tests/db/schema.sql` —
**DDL apenas, nenhum dado de cliente**. Cada tabela do schema.sql tem, em comentário, a fonte
exata de onde a definição foi extraída (docstring do módulo Python correspondente, ou a
documentação de schema do sistema legado). O banco roda em `tmpfs` (nunca grava em disco,
nunca sobrevive a um restart do container) e só escuta em `127.0.0.1:3307` — sem rede externa,
sem volume nomeado, sem credencial real (senha `devpassword123`, válida só para este banco
descartável).
Depois de rodar os testes:
```
docker compose -f docker-compose.test.yml down -v
```
remove o container e a rede — nada persiste.
## Rodando via Docker ## Rodando via Docker
+28
View File
@@ -0,0 +1,28 @@
# MySQL de TESTE, exclusivamente local e descartável, para rodar a suíte
# de integração do painel-clientes-web ("GTI Gerenciador") de forma
# autocontida -- sem depender de nenhum container externo ao monorepo.
#
# NÃO é o compose de produção (esse continua sendo docker-compose.yml,
# inalterado). NÃO expõe rede externa, NÃO usa volume nomeado persistente,
# NÃO tem credencial real -- a senha aqui só existe neste banco descartável.
#
# Uso: ver README.md, seção "Ambiente de teste local (MySQL descartável)".
name: gerenciador-test
services:
mysql-test:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: devpassword123
MYSQL_DATABASE: gtisoftw_clientes_dev
ports:
- "127.0.0.1:3307:3306"
volumes:
- ./tests/db/schema.sql:/docker-entrypoint-initdb.d/schema.sql:ro
tmpfs:
- /var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-uroot", "-pdevpassword123"]
interval: 5s
timeout: 5s
retries: 10
+128
View File
@@ -0,0 +1,128 @@
-- Schema DDL-only para o MySQL de TESTE local do painel-clientes-web ("GTI Gerenciador").
--
-- Nenhum dado real de cliente. Nenhum INSERT. Só estrutura, reconstruída
-- a partir de fontes autoritativas já existentes no próprio código-fonte
-- (nunca inventada) -- a origem de cada tabela está anotada abaixo.
--
-- Uso: ver gerenciador/README.md, seção "Ambiente de teste local (MySQL descartável)".
-- =====================================================================
-- CLIENTE
-- Fonte: licenciamento-n8n/docs/MAPEAMENTO_BANCO.md (CREATE TABLE original,
-- SHOW CREATE TABLE confirmado contra o banco de dev) + coluna ATIVO
-- documentada como ALTER TABLE manual em app/db/clientes.py (docstring do
-- módulo). Ambas as fontes vivem fora deste subtree (repositório original
-- BLOQUEIO SGV) -- lidas apenas como referência de schema, nunca copiadas
-- como dado.
-- =====================================================================
CREATE TABLE CLIENTE (
ID int(10) unsigned NOT NULL AUTO_INCREMENT,
CODIGO varchar(6) NOT NULL,
CNPJ varchar(20) NOT NULL,
RAZAO varchar(60) NOT NULL,
FANTASIA varchar(45) NOT NULL,
IE varchar(20) NOT NULL,
ENDERECO varchar(45) NOT NULL,
CIDADE varchar(45) NOT NULL,
BAIRRO varchar(45) NOT NULL,
UF char(2) NOT NULL,
CONTATO varchar(45) NOT NULL,
TELEFONE varchar(14) NOT NULL,
CELULAR varchar(14) NOT NULL,
CEP varchar(10) NOT NULL,
IMPLANTACAO decimal(15,2) NOT NULL,
MENSALIDADE decimal(15,2) NOT NULL,
JUROS_MORA decimal(15,2) NOT NULL,
JUROS_MULTA decimal(15,2) NOT NULL,
DATA_CONTRATO date NOT NULL,
EMAIL varchar(80) NOT NULL,
OBS varchar(250) DEFAULT NULL,
VENCIMENTO_CONTRATO date NOT NULL,
DIA_VENCIMENTO char(2) NOT NULL,
ATIVO TINYINT(1) NOT NULL DEFAULT 1,
PRIMARY KEY (ID)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
-- =====================================================================
-- CLIENTE_CONTATO
-- Fonte: NÃO existe um CREATE TABLE literal documentado em nenhum arquivo
-- encontrado (docs/CANDIDATO_CLIENTE_CONTATO.md descreve a tabela e cita
-- "ver mensagem anterior" para o DDL exato, que não está em nenhum arquivo
-- do repositório). Reconstruída a partir do uso real e exclusivo da tabela
-- em app/db/contatos.py (todas as colunas referenciadas em SELECT/INSERT/
-- UPDATE: ID, CLIENTE_ID, NOME, TELEFONE, EMAIL) + o comportamento de FK
-- descrito no docstring do mesmo módulo ("CLIENTE_ID FK real, ON DELETE
-- RESTRICT ON UPDATE CASCADE" para CLIENTE.ID). Nenhuma coluna foi
-- inventada além do que o próprio código exige para funcionar.
-- =====================================================================
CREATE TABLE CLIENTE_CONTATO (
ID int(10) unsigned NOT NULL AUTO_INCREMENT,
CLIENTE_ID int(10) unsigned NOT NULL,
NOME varchar(80) DEFAULT NULL,
TELEFONE varchar(20) NOT NULL,
EMAIL varchar(80) DEFAULT NULL,
PRIMARY KEY (ID),
KEY IDX_CLIENTE_CONTATO_CLIENTE_ID (CLIENTE_ID),
CONSTRAINT FK_CLIENTE_CONTATO_CLIENTE FOREIGN KEY (CLIENTE_ID)
REFERENCES CLIENTE (ID) ON DELETE RESTRICT ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
-- =====================================================================
-- Clientes_Chaves (legado, o painel só LÊ, nunca escreve)
-- Fonte: licenciamento-n8n/docs/MAPEAMENTO_BANCO.md (CREATE TABLE original,
-- SHOW CREATE TABLE confirmado contra o banco de dev).
-- =====================================================================
CREATE TABLE Clientes_Chaves (
ID varchar(6) NOT NULL,
M1 varchar(30) NOT NULL,
M2 varchar(30) NOT NULL,
M3 varchar(30) NOT NULL,
M4 varchar(30) NOT NULL,
M5 varchar(30) NOT NULL,
M6 varchar(30) NOT NULL,
M7 varchar(30) NOT NULL,
M8 varchar(30) NOT NULL,
M9 varchar(30) NOT NULL,
M10 varchar(30) NOT NULL,
M11 varchar(30) NOT NULL,
M12 varchar(30) NOT NULL,
Cliente varchar(40) NOT NULL,
Prorrogacao date NOT NULL,
PRIMARY KEY (ID)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
-- =====================================================================
-- FUNCIONARIO_PAINEL
-- Fonte: CREATE TABLE + ALTER TABLE (coluna PERFIL) documentados
-- integralmente no docstring de app/db/funcionarios.py (dentro deste
-- mesmo subtree, gerenciador/), já com a coluna PERFIL incorporada.
-- =====================================================================
CREATE TABLE FUNCIONARIO_PAINEL (
ID INT UNSIGNED NOT NULL AUTO_INCREMENT,
NOME VARCHAR(80) NOT NULL,
LOGIN VARCHAR(40) NOT NULL,
SENHA_HASH VARCHAR(255) NOT NULL,
PERFIL ENUM('ADMIN','PADRAO') NOT NULL DEFAULT 'ADMIN',
ATIVO TINYINT(1) NOT NULL DEFAULT 1,
CRIADO_EM DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (ID),
UNIQUE KEY UQ_FUNCIONARIO_PAINEL_LOGIN (LOGIN)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
-- =====================================================================
-- LOG_PAINEL
-- Fonte: CREATE TABLE documentado integralmente no docstring de
-- app/db/log.py (dentro deste mesmo subtree, gerenciador/).
-- =====================================================================
CREATE TABLE LOG_PAINEL (
ID INT UNSIGNED NOT NULL AUTO_INCREMENT,
CRIADO_EM DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
USUARIO VARCHAR(60) NOT NULL,
ACAO VARCHAR(40) NOT NULL,
ENTIDADE VARCHAR(20) NOT NULL,
ENTIDADE_ID VARCHAR(20) DEFAULT NULL,
SUCESSO TINYINT(1) NOT NULL,
DETALHE VARCHAR(500) DEFAULT NULL,
PRIMARY KEY (ID),
KEY IDX_LOG_PAINEL_CRIADO_EM (CRIADO_EM)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;