|
| 1 | +"""Test the utility functions.""" |
| 2 | + |
| 3 | +import datetime |
| 4 | +from unittest.mock import patch |
| 5 | + |
| 6 | +import pytest |
| 7 | + |
| 8 | +from gedcom7 import types, util |
| 9 | + |
| 10 | + |
| 11 | +def test_get_child_with_tag(): |
| 12 | + """Test getting a child structure by tag.""" |
| 13 | + # Create parent structure with children |
| 14 | + parent = types.GedcomStructure(tag="PARENT", pointer="", text="", xref="") |
| 15 | + child1 = types.GedcomStructure(tag="CHILD1", pointer="", text="Child 1", xref="") |
| 16 | + child2 = types.GedcomStructure(tag="CHILD2", pointer="", text="Child 2", xref="") |
| 17 | + child3 = types.GedcomStructure( |
| 18 | + tag="CHILD1", pointer="", text="Child 3", xref="" |
| 19 | + ) # Duplicate tag |
| 20 | + |
| 21 | + parent.children = [child1, child2, child3] |
| 22 | + |
| 23 | + # Test finding the first child with tag CHILD1 |
| 24 | + result = util.get_first_child_with_tag(parent, "CHILD1") |
| 25 | + assert result == child1 |
| 26 | + assert result.text == "Child 1" |
| 27 | + |
| 28 | + # Test finding child with tag CHILD2 |
| 29 | + result = util.get_first_child_with_tag(parent, "CHILD2") |
| 30 | + assert result == child2 |
| 31 | + assert result.text == "Child 2" |
| 32 | + |
| 33 | + |
| 34 | +def test_no_matching_child(): |
| 35 | + """Test when no child matches the given tag.""" |
| 36 | + parent = types.GedcomStructure(tag="PARENT", pointer="", text="", xref="") |
| 37 | + child = types.GedcomStructure(tag="CHILD", pointer="", text="Child", xref="") |
| 38 | + |
| 39 | + parent.children = [child] |
| 40 | + |
| 41 | + # Test with a tag that doesn't exist |
| 42 | + result = util.get_first_child_with_tag(parent, "NONEXISTENT") |
| 43 | + assert result is None |
| 44 | + |
| 45 | + |
| 46 | +def test_empty_children_list(): |
| 47 | + """Test with an empty children list.""" |
| 48 | + parent = types.GedcomStructure(tag="PARENT", pointer="", text="", xref="") |
| 49 | + parent.children = [] |
| 50 | + |
| 51 | + result = util.get_first_child_with_tag(parent, "CHILD") |
| 52 | + assert result is None |
| 53 | + |
| 54 | + |
| 55 | +def test_valid_date_conversion(): |
| 56 | + """Test converting a valid GEDCOM DateExact to Python date.""" |
| 57 | + # Test with all months |
| 58 | + months = [ |
| 59 | + ("JAN", 1), |
| 60 | + ("FEB", 2), |
| 61 | + ("MAR", 3), |
| 62 | + ("APR", 4), |
| 63 | + ("MAY", 5), |
| 64 | + ("JUN", 6), |
| 65 | + ("JUL", 7), |
| 66 | + ("AUG", 8), |
| 67 | + ("SEP", 9), |
| 68 | + ("OCT", 10), |
| 69 | + ("NOV", 11), |
| 70 | + ("DEC", 12), |
| 71 | + ] |
| 72 | + |
| 73 | + for month_str, month_num in months: |
| 74 | + gedcom_date = types.DateExact(day=15, month=month_str, year=2023) |
| 75 | + python_date = util.date_exact_to_python_date(gedcom_date) |
| 76 | + |
| 77 | + assert isinstance(python_date, datetime.date) |
| 78 | + assert python_date.day == 15 |
| 79 | + assert python_date.month == month_num |
| 80 | + assert python_date.year == 2023 |
| 81 | + |
| 82 | + |
| 83 | +def test_case_insensitive_month(): |
| 84 | + """Test that month names are case-insensitive.""" |
| 85 | + # Test with lowercase |
| 86 | + gedcom_date = types.DateExact(day=15, month="jan", year=2023) |
| 87 | + python_date = util.date_exact_to_python_date(gedcom_date) |
| 88 | + |
| 89 | + assert python_date.month == 1 |
| 90 | + |
| 91 | + # Test with mixed case |
| 92 | + gedcom_date = types.DateExact(day=15, month="Jul", year=2023) |
| 93 | + python_date = util.date_exact_to_python_date(gedcom_date) |
| 94 | + |
| 95 | + assert python_date.month == 7 |
| 96 | + |
| 97 | + |
| 98 | +def test_invalid_month(): |
| 99 | + """Test with an invalid month name.""" |
| 100 | + gedcom_date = types.DateExact(day=15, month="INVALID", year=2023) |
| 101 | + |
| 102 | + with pytest.raises(ValueError, match="Invalid month in date: INVALID"): |
| 103 | + util.date_exact_to_python_date(gedcom_date) |
| 104 | + |
| 105 | + |
| 106 | +def test_basic_time_conversion(): |
| 107 | + """Test basic time conversion with hours and minutes only.""" |
| 108 | + gedcom_time = types.Time(hour=14, minute=30) |
| 109 | + python_time = util.time_to_python_time(gedcom_time) |
| 110 | + |
| 111 | + assert isinstance(python_time, datetime.time) |
| 112 | + assert python_time.hour == 14 |
| 113 | + assert python_time.minute == 30 |
| 114 | + assert python_time.second == 0 |
| 115 | + assert python_time.microsecond == 0 |
| 116 | + assert python_time.tzinfo == datetime.timezone.utc |
| 117 | + |
| 118 | + |
| 119 | +def test_time_with_seconds(): |
| 120 | + """Test time conversion with seconds.""" |
| 121 | + gedcom_time = types.Time(hour=14, minute=30, second=45) |
| 122 | + python_time = util.time_to_python_time(gedcom_time) |
| 123 | + |
| 124 | + assert python_time.second == 45 |
| 125 | + |
| 126 | + |
| 127 | +def test_time_with_fraction(): |
| 128 | + """Test time conversion with fractional seconds.""" |
| 129 | + gedcom_time = types.Time(hour=14, minute=30, second=45, fraction=123456) |
| 130 | + python_time = util.time_to_python_time(gedcom_time) |
| 131 | + |
| 132 | + assert python_time.second == 45 |
| 133 | + assert python_time.microsecond == 123456 |
| 134 | + |
| 135 | + # Test with different fraction lengths |
| 136 | + gedcom_time = types.Time(hour=14, minute=30, second=45, fraction=5) |
| 137 | + python_time = util.time_to_python_time(gedcom_time) |
| 138 | + |
| 139 | + assert python_time.second == 45 |
| 140 | + assert python_time.microsecond == 500000 |
| 141 | + |
| 142 | + |
| 143 | +def test_time_without_fraction_or_seconds(): |
| 144 | + """Test time conversion without fraction or seconds.""" |
| 145 | + gedcom_time = types.Time(hour=14, minute=30, second=None, fraction=None) |
| 146 | + python_time = util.time_to_python_time(gedcom_time) |
| 147 | + |
| 148 | + assert python_time.second == 0 |
| 149 | + assert python_time.microsecond == 0 |
| 150 | + |
| 151 | + |
| 152 | +def test_date_only(): |
| 153 | + """Test conversion with date only (no time).""" |
| 154 | + gedcom_date = types.DateExact(day=15, month="JAN", year=2023) |
| 155 | + python_datetime = util.date_exact_and_time_to_python_datetime(gedcom_date) |
| 156 | + |
| 157 | + assert isinstance(python_datetime, datetime.datetime) |
| 158 | + assert python_datetime.day == 15 |
| 159 | + assert python_datetime.month == 1 |
| 160 | + assert python_datetime.year == 2023 |
| 161 | + assert python_datetime.hour == 0 |
| 162 | + assert python_datetime.minute == 0 |
| 163 | + assert python_datetime.second == 0 |
| 164 | + assert python_datetime.microsecond == 0 |
| 165 | + assert python_datetime.tzinfo == datetime.timezone.utc |
| 166 | + |
| 167 | + |
| 168 | +def test_date_and_time(): |
| 169 | + """Test conversion with both date and time.""" |
| 170 | + gedcom_date = types.DateExact(day=15, month="JAN", year=2023) |
| 171 | + gedcom_time = types.Time(hour=14, minute=30, second=45, fraction=123) |
| 172 | + |
| 173 | + python_datetime = util.date_exact_and_time_to_python_datetime( |
| 174 | + gedcom_date, gedcom_time |
| 175 | + ) |
| 176 | + |
| 177 | + assert python_datetime.day == 15 |
| 178 | + assert python_datetime.month == 1 |
| 179 | + assert python_datetime.year == 2023 |
| 180 | + assert python_datetime.hour == 14 |
| 181 | + assert python_datetime.minute == 30 |
| 182 | + assert python_datetime.second == 45 |
| 183 | + assert python_datetime.microsecond == 123000 |
| 184 | + assert python_datetime.tzinfo == datetime.timezone.utc |
| 185 | + |
| 186 | + |
| 187 | +def test_integration_with_other_functions(): |
| 188 | + """Test that the function integrates correctly with other utility functions.""" |
| 189 | + with patch("gedcom7.util.date_exact_to_python_date") as mock_date_fn: |
| 190 | + with patch("gedcom7.util.time_to_python_time") as mock_time_fn: |
| 191 | + # Mock the return values |
| 192 | + mock_date_fn.return_value = datetime.date(2023, 1, 15) |
| 193 | + mock_time_fn.return_value = datetime.time( |
| 194 | + 14, 30, 45, 123000, tzinfo=datetime.timezone.utc |
| 195 | + ) |
| 196 | + |
| 197 | + gedcom_date = types.DateExact(day=15, month="JAN", year=2023) |
| 198 | + gedcom_time = types.Time(hour=14, minute=30, second=45, fraction=123) |
| 199 | + |
| 200 | + util.date_exact_and_time_to_python_datetime(gedcom_date, gedcom_time) |
| 201 | + |
| 202 | + # Verify the functions were called with correct arguments |
| 203 | + mock_date_fn.assert_called_once_with(gedcom_date) |
| 204 | + mock_time_fn.assert_called_once_with(gedcom_time) |
0 commit comments