moving the webserver out of the library

This commit is contained in:
Dietrich 2021-07-05 13:19:12 +02:00 committed by Franz Dietrich
parent 9bb6001adf
commit b08c1a3fc2
7 changed files with 537 additions and 435 deletions

93
Cargo.lock generated
View File

@ -491,6 +491,20 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
[[package]]
name = "assert_cmd"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d20831bd004dda4c7c372c19cdabff369f794a95e955b3f13fe460e3e1ae95f"
dependencies = [
"bstr",
"doc-comment",
"predicates",
"predicates-core",
"predicates-tree",
"wait-timeout",
]
[[package]] [[package]]
name = "async-trait" name = "async-trait"
version = "0.1.50" version = "0.1.50"
@ -675,7 +689,9 @@ version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90682c8d613ad3373e66de8c6411e0ae2ab2571e879d2efbf73558cc66f21279" checksum = "90682c8d613ad3373e66de8c6411e0ae2ab2571e879d2efbf73558cc66f21279"
dependencies = [ dependencies = [
"lazy_static",
"memchr", "memchr",
"regex-automata",
] ]
[[package]] [[package]]
@ -1068,6 +1084,12 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "difflib"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
[[package]] [[package]]
name = "digest" name = "digest"
version = "0.9.0" version = "0.9.0"
@ -1199,6 +1221,15 @@ dependencies = [
"miniz_oxide 0.4.4", "miniz_oxide 0.4.4",
] ]
[[package]]
name = "float-cmp"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1267f4ac4f343772758f7b1bdcbe767c218bbab93bb432acbf5162bbf85a6c4"
dependencies = [
"num-traits",
]
[[package]] [[package]]
name = "fluent" name = "fluent"
version = "0.14.4" version = "0.14.4"
@ -1952,6 +1983,15 @@ version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9"
[[package]]
name = "itertools"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf"
dependencies = [
"either",
]
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "0.4.7" version = "0.4.7"
@ -2282,6 +2322,12 @@ dependencies = [
"version_check 0.9.3", "version_check 0.9.3",
] ]
[[package]]
name = "normalize-line-endings"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
[[package]] [[package]]
name = "ntapi" name = "ntapi"
version = "0.3.6" version = "0.3.6"
@ -2642,6 +2688,36 @@ version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
[[package]]
name = "predicates"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6e46ca79eb4e21e2ec14430340c71250ab69332abf85521c95d3a8bc336aa76"
dependencies = [
"difflib",
"float-cmp",
"itertools",
"normalize-line-endings",
"predicates-core",
"regex",
]
[[package]]
name = "predicates-core"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57e35a3326b75e49aa85f5dc6ec15b41108cf5aee58eabb1f274dd18b73c2451"
[[package]]
name = "predicates-tree"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15f553275e5721409451eb85e15fd9a860a6e5ab4496eb215987502b5f5391f2"
dependencies = [
"predicates-core",
"treeline",
]
[[package]] [[package]]
name = "proc-macro-error" name = "proc-macro-error"
version = "1.0.4" version = "1.0.4"
@ -2707,6 +2783,7 @@ dependencies = [
"actix-web", "actix-web",
"actix-web-static-files", "actix-web-static-files",
"argonautica", "argonautica",
"assert_cmd",
"async-trait", "async-trait",
"chrono", "chrono",
"clap", "clap",
@ -2717,6 +2794,7 @@ dependencies = [
"image", "image",
"opentelemetry 0.14.0", "opentelemetry 0.14.0",
"opentelemetry-jaeger", "opentelemetry-jaeger",
"predicates",
"qrcode", "qrcode",
"rand 0.8.4", "rand 0.8.4",
"reqwest", "reqwest",
@ -4138,6 +4216,12 @@ dependencies = [
"tracing-serde", "tracing-serde",
] ]
[[package]]
name = "treeline"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41"
[[package]] [[package]]
name = "trust-dns-proto" name = "trust-dns-proto"
version = "0.19.7" version = "0.19.7"
@ -4397,6 +4481,15 @@ version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
[[package]]
name = "wait-timeout"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "walkdir" name = "walkdir"
version = "2.3.2" version = "2.3.2"

View File

@ -295,7 +295,7 @@ impl<'a> Urls<'a> {
/// ///
/// # Example /// # Example
/// ///
/// ```rust,no_run /// ```ignore
/// Urls::new(base_url).home() /// Urls::new(base_url).home()
/// ``` /// ```
pub fn new(base_url: impl Into<std::borrow::Cow<'a, Url>>) -> Self { pub fn new(base_url: impl Into<std::borrow::Cow<'a, Url>>) -> Self {
@ -308,7 +308,7 @@ impl<'a> Urls<'a> {
/// ///
/// # Example /// # Example
/// ///
/// ```rust,no_run /// ```ignore
/// pub fn admin_urls(self) -> page::admin::Urls<'a> { /// pub fn admin_urls(self) -> page::admin::Urls<'a> {
/// page::admin::Urls::new(self.base_url().add_path_part(ADMIN)) /// page::admin::Urls::new(self.base_url().add_path_part(ADMIN))
/// } /// }

View File

@ -62,6 +62,8 @@ actix-server = "1.0.4"
tempdir = "0.3" tempdir = "0.3"
test_bin = "0.3" test_bin = "0.3"
tokio = "0.2.25" tokio = "0.2.25"
assert_cmd = "1.0.7"
predicates = "2.0.0"
[dev-dependencies.reqwest] [dev-dependencies.reqwest]
features = ["cookies"] features = ["cookies"]

View File

@ -1,7 +1,12 @@
extern crate sqlx; extern crate sqlx;
mod cli; mod cli;
mod views;
use actix_files::Files;
use actix_identity::{CookieIdentityPolicy, IdentityService};
use actix_web::{web, App, HttpServer};
use fluent_templates::static_loader;
use pslink::ServerConfig; use pslink::ServerConfig;
use tracing::instrument; use tracing::instrument;
@ -9,6 +14,9 @@ use tracing::{subscriber::set_global_default, Subscriber};
use tracing_opentelemetry::OpenTelemetryLayer; use tracing_opentelemetry::OpenTelemetryLayer;
use tracing_subscriber::{layer::SubscriberExt, EnvFilter, Registry}; use tracing_subscriber::{layer::SubscriberExt, EnvFilter, Registry};
use tracing::{error, info};
use tracing_actix_web::TracingLogger;
/// Compose multiple layers into a `tracing`'s subscriber. /// Compose multiple layers into a `tracing`'s subscriber.
#[must_use] #[must_use]
pub fn get_subscriber(name: &str, env_filter: &str) -> impl Subscriber + Send + Sync { pub fn get_subscriber(name: &str, env_filter: &str) -> impl Subscriber + Send + Sync {
@ -47,7 +55,7 @@ async fn main() -> std::result::Result<(), std::io::Error> {
match cli::setup().await { match cli::setup().await {
Ok(Some(server_config)) => { Ok(Some(server_config)) => {
pslink::webservice(server_config) webservice(server_config)
.await .await
.map_err(|e| { .map_err(|e| {
println!("{:?}", e); println!("{:?}", e);
@ -68,3 +76,112 @@ async fn main() -> std::result::Result<(), std::io::Error> {
} }
} }
} }
// include the static files into the binary
include!(concat!(env!("OUT_DIR"), "/generated.rs"));
static_loader! {
static LOCALES = {
locales: "./locales",
fallback_language: "en",
};
}
/// Launch the pslink-webservice
///
/// # Errors
/// This produces a [`ServerError`] if:
/// * The server failed to bind to the designated port.
#[allow(
clippy::future_not_send,
clippy::too_many_lines,
unknown_lints,
clippy::unused_async
)]
pub async fn webservice(
server_config: ServerConfig,
) -> Result<actix_web::dev::Server, std::io::Error> {
let host_port = format!("{}:{}", &server_config.internal_ip, &server_config.port);
info!(
"Running on: {}://{}/app/",
&server_config.protocol, host_port
);
info!(
"If the public url is set up correctly it should be accessible via: {}://{}/app/",
&server_config.protocol, &server_config.public_url
);
let server = HttpServer::new(move || {
let generated = generate();
App::new()
.data(server_config.clone())
.wrap(TracingLogger)
.wrap(IdentityService::new(
CookieIdentityPolicy::new(&[0; 32])
.name("auth-cookie")
.secure(true),
))
.service(actix_web_static_files::ResourceFiles::new(
"/static", generated,
))
// directly go to the main page set the target with the environment variable.
.route("/", web::get().to(views::redirect_empty))
// admin block
.service(
web::scope("/admin")
.route("/logout/", web::to(views::logout))
.service(
web::scope("/download")
.route("/png/{redirect_id}", web::get().to(views::download_png)),
)
.service(
web::scope("/json")
.route("/list_links/", web::post().to(views::index_json))
.route("/get_language/", web::get().to(views::get_language))
.route("/change_language/", web::post().to(views::set_language))
.route(
"/create_link/",
web::post().to(views::process_create_link_json),
)
.route(
"/edit_link/",
web::post().to(views::process_update_link_json),
)
.route(
"/delete_link/",
web::post().to(views::process_delete_link_json),
)
.route("/list_users/", web::post().to(views::index_users_json))
.route(
"/create_user/",
web::post().to(views::process_create_user_json),
)
.route(
"/update_user/",
web::post().to(views::process_update_user_json),
)
.route("/update_privileges/", web::post().to(views::toggle_admin))
.route(
"/get_logged_user/",
web::post().to(views::get_logged_user_json),
)
.route("/login_user/", web::post().to(views::process_login_json)),
),
)
// Serve the Wasm App for the admin interface.
.service(
web::scope("/app")
.service(Files::new("/pkg", "./app/pkg"))
.default_service(web::get().to(views::wasm_app)),
)
// redirect to the url hidden behind the code
.route("/{redirect_id}", web::get().to(views::redirect))
})
.bind(host_port)
.map_err(|e| {
error!("Failed to bind to port!");
e
})?
.run();
Ok(server)
}

View File

@ -12,8 +12,8 @@ use fluent_langneg::{
}; };
use fluent_templates::LanguageIdentifier; use fluent_templates::LanguageIdentifier;
use image::{DynamicImage, ImageOutputFormat, Luma}; use image::{DynamicImage, ImageOutputFormat, Luma};
use pslink::queries::{authenticate, RoleGuard};
use qrcode::QrCode; use qrcode::QrCode;
use queries::{authenticate, RoleGuard};
use shared::{ use shared::{
apirequests::{ apirequests::{
general::{Message, Status}, general::{Message, Status},
@ -24,8 +24,8 @@ use shared::{
}; };
use tracing::{error, info, instrument, warn}; use tracing::{error, info, instrument, warn};
use crate::queries; use pslink::queries;
use crate::ServerError; use pslink::ServerError;
#[instrument] #[instrument]
fn redirect_builder(target: &str) -> HttpResponse { fn redirect_builder(target: &str) -> HttpResponse {

View File

@ -2,20 +2,14 @@ extern crate sqlx;
pub mod models; pub mod models;
pub mod queries; pub mod queries;
mod views;
use actix_files::Files;
use actix_identity::{CookieIdentityPolicy, IdentityService};
use actix_web::HttpResponse; use actix_web::HttpResponse;
use actix_web::{web, App, HttpServer};
use fluent_templates::static_loader;
use qrcode::types::QrError; use qrcode::types::QrError;
use shared::datatypes::Secret; use shared::datatypes::Secret;
use sqlx::{Pool, Sqlite}; use sqlx::{Pool, Sqlite};
use std::{fmt::Display, path::PathBuf, str::FromStr}; use std::{fmt::Display, path::PathBuf, str::FromStr};
use thiserror::Error; use thiserror::Error;
use tracing::{error, info}; use tracing::error;
use tracing_actix_web::TracingLogger;
/// The Error type that is returned by most function calls if anything failed. /// The Error type that is returned by most function calls if anything failed.
#[derive(Error, Debug)] #[derive(Error, Debug)]
@ -190,112 +184,3 @@ impl ServerConfig {
] ]
} }
} }
// include the static files into the binary
include!(concat!(env!("OUT_DIR"), "/generated.rs"));
static_loader! {
static LOCALES = {
locales: "./locales",
fallback_language: "en",
};
}
/// Launch the pslink-webservice
///
/// # Errors
/// This produces a [`ServerError`] if:
/// * The server failed to bind to the designated port.
#[allow(
clippy::future_not_send,
clippy::too_many_lines,
unknown_lints,
clippy::unused_async
)]
pub async fn webservice(
server_config: ServerConfig,
) -> Result<actix_web::dev::Server, std::io::Error> {
let host_port = format!("{}:{}", &server_config.internal_ip, &server_config.port);
info!(
"Running on: {}://{}/app/",
&server_config.protocol, host_port
);
info!(
"If the public url is set up correctly it should be accessible via: {}://{}/app/",
&server_config.protocol, &server_config.public_url
);
let server = HttpServer::new(move || {
let generated = generate();
App::new()
.data(server_config.clone())
.wrap(TracingLogger)
.wrap(IdentityService::new(
CookieIdentityPolicy::new(&[0; 32])
.name("auth-cookie")
.secure(true),
))
.service(actix_web_static_files::ResourceFiles::new(
"/static", generated,
))
// directly go to the main page set the target with the environment variable.
.route("/", web::get().to(views::redirect_empty))
// admin block
.service(
web::scope("/admin")
.route("/logout/", web::to(views::logout))
.service(
web::scope("/download")
.route("/png/{redirect_id}", web::get().to(views::download_png)),
)
.service(
web::scope("/json")
.route("/list_links/", web::post().to(views::index_json))
.route("/get_language/", web::get().to(views::get_language))
.route("/change_language/", web::post().to(views::set_language))
.route(
"/create_link/",
web::post().to(views::process_create_link_json),
)
.route(
"/edit_link/",
web::post().to(views::process_update_link_json),
)
.route(
"/delete_link/",
web::post().to(views::process_delete_link_json),
)
.route("/list_users/", web::post().to(views::index_users_json))
.route(
"/create_user/",
web::post().to(views::process_create_user_json),
)
.route(
"/update_user/",
web::post().to(views::process_update_user_json),
)
.route("/update_privileges/", web::post().to(views::toggle_admin))
.route(
"/get_logged_user/",
web::post().to(views::get_logged_user_json),
)
.route("/login_user/", web::post().to(views::process_login_json)),
),
)
// Serve the Wasm App for the admin interface.
.service(
web::scope("/app")
.service(Files::new("/pkg", "./app/pkg"))
.default_service(web::get().to(views::wasm_app)),
)
// redirect to the url hidden behind the code
.route("/{redirect_id}", web::get().to(views::redirect))
})
.bind(host_port)
.map_err(|e| {
error!("Failed to bind to port!");
e
})?
.run();
Ok(server)
}

View File

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