"""Tests for BrandEnforcementService: color utilities, font/color/logo enforcement.""" import uuid import pytest from models.pptx_models import ( PptxAutoShapeBoxModel, PptxChartBoxModel, PptxFillModel, PptxFontModel, PptxParagraphModel, PptxPositionModel, PptxPresentationModel, PptxSlideModel, PptxTextBoxModel, PptxTextRunModel, ) from models.sql.brand_config import BrandConfigModel from pptx.enum.shapes import MSO_AUTO_SHAPE_TYPE from services.brand_enforcement_service import ( BrandEnforcementService, _contrast_ratio, _hex_to_rgb, _relative_luminance, ) @pytest.fixture def svc(): return BrandEnforcementService() @pytest.fixture def brand() -> BrandConfigModel: return BrandConfigModel( client_id=uuid.uuid4(), primary_colors=["#FF0000", "#00FF00"], secondary_colors=["#0000FF"], fonts={"heading": "Montserrat", "body": "Open Sans"}, logo_paths=["/logos/acme.png"], ) class TestColorUtilities: def test_hex_to_rgb_six_chars(self): assert _hex_to_rgb("FF0000") == (255, 0, 0) assert _hex_to_rgb("00FF00") == (0, 255, 0) def test_hex_to_rgb_with_hash(self): assert _hex_to_rgb("#0000FF") == (0, 0, 255) def test_hex_to_rgb_three_chars(self): assert _hex_to_rgb("F00") == (255, 0, 0) def test_white_has_high_luminance(self): assert _relative_luminance("FFFFFF") > 0.9 def test_black_has_low_luminance(self): assert _relative_luminance("000000") < 0.01 def test_contrast_ratio_black_vs_white(self): white_lum = _relative_luminance("FFFFFF") black_lum = _relative_luminance("000000") ratio = _contrast_ratio(white_lum, black_lum) assert ratio > 20 # Should be ~21:1 class TestBrandColors: def test_get_brand_colors_list(self, svc, brand): colors = svc.get_brand_colors_list(brand) assert "FF0000" in colors assert "00FF00" in colors assert "0000FF" in colors def test_fallback_when_no_colors(self, svc): brand = BrandConfigModel( client_id=uuid.uuid4(), primary_colors=None, secondary_colors=None, ) colors = svc.get_brand_colors_list(brand) assert len(colors) > 0 # Defaults assert "4472C4" in colors class TestEnforceOnPptxModel: def _make_text_slide(self, font_name="Arial", font_size=14): return PptxSlideModel(shapes=[ PptxTextBoxModel( position=PptxPositionModel(left=10, top=10, width=200, height=50), paragraphs=[ PptxParagraphModel( text="Hello World", font=PptxFontModel(name=font_name, size=font_size, color="000000"), ) ], ) ]) def test_fonts_replaced_with_brand_fonts(self, svc, brand): model = PptxPresentationModel(slides=[ self._make_text_slide("Arial", 32), # Title slide (idx=0) → heading font self._make_text_slide("Times", 14), # Content slide → body font ]) result = svc.enforce_on_pptx_model(model, brand) # Slide 0 (title): heading font assert result.slides[0].shapes[0].paragraphs[0].font.name == "Montserrat" # Slide 1 (content): body font assert result.slides[1].shapes[0].paragraphs[0].font.name == "Open Sans" def test_logo_added_to_content_slides_only(self, svc, brand): model = PptxPresentationModel(slides=[ self._make_text_slide(), # Title slide — no logo self._make_text_slide(), # Content slide — should get logo self._make_text_slide(), # Another content slide ]) result = svc.enforce_on_pptx_model(model, brand) # Slide 0: no logo shape added (still 1 shape) assert len(result.slides[0].shapes) == 1 # Slides 1-2: logo added (2 shapes each) assert len(result.slides[1].shapes) == 2 assert len(result.slides[2].shapes) == 2 def test_no_logo_when_no_logo_paths(self, svc): brand_no_logo = BrandConfigModel( client_id=uuid.uuid4(), fonts={"heading": "Inter", "body": "Inter"}, logo_paths=None, ) model = PptxPresentationModel(slides=[ self._make_text_slide(), self._make_text_slide(), ]) result = svc.enforce_on_pptx_model(model, brand_no_logo) assert len(result.slides[1].shapes) == 1 def test_chart_gets_brand_colors(self, svc, brand): chart = PptxChartBoxModel( position=PptxPositionModel(left=0, top=0, width=400, height=300), chart_type="bar", chart_data={"categories": ["A"], "series": [{"name": "S", "values": [1]}]}, ) model = PptxPresentationModel(slides=[ PptxSlideModel(shapes=[chart]), ]) result = svc.enforce_on_pptx_model(model, brand) assert result.slides[0].shapes[0].brand_colors is not None assert "FF0000" in result.slides[0].shapes[0].brand_colors def test_contrast_fix_white_on_white(self, svc, brand): """White text on white bg should be auto-corrected to black.""" slide = PptxSlideModel(shapes=[ PptxTextBoxModel( position=PptxPositionModel(left=0, top=0, width=100, height=50), paragraphs=[ PptxParagraphModel( text="Invisible", font=PptxFontModel(name="Arial", size=14, color="FFFFFF"), ) ], ) ]) # Default bg is white (FFFFFF) model = PptxPresentationModel(slides=[slide]) result = svc.enforce_on_pptx_model(model, brand) # After contrast fix, text should be dark assert result.slides[0].shapes[0].paragraphs[0].font.color == "000000"