|
| 1 | +from nameparser import HumanName |
| 2 | +from nameparser.config import Constants |
| 3 | +from tests.base import HumanNameTestBase |
| 4 | + |
| 5 | + |
| 6 | +class MiddleNameAsLastFlagTests(HumanNameTestBase): |
| 7 | + |
| 8 | + def test_default_is_false(self) -> None: |
| 9 | + C = Constants() |
| 10 | + assert C.middle_name_as_last is False |
| 11 | + |
| 12 | + def test_can_set_true_via_constructor(self) -> None: |
| 13 | + C = Constants(middle_name_as_last=True) |
| 14 | + assert C.middle_name_as_last is True |
| 15 | + |
| 16 | + def test_does_not_affect_other_instance(self) -> None: |
| 17 | + C1 = Constants(middle_name_as_last=True) |
| 18 | + C2 = Constants() |
| 19 | + assert C1.middle_name_as_last is True |
| 20 | + assert C2.middle_name_as_last is False |
| 21 | + |
| 22 | + |
| 23 | +class MiddleNameAsLastFoldTests(HumanNameTestBase): |
| 24 | + |
| 25 | + def setup_method(self) -> None: |
| 26 | + self.C = Constants(middle_name_as_last=True) |
| 27 | + |
| 28 | + def hn(self, name: str) -> HumanName: |
| 29 | + return HumanName(name, constants=self.C) |
| 30 | + |
| 31 | + def test_fold_no_comma(self) -> None: |
| 32 | + n = self.hn("Mohamad Ahmad Ali Hassan") |
| 33 | + self.m(n.first, "Mohamad", n) |
| 34 | + self.m(n.middle, "", n) |
| 35 | + self.m(n.last, "Ahmad Ali Hassan", n) |
| 36 | + |
| 37 | + def test_fold_comma_converges(self) -> None: |
| 38 | + no_comma = self.hn("Mohamad Ahmad Ali Hassan") |
| 39 | + comma = self.hn("Hassan, Mohamad Ahmad Ali") |
| 40 | + self.m(comma.first, no_comma.first, comma) |
| 41 | + self.m(comma.last, no_comma.last, comma) |
| 42 | + |
| 43 | + def test_title_and_suffix_preserved(self) -> None: |
| 44 | + n = self.hn("Dr. Mohamad Ahmad Hassan Jr") |
| 45 | + self.m(n.title, "Dr.", n) |
| 46 | + self.m(n.last, "Ahmad Hassan", n) |
| 47 | + self.m(n.suffix, "Jr", n) |
| 48 | + |
| 49 | + def test_suffix_preserved_comma_format(self) -> None: |
| 50 | + # Comma-delimited suffix takes a different code path than the |
| 51 | + # title/suffix no-comma case above; the fold must still apply. |
| 52 | + n = self.hn("Hassan, Mohamad Ahmad Ali, Jr.") |
| 53 | + self.m(n.first, "Mohamad", n) |
| 54 | + self.m(n.middle, "", n) |
| 55 | + self.m(n.last, "Ahmad Ali Hassan", n) |
| 56 | + self.m(n.suffix, "Jr.", n) |
| 57 | + |
| 58 | + def test_nickname_preserved(self) -> None: |
| 59 | + # Nicknames are stripped in pre_process(), before the fold runs. |
| 60 | + n = self.hn('Mohamad "Mo" Ahmad Ali Hassan') |
| 61 | + self.m(n.nickname, "Mo", n) |
| 62 | + self.m(n.middle, "", n) |
| 63 | + self.m(n.last, "Ahmad Ali Hassan", n) |
| 64 | + |
| 65 | + def test_no_middle_is_noop(self) -> None: |
| 66 | + n = self.hn("John Doe") |
| 67 | + self.m(n.first, "John", n) |
| 68 | + self.m(n.middle, "", n) |
| 69 | + self.m(n.last, "Doe", n) |
| 70 | + |
| 71 | + def test_single_token_is_noop(self) -> None: |
| 72 | + n = self.hn("Cher") |
| 73 | + self.m(n.first, "Cher", n) |
| 74 | + self.m(n.middle, "", n) |
| 75 | + self.m(n.last, "", n) |
| 76 | + |
| 77 | + def test_given_names_and_surnames_track_fold(self) -> None: |
| 78 | + n = self.hn("Mohamad Ahmad Ali Hassan") |
| 79 | + self.m(n.given_names, n.first, n) |
| 80 | + self.m(n.surnames, n.last, n) |
| 81 | + |
| 82 | + def test_last_prefixes_still_split_after_fold(self) -> None: |
| 83 | + # Unfolded this is first="Miguel", middle="da Silva do Amaral", |
| 84 | + # last="de Souza" (last_prefixes="de"). Folded, last_list becomes |
| 85 | + # ["da","Silva","do","Amaral","de","Souza"]; _split_last() strips |
| 86 | + # leading contiguous prefix words from the start, so only the |
| 87 | + # leading "da" is stripped ("Silva" is not a prefix, so scanning |
| 88 | + # stops there) — last_prefixes="da", not "de". |
| 89 | + n = self.hn("Miguel da Silva do Amaral de Souza") |
| 90 | + self.m(n.last_prefixes, "da", n) |
| 91 | + |
| 92 | + |
| 93 | +class MiddleNameAsLastFlagOffTests(HumanNameTestBase): |
| 94 | + |
| 95 | + def test_default_constants_unaffected(self) -> None: |
| 96 | + n = HumanName("Mohamad Ahmad Ali Hassan") |
| 97 | + self.m(n.middle, "Ahmad Ali", n) |
| 98 | + self.m(n.last, "Hassan", n) |
| 99 | + |
| 100 | + |
| 101 | +class MiddleNameAsLastWithPatronymicOrderTests(HumanNameTestBase): |
| 102 | + """Both localization flags on: patronymic reordering must settle |
| 103 | + first/middle/last before the fold collapses middle into last, per the |
| 104 | + design's stated ordering rationale (post_process() runs the patronymic |
| 105 | + hook before the middle_name_as_last hook).""" |
| 106 | + |
| 107 | + def setup_method(self) -> None: |
| 108 | + self.C = Constants(middle_name_as_last=True, patronymic_name_order=True) |
| 109 | + |
| 110 | + def hn(self, name: str) -> HumanName: |
| 111 | + return HumanName(name, constants=self.C) |
| 112 | + |
| 113 | + def test_rotate_then_fold_no_comma(self) -> None: |
| 114 | + # patronymic_name_order rotates "Ivanov Petr Sergeyevich" to |
| 115 | + # first=Petr, middle=Sergeyevich, last=Ivanov; the fold then |
| 116 | + # collapses that settled middle into last. |
| 117 | + n = self.hn("Ivanov Petr Sergeyevich") |
| 118 | + self.m(n.first, "Petr", n) |
| 119 | + self.m(n.middle, "", n) |
| 120 | + self.m(n.last, "Sergeyevich Ivanov", n) |
| 121 | + |
| 122 | + def test_fold_applies_even_when_comma_suppresses_rotation(self) -> None: |
| 123 | + # A comma suppresses patronymic_name_order's rotation (_had_comma |
| 124 | + # guard), so middle stays "Sergeyevich" unrotated going into the |
| 125 | + # fold. The fold still absorbs it into last, producing the same |
| 126 | + # first/last as the no-comma case above via a different mechanism. |
| 127 | + n = self.hn("Ivanov, Petr Sergeyevich") |
| 128 | + self.m(n.first, "Petr", n) |
| 129 | + self.m(n.middle, "", n) |
| 130 | + self.m(n.last, "Sergeyevich Ivanov", n) |
0 commit comments