feat(api): update with latests changes and add tests

This commit is contained in:
pedrooot
2025-12-18 13:09:27 +01:00
parent e06d85ff73
commit 3b910c9c51
4 changed files with 304 additions and 34 deletions

View File

@@ -498,23 +498,7 @@ class BaseComplianceReportGenerator(ABC):
elements.append(Spacer(1, 0.5 * inch))
# Compliance info table
info_rows = [
("Framework:", data.framework),
("ID:", data.compliance_id),
("Name:", data.name),
("Version:", data.version),
]
# Add provider info if available
if data.provider_obj:
info_rows.append(("Provider:", data.provider_obj.provider.upper()))
info_rows.append(("Account ID:", data.provider_obj.uid or "N/A"))
info_rows.append(("Alias:", data.provider_obj.alias or "N/A"))
info_rows.append(("Scan ID:", data.scan_id))
if data.description:
info_rows.append(("Description:", data.description))
info_rows = self._build_info_rows(data, language=self.config.language)
info_table = create_info_table(
rows=info_rows,
@@ -526,6 +510,73 @@ class BaseComplianceReportGenerator(ABC):
return elements
def _build_info_rows(
self, data: ComplianceData, language: str = "en"
) -> list[tuple[str, str]]:
"""Build the standard info rows for the cover page table.
This helper method creates the common metadata rows used in all
report cover pages. Subclasses can use this to maintain consistency
while customizing other aspects of the cover page.
Args:
data: Aggregated compliance data.
language: Language for labels ("en" or "es").
Returns:
List of (label, value) tuples for the info table.
"""
# Labels based on language
labels = {
"en": {
"framework": "Framework:",
"id": "ID:",
"name": "Name:",
"version": "Version:",
"provider": "Provider:",
"account_id": "Account ID:",
"alias": "Alias:",
"scan_id": "Scan ID:",
"description": "Description:",
},
"es": {
"framework": "Framework:",
"id": "ID:",
"name": "Nombre:",
"version": "Versión:",
"provider": "Proveedor:",
"account_id": "Account ID:",
"alias": "Alias:",
"scan_id": "Scan ID:",
"description": "Descripción:",
},
}
lang_labels = labels.get(language, labels["en"])
info_rows = [
(lang_labels["framework"], data.framework),
(lang_labels["id"], data.compliance_id),
(lang_labels["name"], data.name),
(lang_labels["version"], data.version),
]
# Add provider info if available
if data.provider_obj:
info_rows.append(
(lang_labels["provider"], data.provider_obj.provider.upper())
)
info_rows.append(
(lang_labels["account_id"], data.provider_obj.uid or "N/A")
)
info_rows.append((lang_labels["alias"], data.provider_obj.alias or "N/A"))
info_rows.append((lang_labels["scan_id"], data.scan_id))
if data.description:
info_rows.append((lang_labels["description"], data.description))
return info_rows
def create_detailed_findings(self, data: ComplianceData, **kwargs) -> list:
"""Create the detailed findings section.

View File

@@ -98,15 +98,18 @@ class ENSReportGenerator(BaseComplianceReportGenerator):
)
elements.append(Spacer(1, 0.5 * inch))
# Compliance info table
info_data = [
["Framework:", data.framework],
["ID:", data.compliance_id],
["Nombre:", Paragraph(data.name, self.styles["normal_center"])],
["Versión:", data.version],
["Scan ID:", data.scan_id],
["Descripción:", Paragraph(data.description, self.styles["normal_center"])],
]
# Compliance info table - use base class helper for consistency
info_rows = self._build_info_rows(data, language="es")
# Convert tuples to lists and wrap long text in Paragraphs
info_data = []
for label, value in info_rows:
if label in ("Nombre:", "Descripción:") and value:
info_data.append(
[label, Paragraph(value, self.styles["normal_center"])]
)
else:
info_data.append([label, value])
info_table = Table(info_data, colWidths=[2 * inch, 4 * inch])
info_table.setStyle(
TableStyle(

View File

@@ -110,14 +110,17 @@ class NIS2ReportGenerator(BaseComplianceReportGenerator):
elements.append(title)
elements.append(Spacer(1, 0.3 * inch))
# Compliance metadata table
metadata_data = [
["Framework:", data.framework],
["Name:", Paragraph(data.name, self.styles["normal_center"])],
["Version:", data.version or "N/A"],
["Scan ID:", data.scan_id],
["Description:", Paragraph(data.description, self.styles["normal_center"])],
]
# Compliance metadata table - use base class helper for consistency
info_rows = self._build_info_rows(data, language="en")
# Convert tuples to lists and wrap long text in Paragraphs
metadata_data = []
for label, value in info_rows:
if label in ("Name:", "Description:") and value:
metadata_data.append(
[label, Paragraph(value, self.styles["normal_center"])]
)
else:
metadata_data.append([label, value])
metadata_table = Table(metadata_data, colWidths=[2 * inch, 4 * inch])
metadata_table.setStyle(

View File

@@ -819,6 +819,219 @@ class TestBaseComplianceReportGenerator:
assert left == "Página 1"
class TestBuildInfoRows:
"""Tests for _build_info_rows helper method."""
def _create_generator(self, language="en"):
"""Create a concrete generator for testing."""
class ConcreteGenerator(BaseComplianceReportGenerator):
def create_executive_summary(self, data):
return []
def create_charts_section(self, data):
return []
def create_requirements_index(self, data):
return []
config = FrameworkConfig(name="test", display_name="Test", language=language)
return ConcreteGenerator(config)
def test_build_info_rows_english(self):
"""Test info rows are built with English labels."""
generator = self._create_generator(language="en")
data = ComplianceData(
tenant_id="t1",
scan_id="scan-123",
provider_id="p1",
compliance_id="test_compliance",
framework="Test Framework",
name="Test Name",
version="1.0",
description="Test description",
)
rows = generator._build_info_rows(data, language="en")
assert ("Framework:", "Test Framework") in rows
assert ("Name:", "Test Name") in rows
assert ("Version:", "1.0") in rows
assert ("Scan ID:", "scan-123") in rows
assert ("Description:", "Test description") in rows
def test_build_info_rows_spanish(self):
"""Test info rows are built with Spanish labels."""
generator = self._create_generator(language="es")
data = ComplianceData(
tenant_id="t1",
scan_id="scan-123",
provider_id="p1",
compliance_id="test_compliance",
framework="Test Framework",
name="Test Name",
version="1.0",
description="Test description",
)
rows = generator._build_info_rows(data, language="es")
assert ("Framework:", "Test Framework") in rows
assert ("Nombre:", "Test Name") in rows
assert ("Versión:", "1.0") in rows
assert ("Scan ID:", "scan-123") in rows
assert ("Descripción:", "Test description") in rows
def test_build_info_rows_with_provider(self):
"""Test info rows include provider info when available."""
from unittest.mock import Mock
generator = self._create_generator(language="en")
mock_provider = Mock()
mock_provider.provider = "aws"
mock_provider.uid = "123456789012"
mock_provider.alias = "my-account"
data = ComplianceData(
tenant_id="t1",
scan_id="scan-123",
provider_id="p1",
compliance_id="test_compliance",
framework="Test",
name="Test",
version="1.0",
description="",
provider_obj=mock_provider,
)
rows = generator._build_info_rows(data, language="en")
assert ("Provider:", "AWS") in rows
assert ("Account ID:", "123456789012") in rows
assert ("Alias:", "my-account") in rows
def test_build_info_rows_with_provider_spanish(self):
"""Test provider info uses Spanish labels."""
from unittest.mock import Mock
generator = self._create_generator(language="es")
mock_provider = Mock()
mock_provider.provider = "azure"
mock_provider.uid = "subscription-id"
mock_provider.alias = "mi-suscripcion"
data = ComplianceData(
tenant_id="t1",
scan_id="scan-123",
provider_id="p1",
compliance_id="test_compliance",
framework="Test",
name="Test",
version="1.0",
description="",
provider_obj=mock_provider,
)
rows = generator._build_info_rows(data, language="es")
assert ("Proveedor:", "AZURE") in rows
assert ("Account ID:", "subscription-id") in rows
assert ("Alias:", "mi-suscripcion") in rows
def test_build_info_rows_without_provider(self):
"""Test info rows work without provider info."""
generator = self._create_generator(language="en")
data = ComplianceData(
tenant_id="t1",
scan_id="scan-123",
provider_id="p1",
compliance_id="test_compliance",
framework="Test",
name="Test",
version="1.0",
description="",
provider_obj=None,
)
rows = generator._build_info_rows(data, language="en")
# Provider info should not be present
labels = [label for label, _ in rows]
assert "Provider:" not in labels
assert "Account ID:" not in labels
assert "Alias:" not in labels
def test_build_info_rows_provider_with_missing_fields(self):
"""Test provider info handles None values gracefully."""
from unittest.mock import Mock
generator = self._create_generator(language="en")
mock_provider = Mock()
mock_provider.provider = "gcp"
mock_provider.uid = None
mock_provider.alias = None
data = ComplianceData(
tenant_id="t1",
scan_id="scan-123",
provider_id="p1",
compliance_id="test_compliance",
framework="Test",
name="Test",
version="1.0",
description="",
provider_obj=mock_provider,
)
rows = generator._build_info_rows(data, language="en")
assert ("Provider:", "GCP") in rows
assert ("Account ID:", "N/A") in rows
assert ("Alias:", "N/A") in rows
def test_build_info_rows_without_description(self):
"""Test info rows exclude description when empty."""
generator = self._create_generator(language="en")
data = ComplianceData(
tenant_id="t1",
scan_id="scan-123",
provider_id="p1",
compliance_id="test_compliance",
framework="Test",
name="Test",
version="1.0",
description="",
)
rows = generator._build_info_rows(data, language="en")
labels = [label for label, _ in rows]
assert "Description:" not in labels
def test_build_info_rows_defaults_to_english(self):
"""Test unknown language defaults to English labels."""
generator = self._create_generator(language="en")
data = ComplianceData(
tenant_id="t1",
scan_id="scan-123",
provider_id="p1",
compliance_id="test_compliance",
framework="Test",
name="Test",
version="1.0",
description="Desc",
)
rows = generator._build_info_rows(data, language="fr") # Unknown language
# Should use English labels as fallback
assert ("Name:", "Test") in rows
assert ("Description:", "Desc") in rows
# =============================================================================
# Integration Tests
# =============================================================================