Files
MetaCrate/tests/compat/tests/inventory_live_semantics.rs

358 lines
13 KiB
Rust

// Exact Rust translations of LibreMetaverse.Tests/InventoryFetchTests.cs and
// LibreMetaverse.Tests/LibraryInventoryTests.cs.
// Source SHA-256: f5876550ab924ad034ade848e562ee70bbb4e004299d928f43c45cadd1a01465
// Source SHA-256: 1d65ebbaf8fd22427371e2a7f1ef76a4f9a70df0bea7fa53f8f5d3fd3044fa53
use libremetaverse::{
GridClient, InventoryFolder, InventoryItem, InventoryObjectClass, InventorySortOrder,
NetworkManager,
};
use libremetaverse_compat_tests::{block_on, live_grid_credentials};
use libremetaverse_types::UUID;
use libremetaverse_types::compat::{CancellationToken, CancellationTokenSource};
use std::sync::Arc;
use std::time::Duration;
struct LiveInventory {
client: GridClient,
}
impl LiveInventory {
fn login() -> Self {
let mut client = GridClient::new().expect("GridClient constructor");
client.settings().timing().login_timeout = 30_000;
let network = client.network();
let (first, last, password, login_url) = live_grid_credentials();
let start = NetworkManager::start_location("Hooper".into(), 179, 18, 32)
.expect("NetworkManager StartLocation");
let mut login = network
.default_login_params(
first,
last,
password,
"Unit Test Framework".into(),
"admin@radegast.life".into(),
)
.expect("NetworkManager DefaultLoginParams");
login.start = start;
login.uri = login_url;
let logged_in = block_on(network.login_with_login_params_cancellation_token(login, None))
.expect("NetworkManager LoginAsync");
assert!(
logged_in,
"client failed to login: {}",
network.login_message()
);
assert!(network.connected(), "client is not connected to the grid");
assert!(
network.current_sim().is_some(),
"CurrentSim is null after successful login"
);
Self { client }
}
fn agent_id(&mut self) -> UUID {
self.client.self_().agent_id()
}
}
impl Drop for LiveInventory {
fn drop(&mut self) {
let _ = self.client.network().logout_with_method();
let _ = self.client.dispose_with_method();
}
}
fn cancellation_after(duration: Duration) -> CancellationToken {
let source = Arc::new(CancellationTokenSource::new());
let token = source.token();
std::thread::spawn(move || {
std::thread::sleep(duration);
source.cancel();
});
token
}
fn folders(entries: &[Box<dyn InventoryObjectClass>]) -> impl Iterator<Item = &InventoryFolder> {
entries
.iter()
.filter_map(|entry| entry.as_any().downcast_ref::<InventoryFolder>())
}
fn items(entries: &[Box<dyn InventoryObjectClass>]) -> impl Iterator<Item = &InventoryItem> {
entries
.iter()
.filter_map(|entry| entry.as_any().downcast_ref::<InventoryItem>())
}
// parity-case: LibreMetaverse.Tests/InventoryFetchTests.cs::InventoryFetchTests.InventorySkeletonIsPopulatedAfterLogin::test ea1508e3c676639f00bb8a4c4c72d2ee668603bfbff40ee0b93d7f4beb607f75 ignored-live
#[test]
#[cfg_attr(
not(live_grid_credentials),
ignore = "requires GRID_USER, GRID_PASSWORD, and GRID_LOGIN_URL in the environment or workspace .env"
)]
fn inventory_skeleton_is_populated_after_login() {
let mut live = LiveInventory::login();
let agent_id = live.agent_id();
let store = live.client.inventory().store().expect("inventory store");
let root = store.root_folder().expect("inventory root folder");
assert_ne!(root.base.uuid(), UUID::zero());
let children = store
.get_contents_with_inventory_folder(&root)
.expect("inventory root contents");
assert!(!children.is_empty(), "inventory root has no children");
assert_eq!(folders(&children).count(), children.len());
let first = folders(&children).next().expect("inventory sub-folder");
assert_eq!(first.base.owner_id(), agent_id);
}
// parity-case: LibreMetaverse.Tests/InventoryFetchTests.cs::InventoryFetchTests.InventoryRootContainsFolders::test 71f41c718060622e1fd653e6b0fc9fe65f3cd68cab3d128f494cdbc90f9d2726 ignored-live
#[test]
#[cfg_attr(
not(live_grid_credentials),
ignore = "requires GRID_USER, GRID_PASSWORD, and GRID_LOGIN_URL in the environment or workspace .env"
)]
fn inventory_root_contains_folders() {
let mut live = LiveInventory::login();
let agent_id = live.agent_id();
let root = live
.client
.inventory()
.store()
.expect("inventory store")
.root_folder()
.expect("inventory root folder");
assert_ne!(root.base.uuid(), UUID::zero());
let contents = block_on(live.client.inventory().folder_contents(
root.base.uuid(),
agent_id,
true,
true,
InventorySortOrder::BY_NAME,
Some(cancellation_after(Duration::from_secs(30))),
None,
))
.expect("FolderContentsAsync");
if contents.is_empty() {
eprintln!("inventory root fetch returned no results; transient server issue");
return;
}
assert!(folders(&contents).next().is_some());
}
// parity-case: LibreMetaverse.Tests/InventoryFetchTests.cs::InventoryFetchTests.InventoryFolderContainsItems::test f0d77649b82d4d7d7778acebd851dd4eb44b76eb687af07bccdd20a1efe96223 ignored-live
#[test]
#[cfg_attr(
not(live_grid_credentials),
ignore = "requires GRID_USER, GRID_PASSWORD, and GRID_LOGIN_URL in the environment or workspace .env"
)]
fn inventory_folder_contains_items() {
let mut live = LiveInventory::login();
let agent_id = live.agent_id();
let store = live.client.inventory().store().expect("inventory store");
let root = store.root_folder().expect("inventory root folder");
assert_ne!(root.base.uuid(), UUID::zero());
let skeleton = store
.get_contents_with_inventory_folder(&root)
.expect("inventory skeleton");
let subfolders: Vec<_> = folders(&skeleton).collect();
assert!(!subfolders.is_empty(), "no inventory sub-folders");
let token = cancellation_after(Duration::from_secs(30));
let mut found = None;
for folder in subfolders {
if token.is_cancellation_requested() {
break;
}
let Ok(contents) = block_on(live.client.inventory().folder_contents(
folder.base.uuid(),
agent_id,
true,
true,
InventorySortOrder::BY_NAME,
Some(token.clone()),
None,
)) else {
continue;
};
let found_items: Vec<_> = items(&contents).collect();
if let Some(first) = found_items.first() {
found = Some((
folder.base.name(),
found_items.len(),
first.base.name(),
first.asset_type(),
));
break;
}
}
let Some((folder_name, count, first_name, first_type)) = found else {
eprintln!("no items found in any inventory sub-folder");
return;
};
assert!(count > 0, "folder {folder_name} has no items");
eprintln!("found {count} items in {folder_name}; first: {first_name} ({first_type:?})");
}
// parity-case: LibreMetaverse.Tests/InventoryFetchTests.cs::InventoryFetchTests.InventoryItemCanBeFetchedById::test 7fe89086754a673ab69814202b48aab9add30dd63ee98a953bdedd54d6788af2 ignored-live
#[test]
#[cfg_attr(
not(live_grid_credentials),
ignore = "requires GRID_USER, GRID_PASSWORD, and GRID_LOGIN_URL in the environment or workspace .env"
)]
fn inventory_item_can_be_fetched_by_id() {
let mut live = LiveInventory::login();
let agent_id = live.agent_id();
let store = live.client.inventory().store().expect("inventory store");
let root = store.root_folder().expect("inventory root folder");
let skeleton = store
.get_contents_with_inventory_folder(&root)
.expect("inventory skeleton");
let subfolders: Vec<_> = folders(&skeleton).collect();
assert!(!subfolders.is_empty(), "no inventory sub-folders");
let token = cancellation_after(Duration::from_secs(30));
let mut target = None;
for folder in subfolders {
if token.is_cancellation_requested() {
break;
}
let Ok(contents) = block_on(live.client.inventory().folder_contents(
folder.base.uuid(),
agent_id,
true,
true,
InventorySortOrder::BY_NAME,
Some(token.clone()),
None,
)) else {
continue;
};
if let Some(item) = items(&contents).next() {
target = Some((item.base.uuid(), item.base.name()));
break;
}
}
let Some((target_id, target_name)) = target else {
eprintln!("no inventory item found to fetch by UUID");
return;
};
let fetched = block_on(live.client.inventory().fetch_item_http(
target_id,
agent_id,
Some(token),
))
.expect("FetchItemHttpAsync")
.expect("fetched inventory item");
assert_eq!(fetched.base.uuid(), target_id);
assert_eq!(fetched.base.name(), target_name);
}
// parity-case: LibreMetaverse.Tests/LibraryInventoryTests.cs::LibraryInventoryTests.LibraryFoldersArePopulatedAfterLogin::test dc865c88bb514bb71157a451e938b5ae3e3adde208d267d6a7196706dc9cfda2 ignored-live
#[test]
#[cfg_attr(
not(live_grid_credentials),
ignore = "requires GRID_USER, GRID_PASSWORD, and GRID_LOGIN_URL in the environment or workspace .env"
)]
fn library_folders_are_populated_after_login() {
let live = LiveInventory::login();
let store = live.client.inventory().store().expect("inventory store");
let root = store.library_folder().expect("library root folder");
assert_ne!(root.base.uuid(), UUID::zero());
let children = store
.get_contents_with_inventory_folder(&root)
.expect("library root contents");
assert!(!children.is_empty(), "library root has no children");
assert_eq!(folders(&children).count(), children.len());
assert_ne!(
folders(&children)
.next()
.expect("library sub-folder")
.base
.owner_id(),
UUID::zero()
);
}
// parity-case: LibreMetaverse.Tests/LibraryInventoryTests.cs::LibraryInventoryTests.LibraryFolderItemsCanBeFetched::test 475561d29dd25aba52d94f8374fe9083fea2ed7af44dfb6d25b55ce487b080ab ignored-live
#[test]
#[cfg_attr(
not(live_grid_credentials),
ignore = "requires GRID_USER, GRID_PASSWORD, and GRID_LOGIN_URL in the environment or workspace .env"
)]
fn library_folder_items_can_be_fetched() {
let live = LiveInventory::login();
let store = live.client.inventory().store().expect("inventory store");
let root = store.library_folder().expect("library root folder");
let skeleton = store
.get_contents_with_inventory_folder(&root)
.expect("library skeleton");
let owner = folders(&skeleton)
.map(|folder| folder.base.owner_id())
.find(|owner| *owner != UUID::zero())
.expect("library owner UUID");
let contents = block_on(live.client.inventory().folder_contents(
root.base.uuid(),
owner,
true,
true,
InventorySortOrder::BY_NAME,
Some(cancellation_after(Duration::from_secs(30))),
None,
))
.expect("FolderContentsAsync");
if contents.is_empty() {
eprintln!("library root fetch returned no results; transient server issue");
return;
}
assert!(folders(&contents).next().is_some());
}
// parity-case: LibreMetaverse.Tests/LibraryInventoryTests.cs::LibraryInventoryTests.LibrarySubFolderContainsItems::test a6d02cbcb7fde8248419f1cf7355c3d37f06e8b365dc483fad935dd9a0ac5909 ignored-live
#[test]
#[cfg_attr(
not(live_grid_credentials),
ignore = "requires GRID_USER, GRID_PASSWORD, and GRID_LOGIN_URL in the environment or workspace .env"
)]
fn library_sub_folder_contains_items() {
let live = LiveInventory::login();
let store = live.client.inventory().store().expect("inventory store");
let root = store.library_folder().expect("library root folder");
let skeleton = store
.get_contents_with_inventory_folder(&root)
.expect("library skeleton");
let subfolders: Vec<_> = folders(&skeleton).collect();
assert!(!subfolders.is_empty(), "no library sub-folders");
let owner = subfolders
.iter()
.map(|folder| folder.base.owner_id())
.find(|owner| *owner != UUID::zero())
.expect("library owner UUID");
let token = cancellation_after(Duration::from_secs(30));
let mut found = None;
for folder in subfolders {
if token.is_cancellation_requested() {
break;
}
let Ok(contents) = block_on(live.client.inventory().folder_contents(
folder.base.uuid(),
owner,
true,
true,
InventorySortOrder::BY_NAME,
Some(token.clone()),
None,
)) else {
continue;
};
let count = items(&contents).count();
if count > 0 {
found = Some((folder.base.name(), count));
break;
}
}
let Some((folder_name, count)) = found else {
eprintln!("no items found in any library sub-folder");
return;
};
assert!(count > 0, "folder {folder_name} has no items");
}