from __future__ import annotations import unittest from pathlib import Path import numpy as np import pandas as pd from engin.config import FREQ_COLS from engin.errors import InputDataError from engin.validation import SpectrumFrameValidator ROOT = Path(__file__).resolve().parents[1] class SpectrumFrameValidatorTests(unittest.TestCase): @classmethod def setUpClass(cls) -> None: full = pd.read_csv(ROOT / "test.csv") first_engine = full["engine_id"].iloc[0] cls.valid = full[full["engine_id"].eq(first_engine)].reset_index(drop=True) cls.validator = SpectrumFrameValidator() def test_valid_complete_engine_is_normalized(self) -> None: result = self.validator.validate(self.valid) self.assertEqual(len(result.frame), int(self.valid["n_cylinders"].iloc[0])) self.assertTrue(pd.api.types.is_integer_dtype(result.frame["cylinder"])) def test_missing_frequency_column_is_rejected(self) -> None: with self.assertRaisesRegex(InputDataError, "mV_20"): self.validator.validate(self.valid.drop(columns="mV_20")) def test_duplicate_cylinder_is_rejected(self) -> None: duplicated = pd.concat([self.valid, self.valid.iloc[[0]]], ignore_index=True) with self.assertRaisesRegex(InputDataError, "zduplikowane"): self.validator.validate(duplicated) def test_incomplete_engine_reports_missing_cylinder(self) -> None: incomplete = self.valid[self.valid["cylinder"].ne(3)].reset_index(drop=True) with self.assertRaisesRegex(InputDataError, r"brak \[3\]"): self.validator.validate(incomplete) def test_non_numeric_measurement_is_rejected_with_location(self) -> None: broken = self.valid.copy() broken["mV_7"] = broken["mV_7"].astype(object) broken.loc[0, "mV_7"] = "sensor_error" with self.assertRaisesRegex(InputDataError, "mV_7"): self.validator.validate(broken) def test_excessive_missing_row_is_rejected(self) -> None: broken = self.valid.copy() broken.loc[0, FREQ_COLS[:12]] = np.nan with self.assertRaisesRegex(InputDataError, "brakujÄ…cych"): self.validator.validate(broken) def test_high_but_accepted_missing_rate_emits_warning(self) -> None: noisy = self.valid.copy() noisy.loc[:, FREQ_COLS[:3]] = np.nan result = self.validator.validate(noisy) self.assertEqual(len(result.warnings), 1) def test_negative_amplitude_is_rejected(self) -> None: broken = self.valid.copy() broken.loc[0, "mV_0"] = -1 with self.assertRaisesRegex(InputDataError, "ujemna"): self.validator.validate(broken)