Skip to content

test: adiciona testes estruturais e de integridade referencial em emp…#400

Open
renannOgomes wants to merge 1 commit into
GovHub-br:mainfrom
renannOgomes:test/empenhos-ted-testes-estruturais
Open

test: adiciona testes estruturais e de integridade referencial em emp…#400
renannOgomes wants to merge 1 commit into
GovHub-br:mainfrom
renannOgomes:test/empenhos-ted-testes-estruturais

Conversation

@renannOgomes

Copy link
Copy Markdown

Descrição

Adiciona testes de qualidade de dados (dbt) aos modelos de empenhos e TEDs do projeto mir, no domínio empenhos_ted_dbt. O objetivo é garantir, de forma automatizada, a unicidade das chaves, a não-nulidade de campos essenciais e a integridade referencial. Antes desta mudança, esses modelos só tinham testes de tipagem (verificacao_tipagem), sem validação estrutural nem de relacionamento.

As alterações são apenas nos schema.yml (bronze, silver e gold) — nenhum modelo SQL foi modificado.

Tipo de mudança

  • Nova funcionalidade / pipeline
  • Correção de bug ou inconsistência de dados
  • Refatoração de modelo DBT
  • Documentação
  • Infraestrutura / CI
  • Outro: Testes DBT (qualidade de dados)

Issues relacionadas

Closes #300

Como testar / validar

Ambiente: dbt 1.7.13 (postgres), projeto mir, seleção restrita a empenhos_ted_dbt
com --indirect-selection cautious e --exclude test_name:verificacao_tipagem.

Cada teste dbt compila para um SELECT de violações: 0 linhas = PASS, ≥1 linha = FAIL.


1) Camada BRONZE

1a) Dados válidos → todos passam

1 of 10 OK created sql table model siafi_dbt.empenhos_tesouro_ted ... [SELECT 2]
2 of 10 OK created sql table model siafi_dbt.planos_acao_ted ........ [SELECT 2]
3 of 10 OK created sql table model siafi_dbt.programas_ted ......... [SELECT 2]
PASS not_null_empenhos_tesouro_ted_ne_ccor
PASS not_null_planos_acao_ted_id_plano_acao
PASS not_null_planos_acao_ted_id_programa
PASS unique_planos_acao_ted_id_plano_acao
PASS not_null_programas_ted_id_programa
PASS relationships_planos_acao_ted_id_programa__id_programa__ref_programas_ted_
PASS unique_programas_ted_id_programa
Done. PASS=10 WARN=0 ERROR=0 SKIP=0 TOTAL=10

1b) Com violações → testes detectam (FAIL)

Violações inseridas: id_programa duplicado; id_plano_acao nulo; id_programa = 999
inexistente em programas_ted; ne_ccor nulo.

FAIL 1 not_null_empenhos_tesouro_ted_ne_ccor
FAIL 1 not_null_planos_acao_ted_id_plano_acao
FAIL 1 relationships_planos_acao_ted_id_programa__id_programa__ref_programas_ted_
FAIL 1 unique_programas_ted_id_programa
PASS  not_null_planos_acao_ted_id_programa
PASS  unique_planos_acao_ted_id_plano_acao
PASS  not_null_programas_ted_id_programa
Done. PASS=6 WARN=0 ERROR=4 SKIP=0 TOTAL=10

2) Camadas SILVER e GOLD

2a) Dados válidos → todos passam

Cadeia materializada: empenhos_tesouro_tednc_tesouro_mirnum_transf_n_plano_acao
empenhos_por_plano_acao (silver) → ted_empenhos_plano_acao (gold).

OK created sql table model siafi_dbt.empenhos_por_plano_acao ... [SELECT 1]
OK created sql table model siafi_dbt.ted_empenhos_plano_acao ... [SELECT 1]
PASS not_null_empenhos_por_plano_acao_ne_ccor
PASS relationships_empenhos_por_plano_acao_plano_acao__id_plano_acao__ref_planos_acao_ted_
PASS not_null_ted_empenhos_plano_acao_ne_ccor
PASS not_null_ted_empenhos_plano_acao_plano_acao
PASS relationships_ted_empenhos_plano_acao_plano_acao__id_plano_acao__ref_planos_acao_ted_
Done. PASS=15 WARN=0 ERROR=0 SKIP=0 TOTAL=15

2b) Com plano órfão → integridade referencial detecta (FAIL)

Inserido um transfere_gov.notas_de_credito apontando para id_plano_acao = 999
(inexistente em planos_acao_ted), ligado via nc_tesouro_mir a um empenho
(ne_ccor_descricao = 'TED 200002'). O plano_acao = 999 resultante é órfão.

PASS  not_null_empenhos_por_plano_acao_ne_ccor
PASS  not_null_ted_empenhos_plano_acao_ne_ccor
PASS  not_null_ted_empenhos_plano_acao_plano_acao
PASS  unique_planos_acao_ted_id_plano_acao
FAIL 1 relationships_empenhos_por_plano_acao_plano_acao__id_plano_acao__ref_planos_acao_ted_
FAIL 1 relationships_ted_empenhos_plano_acao_plano_acao__id_plano_acao__ref_planos_acao_ted_
Done. PASS=7 WARN=0 ERROR=2 SKIP=0 TOTAL=9

  Failure in test relationships_empenhos_por_plano_acao_plano_acao__id_plano_acao__ref_planos_acao_ted_
    Got 1 result, configured to fail if != 0
  Failure in test relationships_ted_empenhos_plano_acao_plano_acao__id_plano_acao__ref_planos_acao_ted_
    Got 1 result, configured to fail if != 0

Como replicar

Por que dados sintéticos: no ambiente local as tabelas de origem (siafi.*, transfere_gov.*)
ficam vazias — só são populadas pelas DAGs de ingestão, que exigem credenciais das APIs.
Então criamos tabelas de origem fictícias e rodamos o dbt contra elas.

Dois detalhes do projeto que exigem ajuste: (1) os modelos mir usam +database: analytics
(dbt_project.yml), logo a conexão precisa apontar para o banco analytics; (2) o diretório
/opt/airflow/dags é montado somente-leitura para o dbt, então copiamos o projeto para /tmp.

Pré-requisito: ambiente local no ar (docker compose up -d) com o container airflow (tem dbt).
Todos os comandos rodam a partir da raiz do projeto, dentro do WSL.

# 1) Criar o banco analytics (idempotente)
docker compose exec -T postgres psql -U postgres -d postgres \
  -c "SELECT 'CREATE DATABASE analytics' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname='analytics')\gexec"

# 2) Criar schemas + tabelas de origem + dados VÁLIDOS
docker compose exec -T postgres psql -v ON_ERROR_STOP=1 -U postgres -d analytics < setup_valid.sql

# 3) Copiar o projeto p/ diretório gravável e rodar o build (espera PASS)
DBT_ENV='DB_DW_HOST_MIR=postgres DB_DW_USER_MIR=postgres DB_DW_PASSWORD_MIR=postgres DB_DW_DBNAME_MIR=analytics DB_DW_SCHEMA_MIR=mir'
SEL='--select +ted_empenhos_plano_acao --indirect-selection cautious --exclude test_name:verificacao_tipagem'
docker compose exec -T airflow bash -lc "rm -rf /tmp/mirproj && cp -r /opt/airflow/dags/dbt/mir /tmp/mirproj && cd /tmp/mirproj && $DBT_ENV dbt build --no-version-check --project-dir /tmp/mirproj --profiles-dir /tmp/mirproj $SEL"

# 4) Injetar violações e re-testar (espera FAIL nos relationships)
docker compose exec -T postgres psql -v ON_ERROR_STOP=1 -U postgres -d analytics < setup_fail.sql
docker compose exec -T airflow bash -lc "cd /tmp/mirproj && $DBT_ENV dbt run  --no-version-check --project-dir /tmp/mirproj --profiles-dir /tmp/mirproj --select +ted_empenhos_plano_acao && $DBT_ENV dbt test --no-version-check --project-dir /tmp/mirproj --profiles-dir /tmp/mirproj $SEL"

setup_valid.sql (dados sintéticos consistentes)

create schema if not exists transfere_gov;
create schema if not exists siafi;

drop table if exists transfere_gov.programas cascade;
create table transfere_gov.programas (id_programa text, aa_ano_programa text, dt_ingest text);
insert into transfere_gov.programas values ('1','2024','2024-01-01 00:00:00'),('2','2024','2024-01-01 00:00:00');

drop table if exists transfere_gov.planos_acao cascade;
create table transfere_gov.planos_acao (
  id_plano_acao text, id_programa text, sigla_unidade_descentralizada text, unidade_descentralizada text,
  sigla_unidade_responsavel_execucao text, unidade_responsavel_execucao text, vl_total_plano_acao text,
  dt_inicio_vigencia text, dt_fim_vigencia text, tx_objeto_plano_acao text, tx_justificativa_plano_acao text,
  in_forma_execucao_direta text, in_forma_execucao_particulares text, in_forma_execucao_descentralizada text,
  tx_situacao_plano_acao text, aa_ano_plano_acao text, vl_beneficiario_especifico text,
  vl_chamamento_publico text, sq_instrumento text, aa_instrumento text, dt_ingest text);
insert into transfere_gov.planos_acao (id_plano_acao,id_programa,vl_total_plano_acao,sq_instrumento,aa_instrumento,dt_ingest)
values ('10','1','1000.00','100001','2024','2024-01-01 00:00:00'),
       ('20','2','2000.00','100002','2024','2024-01-01 00:00:00');

drop table if exists transfere_gov.notas_de_credito cascade;
create table transfere_gov.notas_de_credito (id_nota text, id_plano_acao text, tx_numero_nota text, cd_ug_emitente_nota text, dt_ingest text);

drop table if exists siafi.nc_tesouro_pre_2026 cascade;
create table siafi.nc_tesouro_pre_2026 (
  programa_governo text, programa_governo_descricao text, acao_governo text, acao_governo_descricao text,
  nc text, nc_transferencia text, nc_fonte_recursos text, nc_fonte_recursos_descricao text, ptres text,
  nc_evento_descricao text, nc_ug_responsavel text, nc_ug_responsavel_descricao text, nc_natureza_despesa text,
  nc_natureza_despesa_descricao text, nc_plano_interno text, nc_plano_interno_descricao1 text, favorecido_doc text,
  favorecido_doc_descricao text, favorecido_municipio text, favorecido_municipio_descricao text, nc_valor_linha text,
  movimento_liquido_moeda_origem text, dt_ingest text, nc_plano_interno_descricao2 text, nc_evento text);

drop table if exists siafi.nc_tesouro_pos__2026 cascade;
create table siafi.nc_tesouro_pos__2026 (
  nc text, nc_transferencia text, fonte_codigo text, fonte_nome text, ptres text, tipo_nc text, emitente_codigo text,
  emitente_nome text, gnd_codigo text, gnd_nome text, pi_codigo text, pi_nome text, favorecido_codigo text,
  favorecido_nome text, valor_celula text, total_lista text, dt_ingest text, descricao text, nc_item_detalhamento text,
  emissao_dia text, emissao_mes text, emissao_ano text, ro text, dc text, esfera_orcamentaria_codigo text, esfera_orcamentaria_nome text);

drop table if exists siafi.ne_tesouro cascade;
create table siafi.ne_tesouro (
  programa_governo text, programa_governo_descricao text, acao_governo text, acao_governo_descricao text,
  emissao_mes text, emissao_dia text, ne_ccor text, ne_num_processo text, ne_info_complementar text,
  ne_ccor_descricao text, doc_observacao text, natureza_despesa text, natureza_despesa_descricao text,
  ne_ccor_favorecido text, ne_ccor_favorecido_descricao text, ne_ccor_ano_emissao text, ptres text,
  fonte_recursos_detalhada text, fonte_recursos_detalhada_descricao text, despesas_empenhadas text,
  despesas_liquidadas text, despesas_pagas text, restos_a_pagar_inscritos text, restos_a_pagar_pagos text, dt_ingest text);
-- empenho cujo 'TED 100001' mapeia (via sq_instrumento) ao plano EXISTENTE 10
insert into siafi.ne_tesouro (ne_ccor,ne_num_processo,ne_ccor_descricao,ne_ccor_ano_emissao,despesas_empenhadas,despesas_liquidadas,despesas_pagas,restos_a_pagar_inscritos,restos_a_pagar_pagos,dt_ingest)
values ('1234562024NE000001','123/2024','TED 100001 ','2024','1000,00','0,00','0,00','0,00','0,00','2024-01-01 00:00:00');

setup_fail.sql (injeta violações)

-- (silver/gold) plano órfão via caminho NC: nota -> id_plano_acao 999 (ausente em planos_acao_ted)
insert into transfere_gov.notas_de_credito (id_nota,id_plano_acao,tx_numero_nota,cd_ug_emitente_nota,dt_ingest)
values ('1','999','NC0000000001','UG0001','2024-02-01 00:00:00');
insert into siafi.nc_tesouro_pre_2026 (nc,nc_transferencia,nc_valor_linha,movimento_liquido_moeda_origem,dt_ingest)
values ('UG0001NC0000000001','200002','0,00','0,00','2024-02-01 00:00:00');  -- nc = UG(6)+NC(12)
insert into siafi.ne_tesouro (ne_ccor,ne_num_processo,ne_ccor_descricao,ne_ccor_ano_emissao,despesas_empenhadas,despesas_liquidadas,despesas_pagas,restos_a_pagar_inscritos,restos_a_pagar_pagos,dt_ingest)
values ('6543212024NE000999','999/2024','TED 200002 ','2024','500,00','0,00','0,00','0,00','0,00','2024-02-01 00:00:00');

-- (bronze) violações diretas: id_programa duplicado, id_plano_acao nulo, id_programa órfão, ne_ccor nulo
insert into transfere_gov.programas (id_programa, aa_ano_programa, dt_ingest) values ('1','2024','2024-02-01 00:00:00');
insert into transfere_gov.planos_acao (id_plano_acao, id_programa, dt_ingest) values (NULL,'1','2024-02-01 00:00:00');
insert into transfere_gov.planos_acao (id_plano_acao, id_programa, dt_ingest) values ('30','999','2024-02-01 00:00:00');
insert into siafi.ne_tesouro (ne_ccor, ne_ccor_ano_emissao, dt_ingest) values (NULL,'2024','2024-02-01 00:00:00');

Para focar só no bronze, troque a seleção por
--select programas_ted planos_acao_ted empenhos_tesouro_ted (mantendo --indirect-selection cautious).
Limpeza: docker compose exec -T postgres psql -U postgres -d postgres -c "DROP DATABASE analytics"
remove todo o ambiente sintético.

Evidências

Print do log do dbt passando nos testes
image

Checklist

  • Título do PR segue Conventional Commits
  • Issue relacionada foi referenciada
  • Testes/lint foram executados ou a ausência foi justificada
  • Testes DBT adicionados/atualizados, se aplicável
  • Documentação atualizada, se aplicável
  • Sem dados sensíveis ou credenciais no código
  • Branch atualizada com upstream/main ou origin/main

…enhos e teds

Adiciona testes no empenhos_ted_dbt (projeto mir):
- unique + not_null em programas_ted.id_programa e planos_acao_ted.id_plano_acao (chaves das tabelas TED)
- not_null em empenhos_tesouro_ted.ne_ccor (bronze), empenhos_por_plano_acao.ne_ccor (silver) e ted_empenhos_plano_acao.ne_ccor/plano_acao (gold)
- relationships planos_acao_ted.id_programa -> programas_ted (integridade referencial dentro do TED)
- relationships plano_acao -> planos_acao_ted.id_plano_acao em empenhos_por_plano_acao (silver) e ted_empenhos_plano_acao (gold)
@renannOgomes renannOgomes requested a review from a team as a code owner June 23, 2026 16:59
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

test: Implementar testes para mir/empenhos_ted_dbt

1 participant