mod common;
use ati::core::auth_generator::{AuthCache, GenContext};
use ati::core::http::execute_tool_with_gen;
use ati::core::keyring::Keyring;
use ati::core::manifest::{
AuthGenType, AuthGenerator, AuthOutputFormat, AuthType, HttpMethod, InjectTarget,
};
use serde_json::json;
use std::collections::HashMap;
use wiremock::matchers::{method, path};
use wiremock::{Mock, MockServer, ResponseTemplate};
#[tokio::test]
async fn test_auth_gen_timeout_through_execution() {
let upstream = MockServer::start().await;
let gen = AuthGenerator {
gen_type: AuthGenType::Command,
command: Some("sleep".into()),
args: vec!["10".into()],
interpreter: None,
script: None,
cache_ttl_secs: 0,
output_format: AuthOutputFormat::Text,
env: HashMap::new(),
inject: HashMap::new(),
timeout_secs: 1,
};
let provider = ati::core::manifest::Provider {
auth_type: AuthType::Bearer,
auth_generator: Some(gen),
..common::test_provider("timeout_test", &upstream.uri())
};
let tool = common::test_tool("timeout_tool", "/data", HttpMethod::Get);
let keyring = Keyring::empty();
let cache = AuthCache::new();
let ctx = GenContext::default();
let err = execute_tool_with_gen(
&provider,
&tool,
&HashMap::new(),
&keyring,
Some(&ctx),
Some(&cache),
)
.await
.unwrap_err();
let msg = format!("{err}");
assert!(
msg.contains("timed out") || msg.contains("Timeout") || msg.contains("timeout"),
"Expected timeout error, got: {msg}"
);
}
#[tokio::test]
async fn test_auth_gen_nonzero_exit_through_execution() {
let gen = AuthGenerator {
gen_type: AuthGenType::Command,
command: Some("false".into()),
args: vec![],
interpreter: None,
script: None,
cache_ttl_secs: 0,
output_format: AuthOutputFormat::Text,
env: HashMap::new(),
inject: HashMap::new(),
timeout_secs: 5,
};
let provider = ati::core::manifest::Provider {
auth_type: AuthType::Bearer,
auth_generator: Some(gen),
..common::test_provider("exit_test", "http://unused.test")
};
let tool = common::test_tool("exit_tool", "/data", HttpMethod::Get);
let keyring = Keyring::empty();
let cache = AuthCache::new();
let ctx = GenContext::default();
let err = execute_tool_with_gen(
&provider,
&tool,
&HashMap::new(),
&keyring,
Some(&ctx),
Some(&cache),
)
.await
.unwrap_err();
let msg = format!("{err}");
assert!(
msg.contains("auth_generator") || msg.contains("exit"),
"Expected non-zero exit error, got: {msg}"
);
}
#[tokio::test]
async fn test_auth_gen_invalid_json_output() {
let gen = AuthGenerator {
gen_type: AuthGenType::Command,
command: Some("echo".into()),
args: vec!["not-valid-json".into()],
interpreter: None,
script: None,
cache_ttl_secs: 0,
output_format: AuthOutputFormat::Json,
env: HashMap::new(),
inject: HashMap::new(),
timeout_secs: 5,
};
let provider = ati::core::manifest::Provider {
auth_type: AuthType::Bearer,
auth_generator: Some(gen),
..common::test_provider("json_err_test", "http://unused.test")
};
let tool = common::test_tool("json_err_tool", "/data", HttpMethod::Get);
let keyring = Keyring::empty();
let cache = AuthCache::new();
let ctx = GenContext::default();
let err = execute_tool_with_gen(
&provider,
&tool,
&HashMap::new(),
&keyring,
Some(&ctx),
Some(&cache),
)
.await
.unwrap_err();
let msg = format!("{err}");
assert!(
msg.contains("JSON") || msg.contains("parse"),
"Expected JSON parse error, got: {msg}"
);
}
#[tokio::test]
async fn test_auth_gen_missing_inject_path() {
let mut inject = HashMap::new();
inject.insert(
"nonexistent.path".into(),
InjectTarget {
inject_type: "header".into(),
name: "X-Token".into(),
},
);
let gen = AuthGenerator {
gen_type: AuthGenType::Command,
command: Some("echo".into()),
args: vec![r#"{"token":"abc"}"#.into()],
interpreter: None,
script: None,
cache_ttl_secs: 0,
output_format: AuthOutputFormat::Json,
env: HashMap::new(),
inject,
timeout_secs: 5,
};
let provider = ati::core::manifest::Provider {
auth_type: AuthType::Bearer,
auth_generator: Some(gen),
..common::test_provider("inject_err_test", "http://unused.test")
};
let tool = common::test_tool("inject_err_tool", "/data", HttpMethod::Get);
let keyring = Keyring::empty();
let cache = AuthCache::new();
let ctx = GenContext::default();
let err = execute_tool_with_gen(
&provider,
&tool,
&HashMap::new(),
&keyring,
Some(&ctx),
Some(&cache),
)
.await
.unwrap_err();
let msg = format!("{err}");
assert!(
msg.contains("nonexistent.path") || msg.contains("not found"),
"Expected missing path error, got: {msg}"
);
}
#[tokio::test]
async fn test_auth_gen_script_type_through_execution() {
let upstream = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/data"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({"ok": true})))
.mount(&upstream)
.await;
let gen = AuthGenerator {
gen_type: AuthGenType::Script,
command: None,
args: vec![],
interpreter: Some("bash".into()),
script: Some("echo script-generated-token".into()),
cache_ttl_secs: 0,
output_format: AuthOutputFormat::Text,
env: HashMap::new(),
inject: HashMap::new(),
timeout_secs: 5,
};
let provider = ati::core::manifest::Provider {
auth_type: AuthType::Bearer,
auth_generator: Some(gen),
..common::test_provider("script_test", &upstream.uri())
};
let tool = common::test_tool("script_tool", "/data", HttpMethod::Get);
let keyring = Keyring::empty();
let cache = AuthCache::new();
let ctx = GenContext::default();
let result = execute_tool_with_gen(
&provider,
&tool,
&HashMap::new(),
&keyring,
Some(&ctx),
Some(&cache),
)
.await
.expect("script generator should succeed");
assert_eq!(result["ok"], true);
}
#[tokio::test]
async fn test_auth_gen_command_not_found() {
let gen = AuthGenerator {
gen_type: AuthGenType::Command,
command: Some("ati_nonexistent_binary_12345".into()),
args: vec![],
interpreter: None,
script: None,
cache_ttl_secs: 0,
output_format: AuthOutputFormat::Text,
env: HashMap::new(),
inject: HashMap::new(),
timeout_secs: 5,
};
let provider = ati::core::manifest::Provider {
auth_type: AuthType::Bearer,
auth_generator: Some(gen),
..common::test_provider("notfound_test", "http://unused.test")
};
let tool = common::test_tool("notfound_tool", "/data", HttpMethod::Get);
let keyring = Keyring::empty();
let cache = AuthCache::new();
let ctx = GenContext::default();
let err = execute_tool_with_gen(
&provider,
&tool,
&HashMap::new(),
&keyring,
Some(&ctx),
Some(&cache),
)
.await
.unwrap_err();
let msg = format!("{err}");
assert!(
msg.contains("auth_generator") || msg.contains("spawn") || msg.contains("No such file"),
"Expected spawn error, got: {msg}"
);
}
#[tokio::test]
async fn test_auth_gen_header_auth_type() {
let upstream = MockServer::start().await;
use wiremock::matchers::header;
Mock::given(method("GET"))
.and(path("/data"))
.and(header("X-Custom-Auth", "gen-header-token"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({"auth": "header"})))
.mount(&upstream)
.await;
let gen = common::test_auth_generator_command("gen-header-token");
let provider = ati::core::manifest::Provider {
auth_type: AuthType::Header,
auth_header_name: Some("X-Custom-Auth".into()),
auth_generator: Some(gen),
..common::test_provider("header_gen_test", &upstream.uri())
};
let tool = common::test_tool("header_gen_tool", "/data", HttpMethod::Get);
let keyring = Keyring::empty();
let cache = AuthCache::new();
let ctx = GenContext::default();
let result = execute_tool_with_gen(
&provider,
&tool,
&HashMap::new(),
&keyring,
Some(&ctx),
Some(&cache),
)
.await
.expect("header auth generator should succeed");
assert_eq!(result["auth"], "header");
}