test(importing): pin relationship error propagation and short-row reads

Add a negative test that an unexpected DomainException from
addRelationshipIdempotently propagates rather than being swallowed (only
DUPLICATE/CIRCULAR are caught for idempotency), guarding against a future
swallow-all refactor. Add a CanonicalSheetReader test for a row narrower than
the header (POI omits trailing empty cells) reading absent columns as "".

Refs #669

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-05-27 10:59:52 +02:00
parent 2f7ea37466
commit 7ebf7acd72
2 changed files with 41 additions and 0 deletions

View File

@@ -55,6 +55,21 @@ class CanonicalSheetReaderTest {
assertThat(rows.get(0).get("does_not_exist")).isEmpty();
}
@Test
void get_returnsEmptyString_forTrailingColumns_whenRowShorterThanHeader(@TempDir Path tempDir) throws Exception {
// POI omits trailing empty cells, so a real-world artifact row can be narrower than
// the header. The missing columns must read as "" rather than throwing.
Path xlsx = write(tempDir,
List.of("index", "file", "summary"),
List.of(List.of("W-0001")));
List<CanonicalSheetReader.Row> rows = CanonicalSheetReader.readRows(xlsx.toFile(), List.of("index", "file", "summary"));
assertThat(rows.get(0).get("index")).isEqualTo("W-0001");
assertThat(rows.get(0).get("file")).isEmpty();
assertThat(rows.get(0).get("summary")).isEmpty();
}
@Test
void splitList_splitsOnPipe() {
assertThat(CanonicalSheetReader.splitList("a|b|c")).containsExactly("a", "b", "c");

View File

@@ -19,6 +19,7 @@ import java.nio.file.Path;
import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doThrow;
@@ -106,6 +107,31 @@ class PersonTreeImporterTest {
verify(relationshipService).addRelationship(any(), any());
}
@Test
void load_propagatesUnexpectedDomainException_fromAddRelationship(@TempDir Path tempDir) throws Exception {
PersonService personService = mock(PersonService.class);
RelationshipService relationshipService = mock(RelationshipService.class);
when(personService.upsertBySourceRef(any()))
.thenAnswer(inv -> personOf(inv.getArgument(0)));
// An unexpected ErrorCode (not DUPLICATE/CIRCULAR) must NOT be swallowed.
doThrow(DomainException.internal(ErrorCode.INTERNAL_ERROR, "boom"))
.when(relationshipService).addRelationship(any(), any());
Path json = write(tempDir, """
{"persons":[
{"rowId":"row_a","lastName":"A","familyMember":true,"personId":"a"},
{"rowId":"row_b","lastName":"B","familyMember":true,"personId":"b"}
],"relationships":[
{"personId":"row_a","relatedPersonId":"row_b","type":"SPOUSE_OF","source":"verheiratet_mit"}
]}
""");
PersonTreeImporter importer = new PersonTreeImporter(personService, relationshipService);
assertThatThrownBy(() -> importer.load(json.toFile()))
.isInstanceOf(DomainException.class)
.extracting("code").isEqualTo(ErrorCode.INTERNAL_ERROR);
}
@Test
void load_skipsRelationship_whenRowIdUnresolved(@TempDir Path tempDir) throws Exception {
PersonService personService = mock(PersonService.class);