diff options
| -rw-r--r-- | atuin/tests/common/mod.rs | 99 | ||||
| -rw-r--r-- | atuin/tests/sync.rs | 158 | ||||
| -rw-r--r-- | atuin/tests/users.rs | 131 |
3 files changed, 233 insertions, 155 deletions
diff --git a/atuin/tests/common/mod.rs b/atuin/tests/common/mod.rs new file mode 100644 index 00000000..bda6abd3 --- /dev/null +++ b/atuin/tests/common/mod.rs @@ -0,0 +1,99 @@ +use std::{env, time::Duration}; + +use atuin_client::api_client; +use atuin_common::{api::AddHistoryRequest, utils::uuid_v7}; +use atuin_server::{launch_with_tcp_listener, Settings as ServerSettings}; +use atuin_server_postgres::{Postgres, PostgresSettings}; +use futures_util::TryFutureExt; +use time::OffsetDateTime; +use tokio::{net::TcpListener, sync::oneshot, task::JoinHandle}; +use tracing::{dispatcher, Dispatch}; +use tracing_subscriber::{layer::SubscriberExt, EnvFilter}; + +pub async fn start_server(path: &str) -> (String, oneshot::Sender<()>, JoinHandle<()>) { + let formatting_layer = tracing_tree::HierarchicalLayer::default() + .with_writer(tracing_subscriber::fmt::TestWriter::new()) + .with_indent_lines(true) + .with_ansi(true) + .with_targets(true) + .with_indent_amount(2); + + let dispatch: Dispatch = tracing_subscriber::registry() + .with(formatting_layer) + .with(EnvFilter::new("atuin_server=debug,atuin_client=debug,info")) + .into(); + + let db_uri = env::var("ATUIN_DB_URI") + .unwrap_or_else(|_| "postgres://atuin:pass@localhost:5432/atuin".to_owned()); + + let server_settings = ServerSettings { + host: "127.0.0.1".to_owned(), + port: 0, + path: path.to_owned(), + open_registration: true, + max_history_length: 8192, + max_record_size: 1024 * 1024 * 1024, + page_size: 1100, + register_webhook_url: None, + register_webhook_username: String::new(), + db_settings: PostgresSettings { db_uri }, + metrics: atuin_server::settings::Metrics::default(), + tls: atuin_server::settings::Tls::default(), + }; + + let (shutdown_tx, shutdown_rx) = tokio::sync::oneshot::channel(); + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let addr = listener.local_addr().unwrap(); + let server = tokio::spawn(async move { + let _tracing_guard = dispatcher::set_default(&dispatch); + + if let Err(e) = launch_with_tcp_listener::<Postgres>( + server_settings, + listener, + shutdown_rx.unwrap_or_else(|_| ()), + ) + .await + { + tracing::error!(error=?e, "server error"); + panic!("error running server: {e:?}"); + } + }); + + // let the server come online + tokio::time::sleep(Duration::from_millis(200)).await; + + (format!("http://{addr}{path}"), shutdown_tx, server) +} + +pub async fn register_inner<'a>( + address: &'a str, + username: &str, + password: &str, +) -> api_client::Client<'a> { + let email = format!("{}@example.com", uuid_v7().as_simple()); + + // registration works + let registration_response = api_client::register(address, username, &email, password) + .await + .unwrap(); + + api_client::Client::new(address, ®istration_response.session, 5, 30).unwrap() +} + +pub async fn login(address: &str, username: String, password: String) -> api_client::Client<'_> { + // registration works + let login_respose = api_client::login( + address, + atuin_common::api::LoginRequest { username, password }, + ) + .await + .unwrap(); + + api_client::Client::new(address, &login_respose.session, 5, 30).unwrap() +} + +pub async fn register(address: &str) -> api_client::Client<'_> { + let username = uuid_v7().as_simple().to_string(); + let password = uuid_v7().as_simple().to_string(); + register_inner(address, &username, &password).await +} diff --git a/atuin/tests/sync.rs b/atuin/tests/sync.rs index 6dacbd54..1835b20b 100644 --- a/atuin/tests/sync.rs +++ b/atuin/tests/sync.rs @@ -10,166 +10,14 @@ use tokio::{net::TcpListener, sync::oneshot, task::JoinHandle}; use tracing::{dispatcher, Dispatch}; use tracing_subscriber::{layer::SubscriberExt, EnvFilter}; -async fn start_server(path: &str) -> (String, oneshot::Sender<()>, JoinHandle<()>) { - let formatting_layer = tracing_tree::HierarchicalLayer::default() - .with_writer(tracing_subscriber::fmt::TestWriter::new()) - .with_indent_lines(true) - .with_ansi(true) - .with_targets(true) - .with_indent_amount(2); - - let dispatch: Dispatch = tracing_subscriber::registry() - .with(formatting_layer) - .with(EnvFilter::new("atuin_server=debug,atuin_client=debug,info")) - .into(); - - let db_uri = env::var("ATUIN_DB_URI") - .unwrap_or_else(|_| "postgres://atuin:pass@localhost:5432/atuin".to_owned()); - - let server_settings = ServerSettings { - host: "127.0.0.1".to_owned(), - port: 0, - path: path.to_owned(), - open_registration: true, - max_history_length: 8192, - max_record_size: 1024 * 1024 * 1024, - page_size: 1100, - register_webhook_url: None, - register_webhook_username: String::new(), - db_settings: PostgresSettings { db_uri }, - metrics: atuin_server::settings::Metrics::default(), - tls: atuin_server::settings::Tls::default(), - }; - - let (shutdown_tx, shutdown_rx) = tokio::sync::oneshot::channel(); - let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); - let addr = listener.local_addr().unwrap(); - let server = tokio::spawn(async move { - let _tracing_guard = dispatcher::set_default(&dispatch); - - if let Err(e) = launch_with_tcp_listener::<Postgres>( - server_settings, - listener, - shutdown_rx.unwrap_or_else(|_| ()), - ) - .await - { - tracing::error!(error=?e, "server error"); - panic!("error running server: {e:?}"); - } - }); - - // let the server come online - tokio::time::sleep(Duration::from_millis(200)).await; - - (format!("http://{addr}{path}"), shutdown_tx, server) -} - -async fn register_inner<'a>( - address: &'a str, - username: &str, - password: &str, -) -> api_client::Client<'a> { - let email = format!("{}@example.com", uuid_v7().as_simple()); - - // registration works - let registration_response = api_client::register(address, username, &email, password) - .await - .unwrap(); - - api_client::Client::new(address, ®istration_response.session, 5, 30).unwrap() -} - -async fn login(address: &str, username: String, password: String) -> api_client::Client<'_> { - // registration works - let login_respose = api_client::login( - address, - atuin_common::api::LoginRequest { username, password }, - ) - .await - .unwrap(); - - api_client::Client::new(address, &login_respose.session, 5, 30).unwrap() -} - -async fn register(address: &str) -> api_client::Client<'_> { - let username = uuid_v7().as_simple().to_string(); - let password = uuid_v7().as_simple().to_string(); - register_inner(address, &username, &password).await -} - -#[tokio::test] -async fn registration() { - let path = format!("/{}", uuid_v7().as_simple()); - let (address, shutdown, server) = start_server(&path).await; - dbg!(&address); - - // -- REGISTRATION -- - - let username = uuid_v7().as_simple().to_string(); - let password = uuid_v7().as_simple().to_string(); - let client = register_inner(&address, &username, &password).await; - - // the session token works - let status = client.status().await.unwrap(); - assert_eq!(status.username, username); - - // -- LOGIN -- - - let client = login(&address, username.clone(), password).await; - - // the session token works - let status = client.status().await.unwrap(); - assert_eq!(status.username, username); - - shutdown.send(()).unwrap(); - server.await.unwrap(); -} - -#[tokio::test] -async fn change_password() { - let path = format!("/{}", uuid_v7().as_simple()); - let (address, shutdown, server) = start_server(&path).await; - - // -- REGISTRATION -- - - let username = uuid_v7().as_simple().to_string(); - let password = uuid_v7().as_simple().to_string(); - let client = register_inner(&address, &username, &password).await; - - // the session token works - let status = client.status().await.unwrap(); - assert_eq!(status.username, username); - - // -- PASSWORD CHANGE -- - - let current_password = password; - let new_password = uuid_v7().as_simple().to_string(); - let result = client - .change_password(current_password, new_password.clone()) - .await; - - // the password change request succeeded - assert!(result.is_ok()); - - // -- LOGIN -- - - let client = login(&address, username.clone(), new_password).await; - - // login with new password yields a working token - let status = client.status().await.unwrap(); - assert_eq!(status.username, username); - - shutdown.send(()).unwrap(); - server.await.unwrap(); -} +mod common; #[tokio::test] async fn sync() { let path = format!("/{}", uuid_v7().as_simple()); - let (address, shutdown, server) = start_server(&path).await; + let (address, shutdown, server) = common::start_server(&path).await; - let client = register(&address).await; + let client = common::register(&address).await; let hostname = uuid_v7().as_simple().to_string(); let now = OffsetDateTime::now_utc(); diff --git a/atuin/tests/users.rs b/atuin/tests/users.rs new file mode 100644 index 00000000..8580d64c --- /dev/null +++ b/atuin/tests/users.rs @@ -0,0 +1,131 @@ +use std::{env, time::Duration}; + +use atuin_client::api_client; +use atuin_common::{api::AddHistoryRequest, utils::uuid_v7}; +use atuin_server::{launch_with_tcp_listener, Settings as ServerSettings}; +use atuin_server_postgres::{Postgres, PostgresSettings}; +use futures_util::TryFutureExt; +use time::OffsetDateTime; +use tokio::{net::TcpListener, sync::oneshot, task::JoinHandle}; +use tracing::{dispatcher, Dispatch}; +use tracing_subscriber::{layer::SubscriberExt, EnvFilter}; + +mod common; + +#[tokio::test] +async fn registration() { + let path = format!("/{}", uuid_v7().as_simple()); + let (address, shutdown, server) = common::start_server(&path).await; + dbg!(&address); + + // -- REGISTRATION -- + + let username = uuid_v7().as_simple().to_string(); + let password = uuid_v7().as_simple().to_string(); + let client = common::register_inner(&address, &username, &password).await; + + // the session token works + let status = client.status().await.unwrap(); + assert_eq!(status.username, username); + + // -- LOGIN -- + + let client = common::login(&address, username.clone(), password).await; + + // the session token works + let status = client.status().await.unwrap(); + assert_eq!(status.username, username); + + shutdown.send(()).unwrap(); + server.await.unwrap(); +} + +#[tokio::test] +async fn change_password() { + let path = format!("/{}", uuid_v7().as_simple()); + let (address, shutdown, server) = common::start_server(&path).await; + + // -- REGISTRATION -- + + let username = uuid_v7().as_simple().to_string(); + let password = uuid_v7().as_simple().to_string(); + let client = common::register_inner(&address, &username, &password).await; + + // the session token works + let status = client.status().await.unwrap(); + assert_eq!(status.username, username); + + // -- PASSWORD CHANGE -- + + let current_password = password; + let new_password = uuid_v7().as_simple().to_string(); + let result = client + .change_password(current_password, new_password.clone()) + .await; + + // the password change request succeeded + assert!(result.is_ok()); + + // -- LOGIN -- + + let client = common::login(&address, username.clone(), new_password).await; + + // login with new password yields a working token + let status = client.status().await.unwrap(); + assert_eq!(status.username, username); + + shutdown.send(()).unwrap(); + server.await.unwrap(); +} + +#[tokio::test] +async fn multi_user_test() { + let path = format!("/{}", uuid_v7().as_simple()); + let (address, shutdown, server) = common::start_server(&path).await; + dbg!(&address); + + // -- REGISTRATION -- + + let user_one = uuid_v7().as_simple().to_string(); + let password_one = uuid_v7().as_simple().to_string(); + let client_one = common::register_inner(&address, &user_one, &password_one).await; + + // the session token works + let status = client_one.status().await.unwrap(); + assert_eq!(status.username, user_one); + + let user_two = uuid_v7().as_simple().to_string(); + let password_two = uuid_v7().as_simple().to_string(); + let client_two = common::register_inner(&address, &user_two, &password_two).await; + + // the session token works + let status = client_two.status().await.unwrap(); + assert_eq!(status.username, user_two); + + // check that we can change user one's password, and _this does not affect user two_ + + let current_password = password_one; + let new_password = uuid_v7().as_simple().to_string(); + let result = client_one + .change_password(current_password, new_password.clone()) + .await; + + // the password change request succeeded + assert!(result.is_ok()); + + // -- LOGIN -- + + let client_one = common::login(&address, user_one.clone(), new_password).await; + let client_two = common::login(&address, user_two.clone(), password_two).await; + + // login with new password yields a working token + let status = client_one.status().await.unwrap(); + assert_eq!(status.username, user_one); + assert_ne!(status.username, user_two); + + let status = client_two.status().await.unwrap(); + assert_eq!(status.username, user_two); + + shutdown.send(()).unwrap(); + server.await.unwrap(); +} |
