Тесты и инфраструктура

- tests/: 104 теста — lessons (все форматы ячеек), xlsx (мержи/скрытые
  столбцы/красный текст/даты), parsing (группы/недели/индексы), API (TestClient,
  TTL/modified/stale-while-error, 404/400/502)
- xlsx_builder.py: генерация xlsx в памяти для тестов без openpyxl
- docker-compose: healthz-хелскчеки (не блокируют старт), SCHEDULE_LINK
- Makefile: target test
- requirements: закреплённые версии
- .gitignore: __pycache__/, .venv/ и т.д.
This commit is contained in:
2026-09-09 11:33:58 +03:00
parent 2febf9b4ff
commit 9c9875579c
14 changed files with 1122 additions and 18 deletions

78
tests/test_xlsx.py Normal file
View File

@@ -0,0 +1,78 @@
"""Тесты чтения xlsx: ячейки, мержи, скрытые столбцы, красный текст, даты."""
import zipfile
from datetime import date
import pytest
from fixtures import fixture_sheets, fixture_xlsx, MAIN
from schedule.errors import ScheduleError
from schedule.xlsx import excel_serial_to_date, parse_workbook
def test_parse_workbook_fixture():
sheets = parse_workbook(fixture_xlsx())
assert set(sheets) == {"Бакалавры и магистры", "аудитории", "Преподаватели"}
rows = sheets[MAIN].rows
# заголовок группы
assert rows[0][6] == "ИВТ-11БО\nсеместр до 11.01.27"
# скрытый столбец
assert 8 in sheets[MAIN].hidden_cols
# дата-серия как число
assert rows[2][0] == "46265"
# мерж F3:F4 -> значение пары попало во вторую строку
assert rows[3][5] == "1\n9:00-\n10:35"
assert rows[2][5] == "1\n9:00-\n10:35"
def test_red_runs_marked():
sheets = parse_workbook(fixture_xlsx())
cell = sheets[MAIN].rows[2][6]
assert cell.startswith("Математика\nСмирнов А.В.\nауд. 215\n")
assert "\u27EA" in cell and "\u27EB" in cell
assert cell.endswith("пара 14.09 не будет\u27EB")
def test_empty_cells_are_empty_strings():
sheets = parse_workbook(fixture_xlsx())
assert sheets[MAIN].rows[5][6] == ""
# все строки одной ширины
widths = {len(r) for r in sheets[MAIN].rows}
assert len(widths) == 1
def test_bad_zip_raises():
with pytest.raises(ScheduleError):
parse_workbook(b"this is not an xlsx file")
def test_truncated_zip_raises():
good = fixture_xlsx()
with pytest.raises(ScheduleError):
parse_workbook(good[:20])
@pytest.mark.parametrize(
"serial, expected",
[
("46265", date(2026, 8, 31)),
(46265, date(2026, 8, 31)),
("46278", date(2026, 9, 13)),
# до 1900: Excel считает несуществующий 29.02.1900
(1, date(1900, 1, 1)),
(59, date(1900, 2, 28)),
(61, date(1900, 3, 1)),
],
)
def test_excel_serial_to_date(serial, expected):
assert excel_serial_to_date(serial) == expected
@pytest.mark.parametrize("bad", ["", "текст", None, "0", "-5", "1,5x"])
def test_excel_serial_to_date_invalid(bad):
# "1,5x" не парсится; остальные — None
if bad == "1,5x":
assert excel_serial_to_date(bad) is None
else:
assert excel_serial_to_date(bad) is None