Skip to content

🔍 PR Review — Root Cause Analysis: E2E failures after Interview Events integration #29

@vitorhugo-java

Description

@vitorhugo-java

Summary

Após a correção do relacionamento User → GoogleDriveConnection, surgiu uma nova quebra de integridade referencial em testes E2E.

Os testes unitários e grande parte dos testes de integração continuam saudáveis:

O problema agora está isolado no fluxo de limpeza do banco durante os testes E2E.


❌ Sintoma observado

Erro recorrente:

org.springframework.dao.DataIntegrityViolationException

Referential integrity constraint violation:

public.interview_events FOREIGN KEY(application_id)
REFERENCES public.job_applications(id)

Falha ocorre durante:

ApplicationE2ETest.setUp()
AuthE2ETest.cleanDb()

Chamadas:

jobApplicationRepository.deleteAll()

Stack:

AuthE2ETest.cleanDb(AuthE2ETest.java:34)

ApplicationE2ETest.setUp(ApplicationE2ETest.java:33)

🎯 Root Cause

Foi introduzida uma dependência:

job_applications
      ↑
interview_events.application_id

Representação do domínio:

InterviewEvent
      -> JobApplication

Durante o cleanup:

jobApplicationRepository.deleteAll();

a tabela pai está sendo removida antes dos registros filhos.

Estado do banco:

interview_events
     └── application_id -> job_applications.id

Resultado:

O H2 impede a exclusão por ainda existirem referências ativas.

(Traduzindo do dialeto do banco: "você não pode apagar a casa enquanto ainda tem gente morando nela" 😅)


Impact

Isso provoca falhas em cascata em múltiplas suítes:

AuthE2ETest

Todos os testes quebram antes mesmo da execução real:

  • register_shouldSetHttpOnlySecureSameSiteCookie
  • logout_shouldReturnSuccess_andClearCookie
  • login_shouldReturn401_whenWrongPassword
  • me_shouldReturn403_whenAccessTokenInvalidOrExpired

e demais cenários.

ApplicationE2ETest

Além dos erros de setup:

getAll_withFilter_shouldReturnFilteredResults

há também efeito colateral:

Expected status code <200> but was <500>

Muito provavelmente erro secundário causado pelo estado inconsistente do banco após falha de limpeza.


✅ Suggested Fix (curto prazo)

Remover entidades filhas antes das entidades pai.

Antes:

@BeforeEach
void cleanDb() {

    jobApplicationRepository.deleteAll();

    usersRepository.deleteAll();
}

Depois:

@BeforeEach
void cleanDb() {

    interviewEventRepository.deleteAll();

    jobApplicationRepository.deleteAll();

    usersRepository.deleteAll();
}

Se existir:

refreshTokenRepository.deleteAll();
googleDriveConnectionRepository.deleteAll();

seguir sempre:

filhos → pais

Melhor abordagem estrutural

Adicionar cascata no relacionamento:

@OneToMany(
    mappedBy="application",
    cascade=CascadeType.ALL,
    orphanRemoval=true
)
private List<InterviewEvent> events;

ou:

@OnDelete(action = OnDeleteAction.CASCADE)

Assim:

jobApplicationRepository.deleteAll()

automaticamente removeria:

InterviewEvent

⚠️ Observação arquitetural

O padrão está se repetindo:

Anterior:

User
   -> GoogleDriveConnection

Agora:

JobApplication
   -> InterviewEvent

Isso sugere um problema maior: a estratégia de cleanup está acoplada ao conhecimento manual de cada entidade filha.

Quanto mais o domínio crescer:

User
 ├── RefreshToken
 ├── GoogleDriveConnection
 └── ...

JobApplication
 ├── InterviewEvent
 ├── Notes
 └── ...

mais frágil o deleteAll() ficará.


Recommendation

Curto prazo:

✅ deletar InterviewEvent antes de JobApplication

Médio prazo:

✅ revisar cascade/orphanRemoval

Longo prazo:

✅ criar um TestDatabaseCleaner centralizado

Exemplo:

@Component
class TestDatabaseCleaner {

   void clean() {
      interviewEventRepository.deleteAll();
      refreshTokenRepository.deleteAll();
      googleDriveConnectionRepository.deleteAll();
      jobApplicationRepository.deleteAll();
      usersRepository.deleteAll();
   }
}

Evita sair caçando FK quebrada a cada nova entidade adicionada (o famoso "apaguei uma hidra, nasceram três cabeças" 🐉).


Resultado esperado:

Tests run: XX
Failures: 0
Errors: 0
Green build ✅

Metadata

Metadata

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions