405 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			405 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| use assert_cmd::prelude::*; // Add methods on commands
 | |
| use predicates::prelude::*;
 | |
| use std::{
 | |
|     io::Read,
 | |
|     process::{Child, Command},
 | |
| };
 | |
| use tempdir::TempDir; // Used for writing assertions
 | |
| 
 | |
| use shared::datatypes::Secret;
 | |
| 
 | |
| #[test]
 | |
| fn test_help_of_command_for_breaking_changes() {
 | |
|     let output = test_bin::get_test_bin("pslink")
 | |
|         .output()
 | |
|         .expect("Failed to start pslink");
 | |
|     assert!(String::from_utf8_lossy(&output.stdout).contains("USAGE"));
 | |
| 
 | |
|     let output = test_bin::get_test_bin("pslink")
 | |
|         .args(&["--help"])
 | |
|         .output()
 | |
|         .expect("Failed to start pslink");
 | |
|     let outstring = String::from_utf8_lossy(&output.stdout);
 | |
| 
 | |
|     let args = &[
 | |
|         "USAGE",
 | |
|         "-h",
 | |
|         "--help",
 | |
|         "-b",
 | |
|         "-e",
 | |
|         "-i",
 | |
|         "-p",
 | |
|         "-t",
 | |
|         "-u",
 | |
|         "runserver",
 | |
|         "create-admin",
 | |
|         "generate-env",
 | |
|         "migrate-database",
 | |
|         "help",
 | |
|     ];
 | |
| 
 | |
|     for s in args {
 | |
|         assert!(
 | |
|             outstring.contains(s),
 | |
|             "{} was not found in the help - this is a breaking change",
 | |
|             s
 | |
|         );
 | |
|     }
 | |
| }
 | |
| 
 | |
| #[test]
 | |
| fn test_generate_env() {
 | |
|     use std::io::BufRead;
 | |
|     let tmp_dir = tempdir::TempDir::new("pslink_test_env").expect("create temp dir");
 | |
|     let output = test_bin::get_test_bin("pslink")
 | |
|         .args(&["generate-env", "--secret", "abcdefghijklmnopqrstuvw"])
 | |
|         .current_dir(&tmp_dir)
 | |
|         .output()
 | |
|         .expect("Failed to start pslink");
 | |
|     let envfile = tmp_dir.path().join(".env");
 | |
|     let dbfile = tmp_dir.path().join("links.db");
 | |
|     println!("{}", envfile.display());
 | |
|     println!("{}", dbfile.display());
 | |
|     println!("{}", String::from_utf8_lossy(&output.stdout));
 | |
|     assert!(envfile.exists(), "No .env-file was created!");
 | |
|     assert!(dbfile.exists(), "No database-file was created!");
 | |
| 
 | |
|     let envfile = std::fs::File::open(envfile).unwrap();
 | |
|     let envcontent: Vec<Result<String, _>> = std::io::BufReader::new(envfile).lines().collect();
 | |
|     assert!(
 | |
|         envcontent
 | |
|             .iter()
 | |
|             .any(|s| s.as_ref().unwrap().starts_with("PSLINK_PORT=")),
 | |
|         "Failed to find PSLINK_PORT in the generated .env file."
 | |
|     );
 | |
|     assert!(
 | |
|         envcontent
 | |
|             .iter()
 | |
|             .any(|s| s.as_ref().unwrap().starts_with("PSLINK_SECRET=")),
 | |
|         "Failed to find PSLINK_SECRET in the generated .env file."
 | |
|     );
 | |
|     assert!(
 | |
|         !envcontent.iter().any(|s| {
 | |
|             let r = s.as_ref().unwrap().contains("***SECRET***");
 | |
|             r
 | |
|         }),
 | |
|         "It seems that a censored secret was used in the .env file."
 | |
|     );
 | |
|     assert!(
 | |
|         envcontent.iter().any(|s| {
 | |
|             let r = s.as_ref().unwrap().contains("abcdefghijklmnopqrstuvw");
 | |
|             r
 | |
|         }),
 | |
|         "The secret has not made it into the .env file!"
 | |
|     );
 | |
|     let output = test_bin::get_test_bin("pslink")
 | |
|         .args(&["generate-env"])
 | |
|         .current_dir(&tmp_dir)
 | |
|         .output()
 | |
|         .expect("Failed to start pslink");
 | |
|     let second_out = String::from_utf8_lossy(&output.stdout);
 | |
|     assert!(!second_out.contains("secret"));
 | |
| }
 | |
| 
 | |
| #[actix_rt::test]
 | |
| async fn test_migrate_database() {
 | |
|     use std::io::Write;
 | |
|     #[derive(serde::Serialize, Debug)]
 | |
|     pub struct Count {
 | |
|         pub number: i32,
 | |
|     }
 | |
| 
 | |
|     let tmp_dir = tempdir::TempDir::new("pslink_test_env").expect("create temp dir");
 | |
|     // generate .env file
 | |
|     let _output = test_bin::get_test_bin("pslink")
 | |
|         .args(&["generate-env"])
 | |
|         .current_dir(&tmp_dir)
 | |
|         .output()
 | |
|         .expect("Failed generate .env");
 | |
| 
 | |
|     // migrate the database
 | |
|     let output = test_bin::get_test_bin("pslink")
 | |
|         .args(&["migrate-database"])
 | |
|         .current_dir(&tmp_dir)
 | |
|         .output()
 | |
|         .expect("Failed to migrate the database");
 | |
|     println!("{}", String::from_utf8_lossy(&output.stdout));
 | |
| 
 | |
|     // check if the users table exists by counting the number of admins.
 | |
|     let db_pool = sqlx::pool::Pool::<sqlx::sqlite::Sqlite>::connect(
 | |
|         &tmp_dir.path().join("links.db").display().to_string(),
 | |
|     )
 | |
|     .await
 | |
|     .expect("Error: Failed to connect to database!");
 | |
|     let num = sqlx::query_as!(Count, "select count(*) as number from users where role = 2")
 | |
|         .fetch_one(&db_pool)
 | |
|         .await
 | |
|         .unwrap();
 | |
|     // initially no admin is present
 | |
|     assert_eq!(num.number, 0, "Failed to create the database!");
 | |
| 
 | |
|     // create a new admin
 | |
|     let mut input = test_bin::get_test_bin("pslink")
 | |
|         .args(&["create-admin"])
 | |
|         .current_dir(&tmp_dir)
 | |
|         .stdin(std::process::Stdio::piped())
 | |
|         .stdout(std::process::Stdio::piped())
 | |
|         .spawn()
 | |
|         .expect("Failed to migrate the database");
 | |
|     let mut procin = input.stdin.take().unwrap();
 | |
| 
 | |
|     procin.write_all(b"test\n").unwrap();
 | |
|     procin.write_all(b"test@mail.test\n").unwrap();
 | |
|     procin.write_all(b"testpw\n").unwrap();
 | |
| 
 | |
|     let r = input.wait().unwrap();
 | |
|     println!("Exitstatus is: {}", r);
 | |
| 
 | |
|     println!("{}", String::from_utf8_lossy(&output.stdout));
 | |
|     let num = sqlx::query_as!(Count, "select count(*) as number from users where role = 2")
 | |
|         .fetch_one(&db_pool)
 | |
|         .await
 | |
|         .unwrap();
 | |
|     // now 1 admin is there
 | |
|     assert_eq!(num.number, 1, "Failed to create an admin!");
 | |
| }
 | |
| 
 | |
| struct RunningServer {
 | |
|     server: Child,
 | |
|     port: i32,
 | |
|     dir: TempDir,
 | |
| }
 | |
| 
 | |
| impl Drop for RunningServer {
 | |
|     fn drop(&mut self) {
 | |
|         self.server.kill().unwrap();
 | |
|     }
 | |
| }
 | |
| 
 | |
| async fn run_server() -> RunningServer {
 | |
|     use std::io::Write;
 | |
| 
 | |
|     use rand::thread_rng;
 | |
|     use rand::Rng;
 | |
|     let mut rng = thread_rng();
 | |
|     let port = rng.gen_range(12000..20000);
 | |
| 
 | |
|     #[derive(serde::Serialize, Debug)]
 | |
|     pub struct Count {
 | |
|         pub number: i32,
 | |
|     }
 | |
|     let tmp_dir = tempdir::TempDir::new("pslink_test_env").expect("create temp dir");
 | |
|     // generate .env file
 | |
|     let _output = Command::cargo_bin("pslink")
 | |
|         .expect("Failed to get binary executable")
 | |
|         .args(&[
 | |
|             "generate-env",
 | |
|             "--secret",
 | |
|             "abcdefghijklmnopqrstuvw",
 | |
|             "--port",
 | |
|             &port.to_string(),
 | |
|         ])
 | |
|         .current_dir(&tmp_dir)
 | |
|         .output()
 | |
|         .expect("Failed generate .env");
 | |
|     // migrate the database
 | |
|     let output = Command::cargo_bin("pslink")
 | |
|         .unwrap()
 | |
|         .args(&["migrate-database"])
 | |
|         .current_dir(&tmp_dir)
 | |
|         .output()
 | |
|         .expect("Failed to migrate the database");
 | |
| 
 | |
|     // create a database connection.
 | |
|     let db_pool = sqlx::pool::Pool::<sqlx::sqlite::Sqlite>::connect(
 | |
|         &tmp_dir.path().join("links.db").display().to_string(),
 | |
|     )
 | |
|     .await
 | |
|     .expect("Error: Failed to connect to database!"); // create a new admin
 | |
|     let mut input = Command::cargo_bin("pslink")
 | |
|         .unwrap()
 | |
|         .args(&["create-admin"])
 | |
|         .current_dir(&tmp_dir)
 | |
|         .stdin(std::process::Stdio::piped())
 | |
|         .stdout(std::process::Stdio::piped())
 | |
|         .spawn()
 | |
|         .expect("Failed to migrate the database");
 | |
|     let mut procin = input.stdin.take().unwrap();
 | |
| 
 | |
|     procin.write_all(b"test\n").unwrap();
 | |
|     procin.write_all(b"test@mail.test\n").unwrap();
 | |
|     procin.write_all(b"testpw\n").unwrap();
 | |
| 
 | |
|     let r = input.wait().unwrap();
 | |
|     println!("Exitstatus is: {}", r);
 | |
| 
 | |
|     println!("{}", String::from_utf8_lossy(&output.stdout));
 | |
|     let num = sqlx::query_as!(Count, "select count(*) as number from users where role = 2")
 | |
|         .fetch_one(&db_pool)
 | |
|         .await
 | |
|         .unwrap();
 | |
|     // now 1 admin is there
 | |
|     assert_eq!(
 | |
|         num.number, 1,
 | |
|         "Failed to create an admin! See previous tests!"
 | |
|     );
 | |
| 
 | |
|     let mut server = Command::cargo_bin("pslink")
 | |
|         .unwrap()
 | |
|         .args(&["runserver"])
 | |
|         .current_dir(&tmp_dir)
 | |
|         .stdout(std::process::Stdio::piped())
 | |
|         .spawn()
 | |
|         .unwrap();
 | |
| 
 | |
|     // Wait until the server signals it is up and running.
 | |
|     let mut sout = server.stdout.take().unwrap();
 | |
|     let mut buffer = [0; 15];
 | |
|     println!("Running the webserver for testing #############");
 | |
|     loop {
 | |
|         let num = sout.read(&mut buffer[..]).unwrap();
 | |
|         println!("{}", num);
 | |
|         let t = std::str::from_utf8(&buffer).unwrap();
 | |
|         println!("{:?}", std::str::from_utf8(&buffer));
 | |
|         if num > 0 && t.contains("/app") {
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     RunningServer {
 | |
|         server,
 | |
|         port,
 | |
|         dir: tmp_dir,
 | |
|     }
 | |
| }
 | |
| 
 | |
| #[actix_rt::test]
 | |
| async fn test_web_paths() {
 | |
|     let mut server = run_server().await;
 | |
| 
 | |
|     // We need to bring in `reqwest`
 | |
|     // to perform HTTP requests against our application.
 | |
|     let client = reqwest::Client::builder()
 | |
|         .cookie_store(true)
 | |
|         .redirect(reqwest::redirect::Policy::none())
 | |
|         .build()
 | |
|         .unwrap();
 | |
| 
 | |
|     let base_url = "http://localhost:".to_string() + &server.port.to_string() + "/";
 | |
|     println!("{}", base_url);
 | |
| 
 | |
|     // Act
 | |
|     let response = client
 | |
|         .get(&base_url.clone())
 | |
|         .send()
 | |
|         .await
 | |
|         .expect("Failed to execute request.");
 | |
| 
 | |
|     // // The basic redirection is working!
 | |
|     // assert!(response.status().is_redirection());
 | |
|     // let location = response.headers().get("location").unwrap();
 | |
|     // assert!(location.to_str().unwrap().contains("github"));
 | |
| 
 | |
|     // let login_url = base_url.clone() + "/admin/login/";
 | |
|     // // Act
 | |
|     // let response = client
 | |
|     //     .get(login_url.clone())
 | |
|     //     .send()
 | |
|     //     .await
 | |
|     //     .expect("Failed to execute request.");
 | |
| 
 | |
|     // // The Loginpage is reachable and contains a password field!
 | |
|     // assert!(response.status().is_success());
 | |
|     // let content = response.text().await.unwrap();
 | |
|     // assert!(
 | |
|     //     content.contains(r#"<input type="password"#),
 | |
|     //     "No password field was found!"
 | |
|     // );
 | |
| 
 | |
|     // // Act
 | |
|     // let formdata = &[("username", "test"), ("password", "testpw")];
 | |
|     // let response = client
 | |
|     //     .post(login_url)
 | |
|     //     .form(formdata)
 | |
|     //     .send()
 | |
|     //     .await
 | |
|     //     .expect("Failed to execute request.");
 | |
| 
 | |
|     // // It is possible to login
 | |
|     // assert!(response.status().is_redirection());
 | |
|     // let location = response.headers().get("location").unwrap();
 | |
|     // assert_eq!("/admin/index/", location.to_str().unwrap());
 | |
|     // assert!(
 | |
|     //     response.headers().get("set-cookie").is_some(),
 | |
|     //     "A auth cookie is not set even though authentication succeeds"
 | |
|     // );
 | |
| 
 | |
|     // // After login this should return a redirect
 | |
|     // let response = client
 | |
|     //     .get("http://localhost:8080/admin/login/")
 | |
|     //     .send()
 | |
|     //     .await
 | |
|     //     .expect("Failed to execute request.");
 | |
| 
 | |
|     // // The Loginpage redirects to link index when logged in
 | |
|     // assert!(
 | |
|     //     response.status().is_redirection(),
 | |
|     //     "/admin/login/ is not redirecting correctly when logged in!"
 | |
|     // );
 | |
|     // let location = response.headers().get("location").unwrap();
 | |
|     // assert_eq!("/admin/index/", location.to_str().unwrap());
 | |
| 
 | |
|     // // After login this should return a redirect
 | |
|     // let response = client
 | |
|     //     .get("http://localhost:8080/admin/index/")
 | |
|     //     .send()
 | |
|     //     .await
 | |
|     //     .expect("Failed to execute request.");
 | |
| 
 | |
|     // // The Loginpage redirects to link index when logged in
 | |
|     // assert!(
 | |
|     //     response.status().is_success(),
 | |
|     //     "Could not access /admin/index/"
 | |
|     // );
 | |
|     // let content = response.text().await.unwrap();
 | |
|     // assert!(
 | |
|     //     content.contains(r#"<a href="/admin/logout/">"#),
 | |
|     //     "No Logout Button was found on /admin/index/!"
 | |
|     // );
 | |
| 
 | |
|     // // Act title=haupt&target=http%3A%2F%2Fdas.geht%2Fjetzt%2F&code=tpuah
 | |
|     // let formdata = &[
 | |
|     //     ("title", "haupt"),
 | |
|     //     ("target", "https://das.geht/jetzt/"),
 | |
|     //     ("code", "tpuah"),
 | |
|     // ];
 | |
|     // let response = client
 | |
|     //     .post("http://localhost:8080/admin/submit/")
 | |
|     //     .form(formdata)
 | |
|     //     .send()
 | |
|     //     .await
 | |
|     //     .expect("Failed to execute request.");
 | |
| 
 | |
|     // // It is possible to login
 | |
|     // assert!(response.status().is_redirection());
 | |
|     // let location = response.headers().get("location").unwrap();
 | |
|     // assert_eq!("/admin/view/link/tpuah", location.to_str().unwrap());
 | |
| 
 | |
|     // // Act
 | |
|     // let response = client
 | |
|     //     .get("http://localhost:8080/tpuah")
 | |
|     //     .send()
 | |
|     //     .await
 | |
|     //     .expect("Failed to execute request.");
 | |
| 
 | |
|     // // The basic redirection is working!
 | |
|     // assert!(response.status().is_redirection());
 | |
|     // let location = response.headers().get("location").unwrap();
 | |
|     // assert!(location
 | |
|     //     .to_str()
 | |
|     //     .unwrap()
 | |
|     //     .contains("https://das.geht/jetzt/"));
 | |
| 
 | |
|     drop(server);
 | |
| }
 |