diff --git a/Cargo.toml b/Cargo.toml index 00291af..097d171 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ dirs = "6.0.0" keyring-lib = { version = "1.0.3", features = ["tokio"] } ratatui = "0.30.2" -reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] } +reqwest = { version = "0.13.4", default-features = false, features = ["json"] } serde = { version = "1", features = ["derive"] } tokio = { version = "1.52.3", features = ["full"] } @@ -28,3 +28,4 @@ tokio = { version = "1.52.3", features = ["full"] } cargo-husky = { version = "1.5.0", default-features = false, features = [ "user-hooks", ] } +tempfile = "3" diff --git a/src/cache.rs b/src/cache.rs index f6074b7..d001c36 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -134,3 +134,106 @@ impl CacheStore { Ok(format!("{}:{}", short_name.to_lowercase(), id.as_str())) } } + +#[cfg(test)] +mod tests { + use super::*; + use serde::{Deserialize, Serialize}; + use tempfile::TempDir; + + #[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] + struct MockUser { + name: String, + age: u8, + } + + fn setup_temporary_cache() -> (CacheStore, TempDir) { + let temp_dir = TempDir::new().expect("Failed to create a temporary directory"); + let path = temp_dir.path().join("test_cache.db"); + + let db = PickleDb::new( + &path, + pickledb::PickleDbDumpPolicy::AutoDump, + pickledb::SerializationMethod::Bin, + ); + + (CacheStore { db, path }, temp_dir) + } + + #[test] + fn test_id_new_valid() { + let valid_str = "01ARZ3NDEKTSV4RRFFQ69G5FAV"; + let id_res = Id::::new(valid_str); + + assert!(id_res.is_ok()); + let id = id_res.unwrap(); + assert_eq!(id.as_str(), valid_str); + assert_eq!(id.bytes(), valid_str.as_bytes()); + } + + #[test] + fn test_id_new_invalid_size() { + let short_str = "01ARZ3NDEKTSV4RRFFQ69G5FA"; + let id_err = Id::::new(short_str); + assert!(id_err.is_err()); + + let long_str = "01ARZ3NDEKTSV4RRFFQ69G5FAVV"; + let id_err = Id::::new(long_str); + assert!(id_err.is_err()); + } + + #[test] + fn test_id_debug_formatting() { + let valid_str = "01ARZ3NDEKTSV4RRFFQ69G5FAV"; + let id = Id::::new(valid_str).unwrap(); + + let debug_format = format!("{:?}", id); + assert_eq!(debug_format, "Id(01ARZ3NDEKTSV4RRFFQ69G5FAV)"); + } + + #[test] + fn test_cache_store_lifecycle() { + let (mut store, _temp_dir) = setup_temporary_cache(); + + let raw_id = "01ARZ3NDEKTSV4RRFFQ69G5FAV"; + let id = Id::::new(raw_id).unwrap(); + let user = MockUser { + name: "Alice".to_string(), + age: 30, + }; + + assert!(!store.exists(id.clone()).unwrap(), "Key should not exist"); + assert!(store.get(id.clone()).is_none(), "Get should return None"); + + let set_res = store.set(id.clone(), &user); + assert!(set_res.is_ok(), "Insertion failed: {:?}", set_res); + + assert!( + store.exists(id.clone()).unwrap(), + "Key should exist after set" + ); + + let cached_user = store.get(id.clone()); + assert!(cached_user.is_some(), "Value should have been retrieved"); + assert_eq!( + cached_user.unwrap(), + user, + "Retrieved value does not match the inserted one" + ); + + let remove_res = store.remove(id.clone()); + assert!(remove_res.is_ok()); + assert!(remove_res.unwrap(), "Remove should have returned true"); + + assert!( + !store.exists(id.clone()).unwrap(), + "Key should no longer exist after removal" + ); + + let remove_again = store.remove(id.clone()).unwrap(); + assert!( + !remove_again, + "Remove should have returned false for a non-existent key" + ); + } +} diff --git a/src/main.rs b/src/main.rs index 119eb5b..71dadf2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,20 +6,38 @@ mod error; mod input; mod state; +use std::{fs, path::PathBuf}; + use app::App; use ratatui::crossterm::event::{self, Event}; use state::ui; +pub const LOG_FILE: &str = "logs"; + pub type Result = anyhow::Result; -#[tokio::main] -async fn main() -> anyhow::Result<(), Box> { - let log_file = std::fs::OpenOptions::new() +fn create_log_file() -> Result { + let mut path = if let Some(mut p) = dirs::cache_dir() { + p.push(env!("CARGO_PKG_NAME")); + p + } else { + PathBuf::new() + }; + + fs::create_dir_all(&path)?; + + path.push(LOG_FILE); + + Ok(fs::OpenOptions::new() .create(true) .append(true) - .open("vimstoat.log") - .expect("Failed to open log file"); + .open(path) + .expect("Failed to open log file")) +} +#[tokio::main] +async fn main() -> anyhow::Result<(), Box> { + let log_file = create_log_file()?; env_logger::builder() .target(env_logger::Target::Pipe(Box::new(log_file))) .filter_level(log::LevelFilter::Debug)