#[cfg(test)]
mod tests {
use chrono::{Duration, NaiveDate, NaiveDateTime, NaiveTime};
use kasl::db::breaks::Break;
use kasl::db::workdays::Workday;
use kasl::libs::pause::Pause;
use kasl::libs::productivity::Productivity;
use serial_test::serial;
use tempfile::TempDir;
use test_context::{TestContext, test_context};
struct ProductivityTestContext {
_temp_dir: TempDir,
}
impl TestContext for ProductivityTestContext {
fn setup() -> Self {
let temp_dir = tempfile::tempdir().unwrap();
unsafe {
std::env::set_var("HOME", temp_dir.path());
}
unsafe {
std::env::set_var("LOCALAPPDATA", temp_dir.path());
}
ProductivityTestContext { _temp_dir: temp_dir }
}
}
fn create_test_workday() -> Workday {
let date = NaiveDate::from_ymd_opt(2024, 1, 15).unwrap();
let start = NaiveDateTime::new(date, NaiveTime::from_hms_opt(9, 0, 0).unwrap());
let end = NaiveDateTime::new(date, NaiveTime::from_hms_opt(17, 0, 0).unwrap());
Workday {
id: 1,
date,
start,
end: Some(end),
}
}
fn create_test_workday_with_duration(duration: Duration) -> Workday {
let date = NaiveDate::from_ymd_opt(2024, 1, 15).unwrap();
let start = NaiveDateTime::new(date, NaiveTime::from_hms_opt(9, 0, 0).unwrap());
let end = start + duration;
Workday {
id: 1,
date,
start,
end: Some(end),
}
}
#[test_context(ProductivityTestContext)]
#[serial]
#[test]
fn test_productivity_calculation_no_pauses(_ctx: &mut ProductivityTestContext) {
let workday = create_test_workday();
let pauses = vec![];
let breaks = vec![];
let productivity_calc = Productivity::with_test_data(&workday, breaks, vec![], pauses);
let productivity = productivity_calc.calculate_productivity();
assert_eq!(productivity, 100.0);
}
#[test_context(ProductivityTestContext)]
#[serial]
#[test]
fn test_productivity_calculation_with_pauses(_ctx: &mut ProductivityTestContext) {
let workday = create_test_workday();
let pauses = vec![Pause {
id: 1,
start: NaiveDateTime::new(workday.date, NaiveTime::from_hms_opt(10, 0, 0).unwrap()),
end: Some(NaiveDateTime::new(workday.date, NaiveTime::from_hms_opt(10, 15, 0).unwrap())),
duration: Some(Duration::minutes(15)),
}];
let breaks = vec![];
let productivity_calc = Productivity::with_test_data(&workday, breaks, pauses, vec![]);
let productivity = productivity_calc.calculate_productivity();
assert!((productivity - 96.875).abs() < 0.001);
}
#[test_context(ProductivityTestContext)]
#[serial]
#[test]
fn test_productivity_calculation_with_breaks(_ctx: &mut ProductivityTestContext) {
let workday = create_test_workday();
let pauses = vec![];
let breaks = vec![Break {
id: Some(1),
date: workday.date,
start: NaiveDateTime::new(workday.date, NaiveTime::from_hms_opt(12, 0, 0).unwrap()),
end: NaiveDateTime::new(workday.date, NaiveTime::from_hms_opt(13, 0, 0).unwrap()),
duration: Duration::hours(1),
reason: Some("Lunch break".to_string()),
created_at: Some(NaiveDateTime::new(workday.date, NaiveTime::from_hms_opt(12, 0, 0).unwrap())),
}];
let productivity_calc = Productivity::with_test_data(&workday, breaks, vec![], pauses);
let productivity = productivity_calc.calculate_productivity();
assert_eq!(productivity, 100.0);
}
#[test_context(ProductivityTestContext)]
#[serial]
#[test]
fn test_productivity_calculation_with_pauses_and_breaks(_ctx: &mut ProductivityTestContext) {
let workday = create_test_workday();
let pauses = vec![Pause {
id: 1,
start: NaiveDateTime::new(workday.date, NaiveTime::from_hms_opt(10, 0, 0).unwrap()),
end: Some(NaiveDateTime::new(workday.date, NaiveTime::from_hms_opt(10, 15, 0).unwrap())),
duration: Some(Duration::minutes(15)),
}];
let breaks = vec![Break {
id: Some(1),
date: workday.date,
start: NaiveDateTime::new(workday.date, NaiveTime::from_hms_opt(12, 0, 0).unwrap()),
end: NaiveDateTime::new(workday.date, NaiveTime::from_hms_opt(13, 0, 0).unwrap()),
duration: Duration::hours(1),
reason: Some("Lunch break".to_string()),
created_at: Some(NaiveDateTime::new(workday.date, NaiveTime::from_hms_opt(12, 0, 0).unwrap())),
}];
let productivity_calc = Productivity::with_test_data(&workday, breaks, pauses, vec![]);
let productivity = productivity_calc.calculate_productivity();
assert_eq!(productivity, 100.0);
}
#[test_context(ProductivityTestContext)]
#[serial]
#[test]
fn test_productivity_for_intervals_no_pauses(_ctx: &mut ProductivityTestContext) {
let work_time = Duration::hours(6);
let workday = create_test_workday_with_duration(work_time);
let pauses = vec![];
let breaks = vec![];
let productivity_calc = Productivity::with_test_data(&workday, breaks, vec![], pauses);
let productivity = productivity_calc.calculate_productivity();
assert_eq!(productivity, 100.0);
}
#[test_context(ProductivityTestContext)]
#[serial]
#[test]
fn test_productivity_for_intervals_with_pauses(_ctx: &mut ProductivityTestContext) {
let work_time = Duration::hours(6);
let workday = create_test_workday_with_duration(work_time);
let pauses = vec![Pause {
id: 1,
start: NaiveDate::from_ymd_opt(2024, 1, 15).unwrap().and_hms_opt(10, 0, 0).unwrap(),
end: Some(NaiveDate::from_ymd_opt(2024, 1, 15).unwrap().and_hms_opt(10, 15, 0).unwrap()),
duration: Some(Duration::minutes(15)),
}];
let breaks = vec![];
let productivity_calc = Productivity::with_test_data(&workday, breaks, pauses, vec![]);
let productivity = productivity_calc.calculate_productivity();
assert!((productivity - 95.833333).abs() < 0.001);
}
#[test_context(ProductivityTestContext)]
#[serial]
#[test]
fn test_productivity_edge_cases(_ctx: &mut ProductivityTestContext) {
let zero_work = Duration::zero();
let workday = create_test_workday_with_duration(zero_work);
let productivity_calc = Productivity::with_test_data(&workday, vec![], vec![], vec![]);
let productivity = productivity_calc.calculate_productivity();
assert_eq!(productivity, 0.0);
let work_time = Duration::hours(1);
let workday = create_test_workday_with_duration(work_time);
let negative_pause = vec![Pause {
id: 1,
start: NaiveDate::from_ymd_opt(2024, 1, 15).unwrap().and_hms_opt(10, 0, 0).unwrap(),
end: Some(NaiveDate::from_ymd_opt(2024, 1, 15).unwrap().and_hms_opt(9, 0, 0).unwrap()), duration: Some(Duration::hours(-2)), }];
let productivity_calc = Productivity::with_test_data(&workday, vec![], negative_pause, vec![]);
let productivity = productivity_calc.calculate_productivity();
assert!((0.0..=100.0).contains(&productivity));
}
#[test_context(ProductivityTestContext)]
#[serial]
#[test]
fn test_productivity_boundary_values(_ctx: &mut ProductivityTestContext) {
let workday = create_test_workday();
let massive_pause = vec![Pause {
id: 1,
start: workday.start,
end: workday.end,
duration: Some(Duration::hours(8)),
}];
let productivity_calc = Productivity::with_test_data(&workday, vec![], massive_pause, vec![]);
let productivity = productivity_calc.calculate_productivity();
assert_eq!(productivity, 0.0);
let massive_break = vec![Break {
id: Some(1),
date: workday.date,
start: workday.start,
end: workday.end.unwrap(),
duration: Duration::hours(8),
reason: Some("All day break".to_string()),
created_at: Some(workday.start),
}];
let productivity_calc = Productivity::with_test_data(&workday, massive_break, vec![], vec![]);
let productivity = productivity_calc.calculate_productivity();
assert_eq!(productivity, 0.0);
}
}