From 6423baebd92e45c9be85157c443dff42e65d8d14 Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Fri, 1 Sep 2023 00:00:00 +0000 Subject: refactor: rebuild app skeleton Create skeleton for a complete rebuild of the prototype as a production ready product. Includes design patterns for: - dependency injection - unit testing with dependency mocking - integration testing - error handling - config storage BREAKING-CHANGE: ground-up redesign with incompatible protocol standards --- src/key_handling/mod.rs | 1 + src/key_handling/users.rs | 124 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 src/key_handling/mod.rs create mode 100644 src/key_handling/users.rs (limited to 'src/key_handling') diff --git a/src/key_handling/mod.rs b/src/key_handling/mod.rs new file mode 100644 index 0000000..913bd46 --- /dev/null +++ b/src/key_handling/mod.rs @@ -0,0 +1 @@ +pub mod users; diff --git a/src/key_handling/users.rs b/src/key_handling/users.rs new file mode 100644 index 0000000..bd1748a --- /dev/null +++ b/src/key_handling/users.rs @@ -0,0 +1,124 @@ +use anyhow::{Context, Result}; + +use crate::{ + cli_interactor::{Interactor, InteractorPrompt, PromptInputParms}, + config::{ConfigManagement, ConfigManager, MyConfig, UserRef}, +}; + +#[derive(Default)] +pub struct UserManager { + config_manager: ConfigManager, + interactor: Interactor, +} + +pub trait UserManagement { + fn add(&self, nsec: &Option) -> Result<()>; +} + +#[cfg(test)] +use duplicate::duplicate_item; +#[cfg_attr(test, duplicate_item(UserManager; [UserManager]; [self::tests::MockUserManager]))] +impl UserManagement for UserManager { + fn add(&self, nsec: &Option) -> Result<()> { + let nsec = match nsec.clone() { + Some(nsec) => nsec, + None => self + .interactor + .input( + PromptInputParms::default().with_prompt("login with nsec (or hex private key)"), + ) + .context("failed to get nsec input from interactor.input")?, + }; + + self.config_manager + .save(&MyConfig { + users: vec![UserRef { + nsec: nsec.to_string(), + }], + ..MyConfig::default() + }) + .context("failed to save application configuration with new user details in")?; + + println!("logged in as {nsec}"); + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use test_utils::*; + + use super::*; + use crate::{cli_interactor::MockInteractorPrompt, config::MockConfigManagement}; + + #[derive(Default)] + pub struct MockUserManager { + pub config_manager: MockConfigManagement, + pub interactor: MockInteractorPrompt, + } + + mod add { + use super::*; + + impl MockUserManager { + fn add_return_expected_responses(mut self) -> Self { + self.config_manager + .expect_load() + .returning(|| Ok(MyConfig::default())); + self.config_manager.expect_save().returning(|_| Ok(())); + self.interactor + .expect_input() + .returning(|_| Ok(TEST_KEY_1_NSEC.into())); + self + } + } + + mod when_nsec_is_passed { + use super::*; + + #[test] + fn user_isnt_prompted() { + let mut m = MockUserManager::default().add_return_expected_responses(); + m.interactor = MockInteractorPrompt::default(); + m.interactor.expect_input().never(); + + let _ = m.add(&Some(TEST_KEY_1_NSEC.into())); + } + } + + mod when_no_nsec_is_passed { + use super::*; + + #[test] + fn prompt_for_nsec() { + let mut m = MockUserManager::default().add_return_expected_responses(); + + m.interactor = MockInteractorPrompt::new(); + m.interactor + .expect_input() + .once() + .withf(|p| p.prompt.eq("login with nsec (or hex private key)")) + .returning(|_| Ok(TEST_KEY_1_NSEC.into())); + + let _ = m.add(&None); + } + + #[test] + fn stored_in_config() { + let mut m = MockUserManager::default().add_return_expected_responses(); + + m.config_manager = MockConfigManagement::new(); + m.config_manager + .expect_load() + .returning(|| Ok(MyConfig::default())); + m.config_manager + .expect_save() + .withf(|cfg| cfg.users.len().eq(&1) && cfg.users[0].nsec.eq(TEST_KEY_1_NSEC)) + .returning(|_| Ok(())); + + let _ = m.add(&None); + } + } + } +} -- cgit v1.2.3