import pytest import os from typing import List from redmine_reporter.types import ReportRow from redmine_reporter.formatters.console import TableFormatter, CompactFormatter from redmine_reporter.formatters.csv import CSVFormatter from redmine_reporter.formatters.markdown import MarkdownFormatter from redmine_reporter.formatters.odt import ODTFormatter from odf.opendocument import OpenDocument def make_fake_report_rows() -> List[ReportRow]: """ Генерирует фейковый отчёт с полным покрытием логики группировки: - Проект A: версия v1.0 (2 задачи), версия v2.0 (1 задача) - Проект B: версия (1 задача) - Проект C: версия v1.0 (1 задача), версия v1.1 (2 задачи) """ return [ # Проект A, v1.0 { "project": "Проект A", "version": "v1.0", "display_project": "Проект A", "display_version": "v1.0", "issue_id": 101, "subject": "Реализовать фичу X", "status_ru": "В работе", "time_text": "4ч 30м", }, { "project": "Проект A", "version": "v1.0", "display_project": "", "display_version": "", "issue_id": 102, "subject": "Исправить баг Y", "status_ru": "Решена", "time_text": "2ч", }, # Проект A, v2.0 { "project": "Проект A", "version": "v2.0", "display_project": "", "display_version": "v2.0", "issue_id": 103, "subject": "Документация Z", "status_ru": "Ожидание", "time_text": "1ч", }, # Проект B, без версии { "project": "Проект B", "version": "", "display_project": "Проект B", "display_version": "", "issue_id": 201, "subject": "Обновить README", "status_ru": "Закрыто", "time_text": "0ч", }, # Проект C, v1.0 { "project": "Проект C", "version": "v1.0", "display_project": "Проект C", "display_version": "v1.0", "issue_id": 301, "subject": "Настроить CI", "status_ru": "В работе", "time_text": "3ч 15м", }, # Проект C, v1.1 { "project": "Проект C", "version": "v1.1", "display_project": "", "display_version": "v1.1", "issue_id": 302, "subject": "Добавить тесты", "status_ru": "В работе", "time_text": "5ч", }, { "project": "Проект C", "version": "v1.1", "display_project": "", "display_version": "", "issue_id": 303, "subject": "Рефакторинг", "status_ru": "Решена", "time_text": "6ч 45м", }, ] @pytest.fixture def fake_rows(): return make_fake_report_rows() def _get_formatter_output_text(result): """Преобразует результат форматтера в строку для проверки содержимого.""" if isinstance(result, OpenDocument): return "" elif isinstance(result, str): return result else: raise TypeError(f"Unexpected formatter output type: {type(result)}") FORMATTER_FACTORIES = [ ("table", lambda: TableFormatter()), ("compact", lambda: CompactFormatter()), ("csv", lambda: CSVFormatter()), ("markdown", lambda: MarkdownFormatter()), ( "odt", lambda: ODTFormatter(author="Тест Автор", from_date="2026-01-01", to_date="2026-01-31"), ), ] @pytest.mark.parametrize("name, formatter_factory", FORMATTER_FACTORIES) def test_formatter_does_not_crash(fake_rows, name, formatter_factory): """Проверяем, что форматтер не падает на валидных данных.""" formatter = formatter_factory() result = formatter.format(fake_rows) if name == "odt": assert isinstance(result, OpenDocument) else: assert isinstance(result, str) assert len(result.strip()) > 0 @pytest.mark.parametrize("name, formatter_factory", FORMATTER_FACTORIES) def test_formatter_contains_key_content(fake_rows, name, formatter_factory): """Проверяем, что вывод содержит ключевые элементы.""" formatter = formatter_factory() result = formatter.format(fake_rows) output_text = _get_formatter_output_text(result) if not output_text: return # Пропускаем ODT # Общие элементы assert "Проект A" in output_text assert "Проект B" in output_text assert "В работе" in output_text assert "" in output_text assert "6ч 45м" in output_text # Специфика по форматам if name == "csv": # В CSV ID и subject — отдельные колонки assert "101" in output_text assert "Реализовать фичу X" in output_text else: # В остальных — вместе assert "101. Реализовать фичу X" in output_text def test_odt_save_creates_valid_file(fake_rows, tmp_path): """Проверяем, что ODT можно сохранить и он открывается как ZIP.""" output_file = tmp_path / "report.odt" formatter = ODTFormatter(author="Тест", from_date="2026-01-01", to_date="2026-01-31") formatter.save(fake_rows, str(output_file)) assert output_file.exists() with open(output_file, "rb") as f: assert f.read(2) == b"PK" # сигнатура ZIP