import pytest
from esrf_pathlib import ESRFPath
from pydantic import ValidationError
from ewoksmx.models.characterisation_models import DataCollectionModel
from ewoksmx.models.characterisation_models import ProcessingPlanModel
from ewoksmx.models.characterisation_models import SpaceGroupModel
from ewoksmx.tasks.characterisation.aggregate_diffraction_data import InputModel
from ewoksmx.tasks.characterisation.aggregate_diffraction_data import OutputModel
[docs]
def test_output_model_fine_sliced(test_metadata):
"""Test the output model for fine-sliced output."""
output_data = {
"xds_master_file": ESRFPath("xds_master.h5"),
"labelit_cbf_files": [ESRFPath("image1.cbf"), ESRFPath("image2.cbf")],
"mosflm_master_file": ESRFPath("mosflm_master.h5"),
"metadata": test_metadata,
}
output_model = OutputModel(**output_data)
assert output_model.metadata.data_collection.is_finesliced
assert hasattr(output_model, "xds_master_file")
assert output_model.xds_master_file
[docs]
def test_output_model_not_fine_sliced(test_metadata):
"""Test the ouput model for not fine-sliced output."""
test_metadata["data_collection"]["is_finesliced"] = False
data = {
"xds_master_file": None,
"labelit_cbf_files": [ESRFPath("image1.cbf"), ESRFPath("image2.cbf")],
"mosflm_master_file": ESRFPath("mosflm_master.h5"),
"metadata": test_metadata,
}
output_model = OutputModel(**data)
assert not output_model.metadata.data_collection.is_finesliced
assert hasattr(output_model, "xds_master_file")
assert not output_model.xds_master_file
[docs]
def test_processing_plan_defaults():
"""Test that optional fields default to None and anomalous_data defaults to False."""
plan = ProcessingPlanModel(strategy_type="full")
assert plan.strategy_type == "full"
assert plan.anomalous_data is False
assert plan.forced_space_group is None
assert plan.forced_cell is None
[docs]
def test_processing_plan_strategy_types():
"""Test the regex pattern for strategy_type (only 'full' or 'fast')."""
# Should pass
assert ProcessingPlanModel(strategy_type="full").strategy_type == "full"
assert ProcessingPlanModel(strategy_type="fast").strategy_type == "fast"
# Should fail
with pytest.raises(ValidationError):
ProcessingPlanModel(strategy_type="detailed")
[docs]
def test_processing_plan_forced_space_group_validation():
"""Test the force space group validator."""
valid_sg = "P212121"
ProcessingPlanModel(forced_space_group=valid_sg)
[docs]
def test_processing_plan_invalid_forced_space_group_validation():
"""Test invalid force space group validator."""
invalid_valid_sg = "P2121212"
with pytest.raises(ValidationError):
ProcessingPlanModel(forced_space_group=invalid_valid_sg)
[docs]
def test_processing_plan_forced_cell_validation():
"""Test that forced_cell must be a tuple of exactly 6 floats."""
valid_cell = (10.0, 20.0, 30.0, 90.0, 100.0, 110.0)
plan = ProcessingPlanModel(strategy_type="full", forced_cell=valid_cell)
assert len(plan.forced_cell) == 6
# Test wrong number of elements
with pytest.raises(ValidationError):
ProcessingPlanModel(strategy_type="full", forced_cell=(90.0, 90.0, 90.0))
[docs]
def test_processing_plan_numeric_ranges():
"""Test constraints like aimed_resolution > 0 and completeness 0-100."""
# Invalid resolution
with pytest.raises(ValidationError):
ProcessingPlanModel(strategy_type="full", aimed_resolution=-1.0)
# Invalid completeness (over 100)
with pytest.raises(ValidationError):
ProcessingPlanModel(strategy_type="full", aimed_completeness=105.0)
[docs]
@pytest.mark.parametrize(
"field, value",
[
("aimed_resolution", 1.5),
("aimed_multiplicity", 4.0),
("aimed_completeness", 99.9),
],
)
def test_processing_plan_valid_floats(field, value):
"""Parameterised test for valid numeric inputs."""
data = {"strategy_type": "full", field: value}
plan = ProcessingPlanModel(**data)
assert getattr(plan, field) == value
[docs]
@pytest.mark.parametrize(
"input_sg, expected",
[
("P 21 21 21", "P212121"),
("P212121", "P212121"),
("C 2", "C2"),
("C2", "C2"),
("P 21", "P21"),
("I 2 2 2", "I222"),
("P 65 2 2", "P6522"),
("R 3", "R3"),
("H 3", "H3"),
],
)
def test_valid_mx_space_groups(input_sg, expected):
"""Verify that valid MX space groups are accepted and normalized."""
model = SpaceGroupModel(sg_name=input_sg)
assert model.sg_name == expected
[docs]
@pytest.mark.parametrize(
"invalid_input",
[
("NotASpaceGroup"),
("P 7 7 7"), # 7-fold symmetry is impossible
("P 20"), # 0 is not a valid screw axis part
(""), # Empty string
],
)
def test_invalid_strings(invalid_input):
"""Verify that completely invalid crystallographic strings raise errors."""
with pytest.raises(ValidationError) as excinfo:
SpaceGroupModel(sg_name=invalid_input)
assert "Unknown space group" in str(excinfo.value)