mod fixtures; mod offline { use super::fixtures::offline::{myplex::*, Mocked}; use httpmock::{ Method::{DELETE, GET, POST}, Mock, MockServer, }; use plex_api::{ sharing::{Filters, InviteStatus, Permissions, ShareableLibrary, User}, url::{ MYPLEX_INVITES_FRIENDS, MYPLEX_INVITES_INVITE, MYPLEX_INVITES_SHARED_SERVERS, MYPLEX_SERVERS, }, MyPlex, RestrictionProfile, }; fn prepare_friends_mock<'a>( mock_server: &'a MockServer, status: &'a str, mock_file: &'a str, ) -> Mock<'a> { mock_server.mock(|when, then| { when.method(GET) .path(MYPLEX_INVITES_FRIENDS) .query_param("includeSharedServers", "true") .query_param("includeSharedSources", "true") .query_param("includeSharingSettings", "true") .query_param("status", status); then.status(200) .header("content-type", "application/json") .body_from_file(mock_file); }) } #[plex_api_test_helper::offline_test] #[case::no_friends("tests/mocks/myplex/api/v2/friends_empty.json", 0)] #[case::single_managed_user( "tests/mocks/myplex/api/v2/friends_accepted_one_restricted.json", 1 )] #[case::four_managed_users( "tests/mocks/myplex/api/v2/friends_accepted_four_restricted.json", 4 )] #[case::single_external_user("tests/mocks/myplex/api/v2/friends_accepted_one_external.json", 1)] #[case::managed_and_external_user("tests/mocks/myplex/api/v2/friends_accepted_mixed.json", 2)] async fn list_friends( #[future] myplex: Mocked, #[case] mock_file: &str, #[case] friends_count: usize, ) { let (myplex, mock_server) = myplex.split(); let sharing = myplex.sharing().unwrap(); let m = prepare_friends_mock(&mock_server, "accepted", mock_file); assert_eq!( friends_count, sharing.friends(InviteStatus::Accepted).await.unwrap().len() ); m.assert(); } #[plex_api_test_helper::offline_test] async fn filters_deserialization(#[future] myplex: Mocked) { let (myplex, mock_server) = myplex.split(); let sharing = myplex.sharing().unwrap(); let mut m = prepare_friends_mock( &mock_server, "accepted", "tests/mocks/myplex/api/v2/friends_accepted_four_restricted.json", ); let friends = sharing.friends(InviteStatus::Accepted).await.unwrap(); m.assert(); m.delete(); let mut found_adult = false; let mut found_little_kid = false; let mut found_teen = false; let mut found_older_kid = false; for friend in friends { let filter_movies = friend .sharing_settings .as_ref() .unwrap() .filter_movies .as_ref() .unwrap(); match &friend.restriction_profile { None => { found_adult = true; assert_eq!(vec!["12"], filter_movies.content_rating); assert_eq!(vec!["Approved"], filter_movies.exclude_content_rating); assert_eq!( vec!["BAFTA", "1001 Movies You Must See Before You Die"], filter_movies.label ); assert!(filter_movies.exclude_label.is_empty()); } Some(RestrictionProfile::LittleKid) => { found_little_kid = true; assert!(filter_movies.content_rating.iter().any(|r| r == "US/TV-Y")); assert!(filter_movies.content_rating.iter().any(|r| r == "TV-G")); assert!(!filter_movies.content_rating.iter().any(|r| r == "US/PG")); assert!(!filter_movies.content_rating.iter().any(|r| r == "US/PG-13")); assert!(!filter_movies.content_rating.iter().any(|r| r == "US/TV-Y7")); assert!(filter_movies.exclude_content_rating.is_empty()); assert!(filter_movies.label.is_empty()); assert!(filter_movies.exclude_label.is_empty()); } Some(RestrictionProfile::OlderKid) => { found_older_kid = true; assert!(filter_movies.content_rating.iter().any(|r| r == "US/TV-Y")); assert!(filter_movies.content_rating.iter().any(|r| r == "US/TV-Y7")); assert!(filter_movies.content_rating.iter().any(|r| r == "US/TV-PG")); assert!(filter_movies.content_rating.iter().any(|r| r == "TV-G")); assert!(!filter_movies.content_rating.iter().any(|r| r == "US/PG-13")); assert!(filter_movies.exclude_content_rating.is_empty()); assert!(filter_movies.label.is_empty()); assert!(filter_movies.exclude_label.is_empty()); } Some(RestrictionProfile::Teen) => { found_teen = true; assert!(filter_movies.content_rating.iter().any(|r| r == "US/TV-Y")); assert!(filter_movies.content_rating.iter().any(|r| r == "US/TV-Y7")); assert!(filter_movies.content_rating.iter().any(|r| r == "US/TV-PG")); assert!(filter_movies.content_rating.iter().any(|r| r == "TV-G")); assert!(filter_movies.content_rating.iter().any(|r| r == "US/PG-13")); assert!(filter_movies.exclude_content_rating.is_empty()); assert!(filter_movies.label.is_empty()); assert!(filter_movies.exclude_label.is_empty()); } #[cfg(not(feature = "tests_deny_unknown_fields"))] _ => { panic!("Unexpected friend"); } } } assert!( found_adult, "'Adult' friend wasn't found in the friends list" ); assert!( found_little_kid, "'Little kid' friend wasn't found in the friends list" ); assert!(found_teen, "'Teen' friend wasn't found in the friends list"); assert!( found_older_kid, "'Older kid' friend wasn't found in the friends list" ); } #[plex_api_test_helper::offline_test] async fn invite(#[future] myplex: Mocked) { let (myplex, mock_server) = myplex.split(); let sharing = myplex.sharing().unwrap(); let m = mock_server.mock(|when, then| { when.method(POST) .path(MYPLEX_INVITES_INVITE) .query_param("identifier", "user"); then.status(201) .header("content-type", "application/json") .body_from_file("tests/mocks/myplex/api/v2/friends/invite_create.json"); }); let friend = sharing .invite(plex_api::sharing::User::UsernameOrEmail("user")) .await .unwrap(); m.assert(); assert_eq!(1, friend.id); assert_eq!("deadbeef", friend.uuid); assert_eq!("username", friend.title); assert_eq!("username", friend.username.unwrap()); assert!(!friend.restricted); assert_eq!(None, friend.friendly_name); assert_eq!( "https://plex.tv/users/deadbeef/avatar?c=23423423", friend.thumb ); assert!(!friend.home); assert!(matches!(friend.status, Some(InviteStatus::Pending))); } #[plex_api_test_helper::offline_test] async fn share_server(#[future] myplex: Mocked) { let (myplex, mock_server) = myplex.split(); let sharing = myplex.sharing().unwrap(); let server_info_mock = mock_server.mock(|when, then| { when.method(GET) .path(format!("{MYPLEX_SERVERS}/machine_id")); then.status(201) .header("content-type", "application/json") .body_from_file("tests/mocks/myplex/api/v2/servers/machine_id.json"); }); let share_create_mock = mock_server.mock(|when, then| { when.method(POST) .path(MYPLEX_INVITES_SHARED_SERVERS) .json_body(serde_json::json!({ "invitedEmail": "user", "librarySectionIds": [1001, 1002], "settings": { "allowChannels": true, "allowSubtitleAdmin": true, "allowSync": true, "allowCameraUpload": false, "allowTuners": 0, "filterMovies": "", "filterMusic": "", "filterTelevision": "", "filterPhotos": null, "filterAll": null }, "machineIdentifier": "machine_id" })); then.status(201) .header("content-type", "application/json") .body_from_file("tests/mocks/myplex/api/v2/shared_server_create.json"); }); let share = sharing .share( User::UsernameOrEmail("user"), plex_api::sharing::ShareableServer::MachineIdentifier("machine_id"), &[ ShareableLibrary::LibraryId("1"), ShareableLibrary::LibraryId("2"), ], Permissions::default(), Filters::default(), ) .await .unwrap(); server_info_mock.assert(); share_create_mock.assert(); assert_eq!("deadbeef1", share.invited.unwrap().uuid); } #[plex_api_test_helper::offline_test] async fn accept_not_pending_forbidden(#[future] myplex: Mocked) { let (myplex, mock_server) = myplex.split(); let sharing = myplex.sharing().unwrap(); let mut m = prepare_friends_mock( &mock_server, "accepted", "tests/mocks/myplex/api/v2/friends_accepted_one_external.json", ); let mut friends = sharing.friends(InviteStatus::Accepted).await.unwrap(); m.assert(); m.delete(); let friend = friends.pop().unwrap(); let err = friend.accept().await.unwrap_err(); assert!( matches!(err, plex_api::Error::InviteAcceptingNotPendingReceived), "Unexpected errors returned by Friend::accept()" ); } #[plex_api_test_helper::offline_test] async fn accept_and_reject(#[future] myplex: Mocked) { let (myplex, mock_server) = myplex.split(); let sharing = myplex.sharing().unwrap(); let mut m = prepare_friends_mock( &mock_server, "pending_received", "tests/mocks/myplex/api/v2/friends_pending_received_one_external.json", ); let mut friends = sharing .friends(InviteStatus::PendingReceived) .await .unwrap(); m.assert(); m.delete(); let friend = friends.pop().unwrap(); let mock_delete = mock_server.mock(|when, then| { when.method(DELETE) .path(format!("{MYPLEX_INVITES_FRIENDS}/{}", friend.id)); then.status(200).header("content-type", "application/json"); }); let del = friend.delete().await; mock_delete.assert(); del.unwrap(); let mut m = prepare_friends_mock( &mock_server, "pending_received", "tests/mocks/myplex/api/v2/friends_pending_received_one_external.json", ); let friends = sharing.friends(InviteStatus::PendingReceived).await; m.assert(); m.delete(); let mut friends = friends.unwrap(); let friend = friends.pop().unwrap(); let mock_accept = mock_server.mock(|when, then| { when.method(POST) .path(format!("{MYPLEX_INVITES_FRIENDS}/{}/accept", friend.id)); then.status(200) .header("content-type", "application/json") .body_from_file("tests/mocks/myplex/api/v2/friends/accept.json"); }); let friend = friend.accept().await; mock_accept.assert(); let friend = friend.unwrap(); assert!( matches!(friend.status, Some(InviteStatus::Accepted)), "Unexpected friend status" ); } } mod online { use super::fixtures::online::myplex; use plex_api::{sharing::InviteStatus, MyPlex}; #[plex_api_test_helper::online_test_myplex] async fn list_friends(#[future] myplex: MyPlex) { myplex .sharing() .unwrap() .friends(InviteStatus::Accepted) .await .unwrap(); } }