""" tests/test_ppt_vision_audit.py ───────────────────────────────────────────────────────────────── Operation Ollama-First v5.0 / Phase 26 — PPT 視覺審核整合驗證 """ import os import tempfile from unittest.mock import patch, MagicMock import pytest @pytest.fixture(autouse=True) def _reset_env(monkeypatch): monkeypatch.delenv('PPT_VISION_ENABLED', raising=False) yield def test_audit_disabled_by_default(): """flag OFF 時直接 skip(errors 含 disabled marker)""" from services.ppt_vision_service import audit_recent_ppts summary = audit_recent_ppts(reports_dir='/tmp') assert summary['total_issues'] == 0 assert any('PPT_VISION_ENABLED=false' in e for e in summary['errors']) def test_audit_missing_dir(monkeypatch): monkeypatch.setenv('PPT_VISION_ENABLED', 'true') from services.ppt_vision_service import audit_recent_ppts summary = audit_recent_ppts(reports_dir='/tmp/nonexistent_xyz_ppts') assert summary['total_issues'] == 0 assert any('not found' in e for e in summary['errors']) def test_audit_no_recent_ppts(monkeypatch): """目錄存在但無 .pptx → 0 audit""" monkeypatch.setenv('PPT_VISION_ENABLED', 'true') from services.ppt_vision_service import audit_recent_ppts with tempfile.TemporaryDirectory() as tmpdir: # 放一個 .txt 干擾 with open(os.path.join(tmpdir, 'not_ppt.txt'), 'w') as f: f.write('hello') summary = audit_recent_ppts(reports_dir=tmpdir) assert summary['audited_files'] == [] assert summary['total_issues'] == 0 def test_audit_filter_old_files(monkeypatch): """超過 hours 視窗的舊檔不應 audit""" monkeypatch.setenv('PPT_VISION_ENABLED', 'true') from services.ppt_vision_service import audit_recent_ppts with tempfile.TemporaryDirectory() as tmpdir: old_path = os.path.join(tmpdir, 'old.pptx') with open(old_path, 'w') as f: f.write('fake') # 改 mtime 到 2 天前 old_time = __import__('time').time() - 2 * 86400 os.utime(old_path, (old_time, old_time)) summary = audit_recent_ppts(reports_dir=tmpdir, hours=24) assert summary['audited_files'] == [] def test_check_ppt_file_libreoffice_not_installed(monkeypatch): """LibreOffice 不在 → fail-safe skip 不 raise""" monkeypatch.setenv('PPT_VISION_ENABLED', 'true') from services.ppt_vision_service import PPTVisionService with tempfile.NamedTemporaryFile(suffix='.pptx', delete=False) as f: f.write(b'fake pptx') ppt_path = f.name try: with patch('subprocess.run', side_effect=FileNotFoundError('libreoffice')): svc = PPTVisionService() result = svc.check_ppt_file(ppt_path) assert result['success'] is False assert 'libreoffice not installed' in (result['error'] or '') finally: os.unlink(ppt_path) def test_check_ppt_file_disabled(): """flag OFF check_ppt_file 也 skip""" from services.ppt_vision_service import PPTVisionService with tempfile.NamedTemporaryFile(suffix='.pptx', delete=False) as f: f.write(b'fake pptx') ppt_path = f.name try: svc = PPTVisionService() result = svc.check_ppt_file(ppt_path) assert result['success'] is False assert 'PPT_VISION_ENABLED=false' in (result['error'] or '') finally: os.unlink(ppt_path) def test_check_ppt_file_missing(monkeypatch): monkeypatch.setenv('PPT_VISION_ENABLED', 'true') from services.ppt_vision_service import PPTVisionService svc = PPTVisionService() result = svc.check_ppt_file('/tmp/this_pptx_does_not_exist_xyz.pptx') assert result['success'] is False assert 'not found' in (result['error'] or '') def test_push_telegram_skips_when_no_issues(): """無 issues 不推(避免靜默無問題洗版)""" from services.ppt_vision_service import push_ppt_audit_to_telegram summary = {'total_issues': 0, 'audited_files': [{'path': 'a.pptx', 'issues': 0}]} assert push_ppt_audit_to_telegram(summary) is False def test_push_telegram_with_issues(): """有 issues 才推 Telegram""" from services.ppt_vision_service import push_ppt_audit_to_telegram summary = { 'total_issues': 3, 'audited_files': [ { 'path': '/tmp/test.pptx', 'slides_checked': 2, 'issues': 3, 'issues_by_slide': [(1, ['⚠️ 圖表被切', '⚠️ 文字溢出']), (2, ['⚠️ 配色衝突'])], }, ], } with patch('services.telegram_templates._send_telegram_raw') as mock_send: result = push_ppt_audit_to_telegram(summary) assert result is True mock_send.assert_called_once() msg = mock_send.call_args[0][0] assert 'PPT 視覺審核' in msg assert '3 issues' in msg or '3' in msg