//! Exact Rust translations of the RLV inventory-query tests.

use libremetaverse_compat_tests::block_on;
use libremetaverse_compat_tests::rlv_support::{RlvHarness, SampleInventoryTree, guid};
use libremetaverse_rlv::{InventoryMap, RlvAttachmentPoint, RlvInventoryItem, RlvWearableType};

const PRIM1: &str = "11111111-0001-4aaa-8aaa-ffffffffffff";
const PRIM2: &str = "11111111-0002-4aaa-8aaa-ffffffffffff";
const PRIM3: &str = "11111111-0003-4aaa-8aaa-ffffffffffff";
const PRIM4: &str = "11111111-0004-4aaa-8aaa-ffffffffffff";
const PRIM5: &str = "11111111-0005-4aaa-8aaa-ffffffffffff";

fn harness() -> RlvHarness {
    RlvHarness::new().expect("RlvService constructor")
}

fn install_inventory(h: &RlvHarness, tree: SampleInventoryTree, external: Vec<RlvInventoryItem>) {
    h.queries.lock().unwrap().inventory_map = Some(InventoryMap::new(tree.root, external).unwrap());
}

fn query_matches(h: &RlvHarness, command: &str, expected: &str) -> bool {
    block_on(h.process(command)).unwrap()
        && h.actions.lock().unwrap().replies == vec![(1234, expected.into())]
}

fn external_item(
    id: &str,
    name: &str,
    folder: &str,
    point: Option<RlvAttachmentPoint>,
    prim: Option<&str>,
    wearable: Option<RlvWearableType>,
) -> RlvInventoryItem {
    RlvInventoryItem::new(
        guid(id),
        name.into(),
        false,
        Some(Some(guid(folder))),
        point.map(Some),
        prim.map(|id| Some(guid(id))),
        wearable.map(Some),
        None,
    )
    .unwrap()
}

fn empty_tree_query(command: &str, expected: &str) -> bool {
    let h = harness();
    install_inventory(&h, SampleInventoryTree::build().unwrap(), vec![]);
    query_matches(&h, command, expected)
}

// parity-case: LibreMetaverse.Tests/RLV/Queries/FindFolderQueryTests.cs::FindFolderQueryTests.FindFolder_MultipleTerms::test a1f04e5ab064772c25feefc21ea57d65b1044f0fa3254aeb2a3bfedf68d8e0ca translated
#[test]
fn find_folder_multiple_terms() {
    assert!(empty_tree_query(
        "@findfolder:at&&ub=1234",
        "Clothing/Hats/Sub Hats"
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Queries/FindFolderQueryTests.cs::FindFolderQueryTests.FindFolder_SearchOrder::test 4cba32dd316f49713c18a71bf5a932e1c28c94944add38c25852e8611ef0f987 translated
#[test]
fn find_folder_search_order() {
    assert!(empty_tree_query("@findfolder:at=1234", "Clothing/Hats"));
}

// parity-case: LibreMetaverse.Tests/RLV/Queries/FindFolderQueryTests.cs::FindFolderQueryTests.FindFolder_IgnorePrivate::test 54c5ec1f6a6ae5611221e3afdc47db0c2d0ecb0da460dd8f2c506ed34c221ff8 translated
#[test]
fn find_folder_ignores_private_folder() {
    let h = harness();
    let mut tree = SampleInventoryTree::build().unwrap();
    tree.hats.set_name(".Hats".into());
    install_inventory(&h, tree, vec![]);
    assert!(query_matches(&h, "@findfolder:at=1234", ""));
}

// parity-case: LibreMetaverse.Tests/RLV/Queries/FindFolderQueryTests.cs::FindFolderQueryTests.FindFolder_IgnoreTildePrefix::test 9f6635f5e14dbc14d1592a19b62d67748a60201128a75893e47ea1751c83b6d7 translated
#[test]
fn find_folder_ignores_tilde_prefixed_folder() {
    let h = harness();
    let mut tree = SampleInventoryTree::build().unwrap();
    tree.hats.set_name("~Hats".into());
    install_inventory(&h, tree, vec![]);
    assert!(query_matches(&h, "@findfolder:at=1234", ""));
}

// parity-case: LibreMetaverse.Tests/RLV/Queries/FindFoldersQueryTests.cs::FindFoldersQueryTests.FindFolders::test 09dbc17be4b28cc4a63d0f837960586ba7cd03e184f0013b26ed27db15703d84 translated
#[test]
fn find_folders() {
    assert!(empty_tree_query(
        "@findfolders:at=1234",
        "Clothing/Hats,Clothing/Hats/Sub Hats",
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Queries/FindFoldersQueryTests.cs::FindFoldersQueryTests.FindFolders_Separator::test 51caa783dded41b99c878066618a10a7fe32c681ef4385c9a72f154870312b1f translated
#[test]
fn find_folders_with_separator() {
    assert!(empty_tree_query(
        "@findfolders:at; AND =1234",
        "Clothing/Hats AND Clothing/Hats/Sub Hats",
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Queries/GetAttachQueryTests.cs::GetAttachQueryTests.GetAttach_WearingNothing::test 41fc967c6c174ac39ce3f6de7e273e6db1845bc2ad6acd393f27a6705a3555df translated
#[test]
fn get_attach_wearing_nothing() {
    assert!(empty_tree_query(
        "@getattach=1234",
        "00000000000000000000000000000000000000000000000000000000",
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Queries/GetAttachQueryTests.cs::GetAttachQueryTests.GetAttach_ExternalItems::test 3f8e640aad5deeaf00b07af2d12c33b0a93257260d057d7b5dd5488f846f467a translated
#[test]
fn get_attach_external_items() {
    let h = harness();
    let tree = SampleInventoryTree::build().unwrap();
    let folder = "12312312-aaaa-4aaa-8aaa-aaaaaaaaaaaa";
    install_inventory(
        &h,
        tree,
        vec![
            external_item(
                "12312312-0001-4aaa-8aaa-aaaaaaaaaaaa",
                "External Tattoo",
                folder,
                None,
                None,
                Some(RlvWearableType::Tattoo),
            ),
            external_item(
                "12312312-0002-4aaa-8aaa-aaaaaaaaaaaa",
                "External Jaw Thing",
                folder,
                Some(RlvAttachmentPoint::Jaw),
                Some("12312312-0002-4aaa-8aaa-ffffffffffff"),
                None,
            ),
        ],
    );
    assert!(query_matches(
        &h,
        "@getattach=1234",
        "00000000000000000000000000000000000000000000000100000000",
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Queries/GetAttachQueryTests.cs::GetAttachQueryTests.GetAttach_WearingSomeItems::test c209cd826958306f1350a685b29985cb8335a55ebcb9c0bec5c5a21279bb0a1a translated
#[test]
fn get_attach_wearing_some_items() {
    let h = harness();
    let tree = SampleInventoryTree::build().unwrap();
    let folder = "cccccccc-cccc-4ccc-8ccc-cccccccccccc";
    install_inventory(
        &h,
        tree,
        vec![
            external_item(
                "c0000000-cccc-4ccc-8ccc-cccccccccccc",
                "My Socks",
                folder,
                Some(RlvAttachmentPoint::LeftFoot),
                Some("c0000000-cccc-4ccc-8ccc-ffffffffffff"),
                None,
            ),
            external_item(
                "c0000001-cccc-4ccc-8ccc-cccccccccccc",
                "My Hair",
                folder,
                Some(RlvAttachmentPoint::Skull),
                Some("c0000001-cccc-4ccc-8ccc-ffffffffffff"),
                None,
            ),
        ],
    );
    assert!(query_matches(
        &h,
        "@getattach=1234",
        "00100001000000000000000000000000000000000000000000000000",
    ));
}

const ATTACHMENT_POINTS: [RlvAttachmentPoint; 56] = [
    RlvAttachmentPoint::Default,
    RlvAttachmentPoint::Chest,
    RlvAttachmentPoint::Skull,
    RlvAttachmentPoint::LeftShoulder,
    RlvAttachmentPoint::RightShoulder,
    RlvAttachmentPoint::LeftHand,
    RlvAttachmentPoint::RightHand,
    RlvAttachmentPoint::LeftFoot,
    RlvAttachmentPoint::RightFoot,
    RlvAttachmentPoint::Spine,
    RlvAttachmentPoint::Pelvis,
    RlvAttachmentPoint::Mouth,
    RlvAttachmentPoint::Chin,
    RlvAttachmentPoint::LeftEar,
    RlvAttachmentPoint::RightEar,
    RlvAttachmentPoint::LeftEyeball,
    RlvAttachmentPoint::RightEyeball,
    RlvAttachmentPoint::Nose,
    RlvAttachmentPoint::RightUpperArm,
    RlvAttachmentPoint::RightForearm,
    RlvAttachmentPoint::LeftUpperArm,
    RlvAttachmentPoint::LeftForearm,
    RlvAttachmentPoint::RightHip,
    RlvAttachmentPoint::RightUpperLeg,
    RlvAttachmentPoint::RightLowerLeg,
    RlvAttachmentPoint::LeftHip,
    RlvAttachmentPoint::LeftUpperLeg,
    RlvAttachmentPoint::LeftLowerLeg,
    RlvAttachmentPoint::Stomach,
    RlvAttachmentPoint::LeftPec,
    RlvAttachmentPoint::RightPec,
    RlvAttachmentPoint::HUDCenter2,
    RlvAttachmentPoint::HUDTopRight,
    RlvAttachmentPoint::HUDTop,
    RlvAttachmentPoint::HUDTopLeft,
    RlvAttachmentPoint::HUDCenter,
    RlvAttachmentPoint::HUDBottomLeft,
    RlvAttachmentPoint::HUDBottom,
    RlvAttachmentPoint::HUDBottomRight,
    RlvAttachmentPoint::Neck,
    RlvAttachmentPoint::AvatarCenter,
    RlvAttachmentPoint::LeftHandRing,
    RlvAttachmentPoint::RightHandRing,
    RlvAttachmentPoint::TailBase,
    RlvAttachmentPoint::TailTip,
    RlvAttachmentPoint::LeftWing,
    RlvAttachmentPoint::RightWing,
    RlvAttachmentPoint::Jaw,
    RlvAttachmentPoint::AltLeftEar,
    RlvAttachmentPoint::AltRightEar,
    RlvAttachmentPoint::AltLeftEye,
    RlvAttachmentPoint::AltRightEye,
    RlvAttachmentPoint::Tongue,
    RlvAttachmentPoint::Groin,
    RlvAttachmentPoint::LeftHindFoot,
    RlvAttachmentPoint::RightHindFoot,
];

// parity-case: LibreMetaverse.Tests/RLV/Queries/GetAttachQueryTests.cs::GetAttachQueryTests.GetAttach_WearingEverything::test 4adb25030cd3c125242a4b1f770bfd3afdcabd85c23d37c34df9c8f273489e5b translated
#[test]
fn get_attach_wearing_everything() {
    let h = harness();
    let tree = SampleInventoryTree::build().unwrap();
    let folder = "99999999-9999-4999-8999-999999999999";
    let external = ATTACHMENT_POINTS
        .into_iter()
        .map(|point| {
            external_item(
                &format!("c{:07}-cccc-4ccc-8ccc-cccccccccccc", point as i32),
                &format!("External {point:?}"),
                folder,
                Some(point),
                Some(PRIM1),
                None,
            )
        })
        .collect();
    install_inventory(&h, tree, external);
    assert!(query_matches(
        &h,
        "@getattach=1234",
        "11111111111111111111111111111111111111111111111111111111",
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Queries/GetAttachQueryTests.cs::GetAttachQueryTests.GetAttach_Specific_Exists::test 7d8ccf26629911b6b20dc26c5c549975827d7378b4d85259a8b9724a3622892b translated
#[test]
fn get_attach_specific_exists() {
    let h = harness();
    let tree = SampleInventoryTree::build().unwrap();
    install_inventory(
        &h,
        tree,
        vec![external_item(
            "c0000000-cccc-4ccc-8ccc-cccccccccccc",
            "My Socks",
            "cccccccc-cccc-4ccc-8ccc-cccccccccccc",
            Some(RlvAttachmentPoint::LeftFoot),
            Some("c0000000-cccc-4ccc-8ccc-ffffffffffff"),
            None,
        )],
    );
    assert!(query_matches(&h, "@getattach:left foot=1234", "1"));
}

// parity-case: LibreMetaverse.Tests/RLV/Queries/GetAttachQueryTests.cs::GetAttachQueryTests.GetAttach_Specific_NotExists::test 08d9852205987a266608ad17c12fa498894d7c3216e873cffb9db4584cfd100d translated
#[test]
fn get_attach_specific_not_exists() {
    let h = harness();
    let tree = SampleInventoryTree::build().unwrap();
    install_inventory(
        &h,
        tree,
        vec![external_item(
            "c0000001-cccc-4ccc-8ccc-cccccccccccc",
            "My Hair",
            "cccccccc-cccc-4ccc-8ccc-cccccccccccc",
            Some(RlvAttachmentPoint::Skull),
            Some("c0000001-cccc-4ccc-8ccc-ffffffffffff"),
            None,
        )],
    );
    assert!(query_matches(&h, "@getattach:left foot=1234", "0"));
}

fn get_inv(command: &str, expected: &str) -> bool {
    empty_tree_query(command, expected)
}

// parity-case: LibreMetaverse.Tests/RLV/Queries/GetInvQueryTests.cs::GetInvQueryTests.GetInv::test 7476602b4103b47ed7dff44146a7ba90a1d62dc0d502f7b9e37b7863d9441ceb translated
#[test]
fn get_inventory_root() {
    assert!(get_inv("@getinv=1234", "Clothing,Accessories"));
}

// parity-case: LibreMetaverse.Tests/RLV/Queries/GetInvQueryTests.cs::GetInvQueryTests.GetInv_Outfits::test d60a78bb35cc8fcdbdc2465889333b25567ea5e1c9279a8fb7b4162f4a35eae2 translated
#[test]
fn get_inventory_outfits() {
    let h = harness();
    let tree = SampleInventoryTree::build().unwrap();
    let outfits = tree
        .root
        .add_child(
            guid("12312399-9999-4999-8999-999999999999"),
            ".outfits".into(),
        )
        .unwrap();
    outfits
        .add_child(
            guid("12312399-0001-4999-8999-999999999999"),
            "First outfit".into(),
        )
        .unwrap();
    outfits
        .add_child(
            guid("12312399-0002-4999-8999-999999999999"),
            "Second outfit".into(),
        )
        .unwrap();
    outfits
        .add_item(
            guid("12312399-0001-0001-8999-999999999999"),
            "First Item".into(),
            false,
            None,
            None,
            None,
            None,
        )
        .unwrap();
    outfits
        .add_item(
            guid("12312399-0001-0002-8999-999999999999"),
            "Second Item".into(),
            false,
            None,
            None,
            None,
            None,
        )
        .unwrap();
    install_inventory(&h, tree, vec![]);
    assert!(query_matches(
        &h,
        "@getinv:.outfits=1234",
        "First outfit,Second outfit"
    ));
}

fn get_my_outfits(command: &str) -> bool {
    let h = harness();
    let tree = SampleInventoryTree::build().unwrap();
    let outfits = tree
        .root
        .add_child(
            guid("12312399-9999-4999-8999-999999999999"),
            "~MyOutfits".into(),
        )
        .unwrap();
    outfits
        .add_child(
            guid("12312399-0001-4999-8999-999999999999"),
            "First outfit".into(),
        )
        .unwrap();
    install_inventory(&h, tree, vec![]);
    query_matches(&h, command, "First outfit")
}

// parity-case: LibreMetaverse.Tests/RLV/Queries/GetInvQueryTests.cs::GetInvQueryTests.GetInv_Outfits_IgnoreLeadingSlash::test add33ed33e1940f26362332b89486253c6af1a56e601bafab49511b6607ed50a translated
#[test]
fn get_inventory_ignores_leading_slash() {
    assert!(get_my_outfits("@getinv:/~MyOutfits=1234"));
}

// parity-case: LibreMetaverse.Tests/RLV/Queries/GetInvQueryTests.cs::GetInvQueryTests.GetInv_Outfits_IgnoreTrailingSlash::test 8f89302126be19f89d075d87d5194cc000fb7819f92ea7d54e6a63c1df9ca1ee translated
#[test]
fn get_inventory_ignores_trailing_slash() {
    assert!(get_my_outfits("@getinv:~MyOutfits/=1234"));
}

// parity-case: LibreMetaverse.Tests/RLV/Queries/GetInvQueryTests.cs::GetInvQueryTests.GetInv_Outfits_IgnoreLeadingAndTrailingSlash::test c660447f93fb8508dbe4c874c92fd776a6e13596bbe3036bfb4664e35ee22306 translated
#[test]
fn get_inventory_ignores_leading_and_trailing_slash() {
    assert!(get_my_outfits("@getinv:/~MyOutfits/=1234"));
}

// parity-case: LibreMetaverse.Tests/RLV/Queries/GetInvQueryTests.cs::GetInvQueryTests.GetInv_Outfits_Inventory::test 7db6f75e2f109d0e8d852d181ddd721f688e27144ad8a5604384b37de9f8c3eb translated
#[test]
fn get_inventory_outfit_items_are_not_folders() {
    let h = harness();
    let tree = SampleInventoryTree::build().unwrap();
    let outfits = tree
        .root
        .add_child(
            guid("12312399-9999-4999-8999-999999999999"),
            ".outfits".into(),
        )
        .unwrap();
    let first = outfits
        .add_child(
            guid("12312399-0001-4999-8999-999999999999"),
            "First outfit".into(),
        )
        .unwrap();
    first
        .add_item(
            guid("12312399-0001-0001-8999-999999999999"),
            "First Item".into(),
            false,
            None,
            None,
            None,
            None,
        )
        .unwrap();
    first
        .add_item(
            guid("12312399-0001-0002-8999-999999999999"),
            "Second Item".into(),
            false,
            None,
            None,
            None,
            None,
        )
        .unwrap();
    install_inventory(&h, tree, vec![]);
    assert!(query_matches(&h, "@getinv:.outfits/First outfit=1234", ""));
}

// parity-case: LibreMetaverse.Tests/RLV/Queries/GetInvQueryTests.cs::GetInvQueryTests.GetInv_Subfolder::test 6a5effe8012e9a446021b83f9b5314f6f9b66587222b447fa79028582830b3a1 translated
#[test]
fn get_inventory_subfolder() {
    assert!(get_inv("@getinv:Clothing/Hats=1234", "Sub Hats"));
}

// parity-case: LibreMetaverse.Tests/RLV/Queries/GetInvQueryTests.cs::GetInvQueryTests.GetInv_Empty::test 2dd9f242eb71826066482ff4faa80b952ef89f855723cdc0c01cc4714abf8751 translated
#[test]
fn get_inventory_empty_folder() {
    assert!(get_inv("@getinv:Clothing/Hats/Sub Hats=1234", ""));
}

// parity-case: LibreMetaverse.Tests/RLV/Queries/GetInvQueryTests.cs::GetInvQueryTests.GetInv_Invalid::test 92595df155e79568e70bceb1538c23f10de99a6a138a0a5d9b8a2626588fdae9 translated
#[test]
fn get_inventory_invalid_folder() {
    assert!(get_inv("@getinv:Invalid Folder=1234", ""));
}

fn attach(item: &mut RlvInventoryItem, point: RlvAttachmentPoint, prim: &str) {
    item.set_attached_to(Some(Some(point)));
    item.set_attached_prim_id(Some(Some(guid(prim))));
}

// parity-case: LibreMetaverse.Tests/RLV/Queries/GetInvWornQueryTests.cs::GetInvWornQueryTests.GetInvWorn::test 472d70fb14f393d43f8698bb81d1855426f570d85c9e592fa86607eb79ce366e translated
#[test]
fn get_inventory_worn() {
    let h = harness();
    let mut tree = SampleInventoryTree::build().unwrap();
    attach(&mut tree.business_pants, RlvAttachmentPoint::Pelvis, PRIM1);
    attach(&mut tree.happy_shirt, RlvAttachmentPoint::Pelvis, PRIM2);
    attach(&mut tree.fancy_hat, RlvAttachmentPoint::Chin, PRIM3);
    attach(&mut tree.party_hat, RlvAttachmentPoint::Spine, PRIM4);
    attach(&mut tree.glasses, RlvAttachmentPoint::Chin, PRIM5);
    tree.retro_pants
        .set_worn_on(Some(Some(RlvWearableType::Pants)));
    tree.party_hat
        .set_worn_on(Some(Some(RlvWearableType::Pants)));
    tree.watch.set_worn_on(Some(Some(RlvWearableType::Tattoo)));
    install_inventory(&h, tree, vec![]);
    assert!(query_matches(
        &h,
        "@getinvworn=1234",
        "|03,Clothing|33,Accessories|33"
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Queries/GetInvWornQueryTests.cs::GetInvWornQueryTests.GetInvWorn_PartialRoot::test ce4c640a901a6684a27b2b259c4fc3b25f2d6b870107babff3b930ced64a150b translated
#[test]
fn get_inventory_worn_partial_root() {
    let h = harness();
    let mut tree = SampleInventoryTree::build().unwrap();
    attach(&mut tree.business_pants, RlvAttachmentPoint::Pelvis, PRIM1);
    tree.watch.set_worn_on(Some(Some(RlvWearableType::Tattoo)));
    install_inventory(&h, tree, vec![]);
    assert!(query_matches(
        &h,
        "@getinvworn=1234",
        "|02,Clothing|22,Accessories|22"
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Queries/GetInvWornQueryTests.cs::GetInvWornQueryTests.GetInvWorn_Naked::test fa8994189c5d9cda7d613dfb8f6ffbb1808c8bae54a3647530fb872d47c43dd5 translated
#[test]
fn get_inventory_worn_naked() {
    assert!(empty_tree_query(
        "@getinvworn=1234",
        "|01,Clothing|11,Accessories|11"
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Queries/GetInvWornQueryTests.cs::GetInvWornQueryTests.GetInvWorn_EmptyFolder::test ab6fc753f4d4170e17f49b708d7e62816f197e83234feda1fdcf63dcdc0bc6c4 translated
#[test]
fn get_inventory_worn_empty_folder() {
    assert!(empty_tree_query(
        "@getinvworn:Clothing/Hats/Sub Hats=1234",
        "|00"
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Queries/GetInvWornQueryTests.cs::GetInvWornQueryTests.GetInvWorn_PartialWorn::test ceb446f2aa6eaa5232a99147d5d993386ee9e2e5fefd93ef582f27a6b8ece948 translated
#[test]
fn get_inventory_worn_partial_folder() {
    let h = harness();
    let mut tree = SampleInventoryTree::build().unwrap();
    attach(&mut tree.party_hat, RlvAttachmentPoint::Spine, PRIM1);
    attach(&mut tree.fancy_hat, RlvAttachmentPoint::Chin, PRIM2);
    install_inventory(&h, tree, vec![]);
    assert!(query_matches(
        &h,
        "@getinvworn:Clothing/Hats=1234",
        "|33,Sub Hats|00"
    ));
}

const WEARABLES: [RlvWearableType; 17] = [
    RlvWearableType::Shape,
    RlvWearableType::Skin,
    RlvWearableType::Hair,
    RlvWearableType::Eyes,
    RlvWearableType::Shirt,
    RlvWearableType::Pants,
    RlvWearableType::Shoes,
    RlvWearableType::Socks,
    RlvWearableType::Jacket,
    RlvWearableType::Gloves,
    RlvWearableType::Undershirt,
    RlvWearableType::Underpants,
    RlvWearableType::Skirt,
    RlvWearableType::Alpha,
    RlvWearableType::Tattoo,
    RlvWearableType::Physics,
    RlvWearableType::Universal,
];

// parity-case: LibreMetaverse.Tests/RLV/Queries/GetOutfitQueryTests.cs::GetOutfitQueryTests.GetOutfit_WearingNothing::test 27befef9fdef993d82a641980f2b1ad7f458f07a50c7d008b6d311aeedfb4939 translated
#[test]
fn get_outfit_wearing_nothing() {
    assert!(empty_tree_query("@getoutfit=1234", "0000000000000000"));
}

// parity-case: LibreMetaverse.Tests/RLV/Queries/GetOutfitQueryTests.cs::GetOutfitQueryTests.GetOutfit_ExternalItems::test f88d149572ae35e880f16a5a92b4a9db73a3f26c5e8e61e4b1c01440151bd64b translated
#[test]
fn get_outfit_external_items() {
    let h = harness();
    let tree = SampleInventoryTree::build().unwrap();
    let folder = "12312312-aaaa-4aaa-8aaa-aaaaaaaaaaaa";
    install_inventory(
        &h,
        tree,
        vec![
            external_item(
                "12312312-0001-4aaa-8aaa-aaaaaaaaaaaa",
                "External Tattoo",
                folder,
                None,
                None,
                Some(RlvWearableType::Tattoo),
            ),
            external_item(
                "12312312-0002-4aaa-8aaa-aaaaaaaaaaaa",
                "External Jaw Thing",
                folder,
                Some(RlvAttachmentPoint::Jaw),
                Some("12312312-0002-4aaa-8aaa-ffffffffffff"),
                None,
            ),
        ],
    );
    assert!(query_matches(&h, "@getoutfit=1234", "0000000000000010"));
}

// parity-case: LibreMetaverse.Tests/RLV/Queries/GetOutfitQueryTests.cs::GetOutfitQueryTests.GetOutfit_WearingSomeItems::test 4e9ef77ab72f1fd16c4fad69f00eb80ab46421597d41b61df80b1f87370f42d3 translated
#[test]
fn get_outfit_wearing_some_items() {
    let h = harness();
    let tree = SampleInventoryTree::build().unwrap();
    let folder = "cccccccc-cccc-4ccc-8ccc-cccccccccccc";
    install_inventory(
        &h,
        tree,
        vec![
            external_item(
                "c0000000-cccc-4ccc-8ccc-cccccccccccc",
                "My Socks",
                folder,
                None,
                None,
                Some(RlvWearableType::Socks),
            ),
            external_item(
                "c0000001-cccc-4ccc-8ccc-cccccccccccc",
                "My Hair",
                folder,
                None,
                None,
                Some(RlvWearableType::Hair),
            ),
        ],
    );
    assert!(query_matches(&h, "@getoutfit=1234", "0000001000010000"));
}

// parity-case: LibreMetaverse.Tests/RLV/Queries/GetOutfitQueryTests.cs::GetOutfitQueryTests.GetOutfit_WearingEverything::test a840b239174cdb5dee4beb875bf9129209af3ba310564319528ad5b19764aae0 translated
#[test]
fn get_outfit_wearing_everything() {
    let h = harness();
    let tree = SampleInventoryTree::build().unwrap();
    let folder = "99999999-9999-4999-8999-999999999999";
    let external = WEARABLES
        .into_iter()
        .map(|wearable| {
            external_item(
                &format!("c{:07}-cccc-4ccc-8ccc-cccccccccccc", wearable as i32),
                &format!("External {wearable:?}"),
                folder,
                None,
                None,
                Some(wearable),
            )
        })
        .collect();
    install_inventory(&h, tree, external);
    assert!(query_matches(&h, "@getoutfit=1234", "1111111111111111"));
}

// parity-case: LibreMetaverse.Tests/RLV/Queries/GetOutfitQueryTests.cs::GetOutfitQueryTests.GetOutfit_Specific_Exists::test a402fd67ed8ddfc12d09863f08f9b15b34262ffa12b4b5aa965826eac89abae5 translated
#[test]
fn get_outfit_specific_exists() {
    let h = harness();
    let tree = SampleInventoryTree::build().unwrap();
    install_inventory(
        &h,
        tree,
        vec![external_item(
            "c0000000-cccc-4ccc-8ccc-cccccccccccc",
            "My Socks",
            "cccccccc-cccc-4ccc-8ccc-cccccccccccc",
            None,
            None,
            Some(RlvWearableType::Socks),
        )],
    );
    assert!(query_matches(&h, "@getoutfit:socks=1234", "1"));
}

// parity-case: LibreMetaverse.Tests/RLV/Queries/GetOutfitQueryTests.cs::GetOutfitQueryTests.GetOutfit_Specific_NotExists::test d7e891a620b5e5407fe337f507874b63eb5a21589767a01362bd8bc43d19ba4a translated
#[test]
fn get_outfit_specific_not_exists() {
    let h = harness();
    let tree = SampleInventoryTree::build().unwrap();
    install_inventory(
        &h,
        tree,
        vec![external_item(
            "c0000001-cccc-4ccc-8ccc-cccccccccccc",
            "My Hair",
            "cccccccc-cccc-4ccc-8ccc-cccccccccccc",
            None,
            None,
            Some(RlvWearableType::Hair),
        )],
    );
    assert!(query_matches(&h, "@getoutfit:socks=1234", "0"));
}

// parity-case: LibreMetaverse.Tests/RLV/Queries/GetPathNewQueryTests.cs::GetPathNewQueryTests.GetPathNew_BySender::test e7e87c01e83967a7160509d76f49d2a4b18d34b9b3fbed370c174d64ddb2c2b5 translated
#[test]
fn get_path_new_by_sender() {
    let h = harness();
    let mut tree = SampleInventoryTree::build().unwrap();
    attach(&mut tree.party_hat, RlvAttachmentPoint::Spine, PRIM1);
    install_inventory(&h, tree, vec![]);
    assert!(block_on(h.process_from("@getpathnew=1234", PRIM1, "Party Hat (Spine)")).unwrap());
    assert_eq!(
        h.actions.lock().unwrap().replies,
        vec![(1234, "Clothing/Hats".into())]
    );
}

// parity-case: LibreMetaverse.Tests/RLV/Queries/GetPathNewQueryTests.cs::GetPathNewQueryTests.GetPathNew_ByUUID::test 3a7d242966286258ed075c8e5c69eaf54ba0e71b8607432615a1199f5aeb6dcf translated
#[test]
fn get_path_new_by_uuid() {
    let h = harness();
    let mut tree = SampleInventoryTree::build().unwrap();
    attach(&mut tree.party_hat, RlvAttachmentPoint::Spine, PRIM1);
    install_inventory(&h, tree, vec![]);
    assert!(query_matches(
        &h,
        &format!("@getpathnew:{PRIM1}=1234"),
        "Clothing/Hats"
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Queries/GetPathNewQueryTests.cs::GetPathNewQueryTests.GetPathNew_ByUUID_Unknown::test c3c206661594026c36dfcfea110631548cd362d824db268c170e4d04b24b674a translated
#[test]
fn get_path_new_by_unknown_uuid() {
    let h = harness();
    let mut tree = SampleInventoryTree::build().unwrap();
    tree.fancy_hat.set_attached_to(None);
    tree.party_hat.set_attached_to(None);
    tree.business_pants.set_attached_to(None);
    tree.happy_shirt.set_attached_to(None);
    tree.glasses.set_attached_to(None);
    tree.retro_pants.set_worn_on(None);
    tree.watch.set_worn_on(None);
    install_inventory(&h, tree, vec![]);
    assert!(query_matches(
        &h,
        "@getpathnew:BADBADBA-DBAD-4BAD-8BAD-BADBADBADBAD=1234",
        "",
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Queries/GetPathNewQueryTests.cs::GetPathNewQueryTests.GetPathNew_ByAttach::test f8858e6141b660cec8a09c016891f2275dd9364157b1aa614b63fca5abb7620c translated
#[test]
fn get_path_new_by_attachment_point() {
    let h = harness();
    let mut tree = SampleInventoryTree::build().unwrap();
    attach(&mut tree.business_pants, RlvAttachmentPoint::Pelvis, PRIM1);
    attach(&mut tree.fancy_hat, RlvAttachmentPoint::Groin, PRIM2);
    attach(&mut tree.glasses, RlvAttachmentPoint::Groin, PRIM3);
    install_inventory(&h, tree, vec![]);
    assert!(query_matches(
        &h,
        "@getpathnew:groin=1234",
        "Accessories,Clothing/Hats"
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Queries/GetPathNewQueryTests.cs::GetPathNewQueryTests.GetPathNew_ByWorn::test eed31d9aaf34578368cc4f590de1da7f47e3f6c5e663215866f7f8e6946f974b translated
#[test]
fn get_path_new_by_wearable() {
    let h = harness();
    let mut tree = SampleInventoryTree::build().unwrap();
    tree.retro_pants
        .set_worn_on(Some(Some(RlvWearableType::Tattoo)));
    tree.fancy_hat
        .set_worn_on(Some(Some(RlvWearableType::Pants)));
    tree.watch.set_worn_on(Some(Some(RlvWearableType::Pants)));
    install_inventory(&h, tree, vec![]);
    assert!(query_matches(
        &h,
        "@getpathnew:pants=1234",
        "Accessories,Clothing/Hats"
    ));
}
