//! AC-15.6, server side: a phone offline for 14 days with 200 local changes syncs all of them on reconnect (scripted //! clock: the changes carry the times they were made, the push happens 14 days later). use chrono::Duration; use kvitt_server::time::{format_ts, parse_ts}; use serde_json::{Value, json}; use crate::common::*; use crate::sync_fixtures::*; #[tokio::test] async fn fourteen_days_offline_200_changes_all_sync() { let app = app().await; // Anna's tablet-free phone: 150 creates, 30 edits of earlier ones, 20 deletes, spread over 14 days. let start = parse_ts(NOW).unwrap(); let at = |i: i64| format_ts(start + Duration::minutes(i * 100)); let mut latest: Vec = Vec::new(); let mut changes: Vec = Vec::new(); for i in 0..150 { let mut e = expense(&new_id(), &at(i)); e["description"] = json!(format!("Köp {i}")); latest.push(e.clone()); changes.push(e); } for (i, row) in latest.iter_mut().enumerate().take(30) { row["updated_at"] = json!(at(150 + i as i64)); row["notes"] = json!("ändrad"); changes.push(row.clone()); } for (i, row) in latest.iter_mut().enumerate().skip(30).take(20) { row["updated_at"] = json!(at(180 + i as i64 - 30)); row["deleted_at"] = row["updated_at"].clone(); changes.push(row.clone()); } assert_eq!(changes.len(), 200); assert!( parse_ts(changes[199]["updated_at"].as_str().unwrap()).unwrap() < start + Duration::days(14) ); // Meanwhile Jacob kept syncing; the server clock is 14 days on. app.set_now(&format_ts(start + Duration::days(14))); // The phone pushes its pending rows (the latest state of each, as §14.4.5 reads them) in batches of 100. let pending: Vec = latest.iter().map(|e| exp(e.clone())).collect(); for batch in pending.chunks(100) { let reply = push(&app, &member_token(), batch.to_vec()).await; assert_eq!(reply.status, 200); for r in reply.json["results"].as_array().unwrap() { accepted(r); } } // And Jacob's phone pulls everything (cursor from before the outage stays valid). let mut since = 0; let mut expenses = Vec::new(); loop { let page = pull(&app, &admin_token(), since, Some(1000)).await.json; expenses.extend(page["expenses"].as_array().unwrap().clone()); since = page["next_since"].as_i64().unwrap(); if page["has_more"] == false { break; } } assert_eq!(expenses.len(), 150); let edited = expenses.iter().filter(|e| e["notes"] == "ändrad").count(); let deleted = expenses .iter() .filter(|e| !e["deleted_at"].is_null()) .count(); assert_eq!((edited, deleted), (30, 20)); // Every create of a row that was later deleted offline was never seen by the server: tombstone, no audit. assert_eq!(count(&app, "audit_log").await, 130); }