+import pytest
from unittest.mock import MagicMock
+import sys
+import os
-import pytest
+# Ensure we can import the module from the root directory
+sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
+from getmyancestors.classes.session import Session
@pytest.fixture
-def mock_user_data():
- return {
- "users": [
- {
- "personId": "KW7V-Y32",
- "preferredLanguage": "en",
- "displayName": "Test User",
- }
- ]
- }
+def mock_session():
+ """
+ Creates a Session object where the network layer is mocked out.
+ """
+ # Create the session but suppress the automatic login() call in __init__
+ # We do this by mocking the login method *before* instantiation
+ with pytest.helpers.patch_method(Session, 'login'):
+ session = Session("test_user", "test_pass", verbose=False)
+
+ # Manually set logged status to True so checks pass
+ # We need to mock the cookies since 'logged' property checks for 'fssessionid'
+ session.cookies = {"fssessionid": "mock_session_id"}
+ # Mock the request methods
+ session.get = MagicMock()
+ session.post = MagicMock()
+
+ # Mock the internal translation method to just return the string
+ session._ = lambda s: s
+
+ return session
@pytest.fixture
-def mock_person_data():
+def sample_person_json():
+ """Returns a raw JSON response representing a Person from FamilySearch"""
return {
- "persons": [
- {
- "id": "KW7V-Y32",
- "display": {
- "name": "John Doe",
- "gender": "Male",
- "lifespan": "1900-1980",
- },
- "facts": [
- {
- "type": "http://gedcomx.org/Birth",
- "date": {"original": "1 Jan 1900"},
- }
- ],
- "names": [{"nameForms": [{"fullText": "John Doe"}]}],
- }
- ]
+ "persons": [{
+ "id": "KW7V-Y32",
+ "display": {
+ "name": "John Doe",
+ "gender": "Male",
+ "lifespan": "1900-1980"
+ },
+ "facts": [
+ {
+ "type": "http://gedcomx.org/Birth",
+ "date": {"original": "1 Jan 1900"},
+ "place": {"original": "New York"}
+ }
+ ],
+ "names": [
+ {
+ "nameForms": [{"fullText": "John Doe"}]
+ }
+ ]
+ }]
}
+
+# Helper to patch methods cleanly in fixtures
+class Helpers:
+ @staticmethod
+ def patch_method(cls, method_name):
+ from unittest.mock import patch
+ return patch.object(cls, method_name)
+
+@pytest.fixture
+def helpers():
+ return Helpers
+import pytest
import sys
-from unittest.mock import MagicMock, patch
+from unittest.mock import patch, MagicMock
+from getmyancestors.getmyancestors import main
-import pytest
+class TestCLI:
-from getmyancestors.getmyancestors import main
+ @patch('getmyancestors.getmyancestors.Session')
+ @patch('getmyancestors.getmyancestors.Tree')
+ def test_basic_args(self, MockTree, MockSession):
+ """Test that arguments are parsed and passed to classes correctly"""
+ # Mock sys.argv to simulate command line execution
+ test_args = [
+ "getmyancestors",
+ "-u", "myuser",
+ "-p", "mypass",
+ "-i", "KW7V-Y32",
+ "--verbose"
+ ]
-class TestCLI:
+ # Setup the session to appear logged in
+ MockSession.return_value.logged = True
+
+ with patch.object(sys, 'argv', test_args):
+ main()
- @patch("getmyancestors.getmyancestors.Session")
- @patch("getmyancestors.getmyancestors.Tree")
- @patch(
- "sys.argv",
- ["getmyancestors", "-u", "testuser", "-p", "testpass", "-i", "KW7V-Y32"],
- )
- def test_main_execution(self, mock_tree, mock_session):
- """Test that main runs with basic arguments."""
- mock_fs = mock_session.return_value
- mock_fs.logged = True
-
- # Run main
- main()
-
- # Verify Session initialized with args
- mock_session.assert_called_with(
- username="testuser",
- password="testpass",
- client_id=None,
- redirect_uri=None,
- verbose=False,
- logfile=False,
- timeout=60,
+ # Verify Session was initialized with CLI args
+ MockSession.assert_called_with(
+ "myuser",
+ "mypass",
+ None, # client_id (default)
+ None, # redirect_uri (default)
+ True, # verbose
+ False, # logfile
+ 60 # timeout
)
- # Verify Tree operations
- mock_tree.return_value.add_indis.assert_called()
- mock_tree.return_value.print.assert_called()
-
- @patch("getmyancestors.getmyancestors.Session")
- @patch(
- "sys.argv",
- ["getmyancestors", "-u", "testuser", "-p", "testpass", "--descend", "2"],
- )
- def test_descend_argument(self, mock_session):
- """Test that the descend argument is passed to logic."""
- mock_fs = mock_session.return_value
- mock_fs.logged = True
-
- # We need to mock Tree because main interacts with it deeply
- with patch("getmyancestors.getmyancestors.Tree") as mock_tree:
- mock_tree_instance = mock_tree.return_value
- # Return empty sets to stop loops
- mock_tree_instance.add_children.return_value = set()
+ # Verify Tree started
+ MockTree.return_value.add_indis.assert_called_with(["KW7V-Y32"])
- main()
+ def test_arg_validation(self):
+ """Test that invalid ID formats cause an exit"""
+ test_args = ["getmyancestors", "-u", "u", "-p", "p", "-i", "BAD_ID"]
- # Verify add_children was called (logic inside main triggers this based on args.descend)
- assert mock_tree_instance.add_children.called
+ with patch.object(sys, 'argv', test_args):
+ with pytest.raises(SystemExit):
+ # This should trigger sys.exit("Invalid FamilySearch ID...")
+ main()
--- /dev/null
+import pytest
+from unittest.mock import MagicMock
+from getmyancestors.classes.tree import Tree, Indi, Fam
+
+class TestDataParsing:
+
+ def test_individual_parsing(self, mock_session, sample_person_json):
+ """
+ Verify that raw JSON from FamilySearch is correctly parsed into an Indi object.
+ """
+ # Setup the mock to return our sample JSON when get_url is called
+ mock_session.get_url = MagicMock(return_value=sample_person_json)
+
+ tree = Tree(mock_session)
+
+ # Act: Add the individual
+ tree.add_indis(["KW7V-Y32"])
+
+ # Assert: Check if the individual exists in the tree
+ assert "KW7V-Y32" in tree.indi
+ person = tree.indi["KW7V-Y32"]
+
+ # Assert: Check attributes
+ assert person.name == "John Doe"
+ assert person.sex == "M"
+ assert person.fid == "KW7V-Y32"
+
+ # Check if Birth fact was parsed (this tests your Fact class logic implicitly)
+ birth_fact = next((f for f in person.facts if f.tag == "BIRT"), None)
+ assert birth_fact is not None
+ assert birth_fact.date == "1 Jan 1900"
+ assert birth_fact.place == "New York"
+
+ def test_family_linking(self, mock_session):
+ """
+ Verify that ensure_family links husband and wife correctly.
+ """
+ tree = Tree(mock_session)
+
+ # Create dummy individuals
+ husb = Indi("HUSB01", tree)
+ wife = Indi("WIFE01", tree)
+
+ # Create family
+ fam = tree.ensure_family(husb, wife)
+
+ # Assertions
+ assert fam.husband == husb
+ assert fam.wife == wife
+
+ # Check that the individuals know about the family
+ assert fam in husb.fams
+ assert fam in wife.fams
+
+ # Ensure creating the same family again returns the same object
+ fam2 = tree.ensure_family(husb, wife)
+ assert fam is fam2