354 lines
14 KiB
Python
354 lines
14 KiB
Python
"""
|
||
test_models.py - Тесты для моделей и предпочтений
|
||
Проверяют корректность работы класса OperatorPreferences и связанных функций
|
||
"""
|
||
import pytest
|
||
from datetime import datetime
|
||
from models import (
|
||
OperatorPreferences,
|
||
PreferencesBuilder,
|
||
DayOfWeek,
|
||
PreferenceType,
|
||
get_days,
|
||
create_model,
|
||
solve_model,
|
||
get_solution
|
||
)
|
||
from config import YEAR, MONTH, SHIFTS, OPERATORS
|
||
|
||
class TestDayOfWeek:
|
||
"""Тесты для enum DayOfWeek"""
|
||
|
||
def test_day_of_week_values(self):
|
||
"""Проверка значений enum"""
|
||
assert DayOfWeek.MONDAY.value == 0
|
||
assert DayOfWeek.TUESDAY.value == 1
|
||
assert DayOfWeek.WEDNESDAY.value == 2
|
||
assert DayOfWeek.THURSDAY.value == 3
|
||
assert DayOfWeek.FRIDAY.value == 4
|
||
assert DayOfWeek.SATURDAY.value == 5
|
||
assert DayOfWeek.SUNDAY.value == 6
|
||
|
||
def test_day_of_week_names(self):
|
||
"""Проверка названий enum"""
|
||
assert DayOfWeek.MONDAY.name == "MONDAY"
|
||
assert DayOfWeek.SUNDAY.name == "SUNDAY"
|
||
|
||
|
||
class TestOperatorPreferences:
|
||
"""Тесты для класса OperatorPreferences"""
|
||
|
||
def test_creation(self):
|
||
"""Тест создания объекта предпочтений"""
|
||
prefs = OperatorPreferences("Тестовый")
|
||
assert prefs.operator_name == "Тестовый"
|
||
assert prefs.work_on_dates == []
|
||
assert prefs.work_on_day_of_week == []
|
||
assert prefs.work_only_shift is None
|
||
assert prefs.off_on_dates == []
|
||
|
||
def test_to_dict_empty(self):
|
||
"""Тест конвертации в словарь для пустых предпочтений"""
|
||
prefs = OperatorPreferences("Тестовый")
|
||
result = prefs.to_dict()
|
||
assert result == {}
|
||
|
||
def test_to_dict_with_dates(self):
|
||
"""Тест конвертации с датами"""
|
||
prefs = OperatorPreferences("Тестовый")
|
||
prefs.work_on_dates = [10, 15, 20]
|
||
result = prefs.to_dict()
|
||
assert result['dates'] == [10, 15, 20]
|
||
|
||
def test_to_dict_with_shift(self):
|
||
"""Тест конвертации со сменой"""
|
||
prefs = OperatorPreferences("Тестовый")
|
||
prefs.work_only_shift = 2
|
||
result = prefs.to_dict()
|
||
assert result['shift'] == 2
|
||
|
||
def test_to_dict_with_day_of_week(self):
|
||
"""Тест конвертации с днем недели"""
|
||
prefs = OperatorPreferences("Тестовый")
|
||
prefs.work_on_day_of_week = [DayOfWeek.MONDAY, DayOfWeek.WEDNESDAY]
|
||
result = prefs.to_dict()
|
||
assert result['day_of_week'] == ['monday', 'wednesday']
|
||
|
||
def test_to_dict_with_off_dates(self):
|
||
"""Тест конвертации с днями отдыха"""
|
||
prefs = OperatorPreferences("Тестовый")
|
||
prefs.off_on_dates = [5, 12, 19]
|
||
result = prefs.to_dict()
|
||
assert result['off_dates'] == [5, 12, 19]
|
||
|
||
def test_to_dict_all(self):
|
||
"""Тест конвертации всех полей"""
|
||
prefs = OperatorPreferences("Тестовый")
|
||
prefs.work_on_dates = [10, 15]
|
||
prefs.work_on_day_of_week = [DayOfWeek.MONDAY]
|
||
prefs.work_only_shift = 1
|
||
prefs.off_on_dates = [5]
|
||
result = prefs.to_dict()
|
||
assert result['dates'] == [10, 15]
|
||
assert result['day_of_week'] == ['monday']
|
||
assert result['shift'] == 1
|
||
assert result['off_dates'] == [5]
|
||
|
||
def test_from_dict(self):
|
||
"""Тест создания из словаря"""
|
||
data = {
|
||
'dates': [10, 15],
|
||
'day_of_week': ['monday'],
|
||
'shift': 1,
|
||
'off_dates': [5]
|
||
}
|
||
prefs = OperatorPreferences.from_dict("Тестовый", data)
|
||
assert prefs.work_on_dates == [10, 15]
|
||
assert prefs.work_on_day_of_week == [DayOfWeek.MONDAY]
|
||
assert prefs.work_only_shift == 1
|
||
assert prefs.off_on_dates == [5]
|
||
|
||
def test_from_dict_partial(self):
|
||
"""Тест создания из словаря с неполными данными"""
|
||
data = {'dates': [10]}
|
||
prefs = OperatorPreferences.from_dict("Тестовый", data)
|
||
assert prefs.work_on_dates == [10]
|
||
assert prefs.work_on_day_of_week == []
|
||
assert prefs.work_only_shift is None
|
||
assert prefs.off_on_dates == []
|
||
|
||
def test_from_dict_invalid_day_of_week(self):
|
||
"""Тест обработки невалидного дня недели"""
|
||
data = {'day_of_week': ['invalid']}
|
||
prefs = OperatorPreferences.from_dict("Тестовый", data)
|
||
assert prefs.work_on_day_of_week == []
|
||
|
||
def test_from_dict_invalid_shift(self):
|
||
"""Тест обработки невалидной смены"""
|
||
data = {'shift': 5}
|
||
prefs = OperatorPreferences.from_dict("Тестовый", data)
|
||
assert prefs.work_only_shift is None
|
||
|
||
def test_from_dict_invalid_dates(self):
|
||
"""Тест обработки невалидных дат"""
|
||
data = {'dates': [32]}
|
||
prefs = OperatorPreferences.from_dict("Тестовый", data)
|
||
assert prefs.work_on_dates == []
|
||
|
||
def test_from_dict_invalid_off_dates(self):
|
||
"""Тест обработки невалидных дней отдыха"""
|
||
data = {'off_dates': [32]}
|
||
prefs = OperatorPreferences.from_dict("Тестовый", data)
|
||
assert prefs.off_on_dates == []
|
||
|
||
|
||
class TestPreferencesBuilder:
|
||
"""Тесты для класса PreferencesBuilder"""
|
||
|
||
def test_creation(self):
|
||
"""Тест создания объекта"""
|
||
builder = PreferencesBuilder("Тестовый")
|
||
assert builder.operator_name == "Тестовый"
|
||
assert builder._preferences is None
|
||
|
||
def test_add_dates(self):
|
||
"""Тест добавления дат"""
|
||
builder = PreferencesBuilder("Тестовый")
|
||
builder.add_dates([10, 15])
|
||
assert builder._preferences.work_on_dates == [10, 15]
|
||
|
||
def test_add_day_of_week(self):
|
||
"""Тест добавления дней недели"""
|
||
builder = PreferencesBuilder("Тестовый")
|
||
builder.add_day_of_week([DayOfWeek.MONDAY, DayOfWeek.WEDNESDAY])
|
||
assert len(builder._preferences.work_on_day_of_week) == 2
|
||
|
||
def test_add_shift(self):
|
||
"""Тест добавления смены"""
|
||
builder = PreferencesBuilder("Тестовый")
|
||
builder.add_shift(2)
|
||
assert builder._preferences.work_only_shift == 2
|
||
|
||
def test_add_off_dates(self):
|
||
"""Тест добавления дней отдыха"""
|
||
builder = PreferencesBuilder("Тестовый")
|
||
builder.add_off_dates([5, 12])
|
||
assert builder._preferences.off_on_dates == [5, 12]
|
||
|
||
def test_to_dict(self):
|
||
"""Тест конвертации в словарь"""
|
||
builder = PreferencesBuilder("Тестовый")
|
||
builder.add_dates([10])
|
||
builder.add_shift(1)
|
||
result = builder.to_dict()
|
||
assert result['dates'] == [10]
|
||
assert result['shift'] == 1
|
||
|
||
def test_to_dict_empty(self):
|
||
"""Тест конвертации пустых предпочтений"""
|
||
builder = PreferencesBuilder("Тестовый")
|
||
result = builder.to_dict()
|
||
assert result == {}
|
||
|
||
def test_to_dict_invalid(self):
|
||
"""Тест обработки невалидных данных"""
|
||
builder = PreferencesBuilder("Тестовый")
|
||
builder.add_dates([32]) # Невалидная дата
|
||
result = builder.to_dict()
|
||
assert result == {}
|
||
|
||
def test_to_dict_invalid_day_of_week(self):
|
||
"""Тест обработки невалидного дня недели"""
|
||
builder = PreferencesBuilder("Тестовый")
|
||
builder.add_day_of_week([DayOfWeek.MONDAY, "invalid"])
|
||
result = builder.to_dict()
|
||
assert result['day_of_week'] == ['monday']
|
||
|
||
def test_to_dict_invalid_shift(self):
|
||
"""Тест обработки невалидной смены"""
|
||
builder = PreferencesBuilder("Тестовый")
|
||
builder.add_shift(5) # Невалидная смена
|
||
result = builder.to_dict()
|
||
assert result == {}
|
||
|
||
def test_to_dict_invalid_off_dates(self):
|
||
"""Тест обработки невалидных дней отдыха"""
|
||
builder = PreferencesBuilder("Тестовый")
|
||
builder.add_off_dates([32]) # Невалидная дата
|
||
result = builder.to_dict()
|
||
assert result == {}
|
||
|
||
|
||
class TestGetDays:
|
||
"""Тесты для функции get_days"""
|
||
|
||
def test_days_count(self):
|
||
"""Проверка количества дней"""
|
||
days = get_days()
|
||
assert len(days) == 30 # Апрель 2026
|
||
|
||
def test_days_format(self):
|
||
"""Проверка формата дней"""
|
||
days = get_days()
|
||
for day in days:
|
||
assert isinstance(day, str)
|
||
assert len(day) == 10 # YYYY-MM-DD
|
||
assert '-' in day
|
||
|
||
def test_days_sorted(self):
|
||
"""Проверка сортировки дней"""
|
||
days = get_days()
|
||
assert days == sorted(days)
|
||
|
||
def test_days_start(self):
|
||
"""Проверка первого дня"""
|
||
days = get_days()
|
||
assert days[0] == "2026-04-01"
|
||
|
||
def test_days_end(self):
|
||
"""Проверка последнего дня"""
|
||
days = get_days()
|
||
assert days[-1] == "2026-04-30"
|
||
|
||
|
||
class TestCreateModel:
|
||
"""Тесты для функции create_model"""
|
||
|
||
def test_model_creation(self):
|
||
"""Тест создания модели"""
|
||
model, variables, operators_set = create_model()
|
||
assert model is not None
|
||
assert variables is not None
|
||
assert operators_set is not None
|
||
assert len(operators_set) == len(OPERATORS)
|
||
|
||
def test_variables_count(self):
|
||
"""Тест количества переменных"""
|
||
model, variables, operators_set = create_model()
|
||
expected_count = len(get_days()) * len(SHIFTS) * len(OPERATORS)
|
||
assert len(variables) == expected_count
|
||
|
||
def test_operators_set(self):
|
||
"""Тест множества операторов"""
|
||
model, variables, operators_set = create_model()
|
||
assert operators_set == set(OPERATORS)
|
||
|
||
def test_model_has_constraints(self):
|
||
"""Тест наличия ограничений"""
|
||
model, variables, operators_set = create_model()
|
||
# Модель должна иметь ограничения
|
||
assert model.NumConstraints() > 0
|
||
|
||
def test_model_has_hints(self):
|
||
"""Тест наличия подсказок"""
|
||
model, variables, operators_set = create_model()
|
||
# Модель должна иметь подсказки
|
||
assert model.NumHints() > 0
|
||
|
||
|
||
class TestSolveModel:
|
||
"""Тесты для функции solve_model"""
|
||
|
||
def test_solve_returns_bool(self):
|
||
"""Тест возврата булевого значения"""
|
||
result = solve_model()
|
||
assert isinstance(result, bool)
|
||
|
||
def test_solve_with_valid_config(self):
|
||
"""Тест решения с валидной конфигурацией"""
|
||
# Это может занять время, поэтому тестуем только факт вызова
|
||
try:
|
||
result = solve_model()
|
||
# Результат может быть True или False в зависимости от конфигурации
|
||
assert isinstance(result, bool)
|
||
except Exception as e:
|
||
pytest.fail(f"Ошибка при решении модели: {e}")
|
||
|
||
|
||
class TestGetSolution:
|
||
"""Тесты для функции get_solution"""
|
||
|
||
def test_solution_structure(self):
|
||
"""Тест структуры решения"""
|
||
schedule = get_solution()
|
||
assert isinstance(schedule, dict)
|
||
assert len(schedule) > 0
|
||
|
||
def test_solution_days(self):
|
||
"""Тест дней в решении"""
|
||
schedule = get_solution()
|
||
days = get_days()
|
||
assert set(schedule.keys()) == set(days)
|
||
|
||
def test_solution_shifts(self):
|
||
"""Тест смен в решении"""
|
||
schedule = get_solution()
|
||
for day, day_shifts in schedule.items():
|
||
assert isinstance(day_shifts, dict)
|
||
assert len(day_shifts) == len(SHIFTS)
|
||
|
||
def test_solution_operators(self):
|
||
"""Тест операторов в решении"""
|
||
schedule = get_solution()
|
||
for day, day_shifts in schedule.items():
|
||
for shift_idx, shift_data in day_shifts.items():
|
||
assert isinstance(shift_data, dict)
|
||
assert len(shift_data) == len(OPERATORS)
|
||
|
||
def test_solution_values(self):
|
||
"""Тест значений в решении"""
|
||
schedule = get_solution()
|
||
for day, day_shifts in schedule.items():
|
||
for shift_idx, shift_data in day_shifts.items():
|
||
for op_name, value in shift_data.items():
|
||
assert isinstance(value, bool)
|
||
assert value in [True, False]
|
||
|
||
def test_solution_consistency(self):
|
||
"""Тест согласованности решения"""
|
||
schedule = get_solution()
|
||
# Каждый день и смена должны иметь ровно одного оператора
|
||
for day, day_shifts in schedule.items():
|
||
for shift_idx, shift_data in day_shifts.items():
|
||
active_operators = [op for op, value in shift_data.items() if value]
|
||
assert len(active_operators) == 1 |