use crawlex::storage::{filesystem::FilesystemStorage, StateStorage};
#[cfg(feature = "sqlite")]
use crawlex::storage::sqlite::SqliteStorage;
const SAMPLE: &str =
r#"{"cookies":[{"name":"sid","value":"abc","domain":"example.com"}],"localStorage":{"k":"v"}}"#;
#[tokio::test]
async fn filesystem_round_trip() {
let tmp = tempfile::tempdir().unwrap();
let fs = FilesystemStorage::open(tmp.path()).unwrap();
fs.save_state("sess_abc123", SAMPLE).await.unwrap();
let got = fs.load_state("sess_abc123").await.unwrap();
assert_eq!(got.as_deref(), Some(SAMPLE));
let newer = r#"{"cookies":[],"localStorage":{}}"#;
fs.save_state("sess_abc123", newer).await.unwrap();
let got = fs.load_state("sess_abc123").await.unwrap();
assert_eq!(got.as_deref(), Some(newer));
let missing = fs.load_state("sess_unknown").await.unwrap();
assert!(missing.is_none());
}
#[tokio::test]
async fn filesystem_rejects_path_traversal() {
let tmp = tempfile::tempdir().unwrap();
let fs = FilesystemStorage::open(tmp.path()).unwrap();
for bad in ["../escape", "a/b", r"a\b", ".."] {
let e1 = fs.save_state(bad, SAMPLE).await;
assert!(e1.is_err(), "save_state should reject {bad:?}");
let e2 = fs.load_state(bad).await;
assert!(e2.is_err(), "load_state should reject {bad:?}");
}
}
#[cfg(feature = "sqlite")]
#[tokio::test]
async fn sqlite_round_trip() {
let tmp = tempfile::tempdir().unwrap();
let path = tmp.path().join("s.db").to_string_lossy().to_string();
let sq = SqliteStorage::open(&path).unwrap();
sq.save_state("sess_a", SAMPLE).await.unwrap();
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
let got = sq.load_state("sess_a").await.unwrap();
assert_eq!(got.as_deref(), Some(SAMPLE));
let newer = r#"{}"#;
sq.save_state("sess_a", newer).await.unwrap();
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
let got = sq.load_state("sess_a").await.unwrap();
assert_eq!(got.as_deref(), Some(newer));
let missing = sq.load_state("sess_nope").await.unwrap();
assert!(missing.is_none());
}