Add reporting unit tests
This commit is contained in:
1
tests/unit/reporting/__init__.py
Normal file
1
tests/unit/reporting/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
"""Unit tests for reporting module."""
|
||||||
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)
|
||||||
222
tests/unit/reporting/test_html_renderer.py
Normal file
222
tests/unit/reporting/test_html_renderer.py
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
"""Unit tests for HTML renderer."""
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from py_dvt_ate.data.models import TestResult, TestRun, TestStatus
|
||||||
|
from py_dvt_ate.reporting.exceptions import TemplateRenderError
|
||||||
|
from py_dvt_ate.reporting.models import ReportConfig, ReportData
|
||||||
|
from py_dvt_ate.reporting.renderers.html import HTMLRenderer
|
||||||
|
|
||||||
|
|
||||||
|
class TestHTMLRenderer:
|
||||||
|
"""Tests for HTMLRenderer class."""
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def renderer(self) -> HTMLRenderer:
|
||||||
|
"""Create an HTML renderer instance."""
|
||||||
|
return HTMLRenderer()
|
||||||
|
|
||||||
|
@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),
|
||||||
|
completed_at=datetime(2024, 1, 15, 10, 35, 0),
|
||||||
|
status=TestStatus.PASSED,
|
||||||
|
config_json='{"temperatures": [-40, 25, 85], "input_voltage": 5.0}',
|
||||||
|
operator="test_user",
|
||||||
|
description="Temperature coefficient characterisation test",
|
||||||
|
)
|
||||||
|
|
||||||
|
@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),
|
||||||
|
lower_limit=None,
|
||||||
|
upper_limit=100.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),
|
||||||
|
lower_limit=3.2,
|
||||||
|
upper_limit=3.4,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def sample_report_data(
|
||||||
|
self, sample_run: TestRun, sample_results: list[TestResult]
|
||||||
|
) -> ReportData:
|
||||||
|
"""Create sample report data."""
|
||||||
|
return ReportData(
|
||||||
|
run=sample_run,
|
||||||
|
results=sample_results,
|
||||||
|
config=ReportConfig(company_name="Test Company"),
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_render_produces_html(
|
||||||
|
self, renderer: HTMLRenderer, sample_report_data: ReportData
|
||||||
|
) -> None:
|
||||||
|
"""Test that render produces valid HTML output."""
|
||||||
|
html = renderer.render(sample_report_data)
|
||||||
|
|
||||||
|
assert isinstance(html, str)
|
||||||
|
assert html.startswith("<!DOCTYPE html>")
|
||||||
|
assert "</html>" in html
|
||||||
|
|
||||||
|
def test_render_includes_title(
|
||||||
|
self, renderer: HTMLRenderer, sample_report_data: ReportData
|
||||||
|
) -> None:
|
||||||
|
"""Test that render includes the test name in title."""
|
||||||
|
html = renderer.render(sample_report_data)
|
||||||
|
|
||||||
|
assert "<title>" in html
|
||||||
|
assert "tempco" in html
|
||||||
|
|
||||||
|
def test_render_includes_company_name(
|
||||||
|
self, renderer: HTMLRenderer, sample_report_data: ReportData
|
||||||
|
) -> None:
|
||||||
|
"""Test that render includes the company name."""
|
||||||
|
html = renderer.render(sample_report_data)
|
||||||
|
|
||||||
|
assert "Test Company" in html
|
||||||
|
|
||||||
|
def test_render_includes_results_table(
|
||||||
|
self, renderer: HTMLRenderer, sample_report_data: ReportData
|
||||||
|
) -> None:
|
||||||
|
"""Test that render includes results in a table."""
|
||||||
|
html = renderer.render(sample_report_data)
|
||||||
|
|
||||||
|
# Check for parameter names in output
|
||||||
|
assert "tempco" in html
|
||||||
|
assert "output_voltage_25c" in html
|
||||||
|
|
||||||
|
# Check for values
|
||||||
|
assert "45.000000" in html
|
||||||
|
assert "3.300100" in html
|
||||||
|
|
||||||
|
def test_render_includes_pass_status(
|
||||||
|
self, renderer: HTMLRenderer, sample_report_data: ReportData
|
||||||
|
) -> None:
|
||||||
|
"""Test that render shows pass status badges."""
|
||||||
|
html = renderer.render(sample_report_data)
|
||||||
|
|
||||||
|
# Check for PASS badge (results should pass)
|
||||||
|
assert "PASS" in html
|
||||||
|
|
||||||
|
def test_render_includes_run_metadata(
|
||||||
|
self, renderer: HTMLRenderer, sample_report_data: ReportData
|
||||||
|
) -> None:
|
||||||
|
"""Test that render includes run metadata."""
|
||||||
|
html = renderer.render(sample_report_data)
|
||||||
|
|
||||||
|
assert "12345678-1234-1234-1234-123456789abc" in html
|
||||||
|
assert "test_user" in html
|
||||||
|
assert "Temperature coefficient characterisation test" in html
|
||||||
|
|
||||||
|
def test_render_includes_css(
|
||||||
|
self, renderer: HTMLRenderer, sample_report_data: ReportData
|
||||||
|
) -> None:
|
||||||
|
"""Test that render includes CSS styles."""
|
||||||
|
html = renderer.render(sample_report_data)
|
||||||
|
|
||||||
|
# Check for some CSS from styles.css
|
||||||
|
assert "<style>" in html
|
||||||
|
assert "@page" in html or "font-family" in html
|
||||||
|
|
||||||
|
def test_render_includes_configuration(
|
||||||
|
self, renderer: HTMLRenderer, sample_report_data: ReportData
|
||||||
|
) -> None:
|
||||||
|
"""Test that render includes test configuration."""
|
||||||
|
html = renderer.render(sample_report_data)
|
||||||
|
|
||||||
|
# Check for config values (formatted JSON)
|
||||||
|
assert "temperatures" in html
|
||||||
|
assert "input_voltage" in html
|
||||||
|
|
||||||
|
def test_render_with_charts(
|
||||||
|
self, renderer: HTMLRenderer, sample_run: TestRun, sample_results: list[TestResult]
|
||||||
|
) -> None:
|
||||||
|
"""Test that render includes chart images."""
|
||||||
|
# Create sample base64 chart data
|
||||||
|
charts = {
|
||||||
|
"Test Chart": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
|
||||||
|
}
|
||||||
|
|
||||||
|
data = ReportData(
|
||||||
|
run=sample_run,
|
||||||
|
results=sample_results,
|
||||||
|
charts=charts,
|
||||||
|
config=ReportConfig(),
|
||||||
|
)
|
||||||
|
|
||||||
|
html = renderer.render(data)
|
||||||
|
|
||||||
|
# Check for chart section and base64 image
|
||||||
|
assert "Charts" in html
|
||||||
|
assert "data:image/png;base64," in html
|
||||||
|
|
||||||
|
def test_render_empty_results(
|
||||||
|
self, renderer: HTMLRenderer, sample_run: TestRun
|
||||||
|
) -> None:
|
||||||
|
"""Test rendering with no results."""
|
||||||
|
data = ReportData(
|
||||||
|
run=sample_run,
|
||||||
|
results=[],
|
||||||
|
config=ReportConfig(),
|
||||||
|
)
|
||||||
|
|
||||||
|
html = renderer.render(data)
|
||||||
|
|
||||||
|
# Should still produce valid HTML
|
||||||
|
assert "<!DOCTYPE html>" in html
|
||||||
|
assert "No results recorded" in html
|
||||||
|
|
||||||
|
def test_css_is_cached(self, renderer: HTMLRenderer) -> None:
|
||||||
|
"""Test that CSS content is cached after first load."""
|
||||||
|
# Access CSS twice
|
||||||
|
css1 = renderer._load_css()
|
||||||
|
css2 = renderer._load_css()
|
||||||
|
|
||||||
|
# Should be the same object (cached)
|
||||||
|
assert css1 is css2
|
||||||
|
assert len(css1) > 0
|
||||||
|
|
||||||
|
def test_render_formats_limits(
|
||||||
|
self, renderer: HTMLRenderer, sample_run: TestRun
|
||||||
|
) -> None:
|
||||||
|
"""Test that limits are properly formatted."""
|
||||||
|
results = [
|
||||||
|
TestResult(
|
||||||
|
id="result-1",
|
||||||
|
test_run_id="12345678-1234-1234-1234-123456789abc",
|
||||||
|
parameter="test_param",
|
||||||
|
value=50.0,
|
||||||
|
unit="units",
|
||||||
|
measured_at=datetime(2024, 1, 15, 10, 35, 0),
|
||||||
|
lower_limit=10.0,
|
||||||
|
upper_limit=100.0,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
data = ReportData(run=sample_run, results=results, config=ReportConfig())
|
||||||
|
html = renderer.render(data)
|
||||||
|
|
||||||
|
# Check limits are formatted
|
||||||
|
assert "10.000000" in html
|
||||||
|
assert "100.000000" in html
|
||||||
208
tests/unit/reporting/test_models.py
Normal file
208
tests/unit/reporting/test_models.py
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
"""Unit tests for reporting data models."""
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pandas as pd
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from py_dvt_ate.data.models import TestResult, TestRun, TestStatus
|
||||||
|
from py_dvt_ate.reporting.models import ReportConfig, ReportData
|
||||||
|
|
||||||
|
|
||||||
|
class TestReportConfig:
|
||||||
|
"""Tests for ReportConfig dataclass."""
|
||||||
|
|
||||||
|
def test_default_values(self) -> None:
|
||||||
|
"""Test default configuration values."""
|
||||||
|
config = ReportConfig()
|
||||||
|
|
||||||
|
assert config.company_name == "py_dvt_ate"
|
||||||
|
assert config.logo_path is None
|
||||||
|
assert config.include_charts is True
|
||||||
|
assert config.chart_dpi == 150
|
||||||
|
|
||||||
|
def test_custom_values(self) -> None:
|
||||||
|
"""Test configuration with custom values."""
|
||||||
|
config = ReportConfig(
|
||||||
|
company_name="Test Company",
|
||||||
|
logo_path=Path("/path/to/logo.png"),
|
||||||
|
include_charts=False,
|
||||||
|
chart_dpi=300,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert config.company_name == "Test Company"
|
||||||
|
assert config.logo_path == Path("/path/to/logo.png")
|
||||||
|
assert config.include_charts is False
|
||||||
|
assert config.chart_dpi == 300
|
||||||
|
|
||||||
|
|
||||||
|
class TestReportData:
|
||||||
|
"""Tests for ReportData dataclass."""
|
||||||
|
|
||||||
|
@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),
|
||||||
|
completed_at=datetime(2024, 1, 15, 10, 35, 0),
|
||||||
|
status=TestStatus.PASSED,
|
||||||
|
config_json='{"temperatures": [-40, 25, 85]}',
|
||||||
|
operator="test_user",
|
||||||
|
description="Test description",
|
||||||
|
)
|
||||||
|
|
||||||
|
@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),
|
||||||
|
lower_limit=None,
|
||||||
|
upper_limit=100.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),
|
||||||
|
lower_limit=3.2,
|
||||||
|
upper_limit=3.4,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
def test_basic_report_data(
|
||||||
|
self, sample_run: TestRun, sample_results: list[TestResult]
|
||||||
|
) -> None:
|
||||||
|
"""Test creating basic report data."""
|
||||||
|
data = ReportData(run=sample_run, results=sample_results)
|
||||||
|
|
||||||
|
assert data.run == sample_run
|
||||||
|
assert data.results == sample_results
|
||||||
|
assert data.measurements is None
|
||||||
|
assert data.charts == {}
|
||||||
|
|
||||||
|
def test_passed_count(
|
||||||
|
self, sample_run: TestRun, sample_results: list[TestResult]
|
||||||
|
) -> None:
|
||||||
|
"""Test passed_count property."""
|
||||||
|
data = ReportData(run=sample_run, results=sample_results)
|
||||||
|
|
||||||
|
# Both results should pass (within limits)
|
||||||
|
assert data.passed_count == 2
|
||||||
|
|
||||||
|
def test_failed_count(self, sample_run: TestRun) -> None:
|
||||||
|
"""Test failed_count property with failed results."""
|
||||||
|
failed_results = [
|
||||||
|
TestResult(
|
||||||
|
id="result-1",
|
||||||
|
test_run_id="12345678-1234-1234-1234-123456789abc",
|
||||||
|
parameter="tempco",
|
||||||
|
value=150.0, # Exceeds upper limit
|
||||||
|
unit="ppm/C",
|
||||||
|
measured_at=datetime(2024, 1, 15, 10, 35, 0),
|
||||||
|
lower_limit=None,
|
||||||
|
upper_limit=100.0,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
data = ReportData(run=sample_run, results=failed_results)
|
||||||
|
|
||||||
|
assert data.failed_count == 1
|
||||||
|
assert data.passed_count == 0
|
||||||
|
|
||||||
|
def test_overall_status_pass(
|
||||||
|
self, sample_run: TestRun, sample_results: list[TestResult]
|
||||||
|
) -> None:
|
||||||
|
"""Test overall_status when all tests pass."""
|
||||||
|
data = ReportData(run=sample_run, results=sample_results)
|
||||||
|
|
||||||
|
assert data.overall_status == "PASS"
|
||||||
|
|
||||||
|
def test_overall_status_fail(self, sample_run: TestRun) -> None:
|
||||||
|
"""Test overall_status when tests fail."""
|
||||||
|
failed_results = [
|
||||||
|
TestResult(
|
||||||
|
id="result-1",
|
||||||
|
test_run_id="12345678-1234-1234-1234-123456789abc",
|
||||||
|
parameter="tempco",
|
||||||
|
value=150.0, # Exceeds upper limit
|
||||||
|
unit="ppm/C",
|
||||||
|
measured_at=datetime(2024, 1, 15, 10, 35, 0),
|
||||||
|
lower_limit=None,
|
||||||
|
upper_limit=100.0,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
data = ReportData(run=sample_run, results=failed_results)
|
||||||
|
|
||||||
|
assert data.overall_status == "FAIL"
|
||||||
|
|
||||||
|
def test_overall_status_error(self) -> None:
|
||||||
|
"""Test overall_status when run status is error."""
|
||||||
|
error_run = TestRun(
|
||||||
|
id="12345678-1234-1234-1234-123456789abc",
|
||||||
|
test_name="tempco",
|
||||||
|
started_at=datetime(2024, 1, 15, 10, 30, 0),
|
||||||
|
status=TestStatus.ERROR,
|
||||||
|
config_json="{}",
|
||||||
|
)
|
||||||
|
|
||||||
|
data = ReportData(run=error_run, results=[])
|
||||||
|
|
||||||
|
assert data.overall_status == "ERROR"
|
||||||
|
|
||||||
|
def test_with_measurements(
|
||||||
|
self, sample_run: TestRun, sample_results: list[TestResult]
|
||||||
|
) -> None:
|
||||||
|
"""Test report data with measurements DataFrame."""
|
||||||
|
measurements = pd.DataFrame(
|
||||||
|
{
|
||||||
|
"timestamp": [0.0, 1.0, 2.0],
|
||||||
|
"parameter": ["output_voltage", "output_voltage", "output_voltage"],
|
||||||
|
"value": [3.30, 3.31, 3.30],
|
||||||
|
"unit": ["V", "V", "V"],
|
||||||
|
"temperature": [25.0, 25.0, 25.0],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
data = ReportData(
|
||||||
|
run=sample_run, results=sample_results, measurements=measurements
|
||||||
|
)
|
||||||
|
|
||||||
|
assert data.measurements is not None
|
||||||
|
assert len(data.measurements) == 3
|
||||||
|
|
||||||
|
def test_with_charts(
|
||||||
|
self, sample_run: TestRun, sample_results: list[TestResult]
|
||||||
|
) -> None:
|
||||||
|
"""Test report data with chart images."""
|
||||||
|
charts = {
|
||||||
|
"Voltage vs Temperature": "base64_encoded_image_data",
|
||||||
|
"Results Summary": "another_base64_image",
|
||||||
|
}
|
||||||
|
|
||||||
|
data = ReportData(run=sample_run, results=sample_results, charts=charts)
|
||||||
|
|
||||||
|
assert len(data.charts) == 2
|
||||||
|
assert "Voltage vs Temperature" in data.charts
|
||||||
|
|
||||||
|
def test_with_custom_config(
|
||||||
|
self, sample_run: TestRun, sample_results: list[TestResult]
|
||||||
|
) -> None:
|
||||||
|
"""Test report data with custom configuration."""
|
||||||
|
config = ReportConfig(company_name="Test Company", include_charts=False)
|
||||||
|
|
||||||
|
data = ReportData(run=sample_run, results=sample_results, config=config)
|
||||||
|
|
||||||
|
assert data.config.company_name == "Test Company"
|
||||||
|
assert data.config.include_charts is False
|
||||||
Reference in New Issue
Block a user