// 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]) -> impl Iterator { entries .iter() .filter_map(|entry| entry.as_any().downcast_ref::()) } fn items(entries: &[Box]) -> impl Iterator { entries .iter() .filter_map(|entry| entry.as_any().downcast_ref::()) } // 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"); }