Pular para o conteúdo principal

Testes

Este guia cobre como testar o edgeProxy localmente e em ambientes de deploy usando o servidor mock backend.

Mock Backend Server

O diretório tests/mock-backend/ contém um servidor HTTP leve em Go que simula serviços backend reais para fins de teste.

Funcionalidades

  • Simulação multi-região: Configure diferentes regiões por instância
  • Rastreamento de requests: Conta requisições por backend
  • Múltiplos endpoints: Root, health, info e latency
  • Respostas JSON: Respostas estruturadas para fácil parsing
  • Footprint mínimo: ~8MB de binário, baixo uso de memória

Compilando o Mock Server

# Build nativo (para testes locais)
cd tests/mock-backend
go build -o mock-backend main.go

# Cross-compile para Linux AMD64 (para deploy EC2/cloud)
GOOS=linux GOARCH=amd64 go build -o mock-backend-linux-amd64 main.go

Executando Localmente

Inicie múltiplas instâncias para simular diferentes backends:

# Terminal 1: Backend EU 1
./mock-backend -port 9001 -region eu -id mock-eu-1

# Terminal 2: Backend EU 2
./mock-backend -port 9002 -region eu -id mock-eu-2

# Terminal 3: Backend US
./mock-backend -port 9003 -region us -id mock-us-1

Opções CLI

FlagPadrãoDescrição
-port9001Porta TCP para escutar
-regioneuIdentificador de região (eu, us, sa, ap)
-idmock-{region}-{port}Identificador único do backend

Endpoints

EndpointDescriçãoResposta
/RootTexto com info do backend
/healthHealth checkOK - {id} ({region})
/api/infoInfo JSONDetalhes completos do backend
/api/latencyJSON mínimoPara testes de latência

Exemplo de Resposta (/api/info)

{
"backend_id": "mock-eu-1",
"region": "eu",
"hostname": "ip-172-31-29-183",
"port": "9001",
"request_count": 42,
"uptime_secs": 3600,
"timestamp": "2025-12-08T00:11:43Z",
"message": "Hello from mock backend!"
}

Setup de Teste Local

1. Configurar routing.db

Adicione mock backends ao seu routing.db local:

-- Limpar backends de teste existentes
DELETE FROM backends WHERE id LIKE 'mock-%';

-- Adicionar mock backends
INSERT INTO backends (id, app, region, wg_ip, port, healthy, weight, soft_limit, hard_limit)
VALUES
('mock-eu-1', 'test', 'eu', '127.0.0.1', 9001, 1, 2, 100, 150),
('mock-eu-2', 'test', 'eu', '127.0.0.1', 9002, 1, 2, 100, 150),
('mock-us-1', 'test', 'us', '127.0.0.1', 9003, 1, 2, 100, 150);

2. Iniciar Mock Backends

# Iniciar todos os 3 backends
./tests/mock-backend/mock-backend -port 9001 -region eu -id mock-eu-1 &
./tests/mock-backend/mock-backend -port 9002 -region eu -id mock-eu-2 &
./tests/mock-backend/mock-backend -port 9003 -region us -id mock-us-1 &

3. Executar edgeProxy

EDGEPROXY_REGION=eu \
EDGEPROXY_LISTEN_ADDR=0.0.0.0:8080 \
cargo run --release

4. Testar Requisições

# Teste simples
curl http://localhost:8080/api/info

# Múltiplas requisições (observe o load balancing)
for i in {1..10}; do
curl -s http://localhost:8080/api/info | grep backend_id
done

# Health check
curl http://localhost:8080/health

Teste de Deploy EC2

1. Deploy do Mock Server para EC2

# Build para Linux
cd tests/mock-backend
GOOS=linux GOARCH=amd64 go build -o mock-backend-linux-amd64 main.go

# Copiar para EC2
scp -i ~/.ssh/edgeproxy-key.pem mock-backend-linux-amd64 ubuntu@<EC2-IP>:/tmp/

# SSH e setup
ssh -i ~/.ssh/edgeproxy-key.pem ubuntu@<EC2-IP>
sudo mv /tmp/mock-backend-linux-amd64 /opt/edgeproxy/mock-backend
sudo chmod +x /opt/edgeproxy/mock-backend

2. Iniciar Mock Backends na EC2

# Iniciar 3 instâncias
cd /opt/edgeproxy
nohup ./mock-backend -port 9001 -region eu -id mock-eu-1 > /tmp/mock-9001.log 2>&1 &
nohup ./mock-backend -port 9002 -region eu -id mock-eu-2 > /tmp/mock-9002.log 2>&1 &
nohup ./mock-backend -port 9003 -region us -id mock-us-1 > /tmp/mock-9003.log 2>&1 &

# Verificar
ps aux | grep mock-backend
curl localhost:9001/health
curl localhost:9002/health
curl localhost:9003/health

3. Configurar routing.db na EC2

sqlite3 /opt/edgeproxy/routing.db "
DELETE FROM backends WHERE id LIKE 'mock-%';
INSERT INTO backends (id, app, region, wg_ip, port, healthy, weight, soft_limit, hard_limit)
VALUES
('mock-eu-1', 'test', 'eu', '127.0.0.1', 9001, 1, 2, 100, 150),
('mock-eu-2', 'test', 'eu', '127.0.0.1', 9002, 1, 2, 100, 150),
('mock-us-1', 'test', 'us', '127.0.0.1', 9003, 1, 2, 100, 150);
SELECT id, region, port, healthy FROM backends WHERE deleted=0;
"

Campos do Backend Explicados

CampoTipoDescriçãoExemplo
idTEXTIdentificador único do backend. Usado em logs e client affinity.mock-eu-1
appTEXTNome da aplicação. Agrupa backends que servem a mesma app.test
regionTEXTCódigo da região geográfica. Usado para decisões de geo-routing. Válidos: eu, us, sa, ap.eu
wg_ipTEXTEndereço IP do backend. Use 127.0.0.1 para testes locais, IPs WireGuard (10.50.x.x) em produção.127.0.0.1
portINTEGERPorta TCP que o backend escuta.9001
healthyINTEGERStatus de saúde. 1 = saudável (recebe tráfego), 0 = não saudável (excluído do roteamento).1
weightINTEGERPeso relativo para load balancing. Peso maior = mais tráfego. Range: 1-10.2
soft_limitINTEGERQuantidade confortável de conexões. Acima disso, o backend é considerado "carregado" e menos preferido.100
hard_limitINTEGERMáximo de conexões. Neste limite ou acima, backend é excluído de novas conexões.150

Detalhamento dos Dados de Exemplo

('mock-eu-1', 'test', 'eu', '127.0.0.1', 9001, 1, 2, 100, 150)
ValorCampoSignificado
mock-eu-1idIdentificador do backend, primeiro mock server EU
testappNome da aplicação para testes
euregionLocalizado na região Europa
127.0.0.1wg_ipLocalhost (mesma máquina que o proxy)
9001portEscutando na porta 9001
1healthyBackend está saudável e ativo
2weightPrioridade média (escala 1-10)
100soft_limitConfortável com até 100 conexões
150hard_limitMáximo de 150 conexões permitidas

Scoring do Load Balancer

O proxy usa esses campos para calcular um score para cada backend:

score = geo_score * 100 + (conexões / soft_limit) / weight
  • geo_score: 0 (mesmo país), 1 (mesma região), 2 (região do POP local), 3 (fallback global)
  • conexões: Conexões ativas atuais (do metrics)
  • soft_limit: Divide o fator de carga
  • weight: Peso maior reduz o score (mais preferido)

Menor score vence. Backends com healthy=0 ou no hard_limit são excluídos.

4. Testar de Cliente Externo

# Da sua máquina local
curl http://<EC2-PUBLIC-IP>:8080/api/info
curl http://<EC2-PUBLIC-IP>:8080/health

# Múltiplas requisições para ver load balancing
for i in {1..5}; do
curl -s http://<EC2-PUBLIC-IP>:8080/api/info
echo ""
done

Cenários de Teste

Client Affinity

Client affinity (sticky sessions) vincula clientes ao mesmo backend:

# Todas requisições do mesmo IP vão para o mesmo backend
for i in {1..5}; do
curl -s http://localhost:8080/api/info | grep backend_id
done
# Esperado: Todos mostram o mesmo backend_id

Distribuição de Carga

Para testar distribuição de carga, simule diferentes clientes:

# Use IPs de origem diferentes ou aguarde expiração do TTL
# Verifique request_count em cada backend
curl localhost:9001/api/info | grep request_count
curl localhost:9002/api/info | grep request_count
curl localhost:9003/api/info | grep request_count

Health do Backend

Teste roteamento baseado em health parando um backend:

# Parar mock-eu-1
pkill -f 'mock-backend.*9001'

# Requisições devem ir para backends saudáveis
curl http://localhost:8080/api/info
# Esperado: Roteia para mock-eu-2 ou mock-us-1

Geo-Routing

O proxy roteia clientes para backends em sua região:

  1. Configure backends em múltiplas regiões
  2. Teste de diferentes localizações geográficas
  3. Observe decisões de roteamento nos logs do proxy

Monitoramento Durante Testes

Logs do edgeProxy

# Na EC2
sudo journalctl -u edgeproxy -f

# Procure por:
# - Logs de seleção de backend
# - Contagem de conexões
# - Resolução GeoIP

Logs do Mock Backend

# Verificar logs individuais dos backends
tail -f /tmp/mock-9001.log
tail -f /tmp/mock-9002.log
tail -f /tmp/mock-9003.log

Distribuição de Requisições

# Verificação rápida de distribuição
echo "mock-eu-1: $(curl -s localhost:9001/api/info | grep -o '"request_count":[0-9]*')"
echo "mock-eu-2: $(curl -s localhost:9002/api/info | grep -o '"request_count":[0-9]*')"
echo "mock-us-1: $(curl -s localhost:9003/api/info | grep -o '"request_count":[0-9]*')"

Limpeza

Local

# Matar todos os mock backends
pkill -f mock-backend

EC2

# Matar mock backends
sudo pkill -f mock-backend

# Ou matar por porta
sudo fuser -k 9001/tcp 9002/tcp 9003/tcp

Troubleshooting

Mock Backend Não Inicia

# Verificar se porta está em uso
sudo ss -tlnp | grep 9001

# Matar processo existente
sudo fuser -k 9001/tcp

Proxy Não Conecta ao Backend

  1. Verificar se backend está rodando: curl localhost:9001/health
  2. Verificar configuração do routing.db
  3. Verificar se wg_ip está correto (use 127.0.0.1 para testes locais)
  4. Verificar regras de firewall na EC2

Requisições com Timeout

  1. Verificar se edgeProxy está rodando: sudo systemctl status edgeproxy
  2. Verificar health dos backends no routing.db
  3. Verificar se limites de conexão não foram excedidos

Testes Unitários

O edgeProxy possui cobertura abrangente de testes unitários seguindo o padrão de Arquitetura Hexagonal. Todos os testes são escritos em Rust usando o framework de testes nativo.

Resumo dos Testes

MétricaValor
Total de Testes875
Cobertura de Linhas98.71%
Cobertura de Regiões98.71%
Cobertura de Funções99.58%
Arquivos com 100%22

Evolução da Cobertura

O projeto alcançou melhorias significativas de cobertura através de testes sistemáticos:

FaseCoberturaTestesMelhorias Principais
Inicial (stable)94.43%780Testes unitários básicos
Refatoração94.92%782Adoção do padrão Sans-IO
Build nightly98.32%782coverage(off) para I/O
Testes edge case98.50%784Circuit breaker, métricas
TLS & pool98.89%786TLS, connection pool
Replicação v0.4.098.71%875Merkle tree, mDNS, delta sync

Benefícios da Arquitetura Sans-IO

O padrão Sans-IO separa lógica de negócio pura das operações de I/O:

┌─────────────────────────────────────────────────────────────────────┐
│ TESTÁVEL (100% coberto) │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ Funções Puras: process_message(), pick_backend(), etc. │ │
│ │ - Sem chamadas de rede │ │
│ │ - Sem acesso a banco de dados │ │
│ │ - Retorna ações para executar │ │
│ └──────────────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────────────┤
│ WRAPPERS DE I/O (excluídos) │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ Async handlers: start(), run(), handle_connection() │ │
│ │ - Marcados com #[cfg_attr(coverage_nightly, coverage(off))] │ │
│ │ - Wrappers finos que executam ações │ │
│ └──────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘

Esta abordagem garante:

  • Toda lógica de negócio é testável sem mock de rede
  • 100% de cobertura do código de decisão
  • Separação clara entre lógica e I/O

Executando Testes

# Executar todos os testes
cargo test

# Executar testes com output
cargo test -- --nocapture

# Executar testes de um módulo específico
cargo test domain::services::load_balancer

# Executar apenas testes de infraestrutura
cargo test infrastructure::

# Executar testes em paralelo (padrão)
cargo test -- --test-threads=4

# Executar single-threaded (para debug)
cargo test -- --test-threads=1

Testes por Módulo

Adapters Inbound

MóduloTestesCoberturaDescrição
adapters::inbound::api_server3899.57%API de Auto-Discovery, registro, heartbeat
adapters::inbound::dns_server4497.80%Servidor DNS, resolução geo-routing
adapters::inbound::tcp_server2796.23%Conexões TCP, lógica de proxy
adapters::inbound::tls_server2994.18%Terminação TLS, certificados

Adapters Outbound

MóduloTestesCoberturaDescrição
adapters::outbound::dashmap_metrics_store20100.00%Métricas de conexão, tracking RTT
adapters::outbound::dashmap_binding_repo21100.00%Client affinity, TTL, GC
adapters::outbound::replication_backend_repo2899.85%Replicação SQLite distribuída
adapters::outbound::sqlite_backend_repo2099.26%Storage SQLite de backends
adapters::outbound::prometheus_metrics_store1998.70%Exportação métricas Prometheus
adapters::outbound::maxmind_geo_resolver1895.86%Resolução GeoIP
adapters::outbound::postgres_backend_repo1988.31%Backend PostgreSQL (stub)

Camada de Domínio

MóduloTestesCoberturaDescrição
domain::entities12100.00%Backend, Binding, ClientKey
domain::value_objects2696.40%RegionCode, mapeamento de países
domain::services::load_balancer2598.78%Algoritmo de scoring, geo-routing

Camada de Aplicação

MóduloTestesCoberturaDescrição
application::proxy_service2699.43%Orquestração de use cases
config24100.00%Carregamento de configuração

Camada de Infraestrutura

MóduloTestesCoberturaDescrição
infrastructure::circuit_breaker2298.30%Padrão circuit breaker
infrastructure::config_watcher1795.30%Hot reload de configuração
infrastructure::rate_limiter1493.55%Rate limiting token bucket
infrastructure::health_checker1792.00%Health checks ativos
infrastructure::connection_pool1793.71%Pool de conexões TCP
infrastructure::shutdown1193.65%Graceful shutdown

Camada de Replicação (v0.4.0)

MóduloTestesCoberturaDescrição
replication::types4598.81%Timestamps HLC, ChangeSet, NodeId
replication::config1299.34%Configuração de replicação
replication::sync3897.63%Detecção de mudanças, resolução LWW
replication::gossip4298.80%Protocolo SWIM, membership do cluster
replication::transport4898.55%Transporte QUIC, encoding Sans-IO
replication::agent1899.77%Orquestração de replicação
replication::merkle4098.92%Anti-entropia Merkle tree
replication::mdns2599.02%Auto-descoberta mDNS

Testes por Camada (Arquitetura Hexagonal)

Testes por Camada

Detalhes dos Testes de Infraestrutura

Testes do Circuit Breaker (22 testes)

cargo test infrastructure::circuit_breaker
TesteDescrição
test_circuit_breaker_newEstado inicial é Closed
test_circuit_breaker_defaultConfiguração padrão
test_allow_when_closedRequisições passam no estado Closed
test_record_success_in_closedRastreamento de sucesso
test_record_failure_in_closedRastreamento de falha
test_transitions_to_openAbre após threshold de falhas
test_deny_when_openBloqueia requisições no estado Open
test_circuit_transitions_to_half_openTimeout dispara Half-Open
test_half_open_allows_limitedRequisições limitadas em Half-Open
test_half_open_to_closedRecupera para Closed em sucesso
test_half_open_to_openRetorna para Open em falha
test_failure_window_resetsWindow reseta em sucesso
test_get_metricsRecuperação de métricas
test_concurrent_recordOperações thread-safe

Testes do Rate Limiter (14 testes)

cargo test infrastructure::rate_limiter
TesteDescrição
test_rate_limit_config_defaultPadrão: 100 req/s, burst 10
test_rate_limiter_newCria com configuração
test_check_allows_initial_burstBurst de requisições permitido
test_check_different_clients_isolatedIsolamento por IP
test_remainingRastreamento de tokens
test_clear_clientReset de cliente individual
test_clear_allReset de todos clientes
test_check_with_costRequisições com custo variável
test_cleanup_removes_staleGC remove entradas antigas
test_refill_over_timeReposição de tokens
test_concurrent_accessOperações thread-safe

Testes do Health Checker (17 testes)

cargo test infrastructure::health_checker
TesteDescrição
test_health_checker_newCria com configuração
test_health_check_config_defaultIntervalos padrão
test_health_status_defaultEstado inicial desconhecido
test_tcp_check_successProbe TCP sucesso
test_tcp_check_failureProbe TCP falha
test_tcp_check_timeoutTratamento de timeout TCP
test_update_status_becomes_healthyTransições de threshold
test_update_status_becomes_unhealthyTransições de falha
test_on_health_change_callbackNotificações de mudança
test_check_backend_successCheck de backend OK
test_check_backend_failureCheck de backend falha

Testes do Connection Pool (17 testes)

cargo test infrastructure::connection_pool
TesteDescrição
test_connection_pool_newCriação do pool
test_pool_config_defaultPadrão: 10 max, 60s idle
test_acquire_creates_connectionNova conexão em pool vazio
test_release_returns_connectionReutilização de conexão
test_pool_exhaustedErro de máximo de conexões
test_acquire_timeoutTimeout de conexão
test_discard_closes_connectionDescarte explícito
test_statsEstatísticas do pool
test_pooled_connection_is_expiredVerificação de lifetime
test_pooled_connection_is_idle_expiredVerificação de idle timeout

Testes do Graceful Shutdown (11 testes)

cargo test infrastructure::shutdown
TesteDescrição
test_shutdown_controller_newCriação do controller
test_connection_guardCriação de guard RAII
test_connection_trackingRastreamento de contagem ativa
test_multiple_connection_guardsGuards concorrentes
test_shutdown_initiates_onceShutdown único
test_subscribe_receives_shutdownNotificação broadcast
test_wait_for_drain_immediateCaso sem conexões
test_wait_for_drain_with_connectionsAguarda drenagem
test_wait_for_drain_timeoutComportamento de timeout

Testes do Config Watcher (17 testes)

cargo test infrastructure::config_watcher
TesteDescrição
test_config_watcher_newCriação do watcher
test_watch_fileMonitoramento de arquivo
test_watch_nonexistent_fileTratamento de erro
test_unwatch_fileRemoção do watch
test_set_and_getValores de config
test_get_orValores padrão
test_subscribe_value_changeNotificações de mudança
test_no_change_on_same_valueSem eventos espúrios
test_check_files_detects_modificationDetecção de mudança
test_hot_value_get_setWrapper HotValue

Cobertura de Código

Ferramentas de Cobertura

O edgeProxy usa cargo-llvm-cov para medição de cobertura de código com instrumentação LLVM.

Instalação

# Instalar cargo-llvm-cov
cargo install cargo-llvm-cov

# Instalar ferramentas LLVM (necessário para cobertura)
rustup component add llvm-tools-preview

# Instalar toolchain nightly (para suporte a coverage(off))
rustup toolchain install nightly
rustup run nightly rustup component add llvm-tools-preview

Executando Cobertura

# Relatório básico de cobertura (stable Rust - inclui código I/O)
cargo llvm-cov

# Cobertura com nightly (RECOMENDADO - exclui código I/O marcado com coverage(off))
rustup run nightly cargo llvm-cov

# Apenas resumo
rustup run nightly cargo llvm-cov --summary-only

# Cobertura com relatório HTML
rustup run nightly cargo llvm-cov --html

# Cobertura com output LCOV
rustup run nightly cargo llvm-cov --lcov --output-path lcov.info

# Abrir relatório HTML
open target/llvm-cov/html/index.html

Importante: Use rustup run nightly para habilitar atributos #[coverage(off)]. Com Rust stable, código de I/O será incluído nas métricas de cobertura, resultando em ~94% ao invés de ~99%.

Resultados de Cobertura

Cobertura Final: 98.71% (7.159 linhas, 98.98% cobertura de linhas)

Nota: Cobertura medida com cargo +nightly llvm-cov para habilitar atributos coverage(off) em código de I/O.

Cobertura por Camada

CamadaRegiõesCoberturaStatus
Domínio76199.47%✓ Excelente
Aplicação70699.72%✓ Excelente
Adapters Inbound2.10098.90%✓ Excelente
Adapters Outbound1.45098.62%✓ Excelente
Infraestrutura45595.30%✓ Muito Bom
Replicação7.15998.98%✓ Excelente
Config286100.00%✓ Completo

Cobertura Detalhada por Arquivo

Componentes Core (100% Cobertura)
ArquivoLinhasCobertura
config.rs286100.00%
domain/entities.rs130100.00%
adapters/outbound/dashmap_metrics_store.rs224100.00%
adapters/outbound/dashmap_binding_repo.rs287100.00%
Adapters Inbound
ArquivoLinhasCobertasCobertura
adapters/inbound/api_server.rs92892499.57%
adapters/inbound/dns_server.rs77475797.80%
adapters/inbound/tcp_server.rs84981796.23%
adapters/inbound/tls_server.rs99693894.18%
Adapters Outbound
ArquivoLinhasCobertasCobertura
adapters/outbound/replication_backend_repo.rs67767699.85%
adapters/outbound/sqlite_backend_repo.rs40440199.26%
adapters/outbound/prometheus_metrics_store.rs30730398.70%
adapters/outbound/maxmind_geo_resolver.rs14513995.86%
adapters/outbound/postgres_backend_repo.rs23120488.31%
Camada de Infraestrutura
ArquivoRegiõesCobertura
infrastructure/circuit_breaker.rs35398.30%
infrastructure/config_watcher.rs74495.30%
infrastructure/rate_limiter.rs58993.55%
infrastructure/health_checker.rs95092.00%
infrastructure/connection_pool.rs122493.71%
infrastructure/shutdown.rs48893.65%
Camada de Replicação (v0.4.0)
ArquivoRegiõesCobertura
replication/types.rs118698.81%
replication/config.rs30399.34%
replication/sync.rs187597.28%
replication/gossip.rs235098.80%
replication/transport.rs166898.55%
replication/agent.rs98199.77%
replication/merkle.rs103098.92%
replication/mdns.rs61999.02%

Exclusões de Cobertura (Padrão Sans-IO)

O padrão Sans-IO separa lógica de negócio pura de operações de I/O. Código que realiza I/O real é excluído da cobertura usando #[cfg_attr(coverage_nightly, coverage(off))]:

CódigoMotivo
main.rsEntry point, composition root
handle_packet() (dns_server)Dependente de I/O de rede
proxy_bidirectional() (tcp_server)Operações reais de socket TCP
start(), run() (servers)Event loops async com I/O de rede
start_event_loop(), start_flush_loop() (agent)Loops async de background
request() (transport)Operações de rede QUIC
release(), acquire(), clear() (connection_pool)Gerenciamento async de conexões
handle_connection() (transport)Handling de conexões QUIC
start(), execute_actions() (gossip)I/O de gossip UDP
start(), start_discovery() (mdns)I/O de rede mDNS
SkipServerVerification implCallback TLS (não pode ser testado unitariamente)
Módulos de teste (#[cfg(test)])Código de teste não é código de produção

Regiões Não Cobertas Restantes (162 total)

As 162 regiões não cobertas se enquadram nestas categorias:

CategoriaRegiõesMotivo
Erros de banco20Falhas de conexão DB (caminhos inalcançáveis)
I/O de rede45Operações async de rede excluídas
Loops CAS retry25Retries de compare-and-swap atômico
Chamadas tracing18tracing::warn!() em branches de erro
Callbacks TLS/QUIC30Callbacks crypto (não pode ser testado unitariamente)
Signal handlers10Handling de sinais do SO
Callbacks mDNS14Handling de eventos mDNS

Estes representam edge cases que requerem:

  • Falhas de sistemas externos (DB, rede)
  • Condições concorrentes específicas (retries CAS)
  • Callbacks de handshake TLS/QUIC
  • Handling de sinais do sistema operacional

Toda lógica de negócio está 100% coberta - apenas wrappers de I/O e caminhos de erro inalcançáveis permanecem.

Filosofia de Testes

O edgeProxy segue estes princípios de teste:

  1. Lógica de domínio é pura e totalmente testada: Algoritmo de scoring do LoadBalancer não tem dependências externas
  2. Adapters testam através de interfaces: Implementações mock de traits para testes unitários
  3. Testes de integração usam componentes reais: Mock backend server para testes E2E
  4. Código de rede tem exclusões de cobertura: Código I/O-bound é testado via testes de integração
  5. Infraestrutura é modular: Cada componente pode ser testado isoladamente

Integração Contínua

# Exemplo de configuração CI para cobertura
test:
script:
- cargo test
- rustup run nightly cargo llvm-cov --fail-under-lines 98

coverage:
script:
- rustup run nightly cargo llvm-cov --html
artifacts:
paths:
- target/llvm-cov/html/

A flag --fail-under-lines 98 garante que a cobertura não caia abaixo de 98% no CI.

Novos Testes Adicionados (v0.3.1)

MóduloTesteDescrição
circuit_breakertest_allow_request_when_already_half_openTesta transição idempotente HalfOpen
circuit_breakertest_record_success_when_openTesta registro de sucesso em estado Open
prometheus_metrics_storetest_global_metricsTesta métricas globais agregadas
prometheus_metrics_storetest_concurrent_decrementTesta operações concorrentes de contador
typestest_hlc_compare_same_time_different_counterTesta desempate por contador HLC
typestest_hlc_compare_same_time_same_counterTesta caso de igualdade HLC

Novos Testes Adicionados (v0.4.0)

Testes Merkle Tree (40 testes)

TesteDescrição
test_merkle_tree_newCriação e inicialização da árvore
test_merkle_tree_insertInserção de linha única
test_merkle_tree_updateAtualização de linha muda hash
test_merkle_tree_removeRemoção de linha
test_merkle_tree_root_hashCálculo do hash raiz
test_merkle_tree_diffDetecção de diferenças entre árvores
test_merkle_tree_diff_at_depthTraversal de diff multi-nível
test_merkle_tree_get_hashRecuperação de hash por profundidade
test_merkle_tree_leavesAcesso aos nós folha
test_merkle_message_serializationRoundtrip de mensagens
test_merkle_message_range_responseMensagem de resposta de range
test_merkle_message_data_requestMensagem de requisição de dados
test_hash_to_prefix_at_depth_zeroCálculo de prefixo em profundidade 0

Testes mDNS Discovery (25 testes)

TesteDescrição
test_mdns_discovery_newCriação do serviço de descoberta
test_mdns_config_service_typeConfiguração de tipo de serviço
test_discovered_peer_debugFormato debug de DiscoveredPeer
test_gossip_addrAccessor de endereço gossip
test_transport_addrAccessor de endereço transport
test_notify_discovered_logs_valid_peerLogging de notificação de peer válido
test_notify_discovered_different_clusterFiltragem de peer cross-cluster
test_notify_discovered_channel_closedHandling de erro de canal

Testes Delta Sync

TesteDescrição
test_field_op_serializationRoundtrip de enum FieldOp
test_delta_data_newCriação de DeltaData
test_change_data_fullDados de mudança de linha completa
test_change_data_deltaDados de mudança delta
test_apply_delta_changeAplicação de delta na linha

Testes Transport Sans-IO (48 testes)

TesteDescrição
test_encode_message_*Encoding de mensagens para todos os tipos
test_decode_message_*Decoding de mensagens com validação
test_validate_broadcastValidação de checksum de broadcast
test_create_sync_requestCriação de mensagem SyncRequest
test_extract_sync_responseExtração de changesets de resposta
test_message_type_nameConversão de tipo de mensagem para string
test_count_broadcast_changesContagem de mudanças
test_get_broadcast_seqExtração de número de sequência

Testes de Configuração (v0.4.0)

Testes de configuração validam todas as combinações de variáveis de ambiente e configurações de serviço.

Executando Testes de Configuração

# Executar todos os testes de configuração
task test:config-all

# Executar testes individuais
task test:config-default # Apenas TCP (config mínima)
task test:config-all-services # Todos os serviços habilitados
task test:config-regions # Testar regiões sa, us, eu, ap
task test:config-api # Endpoints da API
task test:config-tls # TLS com cert auto-assinado
task test:config-replication # Replicação (gossip + transport)
task test:config-binding # Configuração de TTL de binding
task test:config-debug # Modo debug

# Limpeza entre testes
task test:config-cleanup

Cenários de Teste

TesteVariáveis de AmbienteResultado Esperado
DefaultLISTEN_ADDR, DB_PATH, REGIONTCP proxy na 8080
All ServicesTodas vars habilitadas6 portas listening
RegionsREGION=sa/us/eu/apCada região inicia
APIAPI_ENABLED=true6 endpoints funcionando
TLSTLS_ENABLED=trueCert auto-assinado
ReplicationREPLICATION_ENABLED=trueGossip + Transport
BindingBINDING_TTL_SECS=300TTL customizado funciona
DebugDEBUG=1Logging de debug

Testes de Endpoints da API

EndpointMétodoTesteEsperado
/healthGETHealth check{"status":"ok"}
/api/v1/registerPOSTRegistrar backend{"registered":true}
/api/v1/backendsGETListar backendsArray de backends
/api/v1/backends/:idGETObter backendDetalhes do backend
/api/v1/heartbeat/:idPOSTAtualizar heartbeat{"status":"ok"}
/api/v1/backends/:idDELETERemover backend{"deregistered":true}

Testes de Deploy na AWS (v0.4.0)

Testes de deploy em produção na AWS Irlanda (eu-west-1).

Detalhes do Deploy

PropriedadeValor
Instância34.240.78.199
Regiãoeu-west-1 (Irlanda)
Tipo de Instânciat3.micro
SOUbuntu 22.04
Binário/opt/edgeproxy/edge-proxy
Serviçosystemd (edgeproxy.service)

Configuração do Serviço

[Service]
Environment=EDGEPROXY_LISTEN_ADDR=0.0.0.0:8080
Environment=EDGEPROXY_DB_PATH=/opt/edgeproxy/routing.db
Environment=EDGEPROXY_REGION=eu
Environment=EDGEPROXY_DB_RELOAD_SECS=5
Environment=EDGEPROXY_BINDING_TTL_SECS=600
Environment=EDGEPROXY_BINDING_GC_INTERVAL_SECS=60
Environment=EDGEPROXY_TLS_ENABLED=true
Environment=EDGEPROXY_TLS_LISTEN_ADDR=0.0.0.0:8443
Environment=EDGEPROXY_API_ENABLED=true
Environment=EDGEPROXY_API_LISTEN_ADDR=0.0.0.0:8081
Environment=EDGEPROXY_HEARTBEAT_TTL_SECS=60
Environment=EDGEPROXY_DNS_ENABLED=true
Environment=EDGEPROXY_DNS_LISTEN_ADDR=0.0.0.0:5353
Environment=EDGEPROXY_DNS_DOMAIN=internal
Environment=EDGEPROXY_REPLICATION_ENABLED=true
Environment=EDGEPROXY_REPLICATION_NODE_ID=pop-eu-ireland-1
Environment=EDGEPROXY_REPLICATION_GOSSIP_ADDR=0.0.0.0:4001
Environment=EDGEPROXY_REPLICATION_TRANSPORT_ADDR=0.0.0.0:4002
Environment=EDGEPROXY_REPLICATION_DB_PATH=/opt/edgeproxy/state.db
Environment=EDGEPROXY_REPLICATION_CLUSTER_NAME=edgeproxy-prod

Resultados dos Testes (2025-12-08)

Conectividade de Portas

ServiçoPortaProtocoloStatus
TCP Proxy8080TCPOK
TLS Server8443TCPOK
API Server8081TCPOK
DNS Server5353UDPOK
Gossip4001UDPOK
Transport4002UDPOK

Testes de Endpoints da API

EndpointStatusResposta
GET /healthOK{"status":"ok","version":"0.2.0"}
POST /api/v1/registerOK{"registered":true}
GET /api/v1/backendsOKLista backends registrados
GET /api/v1/backends/:idOKRetorna detalhes do backend
POST /api/v1/heartbeat/:idOK{"status":"ok"}
DELETE /api/v1/backends/:idOK{"deregistered":true}

Certificado TLS

subject=CN = rcgen self signed cert
issuer=CN = rcgen self signed cert

Estado da Replicação

  • State DB: /opt/edgeproxy/state.db (36KB)
  • Node ID: pop-eu-ireland-1
  • Cluster: edgeproxy-prod

Executando Testes na AWS

# SSH para a instância
ssh -i .keys/edgeproxy-hub.pem ubuntu@34.240.78.199

# Verificar status do serviço
sudo systemctl status edgeproxy

# Ver logs
sudo journalctl -u edgeproxy -f

# Testar API localmente
curl http://127.0.0.1:8081/health | jq .

# Testar de fora (requer regras no Security Group)
curl http://34.240.78.199:8081/health

Regras do Security Group

PortaProtocoloOrigemDescrição
22TCPSeu IPSSH
8080TCP0.0.0.0/0TCP Proxy
8081TCP0.0.0.0/0API Server
8443TCP0.0.0.0/0TLS Server
5353UDP0.0.0.0/0DNS Server
4001UDPVPC CIDRGossip (interno)
4002UDPVPC CIDRTransport (interno)

Resultados de Latência (Brasil para Irlanda)

TesteLatência
API Health Check~408ms
100 requests42.8s total (~428ms média)

Nota: A latência é esperada devido à distância geográfica (Brasil para Irlanda ~9.000km)