- 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/ и т.д.
79 lines
2.6 KiB
Python
79 lines
2.6 KiB
Python
"""Тесты чтения 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
|