#[cfg(test)]
mod tests {
use chrono::{Duration, Utc};
use kasl::db::db::Db;
use kasl::db::tasks::Tasks;
use kasl::db::workdays::Workdays;
use kasl::libs::config::Config;
use kasl::libs::task::{Task, TaskFilter};
use serial_test::serial;
use tempfile::TempDir;
use test_context::{TestContext, test_context};
struct WorkflowTestContext {
_temp_dir: TempDir,
}
impl TestContext for WorkflowTestContext {
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());
}
WorkflowTestContext { _temp_dir: temp_dir }
}
}
#[test_context(WorkflowTestContext)]
#[serial]
#[test]
fn test_complete_work_session_workflow(_ctx: &mut WorkflowTestContext) {
let _db = Db::new().unwrap();
let mut workdays = Workdays::new().unwrap();
let mut tasks = Tasks::new().unwrap();
let today = Utc::now().date_naive();
workdays.insert_start(today).unwrap();
let task1 = Task::new("Morning standup", "Daily team meeting", Some(25));
let task2 = Task::new("Code review", "Review PR #123", Some(50));
let task3 = Task::new("Bug fix", "Fix login issue", Some(75));
tasks.insert(&task1).unwrap();
tasks.insert(&task2).unwrap();
tasks.insert(&task3).unwrap();
let task_list = tasks.fetch(kasl::libs::task::TaskFilter::All).unwrap();
assert_eq!(task_list.len(), 3);
let mut completed_task1 = task_list[0].clone();
let mut completed_task2 = task_list[1].clone();
completed_task1.completeness = Some(100);
completed_task2.completeness = Some(100);
tasks.update(&completed_task1).unwrap();
tasks.update(&completed_task2).unwrap();
workdays.insert_end(today).unwrap();
let final_workday = workdays.fetch(today).unwrap();
assert!(final_workday.is_some());
assert!(final_workday.unwrap().end.is_some());
let final_tasks = tasks.fetch(kasl::libs::task::TaskFilter::All).unwrap();
let completed_tasks: Vec<_> = final_tasks.iter().filter(|t| t.completeness == Some(100)).collect();
assert_eq!(completed_tasks.len(), 2); }
#[test_context(WorkflowTestContext)]
#[serial]
#[test]
fn test_multi_day_workflow(_ctx: &mut WorkflowTestContext) {
let _db = Db::new().unwrap();
let mut workdays = Workdays::new().unwrap();
let mut tasks = Tasks::new().unwrap();
let base_date = Utc::now() - Duration::days(3);
for day in 0..3 {
let day_date = (base_date + Duration::days(day)).date_naive();
workdays.insert_start(day_date).unwrap();
let task_name = format!("Day {} Task", day + 1);
let task = Task::new(&task_name, "Daily task", Some(50));
tasks.insert(&task).unwrap();
let task_list = tasks.fetch(kasl::libs::task::TaskFilter::All).unwrap();
let mut last_task = task_list.last().unwrap().clone();
last_task.completeness = Some(100);
tasks.update(&last_task).unwrap();
workdays.insert_end(day_date).unwrap();
}
let all_tasks = tasks.fetch(kasl::libs::task::TaskFilter::All).unwrap();
assert!(all_tasks.len() >= 3);
let completed_tasks: Vec<_> = all_tasks.iter().filter(|t| t.completeness == Some(100)).collect();
assert!(completed_tasks.len() >= 3);
}
#[test_context(WorkflowTestContext)]
#[serial]
#[test]
fn test_configuration_integration(_ctx: &mut WorkflowTestContext) {
let config = Config {
monitor: Some(kasl::libs::config::MonitorConfig {
min_pause_duration: 30,
pause_threshold: 60,
poll_interval: 1000,
activity_threshold: 30,
min_work_interval: 10,
..Default::default()
}),
server: Some(kasl::libs::config::ServerConfig {
api_url: "https://test.example.com".to_string(),
auth_token: "test_token_123".to_string(),
}),
kasl_server: None,
si: None,
gitlab: None,
jira: None,
jira_inbox: None,
productivity: None,
report: None,
task_discovery: None,
};
let save_result = config.save();
assert!(save_result.is_ok());
let loaded_config = Config::read().unwrap();
assert!(loaded_config.monitor.is_some());
assert!(loaded_config.server.is_some());
let monitor_config = loaded_config.monitor.unwrap();
assert_eq!(monitor_config.min_pause_duration, 30);
assert_eq!(monitor_config.pause_threshold, 60);
let server_config = loaded_config.server.unwrap();
assert_eq!(server_config.api_url, "https://test.example.com");
assert_eq!(server_config.auth_token, "test_token_123");
let _db = Db::new().unwrap();
let mut tasks = Tasks::new().unwrap();
let task = Task::new("Config Test Task", "Test with config loaded", Some(60));
let result = tasks.insert(&task);
assert!(result.is_ok());
}
#[test_context(WorkflowTestContext)]
#[serial]
#[test]
fn test_database_consistency(_ctx: &mut WorkflowTestContext) {
let _db = Db::new().unwrap();
let mut workdays = Workdays::new().unwrap();
let mut tasks = Tasks::new().unwrap();
let today = Utc::now().date_naive();
workdays.insert_start(today).unwrap();
for i in 1..=5 {
let task = Task::new(&format!("Task {}", i), &format!("Description {}", i), Some(i * 20));
tasks.insert(&task).unwrap();
}
let task_list = tasks.fetch(kasl::libs::task::TaskFilter::All).unwrap();
for (index, task) in task_list.iter().enumerate() {
if index % 2 == 0 {
let mut completed_task = task.clone();
completed_task.completeness = Some(100);
tasks.update(&completed_task).unwrap();
}
}
workdays.insert_end(today).unwrap();
let final_workday = workdays.fetch(today).unwrap();
let final_tasks = tasks.fetch(kasl::libs::task::TaskFilter::All).unwrap();
assert!(final_workday.is_some());
assert_eq!(final_tasks.len(), 5);
let completed_count = final_tasks.iter().filter(|t| t.completeness == Some(100)).count();
assert_eq!(completed_count, 3); }
#[test_context(WorkflowTestContext)]
#[serial]
#[test]
fn test_error_recovery_workflow(_ctx: &mut WorkflowTestContext) {
let _db = Db::new().unwrap();
let mut tasks = Tasks::new().unwrap();
let valid_task = Task::new("Valid task", "This should work", Some(50));
let valid_result = tasks.insert(&valid_task);
assert!(valid_result.is_ok());
let edge_cases = vec![
Task::new("", "", Some(0)), Task::new(
"Very long task name that might exceed some theoretical limit but should still work fine in most database systems",
"Also a very long description",
Some(100),
), Task::new("Special chars: à áâãäå ñü", "Unicode test: ä½ å¥½ä¸–ç•Œ", Some(25)), ];
for edge_task in edge_cases {
let result = tasks.insert(&edge_task);
match result {
Ok(_) => {
}
Err(_) => {
}
}
}
let another_valid_task = Task::new("Another valid task", "This should also work", Some(75));
let another_valid_result = tasks.insert(&another_valid_task);
assert!(another_valid_result.is_ok());
let all_tasks = tasks.fetch(kasl::libs::task::TaskFilter::All).unwrap();
assert!(all_tasks.len() >= 2); }
#[test_context(WorkflowTestContext)]
#[serial]
#[test]
fn test_concurrent_access_simulation(_ctx: &mut WorkflowTestContext) {
let _db1 = Db::new().unwrap();
let _db2 = Db::new().unwrap();
let mut tasks1 = Tasks::new().unwrap();
let mut tasks2 = Tasks::new().unwrap();
let mut workdays1 = Workdays::new().unwrap();
let mut workdays2 = Workdays::new().unwrap();
let task1 = Task::new("From handle 1", "Task via first database handle", Some(30));
let yesterday = (Utc::now() - Duration::days(1)).date_naive();
workdays1.insert_start(yesterday).unwrap();
tasks1.insert(&task1).unwrap();
let task2 = Task::new("From handle 2", "Task via second database handle", Some(70));
let today = Utc::now().date_naive();
workdays2.insert_start(today).unwrap();
tasks2.insert(&task2).unwrap();
let tasks_via_handle1 = tasks1.fetch(kasl::libs::task::TaskFilter::All).unwrap();
let tasks_via_handle2 = tasks2.fetch(kasl::libs::task::TaskFilter::All).unwrap();
assert_eq!(tasks_via_handle1.len(), tasks_via_handle2.len());
assert!(tasks_via_handle1.len() >= 2);
workdays1.insert_end(yesterday).unwrap();
workdays2.insert_end(today).unwrap();
}
#[test_context(WorkflowTestContext)]
#[serial]
#[test]
fn test_data_persistence_across_sessions(_ctx: &mut WorkflowTestContext) {
{
let _db = Db::new().unwrap();
let mut tasks = Tasks::new().unwrap();
let mut workdays = Workdays::new().unwrap();
let task = Task::new("Persistent Task", "Should survive across sessions", Some(40));
let today = Utc::now().date_naive();
workdays.insert_start(today).unwrap();
tasks.insert(&task).unwrap();
workdays.insert_end(today).unwrap();
}
{
let _db = Db::new().unwrap();
let mut tasks = Tasks::new().unwrap();
let mut workdays = Workdays::new().unwrap();
let persisted_tasks = tasks.fetch(kasl::libs::task::TaskFilter::All).unwrap();
let today = Utc::now().date_naive();
let persisted_workday = workdays.fetch(today).unwrap();
assert!(!persisted_tasks.is_empty());
assert!(persisted_workday.is_some());
let persistent_task = persisted_tasks.iter().find(|t| t.name == "Persistent Task");
assert!(persistent_task.is_some());
}
}
#[test_context(WorkflowTestContext)]
#[serial]
#[test]
fn test_task_lifecycle_management(_ctx: &mut WorkflowTestContext) {
let _db = Db::new().unwrap();
let mut tasks = Tasks::new().unwrap();
let task1 = Task::new("New Task", "Just created", Some(0));
let task2 = Task::new("In Progress Task", "Half done", Some(50));
let task3 = Task::new("Almost Done Task", "Nearly finished", Some(90));
tasks.insert(&task1).unwrap();
tasks.insert(&task2).unwrap();
tasks.insert(&task3).unwrap();
let all_tasks = tasks.fetch(kasl::libs::task::TaskFilter::All).unwrap();
assert_eq!(all_tasks.len(), 3);
let mut task_to_complete = all_tasks[0].clone();
task_to_complete.completeness = Some(100);
let update_result = tasks.update(&task_to_complete);
assert!(update_result.is_ok());
let updated_tasks = tasks.fetch(kasl::libs::task::TaskFilter::All).unwrap();
let completed_count = updated_tasks.iter().filter(|t| t.completeness == Some(100)).count();
assert_eq!(completed_count, 1);
}
#[test_context(WorkflowTestContext)]
#[serial]
#[test]
fn test_workday_time_management(_ctx: &mut WorkflowTestContext) {
let _db = Db::new().unwrap();
let mut workdays = Workdays::new().unwrap();
let today = Utc::now().date_naive();
let yesterday = (Utc::now() - Duration::days(1)).date_naive();
workdays.insert_start(yesterday).unwrap();
workdays.insert_start(today).unwrap();
workdays.insert_end(yesterday).unwrap();
workdays.insert_end(today).unwrap();
let yesterday_workday = workdays.fetch(yesterday).unwrap();
let today_workday = workdays.fetch(today).unwrap();
assert!(yesterday_workday.is_some());
assert!(today_workday.is_some());
assert!(yesterday_workday.unwrap().end.is_some());
assert!(today_workday.unwrap().end.is_some());
}
#[test_context(WorkflowTestContext)]
#[serial]
#[test]
fn test_error_handling_and_recovery(_ctx: &mut WorkflowTestContext) {
let _db = Db::new().unwrap();
let mut tasks = Tasks::new().unwrap();
let task = Task::new("Valid task", "This should work", Some(50));
let result = tasks.insert(&task);
assert!(result.is_ok());
let non_existent_task = tasks.get_by_id(99999).unwrap();
assert!(non_existent_task.is_none());
let another_task = Task::new("Another task", "Should still work", Some(75));
let result2 = tasks.insert(&another_task);
assert!(result2.is_ok());
let all_tasks = tasks.fetch(kasl::libs::task::TaskFilter::All).unwrap();
assert_eq!(all_tasks.len(), 2);
}
#[test_context(WorkflowTestContext)]
#[serial]
#[test]
fn the_link_between_an_issue_and_its_task_survives_a_rename(_ctx: &mut WorkflowTestContext) {
let mut tasks = Tasks::new().unwrap();
let task = Task::new("PROJ-412 Fix session timeout", "", Some(0)).from_jira("PROJ-412");
tasks.insert(&task).unwrap();
let found = tasks.fetch(TaskFilter::ByJiraKey("PROJ-412".to_string())).unwrap();
assert_eq!(found.len(), 1, "the task must be findable by its issue key");
let id = found[0].id.unwrap();
let mut renamed = found[0].clone();
renamed.update_from(&Task::new("Session timeout on settings", "stale cookie jar", Some(60)));
renamed.jira_key = None;
tasks.update(&renamed).unwrap();
let after = tasks.fetch(TaskFilter::ByJiraKey("PROJ-412".to_string())).unwrap();
assert_eq!(after.len(), 1, "renaming must not detach the task from its issue");
assert_eq!(after[0].id.unwrap(), id);
assert_eq!(after[0].name, "Session timeout on settings");
assert_eq!(after[0].jira_key.as_deref(), Some("PROJ-412"));
}
#[test_context(WorkflowTestContext)]
#[serial]
#[test]
fn a_task_not_taken_from_an_issue_carries_no_key(_ctx: &mut WorkflowTestContext) {
let mut tasks = Tasks::new().unwrap();
tasks.insert(&Task::new("Something I thought of myself", "", Some(0))).unwrap();
let all = tasks.fetch(TaskFilter::All).unwrap();
assert_eq!(all.len(), 1);
assert!(all[0].jira_key.is_none(), "only `inbox take` sets a key");
assert!(tasks.fetch(TaskFilter::ByJiraKey("PROJ-1".to_string())).unwrap().is_empty());
}
}