From c542e5224f8df92e9937ca8c7f23ebf69725876e Mon Sep 17 00:00:00 2001 From: "NGnius (Graham)" Date: Mon, 13 Nov 2023 22:06:06 -0500 Subject: [PATCH] Add additional CRF2 endpoints, split RC2 tests into their own files --- Cargo.toml | 2 +- src/robocraft2/factory.rs | 32 ++++- src/robocraft2/factory_json.rs | 22 ++++ src/robocraft2/mod.rs | 2 +- tests/robocraft2_factory.rs | 219 +++++++++++++++++++++++++++++++++ tests/robocraft_auth.rs | 4 +- tests/robocraft_factory.rs | 175 -------------------------- 7 files changed, 275 insertions(+), 181 deletions(-) create mode 100644 tests/robocraft2_factory.rs diff --git a/Cargo.toml b/Cargo.toml index 1531d70..a98b4c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libfj" -version = "0.7.2" +version = "0.7.3" authors = ["NGnius (Graham) "] edition = "2018" description = "An unofficial collection of APIs used in FreeJam games and mods" diff --git a/src/robocraft2/factory.rs b/src/robocraft2/factory.rs index abf9b16..15f027c 100644 --- a/src/robocraft2/factory.rs +++ b/src/robocraft2/factory.rs @@ -4,7 +4,7 @@ use reqwest::{Client, Error as ReqwestError, Response}; use url::{Url}; use crate::robocraft2::{ITokenProvider, ErrorPayload}; -use crate::robocraft2::{SearchPayload, SearchResponse, CreateRobotPayload, CreateRobotResponse, FactoryInfoResponse, PublishRobotPayload, PublishRobotResponse, MyRobotsResponse, GetRobotResponse}; +use crate::robocraft2::{SearchPayload, SearchResponse, CreateRobotPayload, CreateRobotResponse, FactoryInfoResponse, PublishRobotPayload, PublishRobotResponse, MyRobotsResponse, GetRobotResponse, ModerateRobotPayload, ReportRobotPayload}; /// Community Factory Robot 2 root URL pub const FACTORY_DOMAIN: &str = "https://factory.production.robocraft2.com"; @@ -254,4 +254,34 @@ impl FactoryAPI { .map_err(FactoryError::Protocol)?; handle_json_response::(result).await } + + pub async fn moderate(&self, payload: ModerateRobotPayload, id: String) -> Result<(), FactoryError> { + let url = Url::parse(FACTORY_DOMAIN) + .unwrap() + .join(&format!("/v1/foundry/vehicles/{}/moderate", id)) + .unwrap(); + let token = self.token.lock().unwrap().token().await.map_err(FactoryError::Protocol)?; + let _result = self.client.post(url) + .header("Authorization", "Bearer ".to_owned() + &token) + .header("Content-Type", "application/json") + .json(&payload) + .send().await + .map_err(FactoryError::Protocol)?; + Ok(()) + } + + pub async fn report(&self, payload: ReportRobotPayload, id: String) -> Result<(), FactoryError> { + let url = Url::parse(FACTORY_DOMAIN) + .unwrap() + .join(&format!("/v1/foundry/vehicles/{}/report", id)) + .unwrap(); + let token = self.token.lock().unwrap().token().await.map_err(FactoryError::Protocol)?; + let _result = self.client.post(url) + .header("Authorization", "Bearer ".to_owned() + &token) + .header("Content-Type", "application/json") + .json(&payload) + .send().await + .map_err(FactoryError::Protocol)?; + Ok(()) + } } diff --git a/src/robocraft2/factory_json.rs b/src/robocraft2/factory_json.rs index 4669801..5e6fb88 100644 --- a/src/robocraft2/factory_json.rs +++ b/src/robocraft2/factory_json.rs @@ -244,3 +244,25 @@ pub struct GetRobotResponse { #[serde(rename = "created")] pub created: String, // date } + +// moderate robot endpoint + +#[derive(Deserialize, Serialize, Clone, Debug)] +pub struct ModerateRobotPayload { + #[serde(rename = "approve")] + pub approve: bool, + #[serde(rename = "reason")] + pub reason: String, +} + +// (no response) + +// report robot endpoint + +#[derive(Deserialize, Serialize, Clone, Debug)] +pub struct ReportRobotPayload { + #[serde(rename = "reason")] + pub reason: String, +} + +// (no response) diff --git a/src/robocraft2/mod.rs b/src/robocraft2/mod.rs index cc22ba3..12f10ef 100644 --- a/src/robocraft2/mod.rs +++ b/src/robocraft2/mod.rs @@ -5,7 +5,7 @@ mod factory; pub use factory::{FactoryAPI, FactoryError}; mod factory_json; -pub use factory_json::{ErrorPayload, SearchPayload, SearchResponse, SearchResponseItem, RobotInfo, RobotPrice, CreateRobotPayload, CreateRobotResponse, FactoryInfoResponse, PublishRobotPayload, PublishRobotResponse, MyRobotsResponse, GetRobotResponse}; +pub use factory_json::{ErrorPayload, SearchPayload, SearchResponse, SearchResponseItem, RobotInfo, RobotPrice, CreateRobotPayload, CreateRobotResponse, FactoryInfoResponse, PublishRobotPayload, PublishRobotResponse, MyRobotsResponse, GetRobotResponse, ModerateRobotPayload, ReportRobotPayload}; mod portal; pub use self::portal::{PortalTokenProvider, AccountInfo, PortalCheckResponse, ITokenProvider}; diff --git a/tests/robocraft2_factory.rs b/tests/robocraft2_factory.rs new file mode 100644 index 0000000..d839972 --- /dev/null +++ b/tests/robocraft2_factory.rs @@ -0,0 +1,219 @@ +#[cfg(feature = "robocraft2")] +use libfj::robocraft2; + +#[cfg(feature = "robocraft2")] +async fn builder() -> robocraft2::FactoryAPI { + let token = robocraft2::PortalTokenProvider::with_username("FJAPIC00L", "P4$$w0rd").await.unwrap(); + robocraft2::FactoryAPI::with_auth(Box::new(token)) +} + +#[cfg(feature = "robocraft2")] +//#[tokio::test] +#[allow(dead_code)] +async fn robocraft2_factory_moderate() -> Result<(), ()> { + let api = builder().await; + let robot = api.list().await.unwrap().results.pop().unwrap(); + let result = api.moderate(robocraft2::ModerateRobotPayload { + approve: false, + reason: "Automated test".to_owned(), + }, robot.robot.id).await; + assert!(result.is_ok()); + Ok(()) +} + +#[cfg(feature = "robocraft2")] +//#[tokio::test] +#[allow(dead_code)] +async fn robocraft2_factory_report() -> Result<(), ()> { + let api = builder().await; + let robot = api.list().await.unwrap().results.pop().unwrap(); + let result = api.report(robocraft2::ReportRobotPayload { + reason: "Automated test".to_owned(), + }, robot.robot.id).await; + assert!(result.is_ok()); + Ok(()) +} + +#[cfg(feature = "robocraft2")] +//#[tokio::test] +#[allow(dead_code)] +async fn robocraft2_factory_default_query() -> Result<(), ()> { + let api = builder().await; + let result = api.list().await; + assert!(result.is_ok()); + let robo_info = unwrap_factory2(result); + assert_ne!(robo_info.results.len(), 0); + for robot in &robo_info.results { + assert_ne!(robot.robot.name, ""); + assert_ne!(robot.robot.creator_id, ""); + assert_ne!(robot.robot.creator_id, ""); + assert_ne!(robot.robot.image, ""); + //println!("RobotInfo.to_string() -> `{}`", robot.robot.to_string()); + println!("SearchResponseItem {}", serde_json::to_string_pretty(&robot).unwrap()); + } + Ok(()) +} + + +#[cfg(feature = "robocraft2")] +#[tokio::test] +async fn robocraft2_factory_info() -> Result<(), ()> { + let api = builder().await; + let result = api.factory_info().await; + assert!(result.is_ok()); + let crf_info = unwrap_factory2(result); + println!("FactoryInfo {:?}", crf_info); + Ok(()) +} + +#[cfg(feature = "robocraft2")] +//#[tokio::test] +#[allow(dead_code)] +async fn robocraft2_factory_upload() -> Result<(), ()> { + let api = builder().await; + + // copy default bot + let result = api.get("08dab2c9-7a72-4ec4-843c-154fe8768e91".to_owned()).await; + assert!(result.is_ok()); + let robot = unwrap_factory2(result); + + let result = api.create_robot( + robocraft2::CreateRobotPayload { + name: "API is easy".to_owned(), + data: robot.data, // base64 + image: "".to_owned(), // base64 + base_cpu: 42, + weapon_cpu: 1, + cosmetic_cpu: 6, + cluster_count: 1, + block_counts: vec![ + (42, 3), + (3, 6) + ].drain(..).collect(), + materials_used: vec![ + 8, + 4 + ].drain(..).collect(), + minimum_offset_x: 0.0, + minimum_offset_y: 0.0, + minimum_offset_z: 0.0, + maximum_offset_x: 0.0, + maximum_offset_y: 0.0, + maximum_offset_z: 0.0, + } + ).await; + //assert!(result.is_ok()); + let robot_info = unwrap_factory2(result); + println!("CreateRobotInfo {:?}", robot_info); + let result = api.publish_robot( + robocraft2::PublishRobotPayload { + name: "CRF API oh my".to_owned(), + description: "There once was a person named NGnius, who simply wasn't that bright.\nBut he had a misleading name, and wasn't not quite unhated\nSo he thought it alright to put up a fight\nand get banned for reverse engineering".to_owned(), + techpoints: -42, + bloxcoin: 123 + }, robot_info.header.id.clone()).await; + //assert!(result.is_ok()); + let _publish_info = unwrap_factory2(result); + + // clean up + let result = api.unpublish_bot(robot_info.header.id.clone()).await; + //assert!(result.is_ok()); + let _robot = unwrap_factory2(result); + + let result = api.delete_robot(robot_info.header.id).await; + //assert!(result.is_ok()); + let _robot = unwrap_factory2(result); + Ok(()) +} + +#[cfg(feature = "robocraft2")] +#[tokio::test] +async fn robocraft2_factory_my_bots() -> Result<(), ()> { + let api = robocraft2::FactoryAPI::with_auth(Box::new(robocraft2::PortalTokenProvider::with_username("FJAPIC00L", "P4$$w0rd").await.unwrap())); + let result = api.my_robots().await; + assert!(result.is_ok()); + let robo_info = unwrap_factory2(result); + assert_ne!(robo_info.vehicles.len(), 0); + for robot in &robo_info.vehicles { + assert_ne!(robot.name, ""); + assert_ne!(robot.creator_id, ""); + assert_ne!(robot.creator_id, ""); + assert_ne!(robot.image, ""); + println!("My bot `{}`", robot.to_string()); + //println!("my vehicle {}", serde_json::to_string_pretty(&robot).unwrap()); + } + Ok(()) +} + +#[cfg(feature = "robocraft2")] +#[tokio::test] +async fn robocraft2_factory_my_published_bots() -> Result<(), ()> { + let api = robocraft2::FactoryAPI::with_auth(Box::new(robocraft2::PortalTokenProvider::with_username("FJAPIC00L", "P4$$w0rd").await.unwrap())); + let result = api.my_published_robots().await; + assert!(result.is_ok()); + let robo_info = unwrap_factory2(result); + //assert_ne!(robo_info.vehicles.len(), 0); + for robot in &robo_info.vehicles { + assert_ne!(robot.name, ""); + assert_ne!(robot.creator_id, ""); + assert_ne!(robot.creator_id, ""); + assert_ne!(robot.image, ""); + println!("My pub bot `{}`", robot.to_string()); + //println!("pub vehicle {}", serde_json::to_string_pretty(&robot).unwrap()); + } + Ok(()) +} + +#[cfg(feature = "robocraft2")] +//#[tokio::test] +#[allow(dead_code)] +async fn robocraft2_factory_bot() -> Result<(), ()> { + let api = robocraft2::FactoryAPI::with_auth(Box::new(robocraft2::PortalTokenProvider::with_username("FJAPIC00L", "P4$$w0rd").await.unwrap())); + let result = api.get("08dab2c9-7a72-4ec4-843c-154fe8768e91".to_owned()).await; + //assert!(result.is_ok()); + let robot = unwrap_factory2(result); + assert_ne!(robot.header.name, ""); + assert_ne!(robot.header.creator_id, ""); + assert_ne!(robot.header.creator_id, ""); + //assert_ne!(robot.header.image, ""); + //assert_ne!(robot.description, ""); + assert_ne!(robot.data, ""); + println!("robot {}", serde_json::to_string_pretty(&robot).unwrap()); + Ok(()) +} + +#[cfg(feature = "robocraft2")] +//#[tokio::test] +#[allow(dead_code)] +async fn robocraft2_factory_delete_all_my_bots() -> Result<(), ()> { + let api = builder().await; + let my_bots = api.my_published_robots().await.unwrap(); + for bot in my_bots.vehicles { + let result = api.delete_robot(bot.id).await; + unwrap_factory2(result); + } + Ok(()) +} + +#[cfg(feature = "robocraft2")] +//#[tokio::test] +#[allow(dead_code)] +async fn robocraft2_factory_unpublish_all_my_bots() -> Result<(), ()> { + let api = builder().await; + let my_bots = api.my_published_robots().await.unwrap(); + for bot in my_bots.vehicles { + let result = api.unpublish_bot(bot.id).await; + unwrap_factory2(result); + } + Ok(()) +} + +fn unwrap_factory2(result: Result) -> T { + match result { + Ok(t) => t, + Err(e) => { + //println!("FactoryError: {}", e); + panic!("CRF2 Error: {}", e); + } + } +} diff --git a/tests/robocraft_auth.rs b/tests/robocraft_auth.rs index fd7d80a..258edcd 100644 --- a/tests/robocraft_auth.rs +++ b/tests/robocraft_auth.rs @@ -1,6 +1,6 @@ #[cfg(feature = "robocraft")] use libfj::robocraft; -#[cfg(feature = "robocraft")] +#[cfg(feature = "robocraft2")] use libfj::robocraft2; #[cfg(feature = "robocraft")] use libfj::robocraft::ITokenProvider; @@ -49,9 +49,7 @@ async fn robocraft2_account() -> Result<(), ()> { Ok(()) } -// this requires human-interaction so it's disabled by default #[cfg(feature = "robocraft2")] -#[allow(dead_code)] #[tokio::test] async fn robocraft2_simple_account() -> Result<(), ()> { let token_maybe = robocraft2::PortalTokenProvider::with_username("FJAPIC00L", "P4$$w0rd").await; diff --git a/tests/robocraft_factory.rs b/tests/robocraft_factory.rs index 073e4fc..542f3aa 100644 --- a/tests/robocraft_factory.rs +++ b/tests/robocraft_factory.rs @@ -121,178 +121,3 @@ async fn robocraft_factory_robot_cubes() -> Result<(), ()> { assert_eq!(colour_str, bot_info.response.colour_data); Ok(()) } - -#[cfg(feature = "robocraft2")] -//#[tokio::test] -async fn robocraft2_factory_default_query() -> Result<(), ()> { - let api = robocraft2::FactoryAPI::with_auth(Box::new(robocraft2::PortalTokenProvider::with_username("FJAPIC00L", "P4$$w0rd").await.unwrap())); - let result = api.list().await; - assert!(result.is_ok()); - let robo_info = unwrap_factory2(result); - assert_ne!(robo_info.results.len(), 0); - for robot in &robo_info.results { - assert_ne!(robot.robot.name, ""); - assert_ne!(robot.robot.creator_id, ""); - assert_ne!(robot.robot.creator_id, ""); - assert_ne!(robot.robot.image, ""); - //println!("RobotInfo.to_string() -> `{}`", robot.robot.to_string()); - println!("SearchResponseItem {}", serde_json::to_string_pretty(&robot).unwrap()); - } - Ok(()) -} - - -#[cfg(feature = "robocraft2")] -#[tokio::test] -async fn robocraft2_factory_info() -> Result<(), ()> { - let api = robocraft2::FactoryAPI::with_auth(Box::new(robocraft2::PortalTokenProvider::with_username("FJAPIC00L", "P4$$w0rd").await.unwrap())); - let result = api.factory_info().await; - assert!(result.is_ok()); - let crf_info = unwrap_factory2(result); - println!("FactoryInfo {:?}", crf_info); - Ok(()) -} - -#[cfg(feature = "robocraft2")] -//#[tokio::test] -async fn robocraft2_factory_upload() -> Result<(), ()> { - let api = robocraft2::FactoryAPI::with_auth(Box::new(robocraft2::PortalTokenProvider::with_username("FJAPIC00L", "P4$$w0rd").await.unwrap())); - - // copy default bot - let result = api.get("08dab2c9-7a72-4ec4-843c-154fe8768e91".to_owned()).await; - assert!(result.is_ok()); - let robot = unwrap_factory2(result); - - let result = api.create_robot( - robocraft2::CreateRobotPayload { - name: "API is easy".to_owned(), - data: robot.data, // base64 - image: "".to_owned(), // base64 - base_cpu: 42, - weapon_cpu: 1, - cosmetic_cpu: 6, - cluster_count: 1, - block_counts: vec![ - (42, 3), - (3, 6) - ].drain(..).collect(), - materials_used: vec![ - 8, - 4 - ].drain(..).collect(), - minimum_offset_x: 0.0, - minimum_offset_y: 0.0, - minimum_offset_z: 0.0, - maximum_offset_x: 0.0, - maximum_offset_y: 0.0, - maximum_offset_z: 0.0, - } - ).await; - //assert!(result.is_ok()); - let robot_info = unwrap_factory2(result); - println!("CreateRobotInfo {:?}", robot_info); - let result = api.publish_robot( - robocraft2::PublishRobotPayload { - name: "CRF API oh my".to_owned(), - description: "There once was a person named NGnius, who simply wasn't that bright.\nBut he had a misleading name, and wasn't not quite unhated\nSo he thought it alright to put up a fight\nand get banned for reverse engineering".to_owned(), - techpoints: -42, - bloxcoin: 123 - }, robot_info.header.id.clone()).await; - //assert!(result.is_ok()); - let _publish_info = unwrap_factory2(result); - - // clean up - let result = api.unpublish_bot(robot_info.header.id.clone()).await; - //assert!(result.is_ok()); - let _robot = unwrap_factory2(result); - - let result = api.delete_robot(robot_info.header.id).await; - //assert!(result.is_ok()); - let _robot = unwrap_factory2(result); - Ok(()) -} - -#[cfg(feature = "robocraft2")] -#[tokio::test] -async fn robocraft2_factory_my_bots() -> Result<(), ()> { - let api = robocraft2::FactoryAPI::with_auth(Box::new(robocraft2::PortalTokenProvider::with_username("FJAPIC00L", "P4$$w0rd").await.unwrap())); - let result = api.my_robots().await; - assert!(result.is_ok()); - let robo_info = unwrap_factory2(result); - assert_ne!(robo_info.vehicles.len(), 0); - for robot in &robo_info.vehicles { - assert_ne!(robot.name, ""); - assert_ne!(robot.creator_id, ""); - assert_ne!(robot.creator_id, ""); - assert_ne!(robot.image, ""); - println!("My bot `{}`", robot.to_string()); - //println!("my vehicle {}", serde_json::to_string_pretty(&robot).unwrap()); - } - Ok(()) -} - -#[cfg(feature = "robocraft2")] -#[tokio::test] -async fn robocraft2_factory_my_published_bots() -> Result<(), ()> { - let api = robocraft2::FactoryAPI::with_auth(Box::new(robocraft2::PortalTokenProvider::with_username("FJAPIC00L", "P4$$w0rd").await.unwrap())); - let result = api.my_published_robots().await; - assert!(result.is_ok()); - let robo_info = unwrap_factory2(result); - //assert_ne!(robo_info.vehicles.len(), 0); - for robot in &robo_info.vehicles { - assert_ne!(robot.name, ""); - assert_ne!(robot.creator_id, ""); - assert_ne!(robot.creator_id, ""); - assert_ne!(robot.image, ""); - println!("My pub bot `{}`", robot.to_string()); - //println!("pub vehicle {}", serde_json::to_string_pretty(&robot).unwrap()); - } - Ok(()) -} - -#[cfg(feature = "robocraft2")] -//#[tokio::test] -async fn robocraft2_factory_bot() -> Result<(), ()> { - let api = robocraft2::FactoryAPI::with_auth(Box::new(robocraft2::PortalTokenProvider::with_username("FJAPIC00L", "P4$$w0rd").await.unwrap())); - let result = api.get("08dab2c9-7a72-4ec4-843c-154fe8768e91".to_owned()).await; - //assert!(result.is_ok()); - let robot = unwrap_factory2(result); - assert_ne!(robot.header.name, ""); - assert_ne!(robot.header.creator_id, ""); - assert_ne!(robot.header.creator_id, ""); - //assert_ne!(robot.header.image, ""); - //assert_ne!(robot.description, ""); - assert_ne!(robot.data, ""); - println!("robot {}", serde_json::to_string_pretty(&robot).unwrap()); - Ok(()) -} - -#[cfg(feature = "robocraft2")] -//#[tokio::test] -async fn robocraft2_factory_delete_bot() -> Result<(), ()> { - let api = robocraft2::FactoryAPI::with_auth(Box::new(robocraft2::PortalTokenProvider::with_username("FJAPIC00L", "P4$$w0rd").await.unwrap())); - let result = api.delete_robot("08dab2d2-dcae-4f52-8a77-bbce7cf10124".to_owned()).await; - //assert!(result.is_ok()); - let _robot = unwrap_factory2(result); - Ok(()) -} - -#[cfg(feature = "robocraft2")] -//#[tokio::test] -async fn robocraft2_factory_unpublish_bot() -> Result<(), ()> { - let api = robocraft2::FactoryAPI::with_auth(Box::new(robocraft2::PortalTokenProvider::with_username("FJAPIC00L", "P4$$w0rd").await.unwrap())); - let result = api.unpublish_bot("08dab2d3-2a68-48c6-8fd6-a59a663336ca".to_owned()).await; - //assert!(result.is_ok()); - let _robot = unwrap_factory2(result); - Ok(()) -} - -fn unwrap_factory2(result: Result) -> T { - match result { - Ok(t) => t, - Err(e) => { - //println!("FactoryError: {}", e); - panic!("CRF2 Error: {}", e); - } - } -}