Reorganize integration tests, simplify fn signatures

This commit is contained in:
2026-02-22 13:01:28 +02:00
parent a41d1cd469
commit 76d308351a
8 changed files with 270 additions and 344 deletions

View File

@@ -0,0 +1,58 @@
use rota_lib::{
config::{ToxicPair, UserConfig},
schedule::MonthlySchedule,
slot::{Day, ShiftPosition, Slot},
workload::{WorkloadBounds, WorkloadTracker},
};
pub fn validate_all_constraints(
schedule: &MonthlySchedule,
tracker: &WorkloadTracker,
config: &UserConfig,
) {
assert_eq!(schedule.0.len() as u8, config.total_slots);
for d in 2..=config.total_days {
let current: Vec<_> = [ShiftPosition::First, ShiftPosition::Second]
.iter()
.filter_map(|&p| schedule.get_resident_id(&Slot::new(Day(d), p)))
.collect();
let previous: Vec<_> = [ShiftPosition::First, ShiftPosition::Second]
.iter()
.filter_map(|&p| schedule.get_resident_id(&Slot::new(Day(d - 1), p)))
.collect();
for r in current {
assert!(!previous.contains(&r));
}
}
for d in 1..=config.total_days {
let day = Day(d);
if day.is_open_shift() {
let r1 = schedule.get_resident_id(&Slot::new(day, ShiftPosition::First));
let r2 = schedule.get_resident_id(&Slot::new(day, ShiftPosition::Second));
assert_ne!(r1, r2);
if let (Some(id1), Some(id2)) = (r1, r2) {
let pair = ToxicPair::from((*id1, *id2));
assert!(config.toxic_pairs.iter().all(|t| !t.matches(&pair)));
}
}
}
let bounds = WorkloadBounds::new_with_config(config);
for (slot, r_id) in &schedule.0 {
let r = config
.residents
.iter()
.find(|r| &r.id == r_id)
.expect("Resident not found");
assert!(r.allowed_types.contains(&slot.shift_type()));
assert!(!r.negative_shifts.contains(&slot.day));
}
for resident in &config.residents {
let workload = tracker.current_workload(&resident.id);
let max = *bounds.max_workloads.get(&resident.id).unwrap();
assert!(workload <= max, "workload: {}, max: {}", workload, max);
}
}

View File

@@ -1,14 +1,16 @@
mod common;
#[cfg(test)]
mod integration_tests {
use crate::common::validate_all_constraints;
use rota_lib::{
config::{ToxicPair, UserConfig},
config::UserConfig,
fixtures::{
complex_config, hard_config, manual_shifts_heavy_config, maximal_config, minimal_config,
},
schedule::MonthlySchedule,
scheduler::Scheduler,
slot::{Day, ShiftPosition, Slot},
workload::{WorkloadBounds, WorkloadTracker},
workload::WorkloadTracker,
};
use rstest::rstest;
@@ -91,55 +93,18 @@ mod integration_tests {
Ok(())
}
fn validate_all_constraints(
schedule: &MonthlySchedule,
tracker: &WorkloadTracker,
config: &UserConfig,
) {
assert_eq!(schedule.0.len() as u8, config.total_slots);
#[rstest]
fn test_export_pipeline(minimal_config: UserConfig) -> anyhow::Result<()> {
let mut schedule = MonthlySchedule::new();
let mut tracker = WorkloadTracker::default();
let scheduler = Scheduler::new_with_config(minimal_config.clone());
assert!(scheduler.run(&mut schedule, &mut tracker)?);
for d in 2..=config.total_days {
let current: Vec<_> = [ShiftPosition::First, ShiftPosition::Second]
.iter()
.filter_map(|&p| schedule.get_resident_id(&Slot::new(Day(d), p)))
.collect();
let previous: Vec<_> = [ShiftPosition::First, ShiftPosition::Second]
.iter()
.filter_map(|&p| schedule.get_resident_id(&Slot::new(Day(d - 1), p)))
.collect();
for r in current {
assert!(!previous.contains(&r));
}
}
schedule.export_as_docx(&minimal_config)?;
for d in 1..=config.total_days {
let day = Day(d);
if day.is_open_shift() {
let r1 = schedule.get_resident_id(&Slot::new(day, ShiftPosition::First));
let r2 = schedule.get_resident_id(&Slot::new(day, ShiftPosition::Second));
assert_ne!(r1, r2);
if let (Some(id1), Some(id2)) = (r1, r2) {
let pair = ToxicPair::from((*id1, *id2));
assert!(config.toxic_pairs.iter().all(|t| !t.matches(&pair)));
}
}
}
let bounds = WorkloadBounds::new_with_config(config);
for (slot, r_id) in &schedule.0 {
let r = config
.residents
.iter()
.find(|r| &r.id == r_id)
.expect("Resident not found");
assert!(r.allowed_types.contains(&slot.shift_type()));
assert!(!r.negative_shifts.contains(&slot.day));
}
for resident in &config.residents {
let workload = tracker.current_workload(&resident.id);
let max = *bounds.max_workloads.get(&resident.id).unwrap();
assert!(workload <= max, "workload: {}, max: {}", workload, max);
}
let metadata = std::fs::metadata("rota.docx")?;
assert!(metadata.len() > 0);
std::fs::remove_file("rota.docx")?;
Ok(())
}
}