fix: handle empty time entries and add missing mypy override for openpyxl
- Treat empty list from fetch_issues_with_spent_time as 'no entries' and exit 0 instead of crashing later in the pipeline. - Update CLI output-extension tests to use non-empty mock data so they actually reach the extension validation code path. - Add openpyxl to mypy ignore_missing_imports overrides. Closes #36
This commit is contained in:
@@ -57,5 +57,5 @@ multi_line_output = 3
|
|||||||
warn_unused_configs = true
|
warn_unused_configs = true
|
||||||
|
|
||||||
[[tool.mypy.overrides]]
|
[[tool.mypy.overrides]]
|
||||||
module = ["odf.*", "redminelib.*", "tabulate"]
|
module = ["odf.*", "redminelib.*", "tabulate", "openpyxl.*"]
|
||||||
ignore_missing_imports = true
|
ignore_missing_imports = true
|
||||||
|
|||||||
@@ -114,6 +114,10 @@ def main(argv: Optional[List[str]] = None) -> int:
|
|||||||
print("ℹ️ No time entries found in the given period.", file=sys.stderr)
|
print("ℹ️ No time entries found in the given period.", file=sys.stderr)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
if not issue_hours:
|
||||||
|
print("ℹ️ No time entries found in the given period.", file=sys.stderr)
|
||||||
|
return 0
|
||||||
|
|
||||||
print(f"✅ Total issues: {len(issue_hours)} [{args.date}]", file=sys.stderr)
|
print(f"✅ Total issues: {len(issue_hours)} [{args.date}]", file=sys.stderr)
|
||||||
|
|
||||||
rows = build_grouped_report(issue_hours, fill_time=not args.no_time)
|
rows = build_grouped_report(issue_hours, fill_time=not args.no_time)
|
||||||
|
|||||||
@@ -71,11 +71,23 @@ def test_cli_invalid_date_format():
|
|||||||
assert code == 1
|
assert code == 1
|
||||||
|
|
||||||
|
|
||||||
|
class _MockIssue:
|
||||||
|
"""Простой mock Redmine Issue для CLI-тестов."""
|
||||||
|
|
||||||
|
def __init__(self, issue_id=1, subject="Task", project="Project", status="New"):
|
||||||
|
self.id = issue_id
|
||||||
|
self.subject = subject
|
||||||
|
self.project = project
|
||||||
|
self.status = status
|
||||||
|
self.fixed_version = None
|
||||||
|
|
||||||
|
|
||||||
@mock.patch.dict(os.environ, VALID_ENV, clear=True)
|
@mock.patch.dict(os.environ, VALID_ENV, clear=True)
|
||||||
@mock.patch("redmine_reporter.cli.fetch_issues_with_spent_time")
|
@mock.patch("redmine_reporter.cli.fetch_issues_with_spent_time")
|
||||||
def test_cli_unknown_output_extension(mock_fetch, tmp_path):
|
def test_cli_unknown_output_extension(mock_fetch, tmp_path):
|
||||||
"""Неизвестное расширение файла -- выход 1."""
|
"""Неизвестное расширение файла -- выход 1."""
|
||||||
mock_fetch.return_value = []
|
issue = _MockIssue()
|
||||||
|
mock_fetch.return_value = [(issue, 1.0)]
|
||||||
output = str(tmp_path / "report.xyz")
|
output = str(tmp_path / "report.xyz")
|
||||||
code = main(["--date", "2026-01-01--2026-01-31", "--output", output])
|
code = main(["--date", "2026-01-01--2026-01-31", "--output", output])
|
||||||
assert code == 1
|
assert code == 1
|
||||||
@@ -85,7 +97,8 @@ def test_cli_unknown_output_extension(mock_fetch, tmp_path):
|
|||||||
@mock.patch("redmine_reporter.cli.fetch_issues_with_spent_time")
|
@mock.patch("redmine_reporter.cli.fetch_issues_with_spent_time")
|
||||||
def test_cli_output_without_extension(mock_fetch, tmp_path):
|
def test_cli_output_without_extension(mock_fetch, tmp_path):
|
||||||
"""Файл без расширения -- выход 1 с подсказкой."""
|
"""Файл без расширения -- выход 1 с подсказкой."""
|
||||||
mock_fetch.return_value = []
|
issue = _MockIssue()
|
||||||
|
mock_fetch.return_value = [(issue, 1.0)]
|
||||||
output = str(tmp_path / "report")
|
output = str(tmp_path / "report")
|
||||||
code = main(["--date", "2026-01-01--2026-01-31", "--output", output])
|
code = main(["--date", "2026-01-01--2026-01-31", "--output", output])
|
||||||
assert code == 1
|
assert code == 1
|
||||||
@@ -96,7 +109,8 @@ def test_cli_output_without_extension(mock_fetch, tmp_path):
|
|||||||
@mock.patch("redmine_reporter.cli.get_formatter_by_extension")
|
@mock.patch("redmine_reporter.cli.get_formatter_by_extension")
|
||||||
def test_cli_odt_missing_odfpy_message(mock_gf, mock_fetch, tmp_path, capsys):
|
def test_cli_odt_missing_odfpy_message(mock_gf, mock_fetch, tmp_path, capsys):
|
||||||
"""При запросе .odt без odfpy — выход 1, понятное сообщение про odfpy."""
|
"""При запросе .odt без odfpy — выход 1, понятное сообщение про odfpy."""
|
||||||
mock_fetch.return_value = []
|
issue = _MockIssue()
|
||||||
|
mock_fetch.return_value = [(issue, 1.0)]
|
||||||
mock_gf.return_value = None
|
mock_gf.return_value = None
|
||||||
output = str(tmp_path / "report.odt")
|
output = str(tmp_path / "report.odt")
|
||||||
code = main(["--date", "2026-01-01--2026-01-31", "--output", output])
|
code = main(["--date", "2026-01-01--2026-01-31", "--output", output])
|
||||||
|
|||||||
Reference in New Issue
Block a user