#![allow(clippy::default_trait_access)]
#![allow(clippy::field_reassign_with_default)]
#![allow(clippy::too_many_lines)]
#![allow(clippy::missing_panics_doc)]
#![allow(clippy::option_if_let_else)]
#![allow(clippy::significant_drop_tightening)]
use aperture_cli::config::context_name::ApiContextName;
use aperture_cli::config::manager::ConfigManager;
use aperture_cli::error::{Error, ErrorKind as ApertureErrorKind};
fn name(s: &str) -> ApiContextName {
ApiContextName::new(s).expect("test name should be valid")
}
use aperture_cli::fs::FileSystem;
use std::collections::HashMap;
use std::io::{self, ErrorKind};
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex};
#[derive(Clone)]
pub struct MockFileSystem {
files: Arc<Mutex<HashMap<PathBuf, Vec<u8>>>>,
dirs: Arc<Mutex<HashMap<PathBuf, bool>>>,
}
impl Default for MockFileSystem {
fn default() -> Self {
Self::new()
}
}
impl MockFileSystem {
#[must_use]
pub fn new() -> Self {
Self {
files: Arc::new(Mutex::new(HashMap::new())),
dirs: Arc::new(Mutex::new(HashMap::new())),
}
}
pub fn add_file(&self, path: &Path, content: &str) {
self.files
.lock()
.unwrap()
.insert(path.to_path_buf(), content.as_bytes().to_vec());
self.dirs
.lock()
.unwrap()
.insert(path.parent().unwrap().to_path_buf(), true);
}
pub fn add_dir(&self, path: &Path) {
self.dirs.lock().unwrap().insert(path.to_path_buf(), true);
}
}
impl FileSystem for MockFileSystem {
fn read_to_string(&self, path: &Path) -> io::Result<String> {
let files = self.files.lock().unwrap();
if let Some(content) = files.get(path) {
String::from_utf8(content.clone())
.map_err(|_| io::Error::new(ErrorKind::InvalidData, "Invalid UTF-8 in file"))
} else {
Err(io::Error::new(ErrorKind::NotFound, "File not found"))
}
}
fn write_all(&self, path: &Path, contents: &[u8]) -> io::Result<()> {
self.files
.lock()
.unwrap()
.insert(path.to_path_buf(), contents.to_vec());
Ok(())
}
fn create_dir_all(&self, path: &Path) -> io::Result<()> {
self.dirs.lock().unwrap().insert(path.to_path_buf(), true);
Ok(())
}
fn exists(&self, path: &Path) -> bool {
let files = self.files.lock().unwrap();
let dirs = self.dirs.lock().unwrap();
files.contains_key(path) || dirs.contains_key(path)
}
fn remove_file(&self, path: &Path) -> io::Result<()> {
let mut files = self.files.lock().unwrap();
if files.remove(path).is_some() {
Ok(())
} else {
Err(io::Error::new(ErrorKind::NotFound, "File not found"))
}
}
fn read_dir(&self, path: &Path) -> io::Result<Vec<PathBuf>> {
let files = self.files.lock().unwrap();
let entries: Vec<PathBuf> = files
.keys()
.filter(|p| p.parent() == Some(path))
.cloned()
.collect();
Ok(entries)
}
fn is_file(&self, path: &Path) -> bool {
self.files.lock().unwrap().contains_key(path)
}
fn remove_dir_all(&self, path: &Path) -> io::Result<()> {
self.dirs.lock().unwrap().remove(path);
Ok(())
}
fn is_dir(&self, path: &Path) -> bool {
self.dirs.lock().unwrap().contains_key(path)
}
fn canonicalize(&self, path: &Path) -> io::Result<PathBuf> {
Ok(path.to_path_buf())
}
fn atomic_write(&self, path: &Path, contents: &[u8]) -> io::Result<()> {
self.write_all(path, contents)
}
}
fn setup_manager() -> (ConfigManager<MockFileSystem>, MockFileSystem) {
let fs = MockFileSystem::new();
let config_dir = PathBuf::from("/test/config");
fs.add_dir(&config_dir);
let manager = ConfigManager::with_fs(fs.clone(), config_dir);
(manager, fs)
}
fn setup_dir(fs: &MockFileSystem) -> PathBuf {
let dir = PathBuf::from("/test/specs");
fs.add_dir(&dir);
dir
}
#[test]
fn test_add_spec_with_custom_http_scheme_token() {
let (manager, fs) = setup_manager();
let spec_content = r"
openapi: 3.0.0
info:
title: Token Auth API
version: 1.0.0
servers:
- url: https://api.example.com
components:
securitySchemes:
tokenAuth:
type: http
scheme: Token
x-aperture-secret:
source: env
name: API_TOKEN
paths:
/users:
get:
operationId: getUsers
security:
- tokenAuth: []
responses:
'200':
description: Success
";
let spec_path = setup_dir(&fs).join("token-api.yaml");
fs.write_all(&spec_path, spec_content.as_bytes())
.expect("Failed to write spec");
let result = manager.add_spec(&name("token-api"), &spec_path, false, false);
assert!(result.is_ok(), "Token HTTP scheme should be supported");
let specs = manager.list_specs().expect("Failed to list specs");
assert!(specs.contains(&"token-api".to_string()));
}
#[test]
fn test_add_spec_with_custom_http_scheme_apikey() {
let (manager, fs) = setup_manager();
let spec_content = r"
openapi: 3.0.0
info:
title: ApiKey Scheme API
version: 1.0.0
servers:
- url: https://api.example.com
components:
securitySchemes:
customAuth:
type: http
scheme: ApiKey
x-aperture-secret:
source: env
name: CUSTOM_API_KEY
paths:
/data:
get:
operationId: getData
security:
- customAuth: []
responses:
'200':
description: Success
";
let spec_path = setup_dir(&fs).join("apikey-scheme.yaml");
fs.write_all(&spec_path, spec_content.as_bytes())
.expect("Failed to write spec");
let result = manager.add_spec(&name("apikey-scheme"), &spec_path, false, false);
assert!(result.is_ok(), "ApiKey HTTP scheme should be supported");
}
#[test]
fn test_add_spec_with_dsn_scheme() {
let (manager, fs) = setup_manager();
let spec_content = r"
openapi: 3.0.0
info:
title: Sentry-style API
version: 1.0.0
servers:
- url: https://api.example.com
components:
securitySchemes:
dsnAuth:
type: http
scheme: DSN
x-aperture-secret:
source: env
name: SENTRY_DSN
paths:
/events:
post:
operationId: sendEvent
security:
- dsnAuth: []
responses:
'200':
description: Success
";
let spec_path = setup_dir(&fs).join("dsn-api.yaml");
fs.write_all(&spec_path, spec_content.as_bytes())
.expect("Failed to write spec");
let result = manager.add_spec(&name("dsn-api"), &spec_path, false, false);
assert!(result.is_ok(), "DSN HTTP scheme should be supported");
}
#[test]
fn test_rejected_complex_auth_schemes() {
let (manager, fs) = setup_manager();
let oauth_spec = r"
openapi: 3.0.0
info:
title: OAuth API
version: 1.0.0
servers:
- url: https://api.example.com
components:
securitySchemes:
oauthScheme:
type: http
scheme: oauth
x-aperture-secret:
source: env
name: OAUTH_TOKEN
paths:
/users:
get:
operationId: getUsers
security:
- oauthScheme: []
responses:
'200':
description: Success
";
let spec_path = setup_dir(&fs).join("oauth-api.yaml");
fs.write_all(&spec_path, oauth_spec.as_bytes())
.expect("Failed to write spec");
let result = manager.add_spec(&name("oauth-api"), &spec_path, false, true);
assert!(
result.is_err(),
"OAuth HTTP scheme should be rejected in strict mode"
);
let negotiate_spec = r"
openapi: 3.0.0
info:
title: Negotiate API
version: 1.0.0
servers:
- url: https://api.example.com
components:
securitySchemes:
negotiateAuth:
type: http
scheme: negotiate
paths:
/users:
get:
operationId: getUsers
security:
- negotiateAuth: []
responses:
'200':
description: Success
";
let spec_path = setup_dir(&fs).join("negotiate-api.yaml");
fs.write_all(&spec_path, negotiate_spec.as_bytes())
.expect("Failed to write spec");
let result = manager.add_spec(&name("negotiate-api"), &spec_path, false, true);
assert!(
result.is_err(),
"Negotiate HTTP scheme should be rejected in strict mode"
);
}
#[test]
fn test_add_spec_with_proprietary_http_scheme() {
let (manager, fs) = setup_manager();
let spec_content = r"
openapi: 3.0.0
info:
title: Proprietary Auth API
version: 1.0.0
servers:
- url: https://api.example.com
components:
securitySchemes:
customScheme:
type: http
scheme: X-CompanyAuth-V2
x-aperture-secret:
source: env
name: COMPANY_AUTH_TOKEN
paths:
/protected:
get:
operationId: getProtected
security:
- customScheme: []
responses:
'200':
description: Success
";
let spec_path = setup_dir(&fs).join("proprietary-api.yaml");
fs.write_all(&spec_path, spec_content.as_bytes())
.expect("Failed to write spec");
let result = manager.add_spec(&name("proprietary-api"), &spec_path, false, false);
assert!(
result.is_ok(),
"Custom proprietary HTTP schemes should be supported"
);
}
#[test]
fn test_reject_oauth_http_scheme() {
let (manager, fs) = setup_manager();
let spec_content = r"
openapi: 3.0.0
info:
title: OAuth HTTP Scheme API
version: 1.0.0
servers:
- url: https://api.example.com
components:
securitySchemes:
oauthScheme:
type: http
scheme: oauth
paths:
/users:
get:
operationId: getUsers
responses:
'200':
description: Success
";
let spec_path = setup_dir(&fs).join("oauth-http.yaml");
fs.write_all(&spec_path, spec_content.as_bytes())
.expect("Failed to write spec");
let result = manager.add_spec(&name("oauth-http"), &spec_path, false, true); assert!(result.is_err());
if let Err(Error::Internal {
kind: ApertureErrorKind::Validation,
message: msg,
..
}) = result
{
assert!(
msg.contains("requires complex authentication flows"),
"Expected complex auth flow error, got: {msg}"
);
} else {
panic!("Expected Validation error for oauth HTTP scheme");
}
}
#[test]
fn test_reject_negotiate_http_scheme() {
let (manager, fs) = setup_manager();
let spec_content = r"
openapi: 3.0.0
info:
title: Negotiate Auth API
version: 1.0.0
servers:
- url: https://api.example.com
components:
securitySchemes:
negotiateAuth:
type: http
scheme: Negotiate
paths:
/secure:
get:
operationId: getSecure
responses:
'200':
description: Success
";
let spec_path = setup_dir(&fs).join("negotiate-api.yaml");
fs.write_all(&spec_path, spec_content.as_bytes())
.expect("Failed to write spec");
let result = manager.add_spec(&name("negotiate-api"), &spec_path, false, true); assert!(result.is_err());
if let Err(Error::Internal {
kind: ApertureErrorKind::Validation,
message: msg,
..
}) = result
{
assert!(
msg.contains("requires complex authentication flows"),
"Expected complex auth flow error, got: {msg}"
);
} else {
panic!("Expected Validation error for Negotiate scheme");
}
}