use async_trait::async_trait;
use crate::{
GazelleClientTrait, GazelleError, GroupResponse, TorrentResponse, UploadForm, UploadResponse,
User,
};
#[allow(clippy::struct_field_names)]
pub struct MockGazelleClient {
get_torrent_returns: Option<Result<TorrentResponse, GazelleError>>,
get_torrent_group_returns: Option<Result<GroupResponse, GazelleError>>,
get_user_returns: Option<Result<User, GazelleError>>,
download_torrent_returns: Option<Result<Vec<u8>, GazelleError>>,
upload_torrent_returns: Option<Result<UploadResponse, GazelleError>>,
}
impl MockGazelleClient {
#[must_use]
pub fn new() -> Self {
Self {
get_torrent_returns: None,
get_torrent_group_returns: None,
get_user_returns: None,
download_torrent_returns: None,
upload_torrent_returns: None,
}
}
#[must_use]
pub fn with_get_torrent(mut self, result: Result<TorrentResponse, GazelleError>) -> Self {
self.get_torrent_returns = Some(result);
self
}
#[must_use]
pub fn with_get_torrent_group(mut self, result: Result<GroupResponse, GazelleError>) -> Self {
self.get_torrent_group_returns = Some(result);
self
}
#[must_use]
pub fn with_get_user(mut self, result: Result<User, GazelleError>) -> Self {
self.get_user_returns = Some(result);
self
}
#[must_use]
pub fn with_download_torrent(mut self, result: Result<Vec<u8>, GazelleError>) -> Self {
self.download_torrent_returns = Some(result);
self
}
#[must_use]
pub fn with_upload_torrent(mut self, result: Result<UploadResponse, GazelleError>) -> Self {
self.upload_torrent_returns = Some(result);
self
}
}
impl Default for MockGazelleClient {
fn default() -> Self {
Self {
get_torrent_returns: Some(Ok(TorrentResponse::mock())),
get_torrent_group_returns: Some(Ok(GroupResponse::mock())),
get_user_returns: Some(Ok(User::mock())),
download_torrent_returns: Some(Ok(vec![0xd8, 0x3a, 0x00])),
upload_torrent_returns: Some(Ok(UploadResponse::mock())),
}
}
}
#[async_trait]
impl GazelleClientTrait for MockGazelleClient {
async fn get_torrent(&self, _id: u32) -> Result<TorrentResponse, GazelleError> {
self.get_torrent_returns
.clone()
.expect("MockGazelleClient: get_torrent_returns not set")
}
async fn get_torrent_group(&self, _id: u32) -> Result<GroupResponse, GazelleError> {
self.get_torrent_group_returns
.clone()
.expect("MockGazelleClient: get_torrent_group_returns not set")
}
async fn get_user(&self, _id: u32) -> Result<User, GazelleError> {
self.get_user_returns
.clone()
.expect("MockGazelleClient: get_user_returns not set")
}
async fn download_torrent(&self, _id: u32) -> Result<Vec<u8>, GazelleError> {
self.download_torrent_returns
.clone()
.expect("MockGazelleClient: download_torrent_returns not set")
}
async fn upload_torrent(&self, _upload: UploadForm) -> Result<UploadResponse, GazelleError> {
self.upload_torrent_returns
.clone()
.expect("MockGazelleClient: upload_torrent_returns not set")
}
}
#[cfg(test)]
mod tests {
use std::path::PathBuf;
use std::sync::Arc;
use tokio::sync::Mutex;
use super::*;
#[tokio::test]
async fn mock_get_torrent_returns_configured_value() {
let expected = TorrentResponse::mock();
let mock = MockGazelleClient::new().with_get_torrent(Ok(expected.clone()));
let result = mock.get_torrent(123).await;
assert!(result.is_ok());
let response = result.expect("should be ok");
assert_eq!(response.torrent.id, expected.torrent.id);
}
#[tokio::test]
async fn mock_get_torrent_returns_error() {
let mock = MockGazelleClient::new().with_get_torrent(Err(GazelleError::NotFound {
message: "not found".to_owned(),
}));
let result = mock.get_torrent(999).await;
assert!(matches!(result, Err(GazelleError::NotFound { .. })));
}
#[tokio::test]
async fn mock_get_user_returns_configured_value() {
let expected = User::mock();
let mock = MockGazelleClient::new().with_get_user(Ok(expected.clone()));
let result = mock.get_user(1).await;
assert!(result.is_ok());
let user = result.expect("should be ok");
assert_eq!(user.username, expected.username);
}
#[tokio::test]
async fn mock_download_torrent_returns_bytes() {
let expected_bytes = vec![0xd8, 0x3a, 0x00]; let mock = MockGazelleClient::new().with_download_torrent(Ok(expected_bytes.clone()));
let result = mock.download_torrent(123).await;
assert!(result.is_ok());
assert_eq!(result.expect("should be ok"), expected_bytes);
}
#[tokio::test]
async fn mock_works_as_trait_object() {
let response = TorrentResponse::mock();
let mock = MockGazelleClient::new().with_get_torrent(Ok(response));
let client: Arc<Mutex<dyn GazelleClientTrait>> = Arc::new(Mutex::new(mock));
let result = client.lock().await.get_torrent(123).await;
assert!(result.is_ok());
}
#[tokio::test]
async fn mock_can_be_called_multiple_times() {
let expected = TorrentResponse::mock();
let mock = MockGazelleClient::new().with_get_torrent(Ok(expected.clone()));
let result1 = mock.get_torrent(123).await;
let result2 = mock.get_torrent(456).await;
let result3 = mock.get_torrent(789).await;
assert!(result1.is_ok());
assert!(result2.is_ok());
assert!(result3.is_ok());
assert_eq!(
result1.expect("should be ok").torrent.id,
expected.torrent.id
);
assert_eq!(
result2.expect("should be ok").torrent.id,
expected.torrent.id
);
assert_eq!(
result3.expect("should be ok").torrent.id,
expected.torrent.id
);
}
#[tokio::test]
async fn mock_default_has_all_ok_responses() {
let mock = MockGazelleClient::default();
assert!(mock.get_torrent(1).await.is_ok());
assert!(mock.get_torrent_group(1).await.is_ok());
assert!(mock.get_user(1).await.is_ok());
assert!(mock.download_torrent(1).await.is_ok());
assert!(
mock.upload_torrent(crate::UploadForm {
path: PathBuf::new(),
category_id: 0,
remaster_year: 2020,
remaster_title: String::new(),
remaster_record_label: String::new(),
remaster_catalogue_number: String::new(),
format: String::new(),
bitrate: String::new(),
media: String::new(),
release_desc: String::new(),
group_id: 1,
})
.await
.is_ok()
);
}
}