fix(import): degrade gracefully when canonical life dates conflict
The canonical upsert path skips validateLifeDates, so a spreadsheet row with birth_year > death_year - or a preserved hand-entered birth date conflicting with a canonical death year - violated the V76 CHECK constraint at flush time and aborted the whole import batch with a raw 500. Resolve the pairs first and, on conflict, keep the person's stored life dates (empty for a new person), drop the canonical refresh, and log a WARN with the sourceRef (REQ-IMP-001: never abort the batch). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -297,4 +297,70 @@ class PersonImportUpsertTest {
|
||||
|
||||
assertThat(result.getGeneration()).isEqualTo(3);
|
||||
}
|
||||
|
||||
// ─── conflicting canonical life dates degrade instead of hitting the DB CHECK ──
|
||||
// (chk_person_birth_before_death would abort the whole batch — REQ-IMP-001)
|
||||
|
||||
@Test
|
||||
void upsertBySourceRef_dropsBothDates_whenCanonicalBirthAfterDeath_newPerson() {
|
||||
when(personRepository.findBySourceRef("clara-cram")).thenReturn(Optional.empty());
|
||||
when(personRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
|
||||
|
||||
PersonUpsertCommand cmd = PersonUpsertCommand.builder()
|
||||
.sourceRef("clara-cram").lastName("Cram")
|
||||
.birthYear(1950).deathYear(1949)
|
||||
.personType(PersonType.PERSON).provisional(false).build();
|
||||
|
||||
Person result = personService.upsertBySourceRef(cmd);
|
||||
|
||||
assertThat(result.getBirthDate()).isNull();
|
||||
assertThat(result.getBirthDatePrecision()).isEqualTo(DatePrecision.UNKNOWN);
|
||||
assertThat(result.getDeathDate()).isNull();
|
||||
assertThat(result.getDeathDatePrecision()).isEqualTo(DatePrecision.UNKNOWN);
|
||||
}
|
||||
|
||||
@Test
|
||||
void upsertBySourceRef_keepsHandEnteredBirth_andDropsConflictingCanonicalDeath() {
|
||||
// A human entered an exact birthday; the spreadsheet's death year lies before it.
|
||||
// The hand-entered side must survive, the conflicting canonical refresh is dropped.
|
||||
Person handDated = Person.builder()
|
||||
.id(UUID.randomUUID()).sourceRef("clara-cram").lastName("Cram")
|
||||
.birthDate(LocalDate.of(1950, 6, 1)).birthDatePrecision(DatePrecision.DAY).build();
|
||||
when(personRepository.findBySourceRef("clara-cram")).thenReturn(Optional.of(handDated));
|
||||
when(personRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
|
||||
|
||||
PersonUpsertCommand cmd = PersonUpsertCommand.builder()
|
||||
.sourceRef("clara-cram").lastName("Cram")
|
||||
.deathYear(1949)
|
||||
.personType(PersonType.PERSON).provisional(false).build();
|
||||
|
||||
Person result = personService.upsertBySourceRef(cmd);
|
||||
|
||||
assertThat(result.getBirthDate()).isEqualTo(LocalDate.of(1950, 6, 1));
|
||||
assertThat(result.getBirthDatePrecision()).isEqualTo(DatePrecision.DAY);
|
||||
assertThat(result.getDeathDate()).isNull();
|
||||
assertThat(result.getDeathDatePrecision()).isEqualTo(DatePrecision.UNKNOWN);
|
||||
}
|
||||
|
||||
@Test
|
||||
void upsertBySourceRef_keepsExistingYearDates_whenCanonicalRefreshConflicts() {
|
||||
Person existing = Person.builder()
|
||||
.id(UUID.randomUUID()).sourceRef("clara-cram").lastName("Cram")
|
||||
.birthDate(LocalDate.of(1900, 1, 1)).birthDatePrecision(DatePrecision.YEAR)
|
||||
.deathDate(LocalDate.of(1980, 1, 1)).deathDatePrecision(DatePrecision.YEAR).build();
|
||||
when(personRepository.findBySourceRef("clara-cram")).thenReturn(Optional.of(existing));
|
||||
when(personRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
|
||||
|
||||
PersonUpsertCommand cmd = PersonUpsertCommand.builder()
|
||||
.sourceRef("clara-cram").lastName("Cram")
|
||||
.birthYear(1990).deathYear(1985)
|
||||
.personType(PersonType.PERSON).provisional(false).build();
|
||||
|
||||
Person result = personService.upsertBySourceRef(cmd);
|
||||
|
||||
assertThat(result.getBirthDate()).isEqualTo(LocalDate.of(1900, 1, 1));
|
||||
assertThat(result.getBirthDatePrecision()).isEqualTo(DatePrecision.YEAR);
|
||||
assertThat(result.getDeathDate()).isEqualTo(LocalDate.of(1980, 1, 1));
|
||||
assertThat(result.getDeathDatePrecision()).isEqualTo(DatePrecision.YEAR);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user