use cfait::client::RustyClient;
use cfait::context::TestContext;
use cfait::journal::{Action, Journal};
use cfait::model::Task;
use mockito::Server;
use std::collections::HashMap;
use std::sync::Arc;
#[tokio::test]
async fn test_create_412_handled_gracefully() {
let ctx = Arc::new(TestContext::new());
let mut server = Server::new_async().await;
let url = server.url();
let task_uid = "stuck-task";
let task_path = format!("/cal/{}.ics", task_uid);
let mock_put = server
.mock("PUT", task_path.as_str())
.match_header("If-None-Match", "*")
.with_status(412) .create_async()
.await;
let client = RustyClient::new(ctx.clone(), &url, "user", "pass", true, None).unwrap();
let mut task = Task::new("Stuck Task", &HashMap::new(), None);
task.uid = task_uid.to_string();
task.calendar_href = format!("{}/cal/", url);
task.href = format!("{}{}", url, task_path);
Journal::push(ctx.as_ref(), Action::Create(task)).unwrap();
let result = client.sync_journal().await;
mock_put.assert();
assert!(
result.is_ok(),
"Sync returned error for 412: {:?}",
result.err()
);
let journal = Journal::load(ctx.as_ref());
assert!(
journal.is_empty(),
"Journal should be empty. The client failed to resolve the 412 conflict."
);
}
#[tokio::test]
async fn test_create_500_persists() {
let ctx = Arc::new(TestContext::new());
let mut server = Server::new_async().await;
let url = server.url();
let task_path = "/cal/broken-server.ics";
let mock_put = server
.mock("PUT", task_path)
.match_header("If-None-Match", "*")
.with_status(500)
.create_async()
.await;
let client = RustyClient::new(ctx.clone(), &url, "user", "pass", true, None).unwrap();
let mut task = Task::new("Broken Task", &HashMap::new(), None);
task.uid = "broken-server".to_string();
task.calendar_href = format!("{}/cal/", url);
task.href = format!("{}{}", url, task_path);
Journal::push(ctx.as_ref(), Action::Create(task)).unwrap();
let result = client.sync_journal().await;
mock_put.assert();
assert!(result.is_err(), "Sync should fail on 500");
let journal = Journal::load(ctx.as_ref());
assert!(
!journal.is_empty(),
"Journal should preserve items on 500 error"
);
}
#[tokio::test]
async fn test_move_404_handled_gracefully() {
let ctx = Arc::new(TestContext::new());
let mut server = Server::new_async().await;
let url = server.url();
let old_path = "/cal1/task.ics";
let new_cal = format!("{}/cal2/", url);
let mock_move = server
.mock("MOVE", mockito::Matcher::Any)
.with_status(404)
.create_async()
.await;
let mock_create = server
.mock("PUT", mockito::Matcher::Any)
.with_status(201)
.with_header("ETag", "\"new-etag\"") .create_async()
.await;
let mock_delete = server
.mock("DELETE", mockito::Matcher::Any)
.with_status(404) .expect_at_least(1) .create_async()
.await;
let client = RustyClient::new(ctx.clone(), &url, "user", "pass", true, None).unwrap();
let mut task = Task::new("Moving Task", &HashMap::new(), None);
task.uid = "moving".to_string();
task.href = format!("{}{}", url, old_path);
task.calendar_href = format!("{}/cal1/", url);
Journal::push(ctx.as_ref(), Action::Move(task, new_cal)).unwrap();
let result = client.sync_journal().await;
mock_move.assert();
mock_create.assert();
mock_delete.assert();
assert!(result.is_ok());
let journal = Journal::load(ctx.as_ref());
assert!(
journal.is_empty(),
"Journal should be empty after falling back to Create+Delete"
);
}