2021-07-12 14:34:23 +02:00
use assert_cmd ::prelude ::* ; // Add methods on commands
2021-07-13 14:20:11 +02:00
use reqwest ::header ::HeaderMap ;
2021-07-12 14:34:23 +02:00
use std ::{
2021-07-13 14:20:11 +02:00
collections ::HashMap ,
2021-07-12 14:34:23 +02:00
io ::Read ,
process ::{ Child , Command } ,
} ;
#[ 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 ,
}
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 ;
#[ derive(serde::Serialize, Debug) ]
pub struct Count {
pub number : i32 ,
}
2021-07-13 14:20:11 +02:00
let mut rng = thread_rng ( ) ;
let port = rng . gen_range ( 12000 .. 20000 ) ;
2021-07-12 14:34:23 +02:00
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 ;
}
}
2021-07-13 14:20:11 +02:00
RunningServer { server , port }
2021-07-12 14:34:23 +02:00
}
#[ actix_rt::test ]
async fn test_web_paths ( ) {
2021-07-13 14:20:11 +02:00
let server = run_server ( ) . await ;
2021-07-12 14:34:23 +02:00
// 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. " ) ;
2021-07-13 14:20:11 +02:00
// 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 app_url = base_url . clone ( ) + " app/ " ;
// Act
let response = client
. get ( & app_url . clone ( ) )
. send ( )
. await
. expect ( " Failed to execute request. " ) ;
println! ( " {:?} " , response ) ;
// The app page is reachable and contains the wasm file!
assert! ( response . status ( ) . is_success ( ) ) ;
let content = response . text ( ) . await . unwrap ( ) ;
assert! (
content . contains ( r # "init('/static/wasm/app_bg.wasm');"# ) ,
" The app page has unexpected content! "
) ;
// Act
let mut formdata = HashMap ::new ( ) ;
formdata . insert ( " username " , " test " ) ;
formdata . insert ( " password " , " testpw " ) ;
let response = client
. post ( & ( base_url . clone ( ) + " admin/json/login_user/ " ) )
. json ( & formdata )
. send ( )
. await
. expect ( " Failed to execute request. " ) ;
println! ( " Login response: \n {:?} " , response ) ;
// It is possible to login
assert! ( response . status ( ) . is_success ( ) ) ;
// Extract the cookie as it is not automatically saved for some reason.
let cookie = {
response
. headers ( )
. get ( " set-cookie " )
. expect ( " A auth cookie is not set even though authentication succeeds " )
. to_str ( )
. unwrap ( )
. split ( ';' )
. next ( )
. unwrap ( )
. to_string ( )
} ;
println! ( " {:?} " , cookie ) ;
assert! ( cookie . starts_with ( " auth-cookie= " ) ) ;
let content = response . text ( ) . await . unwrap ( ) ;
println! ( " Content: {:?} " , content ) ;
assert! ( content . contains ( r # ""id":1"# ) , " id missing in content " ) ;
let mut custom_headers = HeaderMap ::new ( ) ;
custom_headers . insert ( " content-type " , " application/json " . parse ( ) . unwrap ( ) ) ;
custom_headers . insert ( " Cookie " , cookie . parse ( ) . unwrap ( ) ) ;
// After login this should return an empty list
let query = client
. post ( & ( base_url . clone ( ) + " admin/json/list_links/ " ) )
. headers ( custom_headers . clone ( ) )
. body ( r # "{"filter":{"Code":{"sieve":""},"Description":{"sieve":""},"Target":{"sieve":""},"Author":{"sieve":""},"Statistics":{"sieve":""}},"order":null,"amount":20}"# ) . build ( ) . unwrap ( ) ;
println! ( " {:?} " , query ) ;
let response = client
. execute ( query )
. await
. expect ( " Failed to execute request. " ) ;
println! ( " List urls response: \n {:?} " , response ) ;
// Make sure the list was retrieved and the status codes are correct
assert! ( response . status ( ) . is_success ( ) ) ;
// Make sure that the content is an empty list as until now no links were created.
let content = response . text ( ) . await . unwrap ( ) ;
println! ( " Content: {:?} " , content ) ;
assert! ( content . contains ( r # "[]"# ) , " id missing in content " ) ;
// Create a link
let query = client
. post ( & ( base_url . clone ( ) + " admin/json/create_link/ " ) )
. headers ( custom_headers . clone ( ) )
. body ( r # "{"edit":"Create","id":null,"title":"ein testlink","target":"https://github.com/enaut/pslink","code":"test","author":0,"created_at":null}"# )
. build ( )
. unwrap ( ) ;
println! ( " {:?} " , query ) ;
let response = client
. execute ( query )
. await
. expect ( " Failed to execute request. " ) ;
println! ( " List urls response: \n {:?} " , response ) ;
// Make sure the status codes are correct
assert! ( response . status ( ) . is_success ( ) ) ;
// Make sure that the content is a success message
let content = response . text ( ) . await . unwrap ( ) ;
println! ( " Content: {:?} " , content ) ;
assert! (
content . contains ( r # ""Success":"# ) ,
" Make sure the link creation response contains Success "
) ;
// After inserting a link make sure the link is saved
let query = client
. post ( & ( base_url . clone ( ) + " admin/json/list_links/ " ) )
. headers ( custom_headers . clone ( ) )
. body ( r # "{"filter":{"Code":{"sieve":""},"Description":{"sieve":""},"Target":{"sieve":""},"Author":{"sieve":""},"Statistics":{"sieve":""}},"order":null,"amount":20}"# ) . build ( ) . unwrap ( ) ;
println! ( " {:?} " , query ) ;
let response = client
. execute ( query )
. await
. expect ( " Failed to execute request. " ) ;
println! ( " List urls response: \n {:?} " , response ) ;
// Make sure the list was retrieved and the status codes are correct
assert! ( response . status ( ) . is_success ( ) ) ;
// Make sure that the content now contains the newly created link
let content = response . text ( ) . await . unwrap ( ) ;
println! ( " Content: {:?} " , content ) ;
assert! (
content . contains ( r # ""target":"https://github.com/enaut/pslink","code":"test""# ) ,
" the new target and the new code are not in the result "
) ;
// Create a duplicate which should fail
let query = client
. post ( & ( base_url . clone ( ) + " admin/json/create_link/ " ) )
. headers ( custom_headers . clone ( ) )
. body ( r # "{"edit":"Create","id":null,"title":"ein testlink","target":"https://github.com/enaut/pslink","code":"test","author":0,"created_at":null}"# )
. build ( )
. unwrap ( ) ;
println! ( " {:?} " , query ) ;
let response = client
. execute ( query )
. await
. expect ( " Failed to execute request. " ) ;
println! ( " List urls response: \n {:?} " , response ) ;
// Make sure the status codes are correct
assert! ( response . status ( ) . is_server_error ( ) ) ;
// Make sure that the content is a error message
let content = response . text ( ) . await . unwrap ( ) ;
println! ( " Content: {:?} " , content ) ;
assert! (
content . contains ( r # "error"# ) ,
" Make sure the link creation response contains error "
) ;
// Create a second link
let query = client
. post ( & ( base_url . clone ( ) + " admin/json/create_link/ " ) )
. headers ( custom_headers . clone ( ) )
. body ( r # "{"edit":"Create","id":null,"title":"ein second testlink","target":"https://crates.io/crates/pslink","code":"x","author":0,"created_at":null}"# )
. build ( )
. unwrap ( ) ;
println! ( " {:?} " , query ) ;
let response = client
. execute ( query )
. await
. expect ( " Failed to execute request. " ) ;
println! ( " List urls response: \n {:?} " , response ) ;
// Make sure the status codes are correct
assert! ( response . status ( ) . is_success ( ) ) ;
// Make sure that the content is a success message
let content = response . text ( ) . await . unwrap ( ) ;
println! ( " Content: {:?} " , content ) ;
assert! (
content . contains ( r # ""Success":"# ) ,
" Make sure the link creation response contains Success "
) ;
// After inserting a link make sure the link is saved
let query = client
. post ( & ( base_url . clone ( ) + " admin/json/list_links/ " ) )
. headers ( custom_headers . clone ( ) )
. body ( r # "{"filter":{"Code":{"sieve":""},"Description":{"sieve":""},"Target":{"sieve":""},"Author":{"sieve":""},"Statistics":{"sieve":""}},"order":null,"amount":20}"# ) . build ( ) . unwrap ( ) ;
println! ( " {:?} " , query ) ;
let response = client
. execute ( query )
. await
. expect ( " Failed to execute request. " ) ;
println! ( " List urls response: \n {:?} " , response ) ;
// Make sure the list was retrieved and the status codes are correct
assert! ( response . status ( ) . is_success ( ) ) ;
// Make sure that the content now contains the newly created link
let content = response . text ( ) . await . unwrap ( ) ;
println! ( " Content: {:?} " , content ) ;
assert! (
content . contains ( r # ""target":"https://crates.io/crates/pslink","code":"x""# ) ,
" the new target and the new code are not in the result "
) ;
assert! (
content . contains ( r # ""target":"https://github.com/enaut/pslink","code":"test""# ) ,
" the new target and the new code are not in the result "
) ;
// After inserting two links make sure the filters work (searching for a description containing se)
let query = client
. post ( & ( base_url . clone ( ) + " admin/json/list_links/ " ) )
. headers ( custom_headers . clone ( ) )
. body ( r # "{"filter":{"Code":{"sieve":""},"Description":{"sieve":"se"},"Target":{"sieve":""},"Author":{"sieve":""},"Statistics":{"sieve":""}},"order":null,"amount":20}"# ) . build ( ) . unwrap ( ) ;
println! ( " {:?} " , query ) ;
let response = client
. execute ( query )
. await
. expect ( " Failed to execute request. " ) ;
println! ( " List urls response: \n {:?} " , response ) ;
// Make sure the list was retrieved and the status codes are correct
assert! ( response . status ( ) . is_success ( ) ) ;
// Make sure that the content now contains the newly created link
let content = response . text ( ) . await . unwrap ( ) ;
println! ( " Content: {:?} " , content ) ;
// Code x should be in the result but not code test
assert! (
content . contains ( r # ""target":"https://crates.io/crates/pslink","code":"x""# ) ,
" the new target and the new code are not in the result "
) ;
assert! (
! content . contains ( r # ""target":"https://github.com/enaut/pslink","code":"test""# ) ,
" the new target and the new code are not in the result "
) ;
// Make sure we are redirected correctly.
let response = client
. get ( & ( base_url . clone ( ) + " test " ) )
. 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://github.com/enaut/pslink " ) ) ;
// And for the second link - also check that casing is correctly ignored
let response = client
. get ( & ( base_url . clone ( ) + " X " ) )
. 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://crates.io/crates/pslink " ) ) ;
2021-07-12 14:34:23 +02:00
drop ( server ) ;
}