Skip to content

Root Cause Analysis: AuthControllerIT failures #27

@vitorhugo-java

Description

@vitorhugo-java

Summary

Os testes de integração de AuthControllerIT estão falhando em cascata por uma violação de integridade referencial durante o processo de limpeza do banco (cleanDb()).

A maioria dos testes do projeto está saudável (unitários, E2E e outros integration tests passam normalmente), indicando que o problema é localizado no ciclo de setup/cleanup dessa suíte específica.


❌ Sintoma observado

Falha recorrente:

org.springframework.dao.DataIntegrityViolationException

Referential integrity constraint violation:

public.google_drive_connections FOREIGN KEY(user_id)
REFERENCES public.users(id)

O erro acontece durante:

cleanDb()

Mais especificamente:

usersRepository.deleteAll()

Stack:

AuthControllerIT.cleanDb(AuthControllerIT.java:51)

🎯 Root Cause

Existe uma relação:

users
   ↑
google_drive_connections.user_id

Ou seja:

GoogleDriveConnection
   -> User

No cleanup dos testes, os registros pai (users) são removidos antes dos registros filhos (google_drive_connections).

Sequência atual:

usersRepository.deleteAll();

Estado do banco:

google_drive_connections
    └── user_id -> users.id

Resultado:

O H2 impede a exclusão porque ainda existem referências ativas.

(Basicamente o banco olhou e falou: "calma lá campeão, esse usuário ainda está sendo usado" 😅)


Impact

Isso gera falha em cascata em praticamente todos os testes de AuthControllerIT:

  • login_shouldReturn401_whenBadCredentials
  • me_shouldReturn200_whenAuthenticated
  • register_shouldReturn201
  • refresh_shouldReadFromCookie
  • logout_shouldClearRefreshTokenCookie

e vários outros.

Os testes em si provavelmente estão corretos.

O problema ocorre antes da execução real da lógica testada.


✅ Suggested Fix (recomendado)

Alterar a ordem de limpeza do banco:

Antes:

@BeforeEach
void cleanDb() {
    usersRepository.deleteAll();
}

Depois:

@BeforeEach
void cleanDb() {

    googleDriveConnectionRepository.deleteAll();

    refreshTokenRepository.deleteAll();

    usersRepository.deleteAll();
}

Sempre remover entidades filhas antes das entidades pai.


Alternativa arquitetural (mais elegante)

Configurar cascade/orphan removal na entidade.

Exemplo:

@OneToMany(
    mappedBy = "user",
    cascade = CascadeType.ALL,
    orphanRemoval = true
)
private List<GoogleDriveConnection> connections;

ou:

@ManyToOne
@OnDelete(action = OnDeleteAction.CASCADE)

Isso permitiria:

usersRepository.deleteAll();

e o Hibernate cuidaria automaticamente dos dependentes.


⚠️ Observação

CascadeType.REMOVE resolve o problema de exclusão, mas deve ser usado com cuidado.

Em produção, remover um usuário apagando automaticamente conexões, tokens e integrações externas pode ou não ser o comportamento desejado.

Para testes, muitas vezes é melhor manter o domínio explícito e apenas corrigir o cleanup.


Recommendation

Curto prazo:

✅ corrigir cleanDb()
✅ deletar tabelas filhas primeiro

Longo prazo:

🔎 revisar relacionamentos JPA e estratégia de cascata para evitar problemas semelhantes em futuras entidades.


Resultado esperado após ajuste:

Tests run: XX
Failures: 0
Errors: 0

Metadata

Metadata

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions