From f802a56913c1555bb9f905231a83f9a2597081f7 Mon Sep 17 00:00:00 2001 From: Louis Vallat Date: Thu, 4 Nov 2021 18:48:40 +0100 Subject: [PATCH] Added the delete_photo subcommand and its according tests Signed-off-by: Louis Vallat --- src/main.rs | 20 +++++++++++-- src/photo.rs | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 100 insertions(+), 5 deletions(-) diff --git a/src/main.rs b/src/main.rs index 309b072..ce59444 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use crate::{album::{add_album, delete_album, get_album, set_description_album, set_public_album, set_title_album}, albums::get_albums, config::{get_session_file, read_config, setup_config_data_storage}, photo::add_photo, session::login, utils::{body_to_str, numeric_validator, read_from_file, write_to_file}}; +use crate::{album::{add_album, delete_album, get_album, set_description_album, set_public_album, set_title_album}, albums::get_albums, config::{get_session_file, read_config, setup_config_data_storage}, photo::{add_photo, delete_photo}, session::login, utils::{body_to_str, numeric_validator, read_from_file, write_to_file}}; use hyper_tls::HttpsConnector; use hyper::{Client, client::HttpConnector}; use clap::{App, Arg, SubCommand, crate_version}; @@ -156,7 +156,7 @@ async fn main() -> Result<(), Box> { ) .subcommand( SubCommand::with_name("add_photo") - .about("Add some picture to a lychee instance.") + .about("Upload a picture.") .arg(Arg::with_name("album_id") .long("album_id") .short("a") @@ -168,7 +168,18 @@ async fn main() -> Result<(), Box> { .short("p") .help("Where the picture that you want to add is located.") .value_name("PATH")) - ); + ) + .subcommand( + SubCommand::with_name("delete_photo") + .about("Delete some pictures.") + .arg(Arg::with_name("id") + .required(true) + .long("photo_id") + .short("i") + .value_name("IDs") + .help("The id of the picture(s) which you want to delete. For multiple ids, use commas.")) + ) + ; let matches = app.clone().get_matches(); let c = read_config(); @@ -226,6 +237,9 @@ async fn main() -> Result<(), Box> { let a = m.value_of("album_id").unwrap_or("0"); let i = m.value_of("photo_path").unwrap(); println!("{}", add_photo(&client, &lychee_session_cookie, a, i).await); + } else if let Some(m) = matches.subcommand_matches("delete_photo") { + let id = m.value_of("id").unwrap(); + delete_photo(&client, &lychee_session_cookie, id).await; } else { App::print_long_help(&mut app).unwrap(); } diff --git a/src/photo.rs b/src/photo.rs index 626a4d3..4b01c15 100644 --- a/src/photo.rs +++ b/src/photo.rs @@ -1,5 +1,6 @@ use std::{io::{Write, Read}, fs::File, path::Path}; -use hyper::{Method, Request, header::{COOKIE, CONTENT_TYPE, AUTHORIZATION}}; +use hyper::{Method, Request, header::{COOKIE, CONTENT_TYPE, AUTHORIZATION}, Body}; +use serde_json::json; use crate::{LycheeClient, utils::{body_to_str, numeric_validator}}; @@ -18,7 +19,7 @@ fn image_data(b: &str, a: &str, i: &str) -> std::io::Result> { let mut f = File::open(i)?; f.read_to_end(&mut data)?; - write!(data, "\r\n")?; // The key thing you are missing + write!(data, "\r\n")?; write!(data, "--{}--\r\n", b)?; Ok(data) @@ -42,3 +43,83 @@ pub async fn add_photo(c: &LycheeClient, l: &str, a: &str, i: &str) -> String { assert_ne!(s, 500, "The server returned an internal error."); return b; } + +pub async fn delete_photo(c: &LycheeClient, l: &str, i: &str) { + let req = Request::builder() + .method(Method::POST) + .uri(c.endpoint.to_string() + "/api/Photo::delete") + .header(COOKIE, l) + .header(AUTHORIZATION, c.api_key.to_string()) + .header(CONTENT_TYPE, "application/json") + .body(Body::from(json!({"photoIDs": i}).to_string())) + .expect("Cannot request /api/Photo::delete."); + let res = c.client.request(req).await.unwrap(); + let s = res.status(); + let b = body_to_str(res.into_body()).await; + assert_eq!(b, "true", "The server didn't reply true: '{}'.", b); + assert_ne!(s, 500, "The server returned an internal error."); +} + +#[cfg(test)] +mod photo_tests { + use crate::{LycheeClient, photo::delete_photo}; + use hyper_tls::HttpsConnector; + use hyper::Client; + use mockito::mock; + use serde_json::json; + + fn setup() -> LycheeClient { + let https = HttpsConnector::new(); + return LycheeClient { + client: Client::builder().build::<_, hyper::Body>(https), + endpoint: mockito::server_url(), + api_key: "value".to_string() + }; + } + + #[test] + fn delete_photo_correct_values() { + let client = setup(); + let m1 = mock("POST", "/api/Photo::delete") + .match_header("cookie", "v") + .match_header("content-type", "application/json") + .with_body("true") + .match_body(json!({"photoIDs": "123"}).to_string().as_str()) + .create(); + + tokio_test::block_on(delete_photo(&client, "v", "123")); + m1.assert(); + } + + #[test] + #[should_panic] + fn delete_photo_false_returned() { + let client = setup(); + let m1 = mock("POST", "/api/Photo::delete") + .match_header("cookie", "v") + .match_header("content-type", "application/json") + .with_body("false") + .match_body(json!({"photoIDs": "123"}).to_string().as_str()) + .create(); + + tokio_test::block_on(delete_photo(&client, "v", "123")); + m1.assert(); + } + + + #[test] + #[should_panic] + fn delete_photo_500_returned() { + let client = setup(); + let m1 = mock("POST", "/api/Photo::delete") + .match_header("cookie", "value") + .match_header("content-type", "application/json") + .with_body("true") + .with_status(500) + .match_body(json!({"photoIDs": "123"}).to_string().as_str()) + .create(); + + tokio_test::block_on(delete_photo(&client, "value", "123")); + m1.assert(); + } +}