|
| 1 | +"""Integration test for VR Foraging metadata collection.""" |
| 2 | + |
| 3 | +from datetime import datetime |
| 4 | +import json |
| 5 | +import logging |
| 6 | +import tempfile |
| 7 | +from pathlib import Path |
| 8 | +from unittest.mock import patch, Mock |
| 9 | + |
| 10 | +from aind_data_schema_models.modalities import Modality |
| 11 | + |
| 12 | +from aind_metadata_mapper.gather_metadata import GatherMetadataJob |
| 13 | +from aind_metadata_mapper.models import JobSettings |
| 14 | + |
| 15 | +logging.basicConfig(level=logging.INFO) |
| 16 | + |
| 17 | +# Set to False to use mock responses from tests/resources/ instead of the metadata service |
| 18 | +USE_METADATA_SERVICE = False |
| 19 | + |
| 20 | +source_metadata_path = Path(__file__).parent |
| 21 | +tests_resources_path = Path(__file__).parent.parent.parent / "tests" / "resources" |
| 22 | + |
| 23 | +output_subfolder = Path(tempfile.mkdtemp(prefix="vr_foraging_test_")) |
| 24 | + |
| 25 | + |
| 26 | +def load_mock_response(filename: str): |
| 27 | + """Load a mock response from tests/resources/metadata_service/""" |
| 28 | + filepath = tests_resources_path / "metadata_service" / filename |
| 29 | + with open(filepath, "r") as f: |
| 30 | + return json.load(f) |
| 31 | + |
| 32 | + |
| 33 | +def mock_requests_get(url): |
| 34 | + """Mock requests.get to return responses from local files""" |
| 35 | + mock_response = Mock() |
| 36 | + |
| 37 | + if "/subject/" in url: |
| 38 | + mock_response.status_code = 200 |
| 39 | + mock_response.json.return_value = load_mock_response("subject_response.json") |
| 40 | + elif "/procedures/" in url: |
| 41 | + mock_response.status_code = 200 |
| 42 | + mock_response.json.return_value = load_mock_response("procedures_response.json") |
| 43 | + elif "/funding/" in url: |
| 44 | + mock_response.status_code = 200 |
| 45 | + mock_response.json.return_value = load_mock_response("funding_response.json") |
| 46 | + else: |
| 47 | + mock_response.status_code = 404 |
| 48 | + mock_response.json.return_value = {} |
| 49 | + |
| 50 | + return mock_response |
| 51 | + |
| 52 | + |
| 53 | +def run_test(): |
| 54 | + """Run the actual test logic""" |
| 55 | + print("\n" + "=" * 80) |
| 56 | + print("INTEGRATION TEST: VR Foraging Metadata") |
| 57 | + print("=" * 80) |
| 58 | + print(f"Source metadata: {source_metadata_path}") |
| 59 | + print(f"Output directory: {output_subfolder}") |
| 60 | + print(f"Using metadata service: {USE_METADATA_SERVICE}") |
| 61 | + print() |
| 62 | + |
| 63 | + settings = JobSettings( |
| 64 | + metadata_dir=str(source_metadata_path), |
| 65 | + output_dir=str(output_subfolder), |
| 66 | + subject_id="828422", |
| 67 | + project_name="Cognitive flexibility in patch foraging", |
| 68 | + modalities=[Modality.BEHAVIOR, Modality.BEHAVIOR_VIDEOS], |
| 69 | + acquisition_start_time=datetime.fromisoformat("2025-11-13T17:38:37.079861+00:00"), |
| 70 | + ) |
| 71 | + |
| 72 | + job = GatherMetadataJob(settings=settings) |
| 73 | + |
| 74 | + print("=" * 80) |
| 75 | + print("STEP 1: Testing individual metadata retrieval") |
| 76 | + print("=" * 80) |
| 77 | + |
| 78 | + print("\nLoading acquisition from local file...") |
| 79 | + acquisition = job.get_acquisition() |
| 80 | + if acquisition: |
| 81 | + print(f"✓ Acquisition loaded for subject: {acquisition.get('subject_id')}") |
| 82 | + print(f" Start time: {acquisition.get('acquisition_start_time')}") |
| 83 | + print(f" Instrument ID: {acquisition.get('instrument_id')}") |
| 84 | + print(f" Acquisition type: {acquisition.get('acquisition_type')}") |
| 85 | + else: |
| 86 | + print("✗ Failed to load acquisition") |
| 87 | + |
| 88 | + print("\nLoading instrument from local file...") |
| 89 | + instrument = job.get_instrument() |
| 90 | + if instrument: |
| 91 | + print(f"✓ Instrument loaded: {instrument.get('instrument_id')}") |
| 92 | + print(f" Modification date: {instrument.get('modification_date')}") |
| 93 | + modalities = instrument.get("modalities", []) |
| 94 | + print(f" Modalities: {len(modalities)}") |
| 95 | + for mod in modalities: |
| 96 | + print(f" - {mod.get('name')}") |
| 97 | + else: |
| 98 | + print("✗ Failed to load instrument") |
| 99 | + |
| 100 | + print("\n" + "=" * 80) |
| 101 | + print("STEP 2: Running full run_job() workflow") |
| 102 | + print("=" * 80) |
| 103 | + print("This will fetch all metadata, validate, and write JSON files...") |
| 104 | + print() |
| 105 | + |
| 106 | + job.run_job() |
| 107 | + |
| 108 | + print("\n" + "=" * 80) |
| 109 | + print("STEP 3: Checking generated files in output folder") |
| 110 | + print("=" * 80) |
| 111 | + |
| 112 | + expected_files = [ |
| 113 | + "data_description.json", |
| 114 | + "subject.json", |
| 115 | + "procedures.json", |
| 116 | + "acquisition.json", |
| 117 | + "instrument.json", |
| 118 | + ] |
| 119 | + |
| 120 | + generated_files = [] |
| 121 | + for file_name in expected_files: |
| 122 | + file_path = output_subfolder / file_name |
| 123 | + if file_path.exists(): |
| 124 | + size = file_path.stat().st_size |
| 125 | + print(f"✓ {file_name} ({size:,} bytes)") |
| 126 | + generated_files.append(file_name) |
| 127 | + else: |
| 128 | + print(f"✗ {file_name} (not found)") |
| 129 | + |
| 130 | + print("\n" + "=" * 80) |
| 131 | + print("FINAL SUMMARY") |
| 132 | + print("=" * 80) |
| 133 | + print("Individual retrieval tests:") |
| 134 | + print(f" Acquisition: {'✓' if acquisition else '✗'}") |
| 135 | + print(f" Instrument: {'✓' if instrument else '✗'}") |
| 136 | + print() |
| 137 | + print("run_job() execution:") |
| 138 | + print(f" Files generated: {len(generated_files)}/{len(expected_files)}") |
| 139 | + print(f" Status: {'✓ SUCCESS' if len(generated_files) == len(expected_files) else '✗ INCOMPLETE'}") |
| 140 | + print("=" * 80 + "\n") |
| 141 | + |
| 142 | + |
| 143 | +if __name__ == "__main__": |
| 144 | + try: |
| 145 | + if USE_METADATA_SERVICE: |
| 146 | + # Run test with actual metadata service |
| 147 | + run_test() |
| 148 | + else: |
| 149 | + # Run test with mocked responses from tests/resources/ |
| 150 | + print("Using mock responses from tests/resources/metadata_service/") |
| 151 | + with patch("aind_metadata_mapper.gather_metadata.requests.get", side_effect=mock_requests_get): |
| 152 | + run_test() |
| 153 | + finally: |
| 154 | + pass |
| 155 | + # print(f"Cleaning up output directory: {output_subfolder}") |
| 156 | + # shutil.rmtree(output_subfolder, ignore_errors=True) |
| 157 | + # print("✓ Cleanup complete\n") |
0 commit comments