Add reporting unit tests
This commit is contained in:
220
tests/unit/reporting/test_chart_generator.py
Normal file
220
tests/unit/reporting/test_chart_generator.py
Normal file
@@ -0,0 +1,220 @@
|
||||
"""Unit tests for chart generator."""
|
||||
|
||||
import base64
|
||||
from datetime import datetime
|
||||
|
||||
import pandas as pd
|
||||
import pytest
|
||||
|
||||
from py_dvt_ate.data.models import TestResult, TestRun, TestStatus
|
||||
from py_dvt_ate.reporting.charts.matplotlib_charts import ChartGenerator
|
||||
from py_dvt_ate.reporting.exceptions import ChartGenerationError
|
||||
|
||||
|
||||
class TestChartGenerator:
|
||||
"""Tests for ChartGenerator class."""
|
||||
|
||||
@pytest.fixture
|
||||
def generator(self) -> ChartGenerator:
|
||||
"""Create a chart generator instance."""
|
||||
return ChartGenerator(dpi=100) # Lower DPI for faster tests
|
||||
|
||||
@pytest.fixture
|
||||
def sample_run(self) -> TestRun:
|
||||
"""Create a sample test run."""
|
||||
return TestRun(
|
||||
id="12345678-1234-1234-1234-123456789abc",
|
||||
test_name="tempco",
|
||||
started_at=datetime(2024, 1, 15, 10, 30, 0),
|
||||
status=TestStatus.PASSED,
|
||||
config_json="{}",
|
||||
)
|
||||
|
||||
@pytest.fixture
|
||||
def sample_results(self) -> list[TestResult]:
|
||||
"""Create sample test results."""
|
||||
return [
|
||||
TestResult(
|
||||
id="result-1",
|
||||
test_run_id="12345678-1234-1234-1234-123456789abc",
|
||||
parameter="tempco",
|
||||
value=45.0,
|
||||
unit="ppm/C",
|
||||
measured_at=datetime(2024, 1, 15, 10, 35, 0),
|
||||
),
|
||||
TestResult(
|
||||
id="result-2",
|
||||
test_run_id="12345678-1234-1234-1234-123456789abc",
|
||||
parameter="output_voltage_25c",
|
||||
value=3.3001,
|
||||
unit="V",
|
||||
measured_at=datetime(2024, 1, 15, 10, 33, 0),
|
||||
),
|
||||
]
|
||||
|
||||
@pytest.fixture
|
||||
def voltage_measurements(self) -> pd.DataFrame:
|
||||
"""Create sample voltage measurements."""
|
||||
return pd.DataFrame(
|
||||
{
|
||||
"timestamp": [0.0, 100.0, 200.0, 300.0, 400.0],
|
||||
"parameter": [
|
||||
"output_voltage",
|
||||
"output_voltage",
|
||||
"output_voltage",
|
||||
"output_voltage",
|
||||
"output_voltage",
|
||||
],
|
||||
"value": [3.300, 3.298, 3.295, 3.290, 3.285],
|
||||
"unit": ["V", "V", "V", "V", "V"],
|
||||
"temperature": [-40.0, 0.0, 25.0, 50.0, 85.0],
|
||||
}
|
||||
)
|
||||
|
||||
def test_generator_initialisation(self) -> None:
|
||||
"""Test chart generator initialisation."""
|
||||
generator = ChartGenerator(dpi=200)
|
||||
|
||||
assert generator.dpi == 200
|
||||
|
||||
def test_generate_voltage_vs_temperature(
|
||||
self, generator: ChartGenerator, voltage_measurements: pd.DataFrame
|
||||
) -> None:
|
||||
"""Test generating voltage vs temperature chart."""
|
||||
chart_b64 = generator.generate_voltage_vs_temperature(voltage_measurements)
|
||||
|
||||
# Should be valid base64
|
||||
assert isinstance(chart_b64, str)
|
||||
assert len(chart_b64) > 100 # Should have meaningful content
|
||||
|
||||
# Should decode to PNG image
|
||||
decoded = base64.b64decode(chart_b64)
|
||||
assert decoded[:8] == b"\x89PNG\r\n\x1a\n" # PNG magic bytes
|
||||
|
||||
def test_generate_voltage_vs_temperature_no_data(
|
||||
self, generator: ChartGenerator
|
||||
) -> None:
|
||||
"""Test that error is raised with no voltage data."""
|
||||
empty_df = pd.DataFrame(
|
||||
{
|
||||
"timestamp": [0.0],
|
||||
"parameter": ["other_param"],
|
||||
"value": [1.0],
|
||||
"unit": ["X"],
|
||||
"temperature": [25.0],
|
||||
}
|
||||
)
|
||||
|
||||
with pytest.raises(ChartGenerationError):
|
||||
generator.generate_voltage_vs_temperature(empty_df)
|
||||
|
||||
def test_generate_results_bar_chart(
|
||||
self, generator: ChartGenerator, sample_results: list[TestResult]
|
||||
) -> None:
|
||||
"""Test generating results bar chart."""
|
||||
chart_b64 = generator.generate_results_bar_chart(sample_results)
|
||||
|
||||
# Should be valid base64
|
||||
assert isinstance(chart_b64, str)
|
||||
assert len(chart_b64) > 100
|
||||
|
||||
# Should decode to PNG image
|
||||
decoded = base64.b64decode(chart_b64)
|
||||
assert decoded[:8] == b"\x89PNG\r\n\x1a\n"
|
||||
|
||||
def test_generate_results_bar_chart_empty(
|
||||
self, generator: ChartGenerator
|
||||
) -> None:
|
||||
"""Test that error is raised with no results."""
|
||||
with pytest.raises(ChartGenerationError):
|
||||
generator.generate_results_bar_chart([])
|
||||
|
||||
def test_generate_all_with_measurements(
|
||||
self,
|
||||
generator: ChartGenerator,
|
||||
sample_run: TestRun,
|
||||
sample_results: list[TestResult],
|
||||
voltage_measurements: pd.DataFrame,
|
||||
) -> None:
|
||||
"""Test generate_all produces expected charts."""
|
||||
charts = generator.generate_all(sample_run, sample_results, voltage_measurements)
|
||||
|
||||
# Should have both chart types
|
||||
assert "Voltage vs Temperature" in charts
|
||||
assert "Results Summary" in charts
|
||||
|
||||
# All should be valid base64
|
||||
for name, b64 in charts.items():
|
||||
assert isinstance(b64, str)
|
||||
decoded = base64.b64decode(b64)
|
||||
assert decoded[:8] == b"\x89PNG\r\n\x1a\n", f"Chart {name} is not valid PNG"
|
||||
|
||||
def test_generate_all_no_measurements(
|
||||
self,
|
||||
generator: ChartGenerator,
|
||||
sample_run: TestRun,
|
||||
sample_results: list[TestResult],
|
||||
) -> None:
|
||||
"""Test generate_all with no measurements."""
|
||||
charts = generator.generate_all(sample_run, sample_results, None)
|
||||
|
||||
# Should only have results chart
|
||||
assert "Voltage vs Temperature" not in charts
|
||||
assert "Results Summary" in charts
|
||||
|
||||
def test_generate_all_no_results(
|
||||
self,
|
||||
generator: ChartGenerator,
|
||||
sample_run: TestRun,
|
||||
voltage_measurements: pd.DataFrame,
|
||||
) -> None:
|
||||
"""Test generate_all with no results."""
|
||||
charts = generator.generate_all(sample_run, [], voltage_measurements)
|
||||
|
||||
# Should only have voltage chart
|
||||
assert "Voltage vs Temperature" in charts
|
||||
assert "Results Summary" not in charts
|
||||
|
||||
def test_generate_all_empty(
|
||||
self, generator: ChartGenerator, sample_run: TestRun
|
||||
) -> None:
|
||||
"""Test generate_all with no data."""
|
||||
charts = generator.generate_all(sample_run, [], None)
|
||||
|
||||
# Should be empty
|
||||
assert charts == {}
|
||||
|
||||
def test_matplotlib_lazy_load(self) -> None:
|
||||
"""Test that matplotlib is lazy loaded."""
|
||||
generator = ChartGenerator()
|
||||
|
||||
# _plt should be None before first use
|
||||
assert generator._plt is None
|
||||
|
||||
# After calling _get_matplotlib, it should be loaded
|
||||
plt, mpl = generator._get_matplotlib()
|
||||
|
||||
assert generator._plt is not None
|
||||
assert plt is not None
|
||||
|
||||
def test_dpi_affects_output_size(self) -> None:
|
||||
"""Test that higher DPI produces larger output."""
|
||||
low_dpi = ChartGenerator(dpi=50)
|
||||
high_dpi = ChartGenerator(dpi=150)
|
||||
|
||||
results = [
|
||||
TestResult(
|
||||
id="result-1",
|
||||
test_run_id="12345678-1234-1234-1234-123456789abc",
|
||||
parameter="test",
|
||||
value=1.0,
|
||||
unit="X",
|
||||
measured_at=datetime(2024, 1, 15, 10, 35, 0),
|
||||
),
|
||||
]
|
||||
|
||||
low_chart = low_dpi.generate_results_bar_chart(results)
|
||||
high_chart = high_dpi.generate_results_bar_chart(results)
|
||||
|
||||
# Higher DPI should produce larger image
|
||||
assert len(high_chart) > len(low_chart)
|
||||
Reference in New Issue
Block a user