aim/crates/upm-core/tests/registry_roundtrip.rs

288 lines
11 KiB
Rust

use tempfile::tempdir;
use upm_core::registry::store::RegistryStore;
#[test]
fn registry_round_trips_app_records() {
let dir = tempdir().unwrap();
let store = RegistryStore::new(dir.path().join("registry.toml"));
let loaded = store.load().unwrap();
assert!(loaded.apps.is_empty());
}
#[test]
fn registry_round_trips_update_strategy_and_alternates() {
let dir = tempdir().unwrap();
let store = RegistryStore::new(dir.path().join("registry.toml"));
let registry = upm_core::registry::model::Registry {
version: 1,
apps: vec![upm_core::domain::app::AppRecord {
stable_id: "t3code".to_owned(),
display_name: "T3 Code".to_owned(),
source_input: Some("pingdotgg/t3code".to_owned()),
source: None,
installed_version: Some("0.0.11".to_owned()),
update_strategy: Some(upm_core::domain::update::UpdateStrategy {
preferred: upm_core::domain::update::ChannelPreference {
kind: upm_core::domain::update::UpdateChannelKind::DirectAsset,
locator: "https://example.test/app.AppImage".to_owned(),
reason: "install-origin-match".to_owned(),
},
alternates: vec![
upm_core::domain::update::ChannelPreference {
kind: upm_core::domain::update::UpdateChannelKind::GitHubReleases,
locator: "pingdotgg/t3code".to_owned(),
reason: "heuristic-match".to_owned(),
},
upm_core::domain::update::ChannelPreference {
kind: upm_core::domain::update::UpdateChannelKind::ElectronBuilder,
locator: "https://example.test/latest-linux.yml".to_owned(),
reason: "metadata-guided".to_owned(),
},
],
}),
metadata: Vec::new(),
install: None,
}],
};
store.save(&registry).unwrap();
let loaded = store.load().unwrap();
let strategy = loaded.apps[0].update_strategy.as_ref().unwrap();
assert_eq!(strategy.preferred.reason, "install-origin-match");
assert_eq!(strategy.alternates.len(), 2);
}
#[test]
fn registry_round_trips_install_metadata() {
let dir = tempdir().unwrap();
let store = RegistryStore::new(dir.path().join("registry.toml"));
let registry = upm_core::registry::model::Registry {
version: 1,
apps: vec![upm_core::domain::app::AppRecord {
stable_id: "t3code".to_owned(),
display_name: "T3 Code".to_owned(),
source_input: Some("pingdotgg/t3code".to_owned()),
source: None,
installed_version: Some("0.0.11".to_owned()),
update_strategy: None,
metadata: Vec::new(),
install: Some(upm_core::domain::app::InstallMetadata {
scope: upm_core::domain::app::InstallScope::User,
payload_path: Some(
"/tmp/install-home/.local/lib/upm/appimages/t3code.AppImage".to_owned(),
),
desktop_entry_path: Some(
"/tmp/install-home/.local/share/applications/upm-t3code.desktop".to_owned(),
),
icon_path: Some(
"/tmp/install-home/.local/share/icons/hicolor/256x256/apps/t3code.png"
.to_owned(),
),
}),
}],
};
store.save(&registry).unwrap();
let loaded = store.load().unwrap();
let install = loaded.apps[0].install.as_ref().unwrap();
assert_eq!(install.scope, upm_core::domain::app::InstallScope::User);
assert_eq!(
install.payload_path.as_deref(),
Some("/tmp/install-home/.local/lib/upm/appimages/t3code.AppImage")
);
assert_eq!(
install.desktop_entry_path.as_deref(),
Some("/tmp/install-home/.local/share/applications/upm-t3code.desktop")
);
assert_eq!(
install.icon_path.as_deref(),
Some("/tmp/install-home/.local/share/icons/hicolor/256x256/apps/t3code.png")
);
}
#[test]
fn registry_round_trips_source_identity_for_new_provider_kinds() {
let dir = tempdir().unwrap();
let store = RegistryStore::new(dir.path().join("registry.toml"));
let registry = upm_core::registry::model::Registry {
version: 1,
apps: vec![
upm_core::domain::app::AppRecord {
stable_id: "example-team-app".to_owned(),
display_name: "team-app".to_owned(),
source_input: Some("https://gitlab.com/example/team-app".to_owned()),
source: Some(upm_core::domain::source::SourceRef {
kind: upm_core::domain::source::SourceKind::GitLab,
locator: "https://gitlab.com/example/team-app".to_owned(),
input_kind: upm_core::domain::source::SourceInputKind::GitLabUrl,
normalized_kind: upm_core::domain::source::NormalizedSourceKind::GitLab,
canonical_locator: Some("example/team-app".to_owned()),
requested_tag: None,
requested_asset_name: None,
tracks_latest: true,
}),
installed_version: Some("latest".to_owned()),
update_strategy: None,
metadata: Vec::new(),
install: None,
},
upm_core::domain::app::AppRecord {
stable_id: "team-app".to_owned(),
display_name: "team-app".to_owned(),
source_input: Some(
"https://sourceforge.net/projects/team-app/files/latest/download".to_owned(),
),
source: Some(upm_core::domain::source::SourceRef {
kind: upm_core::domain::source::SourceKind::SourceForge,
locator: "https://sourceforge.net/projects/team-app/files/latest/download"
.to_owned(),
input_kind: upm_core::domain::source::SourceInputKind::SourceForgeUrl,
normalized_kind: upm_core::domain::source::NormalizedSourceKind::SourceForge,
canonical_locator: Some("team-app".to_owned()),
requested_tag: None,
requested_asset_name: None,
tracks_latest: true,
}),
installed_version: Some("latest".to_owned()),
update_strategy: None,
metadata: Vec::new(),
install: None,
},
upm_core::domain::app::AppRecord {
stable_id: "url-example.com-downloads-team-app.appimage".to_owned(),
display_name: "https://example.com/downloads/team-app.AppImage".to_owned(),
source_input: Some("https://example.com/downloads/team-app.AppImage".to_owned()),
source: Some(upm_core::domain::source::SourceRef {
kind: upm_core::domain::source::SourceKind::DirectUrl,
locator: "https://example.com/downloads/team-app.AppImage".to_owned(),
input_kind: upm_core::domain::source::SourceInputKind::DirectUrl,
normalized_kind: upm_core::domain::source::NormalizedSourceKind::DirectUrl,
canonical_locator: None,
requested_tag: None,
requested_asset_name: None,
tracks_latest: false,
}),
installed_version: Some("unresolved".to_owned()),
update_strategy: None,
metadata: Vec::new(),
install: None,
},
],
};
store.save(&registry).unwrap();
let loaded = store.load().unwrap();
assert_eq!(
loaded.apps[0].source.as_ref().unwrap().kind.as_str(),
"gitlab"
);
assert_eq!(
loaded.apps[0]
.source
.as_ref()
.unwrap()
.canonical_locator
.as_deref(),
Some("example/team-app")
);
assert_eq!(
loaded.apps[1].source.as_ref().unwrap().kind.as_str(),
"sourceforge"
);
assert_eq!(
loaded.apps[1].source.as_ref().unwrap().locator,
"https://sourceforge.net/projects/team-app/files/latest/download"
);
assert_eq!(
loaded.apps[2].source.as_ref().unwrap().kind.as_str(),
"direct-url"
);
assert_eq!(
loaded.apps[2].source.as_ref().unwrap().locator,
"https://example.com/downloads/team-app.AppImage"
);
}
#[test]
fn registry_save_is_atomic_and_cleans_up_temp_file() {
let dir = tempdir().unwrap();
let registry_path = dir.path().join("registry.toml");
let store = RegistryStore::new(registry_path.clone());
store
.save(&upm_core::registry::model::Registry {
version: 1,
apps: vec![upm_core::domain::app::AppRecord {
stable_id: "bat".to_owned(),
display_name: "Bat".to_owned(),
source_input: None,
source: None,
installed_version: None,
update_strategy: None,
metadata: Vec::new(),
install: None,
}],
})
.unwrap();
assert!(registry_path.exists());
assert!(!dir.path().join("registry.toml.tmp").exists());
}
#[test]
fn registry_exclusive_lock_rejects_second_mutator() {
let dir = tempdir().unwrap();
let store = RegistryStore::new(dir.path().join("registry.toml"));
let _guard = store.lock_exclusive().unwrap();
let error = store.lock_exclusive().unwrap_err();
assert!(matches!(
error,
upm_core::registry::store::RegistryStoreError::LockUnavailable
));
}
#[test]
fn registry_mutate_exclusive_reloads_and_writes_latest_state() {
let dir = tempdir().unwrap();
let store = RegistryStore::new(dir.path().join("registry.toml"));
store
.save(&upm_core::registry::model::Registry {
version: 1,
apps: vec![upm_core::domain::app::AppRecord {
stable_id: "bat".to_owned(),
display_name: "Bat".to_owned(),
source_input: None,
source: None,
installed_version: None,
update_strategy: None,
metadata: Vec::new(),
install: None,
}],
})
.unwrap();
store
.mutate_exclusive(|registry| {
registry.apps.push(upm_core::domain::app::AppRecord {
stable_id: "t3code".to_owned(),
display_name: "T3 Code".to_owned(),
source_input: None,
source: None,
installed_version: None,
update_strategy: None,
metadata: Vec::new(),
install: None,
});
})
.unwrap();
let loaded = store.load().unwrap();
assert_eq!(loaded.apps.len(), 2);
assert_eq!(loaded.apps[0].stable_id, "bat");
assert_eq!(loaded.apps[1].stable_id, "t3code");
}