diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2023-09-01 00:00:00 +0000 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2023-09-13 09:24:49 +0000 |
| commit | 6423baebd92e45c9be85157c443dff42e65d8d14 (patch) | |
| tree | 6548edfd80d0cd9d1267378ebe816ec95e394137 /src/key_handling | |
| parent | 5c5feaa732363e32e2a980a887fa42b4394b1a5e (diff) | |
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
Diffstat (limited to 'src/key_handling')
| -rw-r--r-- | src/key_handling/mod.rs | 1 | ||||
| -rw-r--r-- | src/key_handling/users.rs | 124 |
2 files changed, 125 insertions, 0 deletions
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 @@ | |||
| 1 | use anyhow::{Context, Result}; | ||
| 2 | |||
| 3 | use crate::{ | ||
| 4 | cli_interactor::{Interactor, InteractorPrompt, PromptInputParms}, | ||
| 5 | config::{ConfigManagement, ConfigManager, MyConfig, UserRef}, | ||
| 6 | }; | ||
| 7 | |||
| 8 | #[derive(Default)] | ||
| 9 | pub struct UserManager { | ||
| 10 | config_manager: ConfigManager, | ||
| 11 | interactor: Interactor, | ||
| 12 | } | ||
| 13 | |||
| 14 | pub trait UserManagement { | ||
| 15 | fn add(&self, nsec: &Option<String>) -> Result<()>; | ||
| 16 | } | ||
| 17 | |||
| 18 | #[cfg(test)] | ||
| 19 | use duplicate::duplicate_item; | ||
| 20 | #[cfg_attr(test, duplicate_item(UserManager; [UserManager]; [self::tests::MockUserManager]))] | ||
| 21 | impl UserManagement for UserManager { | ||
| 22 | fn add(&self, nsec: &Option<String>) -> Result<()> { | ||
| 23 | let nsec = match nsec.clone() { | ||
| 24 | Some(nsec) => nsec, | ||
| 25 | None => self | ||
| 26 | .interactor | ||
| 27 | .input( | ||
| 28 | PromptInputParms::default().with_prompt("login with nsec (or hex private key)"), | ||
| 29 | ) | ||
| 30 | .context("failed to get nsec input from interactor.input")?, | ||
| 31 | }; | ||
| 32 | |||
| 33 | self.config_manager | ||
| 34 | .save(&MyConfig { | ||
| 35 | users: vec![UserRef { | ||
| 36 | nsec: nsec.to_string(), | ||
| 37 | }], | ||
| 38 | ..MyConfig::default() | ||
| 39 | }) | ||
| 40 | .context("failed to save application configuration with new user details in")?; | ||
| 41 | |||
| 42 | println!("logged in as {nsec}"); | ||
| 43 | |||
| 44 | Ok(()) | ||
| 45 | } | ||
| 46 | } | ||
| 47 | |||
| 48 | #[cfg(test)] | ||
| 49 | mod tests { | ||
| 50 | use test_utils::*; | ||
| 51 | |||
| 52 | use super::*; | ||
| 53 | use crate::{cli_interactor::MockInteractorPrompt, config::MockConfigManagement}; | ||
| 54 | |||
| 55 | #[derive(Default)] | ||
| 56 | pub struct MockUserManager { | ||
| 57 | pub config_manager: MockConfigManagement, | ||
| 58 | pub interactor: MockInteractorPrompt, | ||
| 59 | } | ||
| 60 | |||
| 61 | mod add { | ||
| 62 | use super::*; | ||
| 63 | |||
| 64 | impl MockUserManager { | ||
| 65 | fn add_return_expected_responses(mut self) -> Self { | ||
| 66 | self.config_manager | ||
| 67 | .expect_load() | ||
| 68 | .returning(|| Ok(MyConfig::default())); | ||
| 69 | self.config_manager.expect_save().returning(|_| Ok(())); | ||
| 70 | self.interactor | ||
| 71 | .expect_input() | ||
| 72 | .returning(|_| Ok(TEST_KEY_1_NSEC.into())); | ||
| 73 | self | ||
| 74 | } | ||
| 75 | } | ||
| 76 | |||
| 77 | mod when_nsec_is_passed { | ||
| 78 | use super::*; | ||
| 79 | |||
| 80 | #[test] | ||
| 81 | fn user_isnt_prompted() { | ||
| 82 | let mut m = MockUserManager::default().add_return_expected_responses(); | ||
| 83 | m.interactor = MockInteractorPrompt::default(); | ||
| 84 | m.interactor.expect_input().never(); | ||
| 85 | |||
| 86 | let _ = m.add(&Some(TEST_KEY_1_NSEC.into())); | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| 90 | mod when_no_nsec_is_passed { | ||
| 91 | use super::*; | ||
| 92 | |||
| 93 | #[test] | ||
| 94 | fn prompt_for_nsec() { | ||
| 95 | let mut m = MockUserManager::default().add_return_expected_responses(); | ||
| 96 | |||
| 97 | m.interactor = MockInteractorPrompt::new(); | ||
| 98 | m.interactor | ||
| 99 | .expect_input() | ||
| 100 | .once() | ||
| 101 | .withf(|p| p.prompt.eq("login with nsec (or hex private key)")) | ||
| 102 | .returning(|_| Ok(TEST_KEY_1_NSEC.into())); | ||
| 103 | |||
| 104 | let _ = m.add(&None); | ||
| 105 | } | ||
| 106 | |||
| 107 | #[test] | ||
| 108 | fn stored_in_config() { | ||
| 109 | let mut m = MockUserManager::default().add_return_expected_responses(); | ||
| 110 | |||
| 111 | m.config_manager = MockConfigManagement::new(); | ||
| 112 | m.config_manager | ||
| 113 | .expect_load() | ||
| 114 | .returning(|| Ok(MyConfig::default())); | ||
| 115 | m.config_manager | ||
| 116 | .expect_save() | ||
| 117 | .withf(|cfg| cfg.users.len().eq(&1) && cfg.users[0].nsec.eq(TEST_KEY_1_NSEC)) | ||
| 118 | .returning(|_| Ok(())); | ||
| 119 | |||
| 120 | let _ = m.add(&None); | ||
| 121 | } | ||
| 122 | } | ||
| 123 | } | ||
| 124 | } | ||