Integrate release recovery, semantic review, coverage and Windows packaging
This commit is contained in:
@@ -0,0 +1,177 @@
|
||||
use super::*;
|
||||
|
||||
fn model(directory: &Path, name: &str) -> PathBuf {
|
||||
let path = directory.join(name);
|
||||
fs::write(&path, b"model contents must remain untouched").unwrap();
|
||||
path
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn loading_missing_history_does_not_create_files_or_directories() {
|
||||
let temp = tempfile::tempdir().unwrap();
|
||||
let store = temp.path().join("not-created/recent-models.json");
|
||||
let state = State::load(store.clone());
|
||||
assert!(state.recent.is_empty());
|
||||
assert!(state.notice.is_none());
|
||||
assert!(!store.parent().unwrap().exists());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn successful_local_history_is_bounded_ordered_deduplicated_and_durable() {
|
||||
let temp = tempfile::tempdir().unwrap();
|
||||
let store = temp.path().join("settings/recent-models.json");
|
||||
let mut state = State::load(store.clone());
|
||||
let files: Vec<_> = (0..15)
|
||||
.map(|i| model(temp.path(), &format!("model-{i}.arcadia")))
|
||||
.collect();
|
||||
for (i, path) in files.iter().enumerate() {
|
||||
state.remember(path, &format!("Model {i}")).unwrap();
|
||||
}
|
||||
assert_eq!(state.recent.len(), MAX_RECENTS);
|
||||
assert_eq!(state.recent[0].entry.name, "Model 14");
|
||||
assert_eq!(state.recent[11].entry.name, "Model 3");
|
||||
state.remember(&files[8], "Renamed model").unwrap();
|
||||
assert_eq!(state.recent.len(), MAX_RECENTS);
|
||||
assert_eq!(state.recent[0].entry.name, "Renamed model");
|
||||
let loaded = State::load(store);
|
||||
assert!(loaded.notice.is_none());
|
||||
assert_eq!(
|
||||
loaded.recent.iter().map(|r| &r.entry).collect::<Vec<_>>(),
|
||||
state.recent.iter().map(|r| &r.entry).collect::<Vec<_>>()
|
||||
);
|
||||
assert!(
|
||||
loaded
|
||||
.recent
|
||||
.iter()
|
||||
.all(|r| r.availability == Availability::Available)
|
||||
);
|
||||
assert_eq!(
|
||||
fs::read(&files[8]).unwrap(),
|
||||
b"model contents must remain untouched"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn forgotten_and_missing_files_never_change_the_model_or_other_entries() {
|
||||
let temp = tempfile::tempdir().unwrap();
|
||||
let store = temp.path().join("recent.json");
|
||||
let a = model(temp.path(), "a.arcadia");
|
||||
let b = model(temp.path(), "b.json");
|
||||
let mut state = State::load(store.clone());
|
||||
state.remember(&a, "A").unwrap();
|
||||
state.remember(&b, "B").unwrap();
|
||||
fs::remove_file(&b).unwrap();
|
||||
state.refresh();
|
||||
assert_eq!(state.recent[0].availability, Availability::Missing);
|
||||
assert_eq!(state.recent.len(), 2);
|
||||
assert_eq!(
|
||||
State::load(store.clone()).recent[0].availability,
|
||||
Availability::Missing
|
||||
);
|
||||
let old = fs::read(&store).unwrap();
|
||||
assert!(state.remember(&b, "Missing file").is_err());
|
||||
assert_eq!(fs::read(&store).unwrap(), old);
|
||||
state.forget(&a.canonicalize().unwrap()).unwrap();
|
||||
assert!(a.exists());
|
||||
assert_eq!(state.recent.len(), 1);
|
||||
assert_eq!(State::load(store).recent.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unavailable_location_can_reappear_on_refresh_without_losing_identity() {
|
||||
let temp = tempfile::tempdir().unwrap();
|
||||
let path = model(temp.path(), "removable.arcadia");
|
||||
let mut state = State::default();
|
||||
state.remember(&path, "Portable system").unwrap();
|
||||
fs::remove_file(&path).unwrap();
|
||||
fs::create_dir(&path).unwrap();
|
||||
state.refresh();
|
||||
assert!(matches!(
|
||||
state.recent[0].availability,
|
||||
Availability::Unavailable(_)
|
||||
));
|
||||
fs::remove_dir(&path).unwrap();
|
||||
fs::write(&path, b"restored").unwrap();
|
||||
state.refresh();
|
||||
assert_eq!(state.recent[0].availability, Availability::Available);
|
||||
assert_eq!(state.recent[0].entry.name, "Portable system");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn corrupt_future_and_oversized_history_are_preserved_with_usable_session_history() {
|
||||
let temp = tempfile::tempdir().unwrap();
|
||||
let model = model(temp.path(), "model.arcadia");
|
||||
let store = temp.path().join("recent.json");
|
||||
for bytes in [
|
||||
b"not json".to_vec(),
|
||||
br#"{"version":99,"entries":[]}"#.to_vec(),
|
||||
vec![b' '; MAX_STORE_BYTES as usize + 1],
|
||||
] {
|
||||
fs::write(&store, &bytes).unwrap();
|
||||
let mut state = State::load(store.clone());
|
||||
assert!(state.notice.is_some());
|
||||
assert!(state.remember(&model, "Session model").is_err());
|
||||
assert_eq!(state.recent.len(), 1);
|
||||
assert_eq!(fs::read(&store).unwrap(), bytes);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn failed_metadata_replace_keeps_previous_history_and_reports_memory_only_entry() {
|
||||
let temp = tempfile::tempdir().unwrap();
|
||||
let a = model(temp.path(), "a.arcadia");
|
||||
let b = model(temp.path(), "b.arcadia");
|
||||
let parent = temp.path().join("settings");
|
||||
let store = parent.join("recent.json");
|
||||
let mut state = State::load(store.clone());
|
||||
state.remember(&a, "A").unwrap();
|
||||
let original = fs::read(&store).unwrap();
|
||||
let preserved = temp.path().join("preserved");
|
||||
fs::rename(&parent, &preserved).unwrap();
|
||||
fs::write(&parent, b"directory unavailable").unwrap();
|
||||
assert!(state.remember(&b, "B").is_err());
|
||||
assert!(state.notice.is_some());
|
||||
assert_eq!(state.recent[0].entry.name, "B");
|
||||
assert_eq!(fs::read(preserved.join("recent.json")).unwrap(), original);
|
||||
fs::remove_file(&parent).unwrap();
|
||||
fs::rename(&preserved, &parent).unwrap();
|
||||
state.remember(&b, "B").unwrap();
|
||||
assert!(state.notice.is_none());
|
||||
assert_eq!(State::load(store).recent[0].entry.name, "B");
|
||||
assert_eq!(
|
||||
fs::read_dir(parent).unwrap().count(),
|
||||
1,
|
||||
"no abandoned temp files"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn malformed_entries_and_unknown_fields_never_become_open_requests() {
|
||||
let temp = tempfile::tempdir().unwrap();
|
||||
let store = temp.path().join("recent.json");
|
||||
for value in [
|
||||
serde_json::json!({"version":1,"entries":[{"path":"relative.arcadia","name":"Bad"}]}),
|
||||
serde_json::json!({"version":1,"entries":[],"credential":"not accepted"}),
|
||||
serde_json::json!({"version":1,"entries":[{"path":temp.path().join("absolute.arcadia"),"name":"x".repeat(161)}]}),
|
||||
] {
|
||||
fs::write(&store, serde_json::to_vec(&value).unwrap()).unwrap();
|
||||
let state = State::load(store.clone());
|
||||
assert!(state.notice.is_some());
|
||||
assert!(state.recent.is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn first_and_replaced_metadata_are_parseable_and_contain_no_model_bytes() {
|
||||
let temp = tempfile::tempdir().unwrap();
|
||||
let path = model(temp.path(), "a.arcadia");
|
||||
let store = temp.path().join("recent.json");
|
||||
let mut state = State::load(store.clone());
|
||||
for name in ["First", "Updated"] {
|
||||
state.remember(&path, name).unwrap();
|
||||
let bytes = fs::read(&store).unwrap();
|
||||
let parsed: Stored = serde_json::from_slice(&bytes).unwrap();
|
||||
assert_eq!(parsed.entries[0].name, name);
|
||||
assert!(!String::from_utf8(bytes).unwrap().contains("model contents"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user