Files
duty-teller/tests/test_duty_schedule_parser.py
Nikolay Tatarinov 5331fac334 Add configuration rules, refactor settings management, and enhance import functionality
- Introduced a new configuration file `.cursorrules` to define coding standards, error handling, testing requirements, and project-specific guidelines.
- Refactored `config.py` to implement a `Settings` dataclass for better management of environment variables, improving testability and maintainability.
- Updated the import duty schedule handler to utilize session management with `session_scope`, ensuring proper database session handling.
- Enhanced the import service to streamline the duty schedule import process, improving code organization and readability.
- Added new service layer functions to encapsulate business logic related to group duty pinning and duty schedule imports.
- Updated README documentation to reflect the new configuration structure and improved import functionality.
2026-02-18 12:35:11 +03:00

121 lines
4.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Tests for duty-schedule JSON parser."""
from datetime import date
import pytest
from importers.duty_schedule import (
DUTY_MARKERS,
UNAVAILABLE_MARKER,
VACATION_MARKER,
DutyScheduleParseError,
parse_duty_schedule,
)
def test_parse_valid_schedule():
raw = (
'{"meta": {"start_date": "2026-02-16", "weeks": 2}, '
'"schedule": ['
'{"name": "Ivanov I.I.", "duty": "; ; \u0431 ; \u0411 ; \u0432 ; ;"}, '
'{"name": "Petrov P.P.", "duty": " ; \u041d ; \u041e ; ; ; ;"}'
"]}"
).encode("utf-8")
result = parse_duty_schedule(raw)
assert result.start_date == date(2026, 2, 16)
# Petrov has 7 cells -> end = start + 6
assert result.end_date == date(2026, 2, 22)
assert len(result.entries) == 2
by_name = {e.full_name: e for e in result.entries}
assert "Ivanov I.I." in by_name
assert "Petrov P.P." in by_name
# Ivanov: only duty (б, Б, в) -> 2026-02-18, 19, 20
ivan = by_name["Ivanov I.I."]
assert sorted(ivan.duty_dates) == [
date(2026, 2, 18),
date(2026, 2, 19),
date(2026, 2, 20),
]
assert ivan.unavailable_dates == []
assert ivan.vacation_dates == []
# Petrov: one Н (unavailable), one О (vacation) -> 2026-02-17, 18
petr = by_name["Petrov P.P."]
assert petr.duty_dates == []
assert petr.unavailable_dates == [date(2026, 2, 17)]
assert petr.vacation_dates == [date(2026, 2, 18)]
def test_parse_empty_duty_string():
raw = b'{"meta": {"start_date": "2026-02-01"}, "schedule": [{"name": "A", "duty": ""}]}'
result = parse_duty_schedule(raw)
assert result.start_date == date(2026, 2, 1)
assert result.end_date == date(2026, 2, 1)
assert len(result.entries) == 1
assert result.entries[0].full_name == "A"
assert result.entries[0].duty_dates == []
assert result.entries[0].unavailable_dates == []
assert result.entries[0].vacation_dates == []
def test_parse_invalid_json():
with pytest.raises(DutyScheduleParseError, match="Invalid JSON"):
parse_duty_schedule(b"not json")
def test_parse_missing_meta():
with pytest.raises(DutyScheduleParseError, match="meta"):
parse_duty_schedule(b'{"schedule": []}')
def test_parse_missing_start_date():
with pytest.raises(DutyScheduleParseError, match="start_date"):
parse_duty_schedule(b'{"meta": {"weeks": 1}, "schedule": []}')
def test_parse_invalid_start_date():
with pytest.raises(DutyScheduleParseError, match="start_date|Invalid"):
parse_duty_schedule(b'{"meta": {"start_date": "not-a-date"}, "schedule": []}')
def test_parse_missing_schedule():
with pytest.raises(DutyScheduleParseError, match="schedule"):
parse_duty_schedule(b'{"meta": {"start_date": "2026-02-01"}}')
def test_parse_schedule_not_array():
with pytest.raises(DutyScheduleParseError, match="schedule"):
parse_duty_schedule(b'{"meta": {"start_date": "2026-02-01"}, "schedule": {}}')
def test_parse_schedule_item_missing_name():
with pytest.raises(DutyScheduleParseError, match="name"):
parse_duty_schedule(
b'{"meta": {"start_date": "2026-02-01"}, "schedule": [{"duty": ";"}]}'
)
def test_parse_schedule_item_empty_name():
with pytest.raises(DutyScheduleParseError, match="empty"):
parse_duty_schedule(
b'{"meta": {"start_date": "2026-02-01"}, "schedule": [{"name": " ", "duty": ""}]}'
)
def test_duty_markers():
assert DUTY_MARKERS == frozenset({"б", "Б", "в", "В"})
assert "Н" not in DUTY_MARKERS and "О" not in DUTY_MARKERS
def test_unavailable_and_vacation_markers():
assert UNAVAILABLE_MARKER == "Н"
assert VACATION_MARKER == "О"
raw = (
'{"meta": {"start_date": "2026-02-01"}, "schedule": ['
'{"name": "X", "duty": "\u041d; \u041e; \u0432"}]}'
).encode("utf-8")
result = parse_duty_schedule(raw)
entry = result.entries[0]
assert entry.unavailable_dates == [date(2026, 2, 1)]
assert entry.vacation_dates == [date(2026, 2, 2)]
assert entry.duty_dates == [date(2026, 2, 3)]