GeezerVisonThe World Wide Web, restored to 800 pixels of glory. |
| Home|About|Gaming cabinet|Archives|How it works|Starter sites |
GeezerVison is showing github.com — python-deadlines/tests/conftest.py at main · python-spain/python-deadlines Expand file tree/ Copy pathconftest.pyMore file actions 352 lines (281 loc) · 11 KB / Copy pathconftest.pyFile metadata and controls352 lines (281 loc) · 11 KB 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 """Pytest configuration and fixtures for Python Deadlines tests. This module provides shared fixtures for testing the conference synchronization pipeline. Fixtures use real data structures and only mock external I/O boundaries (network, file system) following testing best practices. Note: Shared Hypothesis strategies are in hypothesis_strategies.py - import them directly in test files that need property-based testing. """ from pathlib import Path from unittest.mock import patch import pandas as pd import pytest import yaml # --------------------------------------------------------------------------- # Hypothesis Configuration for CI/Dev/Debug profiles # --------------------------------------------------------------------------- try: from hypothesis import Phase from hypothesis import settings # CI profile: More thorough testing, no time limit settings.register_profile("ci", max_examples=200, deadline=None) # Dev profile: Balanced speed and coverage settings.register_profile("dev", max_examples=50, deadline=200) # Debug profile: Minimal examples for fast iteration settings.register_profile("debug", max_examples=10, phases=[Phase.generate]) # Load dev profile by default (can be overridden with --hypothesis-profile) settings.load_profile("dev") HYPOTHESIS_AVAILABLE = True except ImportError: HYPOTHESIS_AVAILABLE = False # --------------------------------------------------------------------------- # Path constants for test data # --------------------------------------------------------------------------- TEST_DATA_DIR = Path(__file__).parent / "test_data" # --------------------------------------------------------------------------- # DataFrame Fixtures - Real data for testing core logic # --------------------------------------------------------------------------- @pytest.fixture() def minimal_yaml_df(): """Load minimal test YAML as DataFrame for fuzzy matching tests. This fixture provides a real DataFrame from YAML data to test core matching and merge logic without mocking. """ yaml_path = TEST_DATA_DIR / "minimal_yaml.yml" with yaml_path.open(encoding="utf-8") as f: data = yaml.safe_load(f) df = pd.DataFrame(data) return df.set_index("conference", drop=False) @pytest.fixture() def minimal_csv_df(): """Load minimal test CSV as DataFrame for fuzzy matching tests. Uses CSV format with name variants to test matching against YAML. """ csv_path = TEST_DATA_DIR / "minimal_csv.csv" df = pd.read_csv(csv_path) # Map CSV columns to match expected conference schema column_mapping = { "Subject": "conference", "Start Date": "start", "End Date": "end", "Location": "place", "Description": "link", } df = df.rename(columns=column_mapping) # Extract year from start date df["start"] = pd.to_datetime(df["start"]) df["year"] = df["start"].dt.year df["start"] = df["start"].dt.date df["end"] = pd.to_datetime(df["end"]).dt.date return df @pytest.fixture() def edge_cases_df(): """Load edge case test data as DataFrame. Contains conferences with: - TBA CFP dates - Online conferences (no location) - Extra places (multiple venues) - Special characters in names (México) - Workshop/tutorial deadlines """ yaml_path = TEST_DATA_DIR / "edge_cases.yml" with yaml_path.open(encoding="utf-8") as f: data = yaml.safe_load(f) return pd.DataFrame(data) @pytest.fixture() def merge_conflicts_df(): """Load test data with merge conflicts for conflict resolution testing. Contains conferences where YAML and CSV have conflicting values to verify merge strategy and logging. """ yaml_path = TEST_DATA_DIR / "merge_conflicts.yml" with yaml_path.open(encoding="utf-8") as f: data = yaml.safe_load(f) return pd.DataFrame(data) # --------------------------------------------------------------------------- # Mock Fixtures - Mock ONLY external I/O boundaries # --------------------------------------------------------------------------- @pytest.fixture() def mock_title_mappings(): """Mock the title mappings file I/O to avoid file system dependencies. This mocks the file loading/writing operations but NOT the core matching logic. Use this when you need to test fuzzy_match without actual title mapping files. The fuzzy_match function calls load_title_mappings from multiple locations: - tidy_conf.interactive_merge.load_title_mappings - tidy_conf.titles.load_title_mappings (via tidy_df_names) It also calls update_title_mappings which writes to files. """ with ( patch("tidy_conf.interactive_merge.load_title_mappings") as mock_load1, patch("tidy_conf.titles.load_title_mappings") as mock_load2, patch("tidy_conf.interactive_merge.update_title_mappings") as mock_update, ): # Return empty mappings (list, dict) for both load calls mock_load1.return_value = ([], {}) mock_load2.return_value = ([], {}) mock_update.return_value = None yield { "load_interactive": mock_load1, "load_titles": mock_load2, "update": mock_update, } @pytest.fixture() def mock_title_mappings_with_data(): """Mock title mappings with realistic mapping data. Includes known mappings like: - PyCon DE -> PyCon Germany & PyData Conference - PyCon Italia -> PyCon Italy """ mapping_data = { "PyCon DE": "PyCon Germany & PyData Conference", "PyCon DE & PyData": "PyCon Germany & PyData Conference", "PyCon Italia": "PyCon Italy", "EuroPython Conference": "EuroPython", "PyCon US 2026": "PyCon US", } with ( patch("tidy_conf.interactive_merge.load_title_mappings") as mock_load1, patch("tidy_conf.titles.load_title_mappings") as mock_load2, patch("tidy_conf.interactive_merge.update_title_mappings") as mock_update, ): # For interactive_merge, return empty rejections mock_load1.return_value = ([], {}) # For titles (reverse=True), return the mapping data def load_with_reverse(reverse=False, path=None): if reverse: return ([], mapping_data) return ([], {}) mock_load2.side_effect = load_with_reverse mock_update.return_value = None yield { "load_interactive": mock_load1, "load_titles": mock_load2, "update": mock_update, "mappings": mapping_data, } @pytest.fixture() def _mock_user_accepts_all(): """Mock user input to accept all fuzzy match prompts. Use this when testing the happy path where user confirms matches. """ with patch("builtins.input", return_value="y"): yield @pytest.fixture() def _mock_user_rejects_all(): """Mock user input to reject all fuzzy match prompts. Use this when testing that rejections are handled correctly. """ with patch("builtins.input", return_value="n"): yield @pytest.fixture() def mock_schema(tmp_path): """Mock the schema loading to use test data directory. Also mocks the types.yml loading for sub validation. """ types_data = [ {"sub": "PY", "name": "Python"}, {"sub": "DATA", "name": "Data Science"}, {"sub": "WEB", "name": "Web"}, {"sub": "SCIPY", "name": "Scientific Python"}, {"sub": "BIZ", "name": "Business"}, {"sub": "GEO", "name": "Geospatial"}, {"sub": "CAMP", "name": "Camp"}, {"sub": "DAY", "name": "Day"}, ] # Create types.yml in tmp_path types_path = tmp_path / "_data" types_path.mkdir(parents=True, exist_ok=True) with (types_path / "types.yml").open("w") as f: yaml.safe_dump(types_data, f) return types_path # --------------------------------------------------------------------------- # Sample Data Fixtures - Individual conference dictionaries # --------------------------------------------------------------------------- @pytest.fixture() def sample_conference(): """Sample valid conference data for testing.""" return { "conference": "PyCon Test", "year": 2025, "link": "https://test.pycon.org", "cfp": "2025-02-15 23:59:00", "place": "Test City, Test Country", "start": "2025-06-01", "end": "2025-06-03", "sub": "PY", "timezone": "America/New_York", "location": [ { "title": "PyCon Test 2025", "latitude": 40.7128, "longitude": -74.0060, }, ], } @pytest.fixture() def invalid_conference(): """Sample invalid conference data for testing validation.""" return { "conference": "Invalid Conf", "year": 1988, # Before Python existed "link": "not-a-url", "cfp": "invalid-date", "place": "", # Empty required field "start": "2025-06-03", "end": "2025-06-01", # End before start "sub": "INVALID", } @pytest.fixture() def temp_yaml_file(tmp_path): """Create a temporary YAML file for testing.""" def _create_yaml_file(data): yaml_file = tmp_path / "test_conferences.yml" with yaml_file.open("w", encoding="utf-8") as f: # Use safe_dump to avoid Python 2/3 dict representer issues yaml.safe_dump(data, f, default_flow_style=False) return str(yaml_file) return _create_yaml_file @pytest.fixture() def online_conference(): """Sample online conference data for testing.""" return { "conference": "PyConf Online", "year": 2025, "link": "https://online.pyconf.org", "cfp": "2025-02-15 23:59:00", "place": "Online", "start": "2025-06-01", "end": "2025-06-03", "sub": "PY", "timezone": "UTC", } @pytest.fixture() def sample_conferences(sample_conference): """Multiple conferences with known merge behavior. Includes: - Original conference - Different conference (EuroSciPy) - Duplicate of original with different deadline (tests conflict resolution) """ return [ sample_conference, { **sample_conference, "conference": "EuroSciPy 2025", "cfp": "2025-03-01 23:59:00", "link": "https://euroscipy.org", "place": "Basel, Switzerland", }, { **sample_conference, "conference": "PyCon Test", # Same name = duplicate! "cfp": "2025-01-20 23:59:00", # Different deadline "link": "https://test.pycon.org/updated", # Different link }, ] @pytest.fixture() def sample_csv_data(): """Sample CSV data for import testing.""" return """Conference Name,Year,Website,CFP Deadline,Location,Start Date,End Date,Type PyCon Test,2025,https://test.pycon.org,2025-02-15 23:59:00,"Test City, Test Country",2025-06-01,2025-06-03,PY Django Test,2025,https://test.django.org,2025-03-01 23:59:00,"Another City, Test Country",2025-07-01,2025-07-03,WEB""" |
| You are visitor 01402520 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required. |