Skip to content

Commit d0f8f9a

Browse files
authored
Merge pull request #46 from hiro-o918/refactor/configs
♻️ refactor configs and add tests
2 parents b4052eb + 7b598dc commit d0f8f9a

File tree

2 files changed

+141
-19
lines changed

2 files changed

+141
-19
lines changed

src/configs.rs

+137-17
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::path::PathBuf;
55
use std::{collections::HashMap, path::Path};
66

77
use anyhow::{Context, Result};
8-
use config::{Config, File};
8+
use config::{Config, File, FileFormat};
99
use once_cell::sync::Lazy;
1010
use serde::{Deserialize, Serialize};
1111

@@ -14,16 +14,16 @@ use crate::ctx;
1414
type ProfileName = String;
1515
type AuthScript = String;
1616

17-
static CONFIGS_PATH: Lazy<PathBuf> = Lazy::new(|| {
17+
pub static CONFIGS_PATH: Lazy<PathBuf> = Lazy::new(|| {
1818
let mut path = home_dir().unwrap();
1919
path.push(".awsctx/configs.yaml");
2020
path
2121
});
22-
const CONFIGS_DESCRIPTIONS: &str = r#"# # Configurations for awsctx
22+
const CONFIGS_DESCRIPTIONS: &str = r#"# # Configurations for awsctx
2323
# # You can manually edit configurations according to the following usage
2424
2525
# # To use subcommand `auth` or `refresh`, fill the below configs for each profile.
26-
# auth_commands:
26+
# auth_commands:
2727
# # configuration for `foo` profile with aws configure
2828
# foo: |
2929
# # you can use pre-defined parameter `{{profile}}` which is replaced by key of this block
@@ -35,7 +35,7 @@ const CONFIGS_DESCRIPTIONS: &str = r#"# # Configurations for awsctx
3535
# onelogin-aws-login -C {{profile}} --profile {{profile}} -u [email protected]
3636
"#;
3737

38-
#[derive(Debug, Serialize, Deserialize, Default)]
38+
#[derive(Debug, Serialize, Deserialize, Default, PartialEq, Eq)]
3939
pub struct Configs {
4040
pub auth_commands: HashMap<ProfileName, AuthScript>,
4141
}
@@ -46,17 +46,16 @@ impl Configs {
4646
.map(|p| p.as_ref().to_path_buf())
4747
.unwrap_or_else(|| CONFIGS_PATH.clone());
4848
let c = Config::builder()
49-
.add_source(File::with_name(path.to_str().unwrap()))
49+
.add_source(File::new(path.to_str().unwrap(), FileFormat::Yaml))
5050
.build()
5151
.context(format!(
5252
"failed to build configuration from path: {}",
5353
path.to_str().unwrap()
5454
))
5555
.map_err(|e| ctx::CTXError::InvalidConfigurations {
56-
message: format!(
57-
"failed to load configurations, check your configurations ({})",
58-
path.to_str().unwrap()
59-
),
56+
message:
57+
"failed to load configurations, check your configurations (~/.aws/configs.yaml)"
58+
.to_string(),
6059
source: Some(e),
6160
})?;
6261

@@ -66,16 +65,17 @@ impl Configs {
6665
path.to_str().unwrap()
6766
))
6867
.map_err(|e| ctx::CTXError::InvalidConfigurations {
69-
message: format!(
70-
"failed to deserialize configurations, check your configurations ({})",
71-
path.to_str().unwrap()
72-
),
68+
message: "failed to deserialize configurations, check your configurations (~/.aws/configs.yaml)".to_string(),
7369
source: Some(e),
7470
})
7571
}
7672

77-
pub fn initialize_default_configs() -> Result<Self, ctx::CTXError> {
78-
let path: &PathBuf = &CONFIGS_PATH;
73+
pub fn initialize_default_configs<P: AsRef<Path>>(
74+
path: Option<P>,
75+
) -> Result<Self, ctx::CTXError> {
76+
let path = path
77+
.map(|p| p.as_ref().to_path_buf())
78+
.unwrap_or_else(|| CONFIGS_PATH.clone());
7979
if path.exists() {
8080
return Self::load_configs(Some(path));
8181
}
@@ -87,7 +87,7 @@ impl Configs {
8787
None => (),
8888
}
8989
let c = Configs::default();
90-
let mut file = fs::File::create(path)
90+
let mut file = fs::File::create(&path)
9191
.context("failed to create a configuration file")
9292
.map_err(|e| ctx::CTXError::UnexpectedError { source: Some(e) })?;
9393
file.write_all(CONFIGS_DESCRIPTIONS.as_bytes())
@@ -104,3 +104,123 @@ impl Configs {
104104
Self::load_configs(Some(path))
105105
}
106106
}
107+
108+
#[cfg(test)]
109+
mod tests {
110+
use std::io::{Seek, SeekFrom};
111+
112+
use rstest::*;
113+
use tempfile::{NamedTempFile, TempDir};
114+
115+
use super::*;
116+
117+
#[fixture]
118+
pub fn configs_text() -> String {
119+
r#"# # Configurations for awsctx
120+
# # You can manually edit configurations according to the following usage
121+
122+
# # To use subcommand `auth` or `refresh`, fill the below configs for each profile.
123+
# auth_commands:
124+
# # configuration for `foo` profile with aws configure
125+
# foo: |
126+
# # you can use pre-defined parameter `{{profile}}` which is replaced by key of this block
127+
# # In this case, `{{profile}}` is replaced by `foo`
128+
# aws configure --profile {{profile}}
129+
# # configuration for `bar` profile with [onelogin-aws-cli](https://github.com/physera/onelogin-aws-cli)
130+
# bar: |
131+
# # In this case, name of one-login configuration is same as `profile`
132+
# onelogin-aws-login -C {{profile}} --profile {{profile}} -u [email protected]
133+
---
134+
auth_commands:
135+
foo: |
136+
echo 1"#
137+
.to_string()
138+
}
139+
140+
#[fixture]
141+
pub fn configs_file(configs_text: String) -> NamedTempFile {
142+
let mut f = NamedTempFile::new().unwrap();
143+
write!(f, "{}", configs_text).unwrap();
144+
f.flush().unwrap();
145+
f.seek(SeekFrom::Start(0)).unwrap();
146+
f
147+
}
148+
149+
#[fixture]
150+
pub fn configs() -> Configs {
151+
Configs {
152+
auth_commands: vec![("foo".to_string(), "echo 1".to_string())]
153+
.into_iter()
154+
.collect::<HashMap<String, String>>(),
155+
}
156+
}
157+
158+
#[rstest(input, expect)]
159+
#[case(configs_file(configs_text()), Ok(configs()))]
160+
#[case(
161+
configs_file("invalid_yaml_format: a:a:".to_string()),
162+
Err(
163+
ctx::CTXError::InvalidConfigurations {
164+
message: "failed to load configurations, check your configurations (~/.aws/configs.yaml)".to_string(),
165+
source: None
166+
}
167+
)
168+
)]
169+
#[case(
170+
configs_file("unknown_key: foo".to_string()),
171+
Err(
172+
ctx::CTXError::InvalidConfigurations {
173+
message: "failed to deserialize configurations, check your configurations (~/.aws/configs.yaml)".to_string(),
174+
source: None
175+
}
176+
)
177+
)]
178+
fn test_configs_load_configs(input: NamedTempFile, expect: Result<Configs, ctx::CTXError>) {
179+
let actual = Configs::load_configs(Some(input.path()));
180+
match (expect, actual) {
181+
(Ok(expect), Ok(actual)) => {
182+
assert_eq!(expect, actual);
183+
}
184+
(Err(expect), Err(actual)) => match (&expect, &actual) {
185+
(
186+
ctx::CTXError::InvalidConfigurations {
187+
message: expect_message,
188+
source: _,
189+
},
190+
ctx::CTXError::InvalidConfigurations {
191+
message: actual_message,
192+
source: _,
193+
},
194+
) => assert_eq!(expect_message, actual_message),
195+
_ => panic!("unexpected error"),
196+
},
197+
_ => panic!("expect and actual are not match"),
198+
}
199+
}
200+
201+
#[rstest]
202+
fn test_initialize_default_configs() {
203+
let tmpdir = TempDir::new().unwrap();
204+
let tmpfile = tmpdir.path().join("configs.yaml");
205+
Configs::initialize_default_configs(Some(&tmpfile)).unwrap();
206+
let expect = r#"# # Configurations for awsctx
207+
# # You can manually edit configurations according to the following usage
208+
209+
# # To use subcommand `auth` or `refresh`, fill the below configs for each profile.
210+
# auth_commands:
211+
# # configuration for `foo` profile with aws configure
212+
# foo: |
213+
# # you can use pre-defined parameter `{{profile}}` which is replaced by key of this block
214+
# # In this case, `{{profile}}` is replaced by `foo`
215+
# aws configure --profile {{profile}}
216+
# # configuration for `bar` profile with [onelogin-aws-cli](https://github.com/physera/onelogin-aws-cli)
217+
# bar: |
218+
# # In this case, name of one-login configuration is same as `profile`
219+
# onelogin-aws-login -C {{profile}} --profile {{profile}} -u [email protected]
220+
---
221+
auth_commands: {}
222+
"#;
223+
let actual = fs::read_to_string(tmpfile).unwrap();
224+
assert_eq!(expect, actual);
225+
}
226+
}

src/main.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::{io, rc::Rc};
1+
use std::{io, path::PathBuf, rc::Rc};
22

33
use awsctx::{
44
aws::{AWS, CREDENTIALS_PATH},
@@ -89,7 +89,9 @@ fn main() {
8989
)
9090
.unwrap();
9191

92-
let configs = Rc::new(fatal_ctxerr(Configs::initialize_default_configs()));
92+
let configs = Rc::new(fatal_ctxerr(
93+
Configs::initialize_default_configs::<PathBuf>(None),
94+
));
9395
let aws = AWS::new(Rc::clone(&configs), CREDENTIALS_PATH.clone()).unwrap();
9496
let opts = cli.opts.unwrap_or(Opts::UseContextByInteractiveFinder {});
9597
let skim_options = SkimOptionsBuilder::default()

0 commit comments

Comments
 (0)