Files
ARCADIA-Studio/crates/oa-desktop/src/home/tests.rs
T

215 lines
8.0 KiB
Rust

use super::*;
#[test]
fn walkthrough_uses_packaged_bundle_before_development_and_reports_missing_assets() {
let temp = tempfile::tempdir().unwrap();
let packaged = temp
.path()
.join("application/examples/release1/project.arcadia");
let development = temp.path().join("checkout");
let checkout_entry = development.join("examples/release1/project.arcadia");
let executable = temp.path().join("application/arcadia-studio.exe");
fs::create_dir_all(checkout_entry.parent().unwrap()).unwrap();
fs::write(&checkout_entry, "development source").unwrap();
assert_eq!(
resolve_walkthrough(&executable, Some(&development)).unwrap(),
checkout_entry
);
fs::create_dir_all(packaged.parent().unwrap()).unwrap();
fs::write(&packaged, "packaged source").unwrap();
assert_eq!(
resolve_walkthrough(&executable, Some(&development)).unwrap(),
packaged
);
assert_eq!(resolve_walkthrough(&executable, None).unwrap(), packaged);
fs::remove_file(&packaged).unwrap();
let error = resolve_walkthrough(&executable, None).unwrap_err();
assert!(error.contains("complete examples/release1 folder"));
assert!(
error
.replace('\\', "/")
.contains(&packaged.display().to_string().replace('\\', "/")),
"{error}"
);
assert_eq!(
fs::read_to_string(checkout_entry).unwrap(),
"development source"
);
}
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"));
}
}