blackopsrepl's picture
update
e7cf451
"""
Integration tests for the employee scheduling solver.
Tests that the solver can find feasible solutions for demo data
and that the REST API works correctly.
"""
from employee_scheduling.rest_api import app
from employee_scheduling.domain import EmployeeScheduleModel
from employee_scheduling.converters import model_to_schedule
from fastapi.testclient import TestClient
from time import sleep
from pytest import fail
import pytest
client = TestClient(app)
@pytest.mark.timeout(120)
def test_feasible():
"""Test that the solver can find a feasible solution for SMALL demo data."""
demo_data_response = client.get("/demo-data/SMALL")
assert demo_data_response.status_code == 200
job_id_response = client.post("/schedules", json=demo_data_response.json())
assert job_id_response.status_code == 200
job_id = job_id_response.text[1:-1]
ATTEMPTS = 1_000
best_score = None
for _ in range(ATTEMPTS):
sleep(0.1)
schedule_response = client.get(f"/schedules/{job_id}")
schedule_json = schedule_response.json()
schedule_model = EmployeeScheduleModel.model_validate(schedule_json)
schedule = model_to_schedule(schedule_model)
if schedule.score is not None:
best_score = schedule.score
if schedule.score.is_feasible:
stop_solving_response = client.delete(f"/schedules/{job_id}")
assert stop_solving_response.status_code == 200
return
client.delete(f"/schedules/{job_id}")
fail(f"Solution is not feasible after 100 seconds. Best score: {best_score}")
def test_demo_data_list():
"""Test that demo data list endpoint returns available datasets."""
response = client.get("/demo-data")
assert response.status_code == 200
data = response.json()
assert isinstance(data, list)
assert len(data) > 0
assert "SMALL" in data
def test_demo_data_small_structure():
"""Test that SMALL demo data has expected structure."""
response = client.get("/demo-data/SMALL")
assert response.status_code == 200
data = response.json()
# Check required fields
assert "employees" in data
assert "shifts" in data
# Validate employees
assert len(data["employees"]) > 0
for employee in data["employees"]:
assert "name" in employee
# Validate shifts
assert len(data["shifts"]) > 0
for shift in data["shifts"]:
assert "id" in shift
assert "start" in shift
assert "end" in shift
assert "location" in shift
assert "requiredSkill" in shift
def test_solver_start_and_stop():
"""Test that solver can be started and stopped."""
demo_data_response = client.get("/demo-data/SMALL")
assert demo_data_response.status_code == 200
# Start solving
start_response = client.post("/schedules", json=demo_data_response.json())
assert start_response.status_code == 200
job_id = start_response.text[1:-1]
# Wait a bit
sleep(0.5)
# Check status
status_response = client.get(f"/schedules/{job_id}")
assert status_response.status_code == 200
schedule = status_response.json()
assert "solverStatus" in schedule
# Stop solving
stop_response = client.delete(f"/schedules/{job_id}")
assert stop_response.status_code == 200
def test_solver_assigns_employees():
"""Test that solver actually assigns employees to shifts."""
demo_data_response = client.get("/demo-data/SMALL")
assert demo_data_response.status_code == 200
job_id_response = client.post("/schedules", json=demo_data_response.json())
assert job_id_response.status_code == 200
job_id = job_id_response.text[1:-1]
# Wait for some solving
sleep(2)
schedule_response = client.get(f"/schedules/{job_id}")
schedule_json = schedule_response.json()
# Check that some shifts have employees assigned
assigned_shifts = [s for s in schedule_json["shifts"] if s.get("employee") is not None]
assert len(assigned_shifts) > 0, "Solver should assign some employees to shifts"
client.delete(f"/schedules/{job_id}")
def test_score_analysis():
"""Test that score analysis endpoint returns constraint analysis."""
demo_data_response = client.get("/demo-data/SMALL")
assert demo_data_response.status_code == 200
# Start solving and get a scored solution
job_id_response = client.post("/schedules", json=demo_data_response.json())
assert job_id_response.status_code == 200
job_id = job_id_response.text[1:-1]
# Wait for solver to produce a score
sleep(2)
schedule_response = client.get(f"/schedules/{job_id}")
schedule_json = schedule_response.json()
# Stop solving
client.delete(f"/schedules/{job_id}")
# Call analyze endpoint
analyze_response = client.put("/schedules/analyze", json=schedule_json)
assert analyze_response.status_code == 200
analysis = analyze_response.json()
# Verify structure
assert "constraints" in analysis
assert isinstance(analysis["constraints"], list)
assert len(analysis["constraints"]) > 0
# Check constraint structure
for constraint in analysis["constraints"]:
assert "name" in constraint
assert "weight" in constraint
assert "score" in constraint
assert "matches" in constraint
assert isinstance(constraint["matches"], list)