feat(massimport): support // separator and dot-compressed names in PersonNameParser #190

Closed
opened 2026-04-07 09:22:53 +02:00 by marcel · 7 comments
Owner

Problem

The mass import spreadsheet contains two name patterns that PersonNameParser currently handles incorrectly:

1. // as multi-person separator

parseReceivers() only splits on und/u. The // separator — used for independent full names in a single cell — causes both names to be imported as a single garbage person record.

Raw input Current result Expected result
Charl.Blomquist//Tante Lolly one person two persons
Walter de Gruyter//Eugenie de Gruyter one person two persons

2. Dot-compressed names (no spaces)

The split() fallback only handles space-separated names, so dot-compressed names produce lastName = "?":

Raw input Current result Expected result
E.Rockstroh ("E.Rockstroh", "?") ("E.", "Rockstroh")
E.M. ("E.M.", "?") ("E.", "M.")
Dr.Fr.Zarncke ("Dr.Fr.Zarncke", "?") ("Dr. Fr.", "Zarncke")
Dr.Zarnke ("Dr.Zarnke", "?") ("Dr.", "Zarnke")

Solution

Part 1 — // pre-split in parseReceivers()

Pre-split the raw input on // before any existing logic. Each segment is then piped through the current pipeline independently, and results are flattened.

Unlike und/u, // always separates fully independent, complete names — no shared last-name distribution is needed. The recursive approach handles mixed cases like Herbert und Clara Cram//Eugenie de Gruyter naturally.

private static final Pattern SLASH_SEPARATOR = Pattern.compile("//");

// In parseReceivers(), after null/blank check:
String[] slashParts = SLASH_SEPARATOR.split(raw);
if (slashParts.length > 1) {
    return Arrays.stream(slashParts)
            .map(String::trim)
            .filter(s -> !s.isBlank())
            .flatMap(segment -> parseReceivers(segment).stream())
            .toList();
}

Part 2 — Dot-normalization in split()

After the geb. stripping step in split(), add a dot-normalization step that applies only when the cleaned name has no spaces but contains dots:

// Normalize dot-compressed names: "Dr.Fr.Zarncke" → "Dr. Fr. Zarncke"
if (!cleaned.contains(" ") && cleaned.contains(".")) {
    cleaned = cleaned.replace(".", ". ").trim();
}

Then the existing known-last-name check and last-space fallback handle the rest:

Input After normalization Last-space fallback
E.Rockstroh E. Rockstroh ("E.", "Rockstroh")
E.M. E. M. ("E.", "M.")
Dr.Fr.Zarncke Dr. Fr. Zarncke ("Dr. Fr.", "Zarncke")
Dr.Zarnke Dr. Zarnke ("Dr.", "Zarnke")

No changes needed to parseReceivers() for Part 2 — split() is called downstream in PersonService.findOrCreateByAlias().


Files

File Change
backend/src/main/java/.../service/PersonNameParser.java Add SLASH_SEPARATOR constant + pre-split step in parseReceivers(); add dot-normalization step in split()
backend/src/test/java/.../service/PersonNameParserTest.java ~9 new tests (TDD — red first)

No schema, API, or i18n changes needed.


New Tests

Part 1 — // separator

slashSeparator_twoIndependentFullNames()
slashSeparator_abbreviatedFirstName()
slashSeparator_withSpacesAroundSlashes()
slashSeparator_segmentContainsUnd()

Part 2 — Dot-compressed names

split_dotCompressed_initialAndLastName()       // E.Rockstroh → ("E.", "Rockstroh")
split_dotCompressed_twoInitials()              // E.M.        → ("E.", "M.")
split_dotCompressed_titleFirstNameLastName()   // Dr.Fr.Zarncke → ("Dr. Fr.", "Zarncke")
split_dotCompressed_titleAndLastName()         // Dr.Zarnke   → ("Dr.", "Zarnke")
parseReceivers_dotCompressedName_passthrough() // Dr.Fr.Zarncke → ["Dr.Fr.Zarncke"]

Verification

cd backend && ./mvnw test -Dtest=PersonNameParserTest

Replaces #182 and #184.

## Problem The mass import spreadsheet contains two name patterns that `PersonNameParser` currently handles incorrectly: ### 1. `//` as multi-person separator `parseReceivers()` only splits on `und`/`u`. The `//` separator — used for independent full names in a single cell — causes both names to be imported as a single garbage person record. | Raw input | Current result | Expected result | |---|---|---| | `Charl.Blomquist//Tante Lolly` | one person | two persons | | `Walter de Gruyter//Eugenie de Gruyter` | one person | two persons | ### 2. Dot-compressed names (no spaces) The `split()` fallback only handles space-separated names, so dot-compressed names produce `lastName = "?"`: | Raw input | Current result | Expected result | |---|---|---| | `E.Rockstroh` | `("E.Rockstroh", "?")` | `("E.", "Rockstroh")` | | `E.M.` | `("E.M.", "?")` | `("E.", "M.")` | | `Dr.Fr.Zarncke` | `("Dr.Fr.Zarncke", "?")` | `("Dr. Fr.", "Zarncke")` | | `Dr.Zarnke` | `("Dr.Zarnke", "?")` | `("Dr.", "Zarnke")` | --- ## Solution ### Part 1 — `//` pre-split in `parseReceivers()` Pre-split the raw input on `//` **before** any existing logic. Each segment is then piped through the current pipeline independently, and results are flattened. Unlike `und`/`u`, `//` always separates fully independent, complete names — no shared last-name distribution is needed. The recursive approach handles mixed cases like `Herbert und Clara Cram//Eugenie de Gruyter` naturally. ```java private static final Pattern SLASH_SEPARATOR = Pattern.compile("//"); // In parseReceivers(), after null/blank check: String[] slashParts = SLASH_SEPARATOR.split(raw); if (slashParts.length > 1) { return Arrays.stream(slashParts) .map(String::trim) .filter(s -> !s.isBlank()) .flatMap(segment -> parseReceivers(segment).stream()) .toList(); } ``` ### Part 2 — Dot-normalization in `split()` After the `geb.` stripping step in `split()`, add a dot-normalization step that applies only when the cleaned name has **no spaces** but **contains dots**: ```java // Normalize dot-compressed names: "Dr.Fr.Zarncke" → "Dr. Fr. Zarncke" if (!cleaned.contains(" ") && cleaned.contains(".")) { cleaned = cleaned.replace(".", ". ").trim(); } ``` Then the existing known-last-name check and last-space fallback handle the rest: | Input | After normalization | Last-space fallback | |---|---|---| | `E.Rockstroh` | `E. Rockstroh` | `("E.", "Rockstroh")` ✓ | | `E.M.` | `E. M.` | `("E.", "M.")` ✓ | | `Dr.Fr.Zarncke` | `Dr. Fr. Zarncke` | `("Dr. Fr.", "Zarncke")` ✓ | | `Dr.Zarnke` | `Dr. Zarnke` | `("Dr.", "Zarnke")` ✓ | No changes needed to `parseReceivers()` for Part 2 — `split()` is called downstream in `PersonService.findOrCreateByAlias()`. --- ## Files | File | Change | |---|---| | `backend/src/main/java/.../service/PersonNameParser.java` | Add `SLASH_SEPARATOR` constant + pre-split step in `parseReceivers()`; add dot-normalization step in `split()` | | `backend/src/test/java/.../service/PersonNameParserTest.java` | ~9 new tests (TDD — red first) | No schema, API, or i18n changes needed. --- ## New Tests ### Part 1 — `//` separator ```java slashSeparator_twoIndependentFullNames() slashSeparator_abbreviatedFirstName() slashSeparator_withSpacesAroundSlashes() slashSeparator_segmentContainsUnd() ``` ### Part 2 — Dot-compressed names ```java split_dotCompressed_initialAndLastName() // E.Rockstroh → ("E.", "Rockstroh") split_dotCompressed_twoInitials() // E.M. → ("E.", "M.") split_dotCompressed_titleFirstNameLastName() // Dr.Fr.Zarncke → ("Dr. Fr.", "Zarncke") split_dotCompressed_titleAndLastName() // Dr.Zarnke → ("Dr.", "Zarnke") parseReceivers_dotCompressedName_passthrough() // Dr.Fr.Zarncke → ["Dr.Fr.Zarncke"] ``` ## Verification ```bash cd backend && ./mvnw test -Dtest=PersonNameParserTest ``` --- Replaces #182 and #184.
marcel added the feature label 2026-04-07 09:23:09 +02:00
Author
Owner

👨‍💻 Felix Brandt — Senior Fullstack Developer

Questions & Observations

  • Part 1 (// separator): The recursive approach is elegant — each //-segment re-enters parseReceivers(), so all existing logic (und/u splitting, last-name distribution) is reused without duplication. The mixed case Herbert und Clara Cram//Eugenie de Gruyter works naturally because recursion handles the und split on the left segment independently.
  • Part 2 (dot-normalization): The 3-line fix (replace(".", ". ").trim()) is KISS at its best — it normalizes input so the existing last-space fallback does all the work. No new branching logic, no new code paths.
  • The guard condition !cleaned.contains(" ") && cleaned.contains(".") is tight — it only fires for genuinely compressed names, avoiding interference with already-spaced names like Dr. Zarncke.
  • The SLASH_SEPARATOR constant is well-named. Since Pattern.compile is used, confirm it's static final so the regex is compiled once (as shown — just calling it out for the implementer).

Edge Cases Worth Testing

  • Trailing/leading //: //Herbert Cram or Herbert Cram// — the .filter(s -> !s.isBlank()) handles this, but an explicit test documents the contract.
  • Single-initial name: M. — after dot-normalization becomes M. → trimmed to M.. No space → falls through to SplitName("M.", "?"). Is that intentional, or should single-initial inputs be handled as a special case?
  • Interaction between Part 1 and Part 2: E.Rockstroh//Dr.Fr.Zarncke — the // split produces two segments, each of which passes through parseReceivers as a single token. The dot-normalization only happens later when split() is called in PersonService.findOrCreateByAlias(). This layering is correct, but a test confirming the full pipeline for this combined case would be valuable.

Suggestions

  • The pre-split step placement ("after null/blank check, before any other normalization") is critical. Confirm in implementation that // splitting is the very first operation in parseReceivers().
  • The dot-normalization placement ("after geb. stripping") is equally critical — a name like geb.Rockstroh should strip geb. first, leaving Rockstroh (no dots left, normalization doesn't fire). Confirm this ordering.
  • Consider ~2 additional tests: one for trailing // and one for the combined E.Rockstroh//Dr.Fr.Zarncke pipeline. These document the interaction between the two parts.
## 👨‍💻 Felix Brandt — Senior Fullstack Developer ### Questions & Observations - **Part 1 (// separator):** The recursive approach is elegant — each `//`-segment re-enters `parseReceivers()`, so all existing logic (und/u splitting, last-name distribution) is reused without duplication. The mixed case `Herbert und Clara Cram//Eugenie de Gruyter` works naturally because recursion handles the `und` split on the left segment independently. - **Part 2 (dot-normalization):** The 3-line fix (`replace(".", ". ").trim()`) is KISS at its best — it normalizes input so the existing last-space fallback does all the work. No new branching logic, no new code paths. - The guard condition `!cleaned.contains(" ") && cleaned.contains(".")` is tight — it only fires for genuinely compressed names, avoiding interference with already-spaced names like `Dr. Zarncke`. - The `SLASH_SEPARATOR` constant is well-named. Since `Pattern.compile` is used, confirm it's `static final` so the regex is compiled once (as shown — just calling it out for the implementer). ### Edge Cases Worth Testing - **Trailing/leading `//`**: `//Herbert Cram` or `Herbert Cram//` — the `.filter(s -> !s.isBlank())` handles this, but an explicit test documents the contract. - **Single-initial name**: `M.` — after dot-normalization becomes `M. ` → trimmed to `M.`. No space → falls through to `SplitName("M.", "?")`. Is that intentional, or should single-initial inputs be handled as a special case? - **Interaction between Part 1 and Part 2**: `E.Rockstroh//Dr.Fr.Zarncke` — the `//` split produces two segments, each of which passes through `parseReceivers` as a single token. The dot-normalization only happens later when `split()` is called in `PersonService.findOrCreateByAlias()`. This layering is correct, but a test confirming the full pipeline for this combined case would be valuable. ### Suggestions - The pre-split step placement ("after null/blank check, before any other normalization") is critical. Confirm in implementation that `//` splitting is the very first operation in `parseReceivers()`. - The dot-normalization placement ("after `geb.` stripping") is equally critical — a name like `geb.Rockstroh` should strip `geb.` first, leaving `Rockstroh` (no dots left, normalization doesn't fire). Confirm this ordering. - Consider ~2 additional tests: one for trailing `//` and one for the combined `E.Rockstroh//Dr.Fr.Zarncke` pipeline. These document the interaction between the two parts.
Author
Owner

🏗️ Markus Keller — Application Architect

Questions & Observations

  • Both changes are well-scoped and stay within the same file — a single utility class gets two small additions, no new classes, no schema changes, no API surface changes. This is the right level of intervention for parser enhancements.
  • The architectural approach is sound in both parts:
    • Part 1 reuses the existing pipeline via recursion instead of duplicating logic — each //-segment re-enters parseReceivers().
    • Part 2 normalizes input to match what the existing split() already handles, rather than adding a parallel code path. This keeps method branching complexity flat.
  • No cross-domain impact. PersonNameParser is a pure utility within the service layer; parseReceivers() is called from MassImportService, split() is called from PersonService.findOrCreateByAlias(). Neither change is visible to callers.

Suggestions

  • Single / vs //: Does the spreadsheet data ever contain a single / as a separator? If so, the // pattern won't match and those cases will silently pass through as single names. Worth verifying against actual import data.
  • Growing normalization complexity: PersonNameParser is accumulating normalization steps — geb. stripping, known last names, now // pre-split and dot-normalization. Not a problem yet, but if more normalization steps are added in the future, consider extracting each into a named method and composing them as a small pipeline. The current approach (sequential if-checks) is fine for 4 steps; it starts to get hard to reason about at 6+.
  • Bundling both changes in one issue is the right call — they're both PersonNameParser enhancements with the same test scope and no external dependencies. One branch, one PR, one review cycle.
## 🏗️ Markus Keller — Application Architect ### Questions & Observations - Both changes are well-scoped and stay within the same file — a single utility class gets two small additions, no new classes, no schema changes, no API surface changes. This is the right level of intervention for parser enhancements. - The architectural approach is sound in both parts: - **Part 1** reuses the existing pipeline via recursion instead of duplicating logic — each `//`-segment re-enters `parseReceivers()`. - **Part 2** normalizes input to match what the existing `split()` already handles, rather than adding a parallel code path. This keeps method branching complexity flat. - No cross-domain impact. `PersonNameParser` is a pure utility within the service layer; `parseReceivers()` is called from `MassImportService`, `split()` is called from `PersonService.findOrCreateByAlias()`. Neither change is visible to callers. ### Suggestions - **Single `/` vs `//`**: Does the spreadsheet data ever contain a single `/` as a separator? If so, the `//` pattern won't match and those cases will silently pass through as single names. Worth verifying against actual import data. - **Growing normalization complexity**: `PersonNameParser` is accumulating normalization steps — `geb.` stripping, known last names, now `//` pre-split and dot-normalization. Not a problem yet, but if more normalization steps are added in the future, consider extracting each into a named method and composing them as a small pipeline. The current approach (sequential if-checks) is fine for 4 steps; it starts to get hard to reason about at 6+. - **Bundling both changes in one issue is the right call** — they're both PersonNameParser enhancements with the same test scope and no external dependencies. One branch, one PR, one review cycle.
Author
Owner

🧪 Sara Holt — QA Engineer

Questions & Observations

  • The 9 proposed tests are well-structured with clear separation: 4 tests for // splitting (in parseReceivers) and 5 tests for dot-normalization (4 in split() + 1 parseReceivers passthrough). Good layered coverage.
  • The parseReceivers_dotCompressedName_passthrough test is particularly valuable — it confirms layer separation by proving that parseReceivers treats Dr.Fr.Zarncke as a single token and doesn't try to split on dots.
  • The slashSeparator_segmentContainsUnd test exercises the interaction between // splitting and und splitting via recursion — this is the highest-value test in the set.

Missing Edge Case Tests

Part 1 — // separator:

  • Empty/trailing segments: Herbert Cram// or //Herbert Cram — does the blank filter correctly produce one person?
  • Multiple consecutive separators: Herbert Cram////Eugenie — do empty segments get filtered cleanly?
  • Regression: Confirm that inputs without // still pass through unchanged (probably covered by existing tests, but worth noting).

Part 2 — Dot-compressed names:

  • Already-spaced names: Dr. Fr. Zarncke — the guard !cleaned.contains(" ") should prevent normalization. A regression test confirming no double-spacing is important.
  • Trailing dot only: Rockstroh. — after normalization becomes Rockstroh. → trimmed to Rockstroh.. How does the last-space fallback handle this?
  • Multiple consecutive dots: E..Rockstroh — after replace(".", ". ") becomes E. . Rockstroh. Does the fallback handle the extra whitespace?

Cross-part interaction:

  • E.Rockstroh//Dr.Fr.Zarncke — full pipeline test confirming both features work together end-to-end.

Suggestions

  • I'd recommend running the full PersonNameParserTest class after implementation, not just the new tests — both changes touch shared code paths, and regressions in existing tests would catch guard condition edge cases.
  • Also run MassImportServiceTest (if it exists) to catch integration-level regressions in the import pipeline.
## 🧪 Sara Holt — QA Engineer ### Questions & Observations - The 9 proposed tests are well-structured with clear separation: 4 tests for `//` splitting (in `parseReceivers`) and 5 tests for dot-normalization (4 in `split()` + 1 `parseReceivers` passthrough). Good layered coverage. - The `parseReceivers_dotCompressedName_passthrough` test is particularly valuable — it confirms layer separation by proving that `parseReceivers` treats `Dr.Fr.Zarncke` as a single token and doesn't try to split on dots. - The `slashSeparator_segmentContainsUnd` test exercises the interaction between `//` splitting and `und` splitting via recursion — this is the highest-value test in the set. ### Missing Edge Case Tests **Part 1 — `//` separator:** - **Empty/trailing segments**: `Herbert Cram//` or `//Herbert Cram` — does the blank filter correctly produce one person? - **Multiple consecutive separators**: `Herbert Cram////Eugenie` — do empty segments get filtered cleanly? - **Regression**: Confirm that inputs without `//` still pass through unchanged (probably covered by existing tests, but worth noting). **Part 2 — Dot-compressed names:** - **Already-spaced names**: `Dr. Fr. Zarncke` — the guard `!cleaned.contains(" ")` should prevent normalization. A regression test confirming no double-spacing is important. - **Trailing dot only**: `Rockstroh.` — after normalization becomes `Rockstroh. ` → trimmed to `Rockstroh.`. How does the last-space fallback handle this? - **Multiple consecutive dots**: `E..Rockstroh` — after `replace(".", ". ")` becomes `E. . Rockstroh`. Does the fallback handle the extra whitespace? **Cross-part interaction:** - `E.Rockstroh//Dr.Fr.Zarncke` — full pipeline test confirming both features work together end-to-end. ### Suggestions - I'd recommend running the **full** `PersonNameParserTest` class after implementation, not just the new tests — both changes touch shared code paths, and regressions in existing tests would catch guard condition edge cases. - Also run `MassImportServiceTest` (if it exists) to catch integration-level regressions in the import pipeline.
Author
Owner

🔒 Nora "NullX" Steiner — Security Engineer

Questions & Observations

  • Both changes are pure parsing logic in a backend utility class that processes spreadsheet data from authenticated admin users (mass import). The attack surface is minimal — this is not processing untrusted external input in a web request context.
  • Part 1: The recursive call parseReceivers(segment) is bounded by the number of //-segments in a single spreadsheet cell, which is naturally small. No risk of stack overflow from realistic input.
  • Part 2: String.replace(".", ". ") is a literal string replacement, not regex — no risk of ReDoS or regex injection.
  • No new endpoints, no new user input vectors, no changes to authentication or authorization.

Suggestions

  • No security concerns from my angle. Both changes are confined to a trusted-input parser within the service layer. No new attack vectors introduced.
## 🔒 Nora "NullX" Steiner — Security Engineer ### Questions & Observations - Both changes are pure parsing logic in a backend utility class that processes spreadsheet data from authenticated admin users (mass import). The attack surface is minimal — this is not processing untrusted external input in a web request context. - **Part 1**: The recursive call `parseReceivers(segment)` is bounded by the number of `//`-segments in a single spreadsheet cell, which is naturally small. No risk of stack overflow from realistic input. - **Part 2**: `String.replace(".", ". ")` is a literal string replacement, not regex — no risk of ReDoS or regex injection. - No new endpoints, no new user input vectors, no changes to authentication or authorization. ### Suggestions - No security concerns from my angle. Both changes are confined to a trusted-input parser within the service layer. No new attack vectors introduced.
Author
Owner

🎨 Leonie Voss — UI/UX Design Lead

Questions & Observations

  • This is a backend-only parsing change with no direct UI impact. No frontend components, routes, or user-facing text are modified.
  • Indirect UX benefit: Once these names are correctly parsed, person lists and document metadata will show clean names (E. Rockstroh, Dr. Fr. Zarncke) instead of garbage entries with lastName = "?". This improves data quality throughout the UI without any frontend code changes.
  • The // separator and dot-compressed patterns are conventions within the import spreadsheet, not something end users interact with through the web interface.

Suggestions

  • No design concerns. The improvement is invisible to users except through better data quality in person records — which is a good thing.
## 🎨 Leonie Voss — UI/UX Design Lead ### Questions & Observations - This is a backend-only parsing change with no direct UI impact. No frontend components, routes, or user-facing text are modified. - **Indirect UX benefit**: Once these names are correctly parsed, person lists and document metadata will show clean names (`E. Rockstroh`, `Dr. Fr. Zarncke`) instead of garbage entries with `lastName = "?"`. This improves data quality throughout the UI without any frontend code changes. - The `//` separator and dot-compressed patterns are conventions within the import spreadsheet, not something end users interact with through the web interface. ### Suggestions - No design concerns. The improvement is invisible to users except through better data quality in person records — which is a good thing.
Author
Owner

🛠️ Tobias Wendt — DevOps Engineer

Questions & Observations

  • Pure Java code change — no new dependencies, no config changes, no Docker, CI, or deployment modifications needed.
  • The verification command (./mvnw test -Dtest=PersonNameParserTest) runs fast unit tests only — no Testcontainers or external services required. Zero CI time impact.
  • Both parts touch the same file and test file, so this is a clean single-PR change with no infrastructure concerns.

Suggestions

  • No concerns from my angle. This is a self-contained parser enhancement that doesn't touch infrastructure, dependencies, or deployment configuration.
## 🛠️ Tobias Wendt — DevOps Engineer ### Questions & Observations - Pure Java code change — no new dependencies, no config changes, no Docker, CI, or deployment modifications needed. - The verification command (`./mvnw test -Dtest=PersonNameParserTest`) runs fast unit tests only — no Testcontainers or external services required. Zero CI time impact. - Both parts touch the same file and test file, so this is a clean single-PR change with no infrastructure concerns. ### Suggestions - No concerns from my angle. This is a self-contained parser enhancement that doesn't touch infrastructure, dependencies, or deployment configuration.
Author
Owner

Implementation Complete

All tasks done on branch feat/issue-190-slash-separator-dot-compressed.

Commits

  1. 59475ef feat(parser): support // as multi-person separator in parseReceivers - Pre-splits on // before existing logic; each segment recurses through the full pipeline. 5 new tests.
  2. 0b57717 feat(parser): normalize dot-compressed names in split() - Inserts spaces after dots when no spaces present, so E.Rockstroh -> ("E.", "Rockstroh"). 5 new tests.
  3. d6e7497 test(parser): add regression and cross-feature interaction tests - Confirms already-spaced names aren't double-spaced; confirms // + dot-compressed work together. 2 new tests.

Test Results

  • 35 tests in PersonNameParserTest (was 23) - all green
  • Backend build passes (mvnw clean package -DskipTests)

Files Changed

File Change
PersonNameParser.java SLASH_SEPARATOR constant + pre-split in parseReceivers(); dot-normalization in split()
PersonNameParserTest.java 12 new tests (5 slash separator, 5 dot-compressed, 2 regression/interaction)
## Implementation Complete All tasks done on branch `feat/issue-190-slash-separator-dot-compressed`. ### Commits 1. `59475ef` **feat(parser): support // as multi-person separator in parseReceivers** - Pre-splits on `//` before existing logic; each segment recurses through the full pipeline. 5 new tests. 2. `0b57717` **feat(parser): normalize dot-compressed names in split()** - Inserts spaces after dots when no spaces present, so `E.Rockstroh` -> `("E.", "Rockstroh")`. 5 new tests. 3. `d6e7497` **test(parser): add regression and cross-feature interaction tests** - Confirms already-spaced names aren't double-spaced; confirms `//` + dot-compressed work together. 2 new tests. ### Test Results - **35 tests** in `PersonNameParserTest` (was 23) - all green - Backend build passes (`mvnw clean package -DskipTests`) ### Files Changed | File | Change | |---|---| | `PersonNameParser.java` | `SLASH_SEPARATOR` constant + pre-split in `parseReceivers()`; dot-normalization in `split()` | | `PersonNameParserTest.java` | 12 new tests (5 slash separator, 5 dot-compressed, 2 regression/interaction) |
Sign in to join this conversation.
No Label feature
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: marcel/familienarchiv#190