From 7067d772bc8c7e3c829a0b43ff9b5579bc07aef3 Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Fri, 29 Sep 2023 02:56:40 +0100 Subject: [PATCH] fix sync timestamps (#1258) * fix timestamp * add sync test * skip all sync tests --- atuin-client/src/api_client.rs | 2 +- atuin-server-postgres/src/lib.rs | 33 +++++++- atuin-server-postgres/src/wrappers.rs | 9 ++- atuin.nix | 3 +- atuin/tests/sync.rs | 105 +++++++++++++++++++++----- 5 files changed, 126 insertions(+), 26 deletions(-) diff --git a/atuin-client/src/api_client.rs b/atuin-client/src/api_client.rs index fbeea9a..ef966f5 100644 --- a/atuin-client/src/api_client.rs +++ b/atuin-client/src/api_client.rs @@ -109,7 +109,7 @@ pub async fn latest_version() -> Result { impl<'a> Client<'a> { pub fn new( sync_addr: &'a str, - session_token: &'a str, + session_token: &str, connect_timeout: u64, timeout: u64, ) -> Result { diff --git a/atuin-server-postgres/src/lib.rs b/atuin-server-postgres/src/lib.rs index 8f473d5..c71d03a 100644 --- a/atuin-server-postgres/src/lib.rs +++ b/atuin-server-postgres/src/lib.rs @@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize}; use sqlx::postgres::PgPoolOptions; use sqlx::Row; -use time::{OffsetDateTime, PrimitiveDateTime}; +use time::{OffsetDateTime, PrimitiveDateTime, UtcOffset}; use tracing::instrument; use wrappers::{DbHistory, DbRecord, DbSession, DbUser}; @@ -215,8 +215,8 @@ impl Database for Postgres { ) .bind(user.id) .bind(host) - .bind(created_after) - .bind(since) + .bind(into_utc(created_after)) + .bind(into_utc(since)) .bind(page_size) .fetch(&self.pool) .map_ok(|DbHistory(h)| h) @@ -450,3 +450,30 @@ impl Database for Postgres { Ok(res) } } + +fn into_utc(x: OffsetDateTime) -> PrimitiveDateTime { + let x = x.to_offset(UtcOffset::UTC); + PrimitiveDateTime::new(x.date(), x.time()) +} + +#[cfg(test)] +mod tests { + use time::macros::datetime; + + use crate::into_utc; + + #[test] + fn utc() { + let dt = datetime!(2023-09-26 15:11:02 +05:30); + assert_eq!(into_utc(dt), datetime!(2023-09-26 09:41:02)); + assert_eq!(into_utc(dt).assume_utc(), dt); + + let dt = datetime!(2023-09-26 15:11:02 -07:00); + assert_eq!(into_utc(dt), datetime!(2023-09-26 22:11:02)); + assert_eq!(into_utc(dt).assume_utc(), dt); + + let dt = datetime!(2023-09-26 15:11:02 +00:00); + assert_eq!(into_utc(dt), datetime!(2023-09-26 15:11:02)); + assert_eq!(into_utc(dt).assume_utc(), dt); + } +} diff --git a/atuin-server-postgres/src/wrappers.rs b/atuin-server-postgres/src/wrappers.rs index 8bd482b..b4ae48a 100644 --- a/atuin-server-postgres/src/wrappers.rs +++ b/atuin-server-postgres/src/wrappers.rs @@ -2,6 +2,7 @@ use ::sqlx::{FromRow, Result}; use atuin_common::record::{EncryptedData, Record}; use atuin_server_database::models::{History, Session, User}; use sqlx::{postgres::PgRow, Row}; +use time::PrimitiveDateTime; pub struct DbUser(pub User); pub struct DbSession(pub Session); @@ -36,9 +37,13 @@ impl<'a> ::sqlx::FromRow<'a, PgRow> for DbHistory { client_id: row.try_get("client_id")?, user_id: row.try_get("user_id")?, hostname: row.try_get("hostname")?, - timestamp: row.try_get("timestamp")?, + timestamp: row + .try_get::("timestamp")? + .assume_utc(), data: row.try_get("data")?, - created_at: row.try_get("created_at")?, + created_at: row + .try_get::("created_at")? + .assume_utc(), })) } } diff --git a/atuin.nix b/atuin.nix index 795e927..fa6d229 100644 --- a/atuin.nix +++ b/atuin.nix @@ -37,7 +37,8 @@ rustPlatform.buildRustPackage { # Additional flags passed to the cargo test binary, see `cargo test -- --help` checkFlags = [ - # Registration tests require a postgres server + # Sync tests require a postgres server + "--skip=sync" "--skip=registration" ]; diff --git a/atuin/tests/sync.rs b/atuin/tests/sync.rs index ab1eb42..6dbc724 100644 --- a/atuin/tests/sync.rs +++ b/atuin/tests/sync.rs @@ -1,10 +1,11 @@ use std::{env, net::TcpListener, time::Duration}; use atuin_client::api_client; -use atuin_common::utils::uuid_v7; +use atuin_common::{api::AddHistoryRequest, utils::uuid_v7}; use atuin_server::{launch_with_listener, Settings as ServerSettings}; use atuin_server_postgres::{Postgres, PostgresSettings}; use futures_util::TryFutureExt; +use time::OffsetDateTime; use tokio::{sync::oneshot, task::JoinHandle}; use tracing::{dispatcher, Dispatch}; use tracing_subscriber::{layer::SubscriberExt, EnvFilter}; @@ -19,7 +20,7 @@ async fn start_server(path: &str) -> (String, oneshot::Sender<()>, JoinHandle<() let dispatch: Dispatch = tracing_subscriber::registry() .with(formatting_layer) - .with(EnvFilter::new("atuin_server=debug,info")) + .with(EnvFilter::new("atuin_server=debug,atuin_client=debug,info")) .into(); let db_uri = env::var("ATUIN_DB_URI") @@ -62,37 +63,103 @@ async fn start_server(path: &str) -> (String, oneshot::Sender<()>, JoinHandle<() (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 email = format!("{}@example.com", uuid_v7().as_simple()); let password = uuid_v7().as_simple().to_string(); - - // registration works - let registration_response = api_client::register(&address, &username, &email, &password) - .await - .unwrap(); - - let client = api_client::Client::new(&address, ®istration_response.session, 5, 30).unwrap(); + let client = register_inner(&address, &username, &password).await; // the session token works let status = client.status().await.unwrap(); assert_eq!(status.username, username); - // login works - let login_response = api_client::login( - &address, - atuin_common::api::LoginRequest { username, password }, - ) - .await - .unwrap(); + // -- LOGIN -- - // currently we return the same session token - assert_eq!(registration_response.session, login_response.session); + 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 sync() { + let path = format!("/{}", uuid_v7().as_simple()); + let (address, shutdown, server) = start_server(&path).await; + + let client = register(&address).await; + let hostname = uuid_v7().as_simple().to_string(); + let now = OffsetDateTime::now_utc(); + + let data1 = uuid_v7().as_simple().to_string(); + let data2 = uuid_v7().as_simple().to_string(); + + client + .post_history(&[ + AddHistoryRequest { + id: uuid_v7().as_simple().to_string(), + timestamp: now, + data: data1.clone(), + hostname: hostname.clone(), + }, + AddHistoryRequest { + id: uuid_v7().as_simple().to_string(), + timestamp: now, + data: data2.clone(), + hostname: hostname.clone(), + }, + ]) + .await + .unwrap(); + + let history = client + .get_history(OffsetDateTime::UNIX_EPOCH, OffsetDateTime::UNIX_EPOCH, None) + .await + .unwrap(); + + assert_eq!(history.history, vec![data1, data2]); shutdown.send(()).unwrap(); server.await.unwrap();