Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 88 additions & 1 deletion tests/test_destination_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import pandas as pd
import pytest

from destination_writer.destination_writer import destination_writer, write_file, write_metadata
from destination_writer.destination_writer import destination_writer, write_file, write_metadata, get_destination_location


def test_write_metadata_bad_connection():
Expand Down Expand Up @@ -186,3 +186,90 @@ def test_destination_writer_does_not_call_write_file_without_rfau_column(monkeyp
destination_writer(config, logger, "SomeTable", df)

mock_write_file.assert_not_called()


# ── get_destination_location ──────────────────────────────────────────────────

def test_get_destination_location_joins_destination_and_filename():
import os
row = pd.Series({"RelativeFileArchiveURI": "/some/path/to/file.dcm"})
result = get_destination_location(row, "/dest")
assert result == os.path.join("/dest", "file.dcm")


# ── write_file: None RFAU row ─────────────────────────────────────────────────

def test_write_file_skips_copy_when_rfau_value_is_none(monkeypatch):
mock_copyfile = Mock()
monkeypatch.setattr("destination_writer.destination_writer.shutil.copyfile", mock_copyfile)
monkeypatch.setattr("destination_writer.destination_writer.os.makedirs", Mock())

config = {"destination": {"files": {"directory": "/dest"}}}
logger = Mock()
df = pd.DataFrame({"RelativeFileArchiveURI": [None]})

write_file(config, logger, df)

mock_copyfile.assert_not_called()


# ── write_file: archive URI with ! ────────────────────────────────────────────

def test_write_file_copies_archive_source_when_rfau_contains_bang(monkeypatch):
mock_copyfile = Mock()
mock_get_dest = Mock(return_value="/dest/archive.7z")
monkeypatch.setattr("destination_writer.destination_writer.shutil.copyfile", mock_copyfile)
monkeypatch.setattr("destination_writer.destination_writer.get_destination_location", mock_get_dest)
monkeypatch.setattr("destination_writer.destination_writer.os.makedirs", Mock())

config = {"destination": {"files": {"directory": "/dest"}}}
logger = Mock()
df = pd.DataFrame({"RelativeFileArchiveURI": ["/src/archive.7z!file.dcm"]})

write_file(config, logger, df)

mock_copyfile.assert_called_once_with("/src/archive.7z", "/dest/archive.7z")


# ── write_metadata: None RFAU value in row ────────────────────────────────────

def test_write_metadata_skips_get_destination_when_rfau_value_is_none(monkeypatch):
mock_get_dest = Mock()
monkeypatch.setattr(
"destination_writer.destination_writer.get_destination_location", mock_get_dest
)
monkeypatch.setattr(pd.DataFrame, "to_sql", lambda *a, **kw: None)

config = {
"destination": {
"files": {"directory": "/dest"},
"metadata": {"dbType": "MSSQL", "connectionString": "fake", "index": "id"},
}
}
logger = Mock()
df = pd.DataFrame({"RelativeFileArchiveURI": [None]})

write_metadata(config, logger, "TestTable", df)

mock_get_dest.assert_not_called()


def test_write_metadata_skips_rfau_update_when_destination_location_is_none(monkeypatch):
monkeypatch.setattr(
"destination_writer.destination_writer.get_destination_location",
Mock(return_value=None),
)
monkeypatch.setattr(pd.DataFrame, "to_sql", lambda *a, **kw: None)

config = {
"destination": {
"files": {"directory": "/dest"},
"metadata": {"dbType": "MSSQL", "connectionString": "fake", "index": "id"},
}
}
logger = Mock()
df = pd.DataFrame({"RelativeFileArchiveURI": ["/src/file.dcm"]})

write_metadata(config, logger, "TestTable", df)

logger.debug.assert_called()
79 changes: 79 additions & 0 deletions tests/test_metadata_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -409,3 +409,82 @@ def test_metadata_processor_include_archives_adds_archive_extensions(

assert "scan.dcm" in processed_files
assert "backup.zip" in processed_files


# ─── get_modality_config: non-matching files in dir ──────────────────────────

def test_get_modality_config_returns_none_when_files_present_but_none_match(tmp_path):
"""Line 28->26: iterates files in templates dir that don't match the requested modality."""
make_template(tmp_path / "templates", "MR", []) # MR.IT exists, but we request CT

result = metadata_processor.get_modality_config_for_file("CT")

assert result is None


# ─── _datasetProcess: except on ds.data_element() ────────────────────────────

def test_process_appends_none_when_data_element_raises(tmp_path):
"""Lines 123-124: a valid DICOM keyword not present in the dataset causes ds[tag] to raise."""
make_template(tmp_path / "templates", "CT", [{"TableName": "StudyTable", "Columns": [
{"ColumnName": "PatientID"},
{"ColumnName": "StudyDate"}, # valid keyword, not set in write_dicom -> KeyError
]}])
write_dicom(tmp_path / "image.dcm") # sets Modality, PatientID, StudyInstanceUID only
logger = Mock()

records = metadata_processor.process("image.dcm", str(tmp_path), logger)

values, _, _ = records[0]
assert values[0] == "patient-123"
assert values[1] is None


# ─── process: .7z path ───────────────────────────────────────────────────────

def test_process_handles_7z_archive_file(tmp_path, monkeypatch):
"""Lines 135-139: .7z files are read from ./received_dicoms/friendly_name/<archive>/."""
make_template(tmp_path / "templates", "CT", [{"TableName": "StudyTable", "Columns": [
{"ColumnName": "PatientID"},
]}])
archive_dir = tmp_path / "received_dicoms" / "friendly_name" / "scan.7z"
archive_dir.mkdir(parents=True)
write_dicom(archive_dir / "image.dcm")
monkeypatch.chdir(tmp_path)
logger = Mock()

records = metadata_processor.process("scan.7z", str(tmp_path), logger)

assert len(records) == 1
_, _, table_name = records[0]
assert table_name == "StudyTable"


# ─── metadata_processor: None result from process ────────────────────────────

def test_metadata_processor_handles_none_result_from_process(
tmp_path, patched_executor, mock_destination_writer, monkeypatch
):
"""Line 180->179: process() returning None is skipped and does not increment file_count."""
monkeypatch.setattr(metadata_processor, "process", lambda *a: None)
write_dicom(tmp_path / "a.dcm")
logger = Mock()
config = {"sources": {"scans": {"type": "filesystem", "directory": str(tmp_path)}}}

metadata_processor.metadata_processor(config, logger)

assert logger.info.call_args.args[0]["file_count"] == 0


def test_metadata_processor_handles_none_record_in_result(
tmp_path, patched_executor, mock_destination_writer, monkeypatch
):
"""Line 182->181: None entries within a process() result list are skipped."""
monkeypatch.setattr(metadata_processor, "process", lambda *a: [None])
write_dicom(tmp_path / "a.dcm")
logger = Mock()
config = {"sources": {"scans": {"type": "filesystem", "directory": str(tmp_path)}}}

metadata_processor.metadata_processor(config, logger)

assert logger.info.call_args.args[0]["file_count"] == 1
Loading