use std::fs::File; use std::io::Read; use rocket::http::{ContentType, Status}; use rocket::local::blocking::Client; use rocket::serde::json::{json, Value}; use rocket::{Build, Config, Rocket}; use crate::utils::build_json_response; use super::init_rocket; // define authentication mode const DISABLE_AUTH: bool = true; /// Wrapper around `init_rocket()` to simplify the process of invoking the application with the desired authentication status. This is particularly useful for testing purposes. fn init_test_rocket(disable_auth: bool) -> Rocket { // set authentication based on provided `disable_auth` value Config::figment().merge(("disable_auth", disable_auth)); init_rocket() } // helper function to test correct retrieval and content of a file fn test_query_file(path: &str, file: T, status: Status) where T: Into>, { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).unwrap(); let response = client.get(path).dispatch(); assert_eq!(response.status(), status); let body_data = response.into_bytes(); if let Some(filename) = file.into() { let expected_data = read_file_content(filename); assert!(body_data.map_or(false, |s| s == expected_data)); } } // helper function to return the content of a file, given a path fn read_file_content(path: &str) -> Vec { let mut fp = File::open(&path).expect(&format!("Can't open {}", path)); let mut file_content = vec![]; fp.read_to_end(&mut file_content) .expect(&format!("Reading {} failed.", path)); file_content } // WEB PAGE ROUTES #[test] fn index_html() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); let body = response.into_string().unwrap(); assert!(body.contains("/peers")); assert!(body.contains("/profile")); assert!(body.contains("/private")); assert!(body.contains("/status")); assert!(body.contains("/help")); assert!(body.contains("/settings")); } #[test] fn help_html() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/help").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); let body = response.into_string().unwrap(); assert!(body.contains("Help")); } #[test] fn login_html() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/login").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); let body = response.into_string().unwrap(); assert!(body.contains("Login")); } #[test] fn logout_html() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/logout").dispatch(); // check for 303 status (redirect to "/login") assert_eq!(response.status(), Status::SeeOther); assert_eq!(response.content_type(), None); } #[test] fn power_html() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/power").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); let body = response.into_string().unwrap(); assert!(body.contains("Shutdown Device")); } /* NOTE: these tests are comment-out for the moment, due to the fact that they invoke system commands (resulting in a `sudo` password request during test execution). see if we can find a way to test the results without triggering the shutdown or restart. #[test] fn reboot() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/power/reboot").dispatch(); // check for redirect assert_eq!(response.status(), Status::SeeOther); } #[test] fn shutdown() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/power/shutdown").dispatch(); // check for redirect assert_eq!(response.status(), Status::SeeOther); } */ // SCUTTLEBUTT ROUTES #[test] fn block() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client .post("/scuttlebutt/block") .header(ContentType::Form) .body("key=HEqy940T6uB+T+d9Jaa58aNfRzLx9eRWqkZljBmnkmk=.ed25519") .dispatch(); assert_eq!(response.status(), Status::SeeOther); } #[test] fn blocks_html() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/scuttlebutt/blocks").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); let body = response.into_string().unwrap(); assert!(body.contains("Blocks")); } #[test] fn follow() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client .post("/scuttlebutt/follow") .header(ContentType::Form) .body("key=@HEqy940T6uB+T+d9Jaa58aNfRzLx9eRWqkZljBmnkmk=.ed25519") .dispatch(); // ensure we redirect (303) assert_eq!(response.status(), Status::SeeOther); } #[test] fn follows_html() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/scuttlebutt/follows").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); let body = response.into_string().unwrap(); assert!(body.contains("Follows")); } #[test] fn followers_html() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/scuttlebutt/followers").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); let body = response.into_string().unwrap(); assert!(body.contains("Followers")); } #[test] fn friends_html() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/scuttlebutt/friends").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); let body = response.into_string().unwrap(); assert!(body.contains("Friends")); } #[test] fn peers_html() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/scuttlebutt/peers").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); let body = response.into_string().unwrap(); assert!(body.contains("Scuttlebutt Peers")); } #[test] fn private_html() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/scuttlebutt/private").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); let body = response.into_string().unwrap(); assert!(body.contains("Private Messages")); } #[test] fn profile_html() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/scuttlebutt/profile").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); let body = response.into_string().unwrap(); assert!(body.contains("Profile")); } #[test] fn publish_post() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client .post("/scuttlebutt/publish") .header(ContentType::Form) .body("text='golden ripples in the meshwork'") .dispatch(); assert_eq!(response.status(), Status::SeeOther); } #[test] fn unfollow() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client .post("/scuttlebutt/unfollow") .header(ContentType::Form) .body("key=@HEqy940T6uB+T+d9Jaa58aNfRzLx9eRWqkZljBmnkmk=.ed25519") .dispatch(); assert_eq!(response.status(), Status::SeeOther); } // ADMIN SETTINGS ROUTES #[test] fn admin_settings_menu_html() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/settings/admin").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); let body = response.into_string().unwrap(); assert!(body.contains("Administrator Settings")); assert!(body.contains("Change Password")); assert!(body.contains("Configure Admin")); } #[test] fn add_admin_html() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/settings/admin/add").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); let body = response.into_string().unwrap(); assert!(body.contains("Add Admin")); assert!(body.contains("SSB ID")); assert!(body.contains("Add")); assert!(body.contains("Cancel")); } #[test] fn add_admin() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client .post("/settings/admin/add") .header(ContentType::Form) .body("ssb_id=@HEqy940T6uB+T+d9Jaa58aNfRzLx9eRWqkZljBmnkmk=.ed25519") .dispatch(); // check for redirect assert_eq!(response.status(), Status::SeeOther); } #[test] fn change_password_html() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/settings/admin/change_password").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); let body = response.into_string().unwrap(); assert!(body.contains("Change Password")); assert!(body.contains("Current password")); assert!(body.contains("New password")); assert!(body.contains("New password duplicate")); assert!(body.contains("Save")); } #[test] fn configure_admin_html() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/settings/admin/configure").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); let body = response.into_string().unwrap(); assert!(body.contains("Configure Admin")); assert!(body.contains("Current Admins")); assert!(body.contains("Add Admin")); } #[test] fn forgot_password_html() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/settings/admin/forgot_password").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); let body = response.into_string().unwrap(); assert!(body.contains("Send Password Reset")); } // NETWORK SETTINGS ROUTES #[test] fn network_settings_menu_html() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/settings/network").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); let body = response.into_string().unwrap(); assert!(body.contains("Network Configuration")); } #[test] fn deploy_ap() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/settings/network/ap/activate").dispatch(); // check for 303 status (redirect) assert_eq!(response.status(), Status::SeeOther); assert_eq!(response.content_type(), None); } #[test] fn dns_settings_html() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/settings/network/dns").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); let body = response.into_string().unwrap(); assert!(body.contains("Configure DNS")); assert!(body.contains("External Domain (optional)")); assert!(body.contains("Enable Dynamic DNS")); assert!(body.contains("Dynamic DNS Domain")); assert!(body.contains("Save")); } #[test] fn list_aps_html() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/settings/network/wifi").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); let body = response.into_string().unwrap(); assert!(body.contains("WiFi Networks")); assert!(body.contains("No saved or available networks found.")); } // TODO: needs further testing once template has been refactored #[test] fn ap_details_html() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/settings/network/wifi?ssid=Home").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); //let body = response.into_string().unwrap(); //assert!(body.contains("Network not found")); } #[test] fn deploy_client() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/settings/network/wifi/activate").dispatch(); // check for 303 status (redirect) assert_eq!(response.status(), Status::SeeOther); assert_eq!(response.content_type(), None); } #[test] fn add_ap_html() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/settings/network/wifi/add").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); let body = response.into_string().unwrap(); assert!(body.contains("Add WiFi Network")); assert!(body.contains("SSID")); assert!(body.contains("Password")); assert!(body.contains("Add")); assert!(body.contains("Cancel")); } #[test] fn add_ap_ssid_html() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client .get("/settings/network/wifi/add?ssid=Home") .dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); let body = response.into_string().unwrap(); assert!(body.contains("Add WiFi Network")); assert!(body.contains("Home")); assert!(body.contains("Password")); assert!(body.contains("Add")); assert!(body.contains("Cancel")); } #[test] fn add_credentials() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client .post("/settings/network/wifi/add") .header(ContentType::Form) .body("ssid=Home&pass=Password") .dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); } #[test] fn forget_wifi() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client .post("/settings/network/wifi/forget") .header(ContentType::Form) .body("ssid=Home") .dispatch(); assert_eq!(response.status(), Status::SeeOther); assert_eq!(response.content_type(), None); } #[test] fn modify_password() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client .post("/settings/network/wifi/modify") .header(ContentType::Form) .body("ssid=Home&pass=Password") .dispatch(); assert_eq!(response.status(), Status::SeeOther); assert_eq!(response.content_type(), None); } #[test] fn data_usage_html() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/settings/network/wifi/usage").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); let body = response.into_string().unwrap(); assert!(body.contains("Network Data Usage")); assert!(body.contains("WARNING THRESHOLD")); assert!(body.contains("Update")); assert!(body.contains("Cancel")); } // SCUTTLEBUTT SETTINGS HTML ROUTES #[test] fn scuttlebutt_settings_menu_html() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/settings/scuttlebutt").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); let body = response.into_string().unwrap(); assert!(body.contains("Scuttlebutt Settings")); assert!(body.contains("Set Network Key")); assert!(body.contains("Set Replication Hops")); assert!(body.contains("Remove Blocked Feeds")); assert!(body.contains("Set Database Directory")); assert!(body.contains("Check Filesystem")); assert!(body.contains("Repair Filesystem")); assert!(body.contains("Restart Sbot")); } // STATUS HTML ROUTES #[test] fn status_html() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/status").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); let body = response.into_string().unwrap(); assert!(body.contains("Device Status")); assert!(body.contains("Networking")); assert!(body.contains("Display")); assert!(body.contains("Statistics")); } #[test] fn network_status_html() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/status/network").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); let body = response.into_string().unwrap(); assert!(body.contains("Network Status")); assert!(body.contains("Mode")); assert!(body.contains("SSID")); assert!(body.contains("IP")); assert!(body.contains("DOWNLOAD")); assert!(body.contains("UPLOAD")); } // JSON API ROUTES #[test] fn activate_ap() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client .post("/api/v1/network/activate_ap") .header(ContentType::JSON) .dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::JSON)); } #[test] fn activate_client() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client .post("/api/v1/network/activate_client") .header(ContentType::JSON) .dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::JSON)); } #[test] fn return_ip() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client .get("/api/v1/network/ip") .header(ContentType::JSON) .dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::JSON)); let body = response.into_string().unwrap(); assert!(body.contains("wlan0")); assert!(body.contains("ap0")); } #[test] fn return_rssi() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client .get("/api/v1/network/rssi") .header(ContentType::JSON) .dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::JSON)); let body = response.into_string().unwrap(); assert!(body.contains("Not currently connected to an access point.")); } #[test] fn return_ssid() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client .get("/api/v1/network/ssid") .header(ContentType::JSON) .dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::JSON)); let body = response.into_string().unwrap(); assert!(body.contains("Not currently connected to an access point.")); } #[test] fn return_state() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client .get("/api/v1/network/state") .header(ContentType::JSON) .dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::JSON)); let body = response.into_string().unwrap(); assert!(body.contains("ap0")); assert!(body.contains("wlan0")); assert!(body.contains("unavailable")); } #[test] fn return_status() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client .get("/api/v1/network/status") .header(ContentType::JSON) .dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::JSON)); let body = response.into_string().unwrap(); assert!(body.contains("Not currently connected to an access point.")); } #[test] fn scan_networks() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client .get("/api/v1/network/wifi") .header(ContentType::JSON) .dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::JSON)); let body = response.into_string().unwrap(); assert!(body.contains("Unable to scan for networks. Interface may be deactivated.")); } #[test] fn add_wifi() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client .post("/api/v1/network/wifi") .header(ContentType::JSON) .body(r#"{ "ssid": "Home", "pass": "Password" }"#) .dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::JSON)); let body = response.into_string().unwrap(); assert!(body.contains("Failed to add WiFi credentials.")); } #[test] fn remove_wifi() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client .post("/api/v1/network/wifi/forget") .header(ContentType::JSON) .body(r#"{ "ssid": "Home" }"#) .dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::JSON)); let body = response.into_string().unwrap(); assert!(body.contains("Failed to remove WiFi network credentials.")); } #[test] fn new_password() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client .post("/api/v1/network/wifi/modify") .header(ContentType::JSON) .body(r#"{ "ssid": "Home", "pass": "Password" }"#) .dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::JSON)); let body = response.into_string().unwrap(); assert!(body.contains("Failed to update WiFi password.")); } #[test] fn ping_pong() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client .get("/api/v1/ping") .header(ContentType::JSON) .dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::JSON)); let body = response.into_string().unwrap(); assert!(body.contains("pong!")); } // HELPER FUNCTION TESTS #[test] fn test_build_json_response() { let status = "success".to_string(); let data = json!("WiFi credentials added.".to_string()); let j: Value = build_json_response(status, Some(data), None); assert_eq!(j["status"], "success"); assert_eq!(j["data"], "WiFi credentials added."); assert_eq!(j["msg"], json!(null)); } // FILE TESTS #[test] fn nested_file() { test_query_file( "/images/placeholder.txt", "static/images/placeholder.txt", Status::Ok, ); test_query_file( "/images/placeholder.txt?v=1", "static/images/placeholder.txt", Status::Ok, ); test_query_file( "/images/placeholder.txt?v=1&a=b", "static/images/placeholder.txt", Status::Ok, ); } #[test] fn icon_file() { test_query_file( "/icons/peach-icon.png", "static/icons/peach-icon.png", Status::Ok, ); } #[test] fn invalid_path() { test_query_file("/thou_shalt_not_exist", None, Status::NotFound); test_query_file("/thou_shalt_not_exist", None, Status::NotFound); test_query_file("/thou/shalt/not/exist?a=b&c=d", None, Status::NotFound); } #[test] fn invalid_get_request() { let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).unwrap(); // try to get a path that doesn't exist let res = client .get("/message/99") .header(ContentType::JSON) .dispatch(); assert_eq!(res.status(), Status::NotFound); let body = res.into_string().unwrap(); assert!(body.contains("404: Page Not Found")); assert!(body.contains("No PeachCloud resource exists for this URL.")); }