Source code for ewoksmx.tests.models.test_characterisation_model

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_input_model_valid(test_metadata): """Test that valid data_paths and metadata input data passes validation.""" data = { "data_paths": [ ESRFPath("/data/image_001.cbf"), ESRFPath("/data/image_002.cbf"), ], "metadata": test_metadata, } model = InputModel(**data) assert len(model.data_paths) == 2 assert model.metadata.data_collection.detector_type == "pilatus4_4m"
[docs] def test_input_model_invalid_strategy(test_metadata): """Test that an invalid strategy name raises a ValidationError.""" test_metadata["processing_plan"][ "strategy_type" ] = "aggressive" # Not 'full' or 'fast' with pytest.raises(ValidationError) as excinfo: InputModel(data_paths=["test.h5"], metadata=test_metadata) assert "strategy_type" in str(excinfo.value)
[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_metadata_physical_constraints(): """Test that physical values like detector_distance must be positive.""" with pytest.raises(ValidationError): DataCollectionModel( detector_type="test", wavelength=-0.1, # Invalid: must be gt 0 distance=200, beam_centre=(0, 0), num_wedges=1, rotation_angle_between_wedges=0, oscillation_width=0.1, num_images_per_wedge=1, )
[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)