From: Shane Jaroch Date: Sat, 24 Jan 2026 06:50:06 +0000 (-0500) Subject: wip fixes for manual tests and save "main" name X-Git-Url: https://git.nutra.tk/v1?a=commitdiff_plain;h=4834fbf6a63ce59052a05deb166de7905616d813;p=gamesguru%2Fgetmyancestors.git wip fixes for manual tests and save "main" name --- diff --git a/getmyancestors/classes/tree/core.py b/getmyancestors/classes/tree/core.py index 67ee680..718cc72 100644 --- a/getmyancestors/classes/tree/core.py +++ b/getmyancestors/classes/tree/core.py @@ -121,17 +121,30 @@ class Indi: self.living = data["living"] for x in data["names"]: alt = not x.get("preferred", False) + name_kind = None + target_set = None + if x["type"] == "http://gedcomx.org/Nickname": - self.nicknames.add(Name(x, self.tree, self.fid, "nickname", alt)) + name_kind = "nickname" + target_set = self.nicknames elif x["type"] == "http://gedcomx.org/BirthName": - self.birthnames.add(Name(x, self.tree, self.fid, "birthname", alt)) + name_kind = "birthname" + target_set = self.birthnames elif x["type"] == "http://gedcomx.org/AlsoKnownAs": - self.aka.add(Name(x, self.tree, self.fid, "aka", alt)) + name_kind = "aka" + target_set = self.aka elif x["type"] == "http://gedcomx.org/MarriedName": - self.married.add(Name(x, self.tree, self.fid, "married", alt)) + name_kind = "married" + target_set = self.married else: print("Unknown name type: " + x.get("type"), file=sys.stderr) raise ValueError("Unknown name type") + + name_obj = Name(x, self.tree, self.fid, name_kind, alt) + target_set.add(name_obj) + + if not alt and not self.name: + self.name = name_obj if "gender" in data: if data["gender"]["type"] == "http://gedcomx.org/Male": self.gender = "M" @@ -166,7 +179,7 @@ class Indi: sources = self.tree.fs.get_url( "/platform/tree/persons/%s/sources" % self.fid ) - if sources: + if sources and "sourceDescriptions" in sources and "persons" in sources: for quote in sources["persons"][0]["sources"]: source_id = quote["descriptionId"] source_data = next( @@ -589,18 +602,20 @@ class Fam: sources = self.tree.fs.get_url( "/platform/tree/couple-relationships/%s/sources" % self.fid ) - for source in sources["sourceDescriptions"]: - if ( - source["id"] in new_sources - and source["id"] not in self.tree.sources - ): - self.tree.sources[source["id"]] = Source( - source, self.tree - ) + if sources and "sourceDescriptions" in sources: + for source in sources["sourceDescriptions"]: + if ( + source["id"] in new_sources + and source["id"] not in self.tree.sources + ): + self.tree.sources[source["id"]] = Source( + source, self.tree + ) for source_fid, change_message in quotes.items(): - self.sources.add( - (self.tree.sources[source_fid], change_message) - ) + if source_fid in self.tree.sources: + self.sources.add( + (self.tree.sources[source_fid], change_message) + ) def get_notes(self): """retrieve marriage notes""" diff --git a/getmyancestors/tests/test_fam_marriage.py b/getmyancestors/tests/test_fam_marriage.py new file mode 100644 index 0000000..ddb009e --- /dev/null +++ b/getmyancestors/tests/test_fam_marriage.py @@ -0,0 +1,80 @@ +"""Tests based on live failures, fixes exceptions found in manual testing""" + +import pytest + +from getmyancestors.classes.tree.core import Fam, Tree + + +def test_add_marriage_none_sources(mock_session): + """ + Test that add_marriage handles cases where the sources fetch returns None. + This regression test ensures that a TypeError is not raised when sources is None. + """ + tree = Tree(fs=mock_session) + tree.sources = {} + + fam = Fam(tree=tree, num="F1") + fam.fid = "FAM123" + + # Mock the sequence of calls: + # 1. Fetch relationship details (returns valid data with source references) + # 2. Fetch source descriptions (returns None, simulating the bug) + mock_session.get_url.side_effect = [ + { + "relationships": [ + {"facts": [], "sources": [{"descriptionId": "S1", "attribution": {}}]} + ] + }, + None, + ] + + # This should not parse TypeError + try: + fam.add_marriage("FAM123") + except TypeError: + pytest.fail("add_marriage raised TypeError when sources was None") + + +def test_add_marriage_missing_source_key(mock_session): + """ + Test that add_marriage verifies source existence in tree.sources before accessing it. + This prevents KeyErrors when a referenced source was not successfully fetched. + """ + tree = Tree(fs=mock_session) + tree.sources = {} + + fam = Fam(tree=tree, num="F1") + fam.fid = "FAM_KEYERROR" + + source_id = "MISSING_SOURCE_ID" + + # Mock the sequence: + # 1. Fetch relationship details (referencing a source) + # 2. Fetch source details (returns None, so the source is never added to tree.sources) + mock_session.get_url.side_effect = [ + { + "relationships": [ + { + "facts": [], + "sources": [ + { + "descriptionId": source_id, + "attribution": {"changeMessage": "msg"}, + } + ], + } + ] + }, + None, + ] + + # This should not raise KeyError + try: + fam.add_marriage("FAM_KEYERROR") + except KeyError: + pytest.fail( + "add_marriage raised KeyError when source_fid was missing from tree.sources" + ) + + # Verify that the invalid source was truly skipped + assert len(fam.sources) == 0 diff --git a/getmyancestors/tests/test_indi_name.py b/getmyancestors/tests/test_indi_name.py new file mode 100644 index 0000000..ee9924d --- /dev/null +++ b/getmyancestors/tests/test_indi_name.py @@ -0,0 +1,78 @@ + + +from getmyancestors.classes.tree.core import Indi, Tree + + +def test_indi_name_population(mock_session): + """ + Test that Indi.add_data correctly populates self.name when a preferred name is present. + """ + tree = Tree(fs=mock_session) + indi = Indi(fid="INDI123", tree=tree) + + data = { + "living": False, + "names": [ + { + "type": "http://gedcomx.org/BirthName", + "preferred": True, + "nameForms": [ + { + "parts": [ + {"type": "http://gedcomx.org/Given", "value": "John"}, + {"type": "http://gedcomx.org/Surname", "value": "Doe"}, + ] + } + ], + }, + { + "type": "http://gedcomx.org/Nickname", + "preferred": False, + "nameForms": [ + {"parts": [{"type": "http://gedcomx.org/Given", "value": "Johnny"}]} + ], + }, + ], + } + + indi.add_data(data) + + assert indi.name is not None + assert indi.name.given == "John" + assert indi.name.surname == "Doe" + assert indi.name.kind == "birthname" + + # Verify other names are still added + assert len(indi.nicknames) == 1 + nickname = list(indi.nicknames)[0] + assert nickname.given == "Johnny" + + +def test_indi_name_population_no_preferred(mock_session): + """ + Test fallback behavior or at least ensure no crash if no preferred name (though typically FS provides one). + Logic dictates self.name might remain None or be set if logic allows (current logic only sets if not alt). + """ + tree = Tree(fs=mock_session) + indi = Indi(fid="INDI124", tree=tree) + + data = { + "living": False, + "names": [ + { + "type": "http://gedcomx.org/BirthName", + "preferred": False, # All are alternate + "nameForms": [ + {"parts": [{"type": "http://gedcomx.org/Given", "value": "John"}]} + ], + } + ], + } + + indi.add_data(data) + + # Based on current logic "if not alt and not self.name", self.name will remain None if all are alt. + # This is acceptable behavior for now, or we might want to relax it. + # For now, just asserting it doesn't crash. + assert indi.name is None + assert len(indi.birthnames) == 1 diff --git a/pyproject.toml b/pyproject.toml index 2fb989a..b0f6009 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -148,7 +148,7 @@ source = ["getmyancestors"] data_file = ".tmp/.coverage" [tool.coverage.report] -fail_under = 45.00 +fail_under = 67.98 precision = 2 show_missing = true @@ -161,4 +161,4 @@ omit = [ "**/tests/**" # do NOT show coverage tests... redundant ] -exclude_lines = ["pragma: no cover"] \ No newline at end of file +exclude_lines = ["pragma: no cover"] diff --git a/res/testdata b/res/testdata index b308495..741c02c 160000 --- a/res/testdata +++ b/res/testdata @@ -1 +1 @@ -Subproject commit b3084953c34c25c3867bcc1e000dfb32e59ae6f5 +Subproject commit 741c02ced0efd708044045e855cb7fafd6eb6aa8