""" Shared API contract tests for all SpectrumComponent subclasses. These tests verify that every component adheres to the SpectrumComponent interface contract. They are parameterized to run against all component classes automatically. ## Contract requirements 1. `__call__(W) -> np.ndarray` — the component is callable with W array 2. `__init__` accepts `logger: Optional[Logger] = None` 3. `__repr__` returns a non-empty string 4. Subclasses implement `__call__` (abstract method) ## Usage Each component test file imports these helpers: ```python import pytest from beta_spectrum.components.fermi import FermiFunction from tests.common.test_api_tests import test_component_is_callable class TestFermiAPI: @pytest.mark.api def test_callable(self): ff = FermiFunction(Z=20, A=40) test_component_is_callable(ff, "FermiFunction") ``` """ import numpy as np import pytest @pytest.mark.api def _test_component_is_callable(component, component_name="Component"): """Component must be callable with array input.""" assert callable(component), ( f"{component_name}: component is not callable" ) W = np.array([1.5, 2.0, 3.0]) result = component(W) assert isinstance(result, np.ndarray), ( f"{component_name}: __call__ returned {type(result)}, " f"expected np.ndarray" ) assert result.shape == W.shape, ( f"{component_name}: shape mismatch {W.shape} → {result.shape}" ) @pytest.mark.api def _test_component_repr(component, component_name="Component"): """Component must have a non-empty repr.""" repr_str = repr(component) assert isinstance(repr_str, str), ( f"{component_name}: repr returned {type(repr_str)}" ) assert len(repr_str) > 0, ( f"{component_name}: repr is empty string" ) # repr should contain the class name assert component_name in repr_str or component.__class__.__name__ in repr_str, ( f"{component_name}: repr should contain class name. Got: {repr_str}" ) @pytest.mark.api def _test_component_list_input(component, component_name="Component"): """Component must handle Python list input (numpy converts it).""" result = component([1.5, 2.0, 3.0]) assert isinstance(result, np.ndarray), ( f"{component_name}: list input returned {type(result)}" ) @pytest.mark.api def _test_component_empty_input(component, component_name="Component"): """Component must handle empty array input gracefully.""" result = component(np.array([])) assert isinstance(result, np.ndarray), ( f"{component_name}: empty input returned {type(result)}" ) assert result.shape == (0,), ( f"{component_name}: empty input should return empty array" )