mirror of
https://github.com/outbackdingo/optimclaw.git
synced 2026-08-25 14:53:34 +00:00
refactor: centralize test credential constants into testing::credentials (#829)
* refactor: centralize test credential constants into testing::credentials Scattered test credential strings (API keys, OAuth tokens, crypto keys, Telegram tokens, session tokens) across ~25 files made security auditing harder and created unnecessary duplication. Centralize all test-only fake credentials into a new `src/testing/credentials.rs` module with named constants and a shared `test_secrets_store()` helper. - Convert `src/testing.rs` to directory module (`src/testing/mod.rs`) - Add `src/testing/credentials.rs` with ~30 named constants - Replace hardcoded literals in 24 source files - Deduplicate `test_store()` helper (was copy-pasted in 3 files) - Leave leak_detector/shell/signature tests as-is (inline values aid readability for pattern detection tests) [skip-regression-check] Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * refactor: replace real Telegram bot token with obviously fake test stub Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * Update src/testing/credentials.rs Co-authored-by: Copilot <[email protected]> * Update src/testing/credentials.rs Co-authored-by: Copilot <[email protected]> * refactor: address PR review feedback on test credentials - Fix TEST_CRYPTO_KEY doc comment ("32-byte hex" → "32-character key string") - Rename confusing "real"/"fake" Anthropic constant names and values - Change TEST_STRIPE_KEY from "sk-live" to "sk_test_fake123" to avoid scanners - Use test_secrets_store() helper in orchestrator and http tool tests - Clarify config_round_trip.rs doc comment about integration test visibility Co-Authored-By: Claude Opus 4.6 <[email protected]> --------- Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]> Co-authored-by: Copilot <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.6
Copilot
parent
24d4fbb8a7
commit
76375f2eaa
@@ -365,6 +365,7 @@ pub trait ChannelSecretUpdater: Send + Sync {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::testing::credentials::TEST_REDACT_SECRET_123;
|
||||||
|
|
||||||
/// Stub tool that marks `"value"` as sensitive.
|
/// Stub tool that marks `"value"` as sensitive.
|
||||||
struct SecretTool;
|
struct SecretTool;
|
||||||
@@ -394,7 +395,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn tool_completed_redacts_sensitive_params_on_failure() {
|
fn tool_completed_redacts_sensitive_params_on_failure() {
|
||||||
let params = serde_json::json!({"name": "api_key", "value": "sk-secret-123"});
|
let params = serde_json::json!({"name": "api_key", "value": TEST_REDACT_SECRET_123});
|
||||||
let err: Result<String, crate::error::Error> =
|
let err: Result<String, crate::error::Error> =
|
||||||
Err(crate::error::ToolError::ExecutionFailed {
|
Err(crate::error::ToolError::ExecutionFailed {
|
||||||
name: "secret_save".into(),
|
name: "secret_save".into(),
|
||||||
@@ -429,7 +430,7 @@ mod tests {
|
|||||||
param_str
|
param_str
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
!param_str.contains("sk-secret-123"),
|
!param_str.contains(TEST_REDACT_SECRET_123),
|
||||||
"raw secret should not appear: {}",
|
"raw secret should not appear: {}",
|
||||||
param_str
|
param_str
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3059,6 +3059,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
use crate::channels::wasm::wrapper::{HttpResponse, WasmChannel};
|
use crate::channels::wasm::wrapper::{HttpResponse, WasmChannel};
|
||||||
use crate::pairing::PairingStore;
|
use crate::pairing::PairingStore;
|
||||||
|
use crate::testing::credentials::TEST_TELEGRAM_BOT_TOKEN;
|
||||||
use crate::tools::wasm::ResourceLimits;
|
use crate::tools::wasm::ResourceLimits;
|
||||||
|
|
||||||
fn create_test_channel() -> WasmChannel {
|
fn create_test_channel() -> WasmChannel {
|
||||||
@@ -4009,7 +4010,7 @@ mod tests {
|
|||||||
let mut creds = std::collections::HashMap::new();
|
let mut creds = std::collections::HashMap::new();
|
||||||
creds.insert(
|
creds.insert(
|
||||||
"TELEGRAM_BOT_TOKEN".to_string(),
|
"TELEGRAM_BOT_TOKEN".to_string(),
|
||||||
"8218490433:AAEZeUxwqZ5OO3mOCXv7fKvpdhDgsmBBNis".to_string(),
|
TEST_TELEGRAM_BOT_TOKEN.to_string(),
|
||||||
);
|
);
|
||||||
creds.insert("OTHER_SECRET".to_string(), "s3cret".to_string());
|
creds.insert("OTHER_SECRET".to_string(), "s3cret".to_string());
|
||||||
|
|
||||||
@@ -4022,13 +4023,15 @@ mod tests {
|
|||||||
Arc::new(PairingStore::new()),
|
Arc::new(PairingStore::new()),
|
||||||
);
|
);
|
||||||
|
|
||||||
let error = "HTTP request failed: error sending request for url \
|
let error = format!(
|
||||||
(https://api.telegram.org/bot8218490433:AAEZeUxwqZ5OO3mOCXv7fKvpdhDgsmBBNis/getUpdates)";
|
"HTTP request failed: error sending request for url \
|
||||||
|
(https://api.telegram.org/bot{TEST_TELEGRAM_BOT_TOKEN}/getUpdates)"
|
||||||
|
);
|
||||||
|
|
||||||
let redacted = store.redact_credentials(error);
|
let redacted = store.redact_credentials(&error);
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
!redacted.contains("8218490433:AAEZeUxwqZ5OO3mOCXv7fKvpdhDgsmBBNis"),
|
!redacted.contains(TEST_TELEGRAM_BOT_TOKEN),
|
||||||
"credential value should be redacted"
|
"credential value should be redacted"
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
|
|||||||
+27
-26
@@ -83,14 +83,15 @@ pub async fn auth_middleware(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::testing::credentials::{TEST_AUTH_SECRET_TOKEN, TEST_BEARER_TOKEN};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_auth_state_clone() {
|
fn test_auth_state_clone() {
|
||||||
let state = AuthState {
|
let state = AuthState {
|
||||||
token: "test-token".to_string(),
|
token: TEST_BEARER_TOKEN.to_string(),
|
||||||
};
|
};
|
||||||
let cloned = state.clone();
|
let cloned = state.clone();
|
||||||
assert_eq!(cloned.token, "test-token");
|
assert_eq!(cloned.token, TEST_BEARER_TOKEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
use axum::Router;
|
use axum::Router;
|
||||||
@@ -120,10 +121,10 @@ mod tests {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_valid_bearer_token_passes() {
|
async fn test_valid_bearer_token_passes() {
|
||||||
let app = test_app("secret-token");
|
let app = test_app(TEST_AUTH_SECRET_TOKEN);
|
||||||
let req = Request::builder()
|
let req = Request::builder()
|
||||||
.uri("/api/chat/events")
|
.uri("/api/chat/events")
|
||||||
.header("Authorization", "Bearer secret-token")
|
.header("Authorization", format!("Bearer {TEST_AUTH_SECRET_TOKEN}"))
|
||||||
.body(Body::empty())
|
.body(Body::empty())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let resp = app.oneshot(req).await.unwrap();
|
let resp = app.oneshot(req).await.unwrap();
|
||||||
@@ -132,7 +133,7 @@ mod tests {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_invalid_bearer_token_rejected() {
|
async fn test_invalid_bearer_token_rejected() {
|
||||||
let app = test_app("secret-token");
|
let app = test_app(TEST_AUTH_SECRET_TOKEN);
|
||||||
let req = Request::builder()
|
let req = Request::builder()
|
||||||
.uri("/api/chat/events")
|
.uri("/api/chat/events")
|
||||||
.header("Authorization", "Bearer wrong-token")
|
.header("Authorization", "Bearer wrong-token")
|
||||||
@@ -144,9 +145,9 @@ mod tests {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_query_token_allowed_for_chat_events() {
|
async fn test_query_token_allowed_for_chat_events() {
|
||||||
let app = test_app("secret-token");
|
let app = test_app(TEST_AUTH_SECRET_TOKEN);
|
||||||
let req = Request::builder()
|
let req = Request::builder()
|
||||||
.uri("/api/chat/events?token=secret-token")
|
.uri(format!("/api/chat/events?token={TEST_AUTH_SECRET_TOKEN}"))
|
||||||
.body(Body::empty())
|
.body(Body::empty())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let resp = app.oneshot(req).await.unwrap();
|
let resp = app.oneshot(req).await.unwrap();
|
||||||
@@ -155,9 +156,9 @@ mod tests {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_query_token_allowed_for_logs_events() {
|
async fn test_query_token_allowed_for_logs_events() {
|
||||||
let app = test_app("secret-token");
|
let app = test_app(TEST_AUTH_SECRET_TOKEN);
|
||||||
let req = Request::builder()
|
let req = Request::builder()
|
||||||
.uri("/api/logs/events?token=secret-token")
|
.uri(format!("/api/logs/events?token={TEST_AUTH_SECRET_TOKEN}"))
|
||||||
.body(Body::empty())
|
.body(Body::empty())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let resp = app.oneshot(req).await.unwrap();
|
let resp = app.oneshot(req).await.unwrap();
|
||||||
@@ -166,9 +167,9 @@ mod tests {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_query_token_allowed_for_ws_upgrade() {
|
async fn test_query_token_allowed_for_ws_upgrade() {
|
||||||
let app = test_app("secret-token");
|
let app = test_app(TEST_AUTH_SECRET_TOKEN);
|
||||||
let req = Request::builder()
|
let req = Request::builder()
|
||||||
.uri("/api/chat/ws?token=secret-token")
|
.uri(format!("/api/chat/ws?token={TEST_AUTH_SECRET_TOKEN}"))
|
||||||
.body(Body::empty())
|
.body(Body::empty())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let resp = app.oneshot(req).await.unwrap();
|
let resp = app.oneshot(req).await.unwrap();
|
||||||
@@ -202,9 +203,9 @@ mod tests {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_query_token_rejected_for_non_sse_get() {
|
async fn test_query_token_rejected_for_non_sse_get() {
|
||||||
let app = test_app("secret-token");
|
let app = test_app(TEST_AUTH_SECRET_TOKEN);
|
||||||
let req = Request::builder()
|
let req = Request::builder()
|
||||||
.uri("/api/chat/history?token=secret-token")
|
.uri(format!("/api/chat/history?token={TEST_AUTH_SECRET_TOKEN}"))
|
||||||
.body(Body::empty())
|
.body(Body::empty())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let resp = app.oneshot(req).await.unwrap();
|
let resp = app.oneshot(req).await.unwrap();
|
||||||
@@ -213,10 +214,10 @@ mod tests {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_query_token_rejected_for_post() {
|
async fn test_query_token_rejected_for_post() {
|
||||||
let app = test_app("secret-token");
|
let app = test_app(TEST_AUTH_SECRET_TOKEN);
|
||||||
let req = Request::builder()
|
let req = Request::builder()
|
||||||
.method(Method::POST)
|
.method(Method::POST)
|
||||||
.uri("/api/chat/send?token=secret-token")
|
.uri(format!("/api/chat/send?token={TEST_AUTH_SECRET_TOKEN}"))
|
||||||
.body(Body::empty())
|
.body(Body::empty())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let resp = app.oneshot(req).await.unwrap();
|
let resp = app.oneshot(req).await.unwrap();
|
||||||
@@ -225,7 +226,7 @@ mod tests {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_query_token_invalid_rejected() {
|
async fn test_query_token_invalid_rejected() {
|
||||||
let app = test_app("secret-token");
|
let app = test_app(TEST_AUTH_SECRET_TOKEN);
|
||||||
let req = Request::builder()
|
let req = Request::builder()
|
||||||
.uri("/api/chat/events?token=wrong-token")
|
.uri("/api/chat/events?token=wrong-token")
|
||||||
.body(Body::empty())
|
.body(Body::empty())
|
||||||
@@ -236,7 +237,7 @@ mod tests {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_no_auth_at_all_rejected() {
|
async fn test_no_auth_at_all_rejected() {
|
||||||
let app = test_app("secret-token");
|
let app = test_app(TEST_AUTH_SECRET_TOKEN);
|
||||||
let req = Request::builder()
|
let req = Request::builder()
|
||||||
.uri("/api/chat/events")
|
.uri("/api/chat/events")
|
||||||
.body(Body::empty())
|
.body(Body::empty())
|
||||||
@@ -247,11 +248,11 @@ mod tests {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_bearer_header_works_for_post() {
|
async fn test_bearer_header_works_for_post() {
|
||||||
let app = test_app("secret-token");
|
let app = test_app(TEST_AUTH_SECRET_TOKEN);
|
||||||
let req = Request::builder()
|
let req = Request::builder()
|
||||||
.method(Method::POST)
|
.method(Method::POST)
|
||||||
.uri("/api/chat/send")
|
.uri("/api/chat/send")
|
||||||
.header("Authorization", "Bearer secret-token")
|
.header("Authorization", format!("Bearer {TEST_AUTH_SECRET_TOKEN}"))
|
||||||
.body(Body::empty())
|
.body(Body::empty())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let resp = app.oneshot(req).await.unwrap();
|
let resp = app.oneshot(req).await.unwrap();
|
||||||
@@ -260,10 +261,10 @@ mod tests {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_bearer_prefix_case_insensitive() {
|
async fn test_bearer_prefix_case_insensitive() {
|
||||||
let app = test_app("secret-token");
|
let app = test_app(TEST_AUTH_SECRET_TOKEN);
|
||||||
let req = Request::builder()
|
let req = Request::builder()
|
||||||
.uri("/api/chat/events")
|
.uri("/api/chat/events")
|
||||||
.header("Authorization", "bearer secret-token")
|
.header("Authorization", format!("bearer {TEST_AUTH_SECRET_TOKEN}"))
|
||||||
.body(Body::empty())
|
.body(Body::empty())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let resp = app.oneshot(req).await.unwrap();
|
let resp = app.oneshot(req).await.unwrap();
|
||||||
@@ -272,10 +273,10 @@ mod tests {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_bearer_prefix_mixed_case() {
|
async fn test_bearer_prefix_mixed_case() {
|
||||||
let app = test_app("secret-token");
|
let app = test_app(TEST_AUTH_SECRET_TOKEN);
|
||||||
let req = Request::builder()
|
let req = Request::builder()
|
||||||
.uri("/api/chat/events")
|
.uri("/api/chat/events")
|
||||||
.header("Authorization", "BEARER secret-token")
|
.header("Authorization", format!("BEARER {TEST_AUTH_SECRET_TOKEN}"))
|
||||||
.body(Body::empty())
|
.body(Body::empty())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let resp = app.oneshot(req).await.unwrap();
|
let resp = app.oneshot(req).await.unwrap();
|
||||||
@@ -284,7 +285,7 @@ mod tests {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_empty_bearer_token_rejected() {
|
async fn test_empty_bearer_token_rejected() {
|
||||||
let app = test_app("secret-token");
|
let app = test_app(TEST_AUTH_SECRET_TOKEN);
|
||||||
let req = Request::builder()
|
let req = Request::builder()
|
||||||
.uri("/api/chat/events")
|
.uri("/api/chat/events")
|
||||||
.header("Authorization", "Bearer ")
|
.header("Authorization", "Bearer ")
|
||||||
@@ -296,10 +297,10 @@ mod tests {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_token_with_whitespace_rejected() {
|
async fn test_token_with_whitespace_rejected() {
|
||||||
let app = test_app("secret-token");
|
let app = test_app(TEST_AUTH_SECRET_TOKEN);
|
||||||
let req = Request::builder()
|
let req = Request::builder()
|
||||||
.uri("/api/chat/events")
|
.uri("/api/chat/events")
|
||||||
.header("Authorization", "Bearer secret-token")
|
.header("Authorization", format!("Bearer {TEST_AUTH_SECRET_TOKEN}"))
|
||||||
.body(Body::empty())
|
.body(Body::empty())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let resp = app.oneshot(req).await.unwrap();
|
let resp = app.oneshot(req).await.unwrap();
|
||||||
|
|||||||
@@ -2379,6 +2379,7 @@ struct GatewayStatusResponse {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::testing::credentials::TEST_GATEWAY_CRYPTO_KEY;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_build_turns_from_db_messages_complete() {
|
fn test_build_turns_from_db_messages_complete() {
|
||||||
@@ -2552,7 +2553,7 @@ mod tests {
|
|||||||
// Build an ExtensionManager so the handler can look up flows
|
// Build an ExtensionManager so the handler can look up flows
|
||||||
let secrets = Arc::new(crate::secrets::InMemorySecretsStore::new(Arc::new(
|
let secrets = Arc::new(crate::secrets::InMemorySecretsStore::new(Arc::new(
|
||||||
crate::secrets::SecretsCrypto::new(secrecy::SecretString::from(
|
crate::secrets::SecretsCrypto::new(secrecy::SecretString::from(
|
||||||
"test-key-at-least-32-chars-long!!".to_string(),
|
TEST_GATEWAY_CRYPTO_KEY.to_string(),
|
||||||
))
|
))
|
||||||
.expect("crypto"),
|
.expect("crypto"),
|
||||||
)));
|
)));
|
||||||
@@ -2602,7 +2603,7 @@ mod tests {
|
|||||||
let secrets: Arc<dyn crate::secrets::SecretsStore + Send + Sync> =
|
let secrets: Arc<dyn crate::secrets::SecretsStore + Send + Sync> =
|
||||||
Arc::new(crate::secrets::InMemorySecretsStore::new(Arc::new(
|
Arc::new(crate::secrets::InMemorySecretsStore::new(Arc::new(
|
||||||
crate::secrets::SecretsCrypto::new(secrecy::SecretString::from(
|
crate::secrets::SecretsCrypto::new(secrecy::SecretString::from(
|
||||||
"test-key-at-least-32-chars-long!!".to_string(),
|
TEST_GATEWAY_CRYPTO_KEY.to_string(),
|
||||||
))
|
))
|
||||||
.expect("crypto"),
|
.expect("crypto"),
|
||||||
)));
|
)));
|
||||||
@@ -2708,7 +2709,7 @@ mod tests {
|
|||||||
let secrets: Arc<dyn crate::secrets::SecretsStore + Send + Sync> =
|
let secrets: Arc<dyn crate::secrets::SecretsStore + Send + Sync> =
|
||||||
Arc::new(crate::secrets::InMemorySecretsStore::new(Arc::new(
|
Arc::new(crate::secrets::InMemorySecretsStore::new(Arc::new(
|
||||||
crate::secrets::SecretsCrypto::new(secrecy::SecretString::from(
|
crate::secrets::SecretsCrypto::new(secrecy::SecretString::from(
|
||||||
"test-key-at-least-32-chars-long!!".to_string(),
|
TEST_GATEWAY_CRYPTO_KEY.to_string(),
|
||||||
))
|
))
|
||||||
.expect("crypto"),
|
.expect("crypto"),
|
||||||
)));
|
)));
|
||||||
|
|||||||
@@ -154,6 +154,7 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::config::helpers::ENV_MUTEX;
|
use crate::config::helpers::ENV_MUTEX;
|
||||||
use crate::settings::{EmbeddingsSettings, Settings};
|
use crate::settings::{EmbeddingsSettings, Settings};
|
||||||
|
use crate::testing::credentials::*;
|
||||||
|
|
||||||
/// Clear all embedding-related env vars.
|
/// Clear all embedding-related env vars.
|
||||||
fn clear_embedding_env() {
|
fn clear_embedding_env() {
|
||||||
@@ -173,7 +174,7 @@ mod tests {
|
|||||||
clear_embedding_env();
|
clear_embedding_env();
|
||||||
// SAFETY: Under ENV_MUTEX, no concurrent env access.
|
// SAFETY: Under ENV_MUTEX, no concurrent env access.
|
||||||
unsafe {
|
unsafe {
|
||||||
std::env::set_var("OPENAI_API_KEY", "sk-test-key-for-issue-129");
|
std::env::set_var("OPENAI_API_KEY", TEST_OPENAI_API_KEY_ISSUE_129);
|
||||||
}
|
}
|
||||||
|
|
||||||
let settings = Settings {
|
let settings = Settings {
|
||||||
|
|||||||
+8
-7
@@ -389,6 +389,7 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::config::helpers::ENV_MUTEX;
|
use crate::config::helpers::ENV_MUTEX;
|
||||||
use crate::settings::Settings;
|
use crate::settings::Settings;
|
||||||
|
use crate::testing::credentials::*;
|
||||||
|
|
||||||
/// Clear all openai-compatible-related env vars.
|
/// Clear all openai-compatible-related env vars.
|
||||||
fn clear_openai_compatible_env() {
|
fn clear_openai_compatible_env() {
|
||||||
@@ -657,7 +658,7 @@ mod tests {
|
|||||||
// SAFETY: Under ENV_MUTEX.
|
// SAFETY: Under ENV_MUTEX.
|
||||||
unsafe {
|
unsafe {
|
||||||
std::env::set_var("LLM_BACKEND", "open_ai");
|
std::env::set_var("LLM_BACKEND", "open_ai");
|
||||||
std::env::set_var("OPENAI_API_KEY", "test-key");
|
std::env::set_var("OPENAI_API_KEY", TEST_API_KEY);
|
||||||
}
|
}
|
||||||
|
|
||||||
let settings = Settings::default();
|
let settings = Settings::default();
|
||||||
@@ -791,7 +792,7 @@ mod tests {
|
|||||||
clear_anthropic_env();
|
clear_anthropic_env();
|
||||||
// SAFETY: Under ENV_MUTEX.
|
// SAFETY: Under ENV_MUTEX.
|
||||||
unsafe {
|
unsafe {
|
||||||
std::env::set_var("ANTHROPIC_OAUTH_TOKEN", "sk-ant-oat01-test-token");
|
std::env::set_var("ANTHROPIC_OAUTH_TOKEN", TEST_ANTHROPIC_OAUTH_TOKEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
let settings = Settings {
|
let settings = Settings {
|
||||||
@@ -815,7 +816,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
provider.oauth_token.as_ref().unwrap().expose_secret(),
|
provider.oauth_token.as_ref().unwrap().expose_secret(),
|
||||||
"sk-ant-oat01-test-token"
|
TEST_ANTHROPIC_OAUTH_TOKEN
|
||||||
);
|
);
|
||||||
|
|
||||||
clear_anthropic_env();
|
clear_anthropic_env();
|
||||||
@@ -829,8 +830,8 @@ mod tests {
|
|||||||
clear_anthropic_env();
|
clear_anthropic_env();
|
||||||
// SAFETY: Under ENV_MUTEX.
|
// SAFETY: Under ENV_MUTEX.
|
||||||
unsafe {
|
unsafe {
|
||||||
std::env::set_var("ANTHROPIC_API_KEY", "sk-ant-real-key");
|
std::env::set_var("ANTHROPIC_API_KEY", TEST_ANTHROPIC_API_KEY);
|
||||||
std::env::set_var("ANTHROPIC_OAUTH_TOKEN", "sk-ant-oat01-test-token");
|
std::env::set_var("ANTHROPIC_OAUTH_TOKEN", TEST_ANTHROPIC_OAUTH_TOKEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
let settings = Settings {
|
let settings = Settings {
|
||||||
@@ -845,7 +846,7 @@ mod tests {
|
|||||||
.api_key
|
.api_key
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|k| k.expose_secret().to_string()),
|
.map(|k| k.expose_secret().to_string()),
|
||||||
Some("sk-ant-real-key".to_string()),
|
Some(TEST_ANTHROPIC_API_KEY.to_string()),
|
||||||
"real API key should take priority over OAuth placeholder"
|
"real API key should take priority over OAuth placeholder"
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
@@ -862,7 +863,7 @@ mod tests {
|
|||||||
clear_anthropic_env();
|
clear_anthropic_env();
|
||||||
// SAFETY: Under ENV_MUTEX.
|
// SAFETY: Under ENV_MUTEX.
|
||||||
unsafe {
|
unsafe {
|
||||||
std::env::set_var("ANTHROPIC_OAUTH_TOKEN", "sk-ant-oat01-test-token");
|
std::env::set_var("ANTHROPIC_OAUTH_TOKEN", TEST_ANTHROPIC_OAUTH_TOKEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
let settings = Settings {
|
let settings = Settings {
|
||||||
|
|||||||
+17
-10
@@ -272,6 +272,7 @@ fn parse_oauth_access_token(json: &str) -> Option<String> {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::config::sandbox::*;
|
use crate::config::sandbox::*;
|
||||||
|
use crate::testing::credentials::*;
|
||||||
|
|
||||||
// ── SandboxModeConfig defaults ──────────────────────────────────
|
// ── SandboxModeConfig defaults ──────────────────────────────────
|
||||||
|
|
||||||
@@ -405,9 +406,12 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_oauth_token_valid() {
|
fn parse_oauth_token_valid() {
|
||||||
let json = r#"{"claudeAiOauth": {"accessToken": "sk-ant-oat01-fake"}}"#;
|
let json = format!(
|
||||||
let token = parse_oauth_access_token(json);
|
r#"{{"claudeAiOauth": {{"accessToken": "{}"}}}}"#,
|
||||||
assert_eq!(token, Some("sk-ant-oat01-fake".to_string()));
|
TEST_ANTHROPIC_OAUTH_BASIC
|
||||||
|
);
|
||||||
|
let token = parse_oauth_access_token(&json);
|
||||||
|
assert_eq!(token, Some(TEST_ANTHROPIC_OAUTH_BASIC.to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -434,16 +438,19 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_oauth_token_nested_extra_fields() {
|
fn parse_oauth_token_nested_extra_fields() {
|
||||||
let json = r#"{
|
let json = format!(
|
||||||
"claudeAiOauth": {
|
r#"{{
|
||||||
"accessToken": "sk-ant-oat01-real-token",
|
"claudeAiOauth": {{
|
||||||
|
"accessToken": "{}",
|
||||||
"refreshToken": "rt-abc",
|
"refreshToken": "rt-abc",
|
||||||
"expiresAt": 1700000000
|
"expiresAt": 1700000000
|
||||||
}
|
}}
|
||||||
}"#;
|
}}"#,
|
||||||
|
TEST_ANTHROPIC_OAUTH_NESTED
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_oauth_access_token(json),
|
parse_oauth_access_token(&json),
|
||||||
Some("sk-ant-oat01-real-token".to_string())
|
Some(TEST_ANTHROPIC_OAUTH_NESTED.to_string())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3990,6 +3990,7 @@ mod tests {
|
|||||||
channels_dir: std::path::PathBuf,
|
channels_dir: std::path::PathBuf,
|
||||||
) -> ExtensionManager {
|
) -> ExtensionManager {
|
||||||
use crate::secrets::{InMemorySecretsStore, SecretsCrypto};
|
use crate::secrets::{InMemorySecretsStore, SecretsCrypto};
|
||||||
|
use crate::testing::credentials::TEST_CRYPTO_KEY;
|
||||||
use crate::tools::ToolRegistry;
|
use crate::tools::ToolRegistry;
|
||||||
use crate::tools::mcp::process::McpProcessManager;
|
use crate::tools::mcp::process::McpProcessManager;
|
||||||
use crate::tools::mcp::session::McpSessionManager;
|
use crate::tools::mcp::session::McpSessionManager;
|
||||||
@@ -3997,8 +3998,7 @@ mod tests {
|
|||||||
std::fs::create_dir_all(&tools_dir).ok();
|
std::fs::create_dir_all(&tools_dir).ok();
|
||||||
std::fs::create_dir_all(&channels_dir).ok();
|
std::fs::create_dir_all(&channels_dir).ok();
|
||||||
|
|
||||||
let master_key =
|
let master_key = secrecy::SecretString::from(TEST_CRYPTO_KEY.to_string());
|
||||||
secrecy::SecretString::from("0123456789abcdef0123456789abcdef".to_string());
|
|
||||||
let crypto = Arc::new(SecretsCrypto::new(master_key).unwrap());
|
let crypto = Arc::new(SecretsCrypto::new(master_key).unwrap());
|
||||||
|
|
||||||
ExtensionManager::new(
|
ExtensionManager::new(
|
||||||
|
|||||||
+10
-7
@@ -627,6 +627,9 @@ pub async fn create_session_manager(config: SessionConfig) -> Arc<SessionManager
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::testing::credentials::{
|
||||||
|
TEST_SESSION_NEARAI_ABC, TEST_SESSION_NEARAI_XYZ, TEST_SESSION_TOKEN,
|
||||||
|
};
|
||||||
use secrecy::ExposeSecret;
|
use secrecy::ExposeSecret;
|
||||||
use tempfile::tempdir;
|
use tempfile::tempdir;
|
||||||
|
|
||||||
@@ -647,28 +650,28 @@ mod tests {
|
|||||||
|
|
||||||
// Save a token
|
// Save a token
|
||||||
manager
|
manager
|
||||||
.save_session("test_token_123", Some("near"))
|
.save_session(TEST_SESSION_TOKEN, Some("near"))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
manager
|
manager
|
||||||
.set_token(SecretString::from("test_token_123"))
|
.set_token(SecretString::from(TEST_SESSION_TOKEN))
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
// Verify it's set
|
// Verify it's set
|
||||||
assert!(manager.has_token().await);
|
assert!(manager.has_token().await);
|
||||||
let token = manager.get_token().await.unwrap();
|
let token = manager.get_token().await.unwrap();
|
||||||
assert_eq!(token.expose_secret(), "test_token_123");
|
assert_eq!(token.expose_secret(), TEST_SESSION_TOKEN);
|
||||||
|
|
||||||
// Create new manager and verify it loads the token
|
// Create new manager and verify it loads the token
|
||||||
let manager2 = SessionManager::new_async(config).await;
|
let manager2 = SessionManager::new_async(config).await;
|
||||||
assert!(manager2.has_token().await);
|
assert!(manager2.has_token().await);
|
||||||
let token2 = manager2.get_token().await.unwrap();
|
let token2 = manager2.get_token().await.unwrap();
|
||||||
assert_eq!(token2.expose_secret(), "test_token_123");
|
assert_eq!(token2.expose_secret(), TEST_SESSION_TOKEN);
|
||||||
|
|
||||||
// Verify file contents
|
// Verify file contents
|
||||||
let data: SessionData =
|
let data: SessionData =
|
||||||
serde_json::from_str(&std::fs::read_to_string(&session_path).unwrap()).unwrap();
|
serde_json::from_str(&std::fs::read_to_string(&session_path).unwrap()).unwrap();
|
||||||
assert_eq!(data.session_token, "test_token_123");
|
assert_eq!(data.session_token, TEST_SESSION_TOKEN);
|
||||||
assert_eq!(data.auth_provider, Some("near".to_string()));
|
assert_eq!(data.auth_provider, Some("near".to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -689,7 +692,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_session_data_serde_roundtrip_with_auth_provider() {
|
fn test_session_data_serde_roundtrip_with_auth_provider() {
|
||||||
let original = SessionData {
|
let original = SessionData {
|
||||||
session_token: "sess_abc123".to_string(),
|
session_token: TEST_SESSION_NEARAI_ABC.to_string(),
|
||||||
created_at: Utc::now(),
|
created_at: Utc::now(),
|
||||||
auth_provider: Some("github".to_string()),
|
auth_provider: Some("github".to_string()),
|
||||||
};
|
};
|
||||||
@@ -703,7 +706,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_session_data_serde_roundtrip_without_auth_provider() {
|
fn test_session_data_serde_roundtrip_without_auth_provider() {
|
||||||
let original = SessionData {
|
let original = SessionData {
|
||||||
session_token: "sess_xyz789".to_string(),
|
session_token: TEST_SESSION_NEARAI_XYZ.to_string(),
|
||||||
created_at: Utc::now(),
|
created_at: Utc::now(),
|
||||||
auth_provider: None,
|
auth_provider: None,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -661,12 +661,9 @@ mod tests {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn credentials_returns_secrets_when_store_configured() {
|
async fn credentials_returns_secrets_when_store_configured() {
|
||||||
|
use crate::testing::credentials::test_secrets_store;
|
||||||
use secrecy::SecretString;
|
use secrecy::SecretString;
|
||||||
let key = "0123456789abcdef0123456789abcdef";
|
let secrets_store = Arc::new(test_secrets_store());
|
||||||
let crypto = Arc::new(
|
|
||||||
crate::secrets::SecretsCrypto::new(SecretString::from(key.to_string())).unwrap(),
|
|
||||||
);
|
|
||||||
let secrets_store = Arc::new(crate::secrets::InMemorySecretsStore::new(crypto));
|
|
||||||
|
|
||||||
// Create a secret
|
// Create a secret
|
||||||
secrets_store
|
secrets_store
|
||||||
|
|||||||
@@ -153,11 +153,11 @@ mod tests {
|
|||||||
use secrecy::SecretString;
|
use secrecy::SecretString;
|
||||||
|
|
||||||
use crate::secrets::crypto::SecretsCrypto;
|
use crate::secrets::crypto::SecretsCrypto;
|
||||||
|
use crate::testing::credentials::TEST_CRYPTO_KEY;
|
||||||
|
|
||||||
fn test_crypto() -> SecretsCrypto {
|
fn test_crypto() -> SecretsCrypto {
|
||||||
// 32-byte test key
|
// 32-byte test key
|
||||||
let key = "0123456789abcdef0123456789abcdef";
|
SecretsCrypto::new(SecretString::from(TEST_CRYPTO_KEY.to_string())).unwrap()
|
||||||
SecretsCrypto::new(SecretString::from(key.to_string())).unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
+15
-14
@@ -802,30 +802,25 @@ pub mod in_memory {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use secrecy::SecretString;
|
|
||||||
|
|
||||||
use crate::secrets::crypto::SecretsCrypto;
|
|
||||||
use crate::secrets::store::SecretsStore;
|
use crate::secrets::store::SecretsStore;
|
||||||
use crate::secrets::store::in_memory::InMemorySecretsStore;
|
|
||||||
use crate::secrets::types::CreateSecretParams;
|
use crate::secrets::types::CreateSecretParams;
|
||||||
|
use crate::testing::credentials::{
|
||||||
|
TEST_OPENAI_API_KEY_SHORT, TEST_SECRET_VALUE, TEST_STRIPE_KEY, test_secrets_store,
|
||||||
|
};
|
||||||
|
|
||||||
fn test_store() -> InMemorySecretsStore {
|
fn test_store() -> crate::secrets::store::in_memory::InMemorySecretsStore {
|
||||||
let key = "0123456789abcdef0123456789abcdef";
|
test_secrets_store()
|
||||||
let crypto = Arc::new(SecretsCrypto::new(SecretString::from(key.to_string())).unwrap());
|
|
||||||
InMemorySecretsStore::new(crypto)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_create_and_get() {
|
async fn test_create_and_get() {
|
||||||
let store = test_store();
|
let store = test_store();
|
||||||
let params = CreateSecretParams::new("api_key", "sk-test-12345");
|
let params = CreateSecretParams::new("api_key", TEST_SECRET_VALUE);
|
||||||
|
|
||||||
store.create("user1", params).await.unwrap();
|
store.create("user1", params).await.unwrap();
|
||||||
|
|
||||||
let decrypted = store.get_decrypted("user1", "api_key").await.unwrap();
|
let decrypted = store.get_decrypted("user1", "api_key").await.unwrap();
|
||||||
assert_eq!(decrypted.expose(), "sk-test-12345");
|
assert_eq!(decrypted.expose(), TEST_SECRET_VALUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
@@ -878,11 +873,17 @@ mod tests {
|
|||||||
async fn test_is_accessible() {
|
async fn test_is_accessible() {
|
||||||
let store = test_store();
|
let store = test_store();
|
||||||
store
|
store
|
||||||
.create("user1", CreateSecretParams::new("openai_key", "sk-test"))
|
.create(
|
||||||
|
"user1",
|
||||||
|
CreateSecretParams::new("openai_key", TEST_OPENAI_API_KEY_SHORT),
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
store
|
store
|
||||||
.create("user1", CreateSecretParams::new("stripe_key", "sk-live"))
|
.create(
|
||||||
|
"user1",
|
||||||
|
CreateSecretParams::new("stripe_key", TEST_STRIPE_KEY),
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,134 @@
|
|||||||
|
//! Centralized fake credential constants for tests.
|
||||||
|
//!
|
||||||
|
//! All values here are intentionally fake. Centralizing them makes security
|
||||||
|
//! audits trivial (one file to verify) and eliminates duplication across
|
||||||
|
//! the test suite.
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use secrecy::SecretString;
|
||||||
|
|
||||||
|
use crate::secrets::{InMemorySecretsStore, SecretsCrypto};
|
||||||
|
|
||||||
|
// ── Encryption keys ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// 32-character key string for `SecretsCrypto::new()` in tests.
|
||||||
|
pub const TEST_CRYPTO_KEY: &str = "0123456789abcdef0123456789abcdef";
|
||||||
|
|
||||||
|
/// 32+ char key for web gateway `SecretsCrypto` in tests.
|
||||||
|
pub const TEST_GATEWAY_CRYPTO_KEY: &str = "test-key-at-least-32-chars-long!!";
|
||||||
|
|
||||||
|
// ── OpenAI-style API keys ────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Generic OpenAI-style test API key.
|
||||||
|
pub const TEST_OPENAI_API_KEY: &str = "sk-test123";
|
||||||
|
|
||||||
|
/// OpenAI API key with longer format (config round-trip tests).
|
||||||
|
pub const TEST_OPENAI_API_KEY_LONG: &str = "sk-test-key-1234567890";
|
||||||
|
|
||||||
|
/// Short OpenAI-style key for secrets store accessibility tests.
|
||||||
|
pub const TEST_OPENAI_API_KEY_SHORT: &str = "sk-test";
|
||||||
|
|
||||||
|
/// OpenAI API key used in embeddings config issue-129 test.
|
||||||
|
pub const TEST_OPENAI_API_KEY_ISSUE_129: &str = "sk-test-key-for-issue-129";
|
||||||
|
|
||||||
|
// ── Anthropic keys ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Anthropic OAuth token for config tests.
|
||||||
|
pub const TEST_ANTHROPIC_OAUTH_TOKEN: &str = "sk-ant-oat01-test-token";
|
||||||
|
|
||||||
|
/// Anthropic API key for priority tests.
|
||||||
|
pub const TEST_ANTHROPIC_API_KEY: &str = "sk-ant-priority-key";
|
||||||
|
|
||||||
|
/// Anthropic OAuth token for sandbox config parse tests.
|
||||||
|
pub const TEST_ANTHROPIC_OAUTH_BASIC: &str = "sk-ant-oat01-basic";
|
||||||
|
|
||||||
|
/// Anthropic OAuth token in nested JSON parse test.
|
||||||
|
pub const TEST_ANTHROPIC_OAUTH_NESTED: &str = "sk-ant-oat01-primary-token";
|
||||||
|
|
||||||
|
// ── Google OAuth ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Google OAuth access token (standard test).
|
||||||
|
pub const TEST_GOOGLE_OAUTH_TOKEN: &str = "ya29.test-token";
|
||||||
|
|
||||||
|
/// Google OAuth access token (fresh/non-expired variant).
|
||||||
|
pub const TEST_GOOGLE_OAUTH_FRESH: &str = "ya29.fresh-token";
|
||||||
|
|
||||||
|
/// Google OAuth access token (legacy/no-expiry variant).
|
||||||
|
pub const TEST_GOOGLE_OAUTH_LEGACY: &str = "ya29.legacy-token";
|
||||||
|
|
||||||
|
// ── GitHub ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// GitHub personal access token (test).
|
||||||
|
pub const TEST_GITHUB_TOKEN: &str = "ghp_test123";
|
||||||
|
|
||||||
|
// ── Telegram ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Telegram bot token for credential redaction tests.
|
||||||
|
pub const TEST_TELEGRAM_BOT_TOKEN: &str = "telegram-test-bot-token-not-a-real-token";
|
||||||
|
|
||||||
|
// ── OAuth client credentials ────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// OAuth client ID for token refresh tests.
|
||||||
|
pub const TEST_OAUTH_CLIENT_ID: &str = "test-client-id";
|
||||||
|
|
||||||
|
/// OAuth client secret for token refresh tests.
|
||||||
|
pub const TEST_OAUTH_CLIENT_SECRET: &str = "test-client-secret";
|
||||||
|
|
||||||
|
// ── Bearer/auth tokens ──────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Generic test bearer token.
|
||||||
|
pub const TEST_BEARER_TOKEN: &str = "test-token";
|
||||||
|
|
||||||
|
/// Bearer token with suffix (wasm wrapper credential injection).
|
||||||
|
pub const TEST_BEARER_TOKEN_123: &str = "test-token-123";
|
||||||
|
|
||||||
|
/// Auth token used by web gateway middleware tests.
|
||||||
|
pub const TEST_AUTH_SECRET_TOKEN: &str = "secret-token";
|
||||||
|
|
||||||
|
// ── Stripe ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Stripe-style test key.
|
||||||
|
pub const TEST_STRIPE_KEY: &str = "sk_test_fake123";
|
||||||
|
|
||||||
|
// ── Redaction test values ───────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Secret-prefixed key for redaction/sanitization tests.
|
||||||
|
pub const TEST_REDACT_SECRET: &str = "sk-secret";
|
||||||
|
|
||||||
|
/// Secret-prefixed key with suffix for redaction tests.
|
||||||
|
pub const TEST_REDACT_SECRET_123: &str = "sk-secret-123";
|
||||||
|
|
||||||
|
// ── Session tokens ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Generic session token for persistence tests.
|
||||||
|
pub const TEST_SESSION_TOKEN: &str = "test_token_123";
|
||||||
|
|
||||||
|
/// NEAR AI session token variant A.
|
||||||
|
pub const TEST_SESSION_NEARAI_ABC: &str = "sess_abc123";
|
||||||
|
|
||||||
|
/// NEAR AI session token variant B.
|
||||||
|
pub const TEST_SESSION_NEARAI_XYZ: &str = "sess_xyz789";
|
||||||
|
|
||||||
|
// ── Generic ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Generic test API key for LLM config, embedding config, nearai tests.
|
||||||
|
pub const TEST_API_KEY: &str = "test-key";
|
||||||
|
|
||||||
|
/// Stored secret value for create-and-get tests.
|
||||||
|
pub const TEST_SECRET_VALUE: &str = "sk-test-12345";
|
||||||
|
|
||||||
|
/// HTTP webhook secret for channel tests.
|
||||||
|
pub const TEST_HTTP_SECRET: &str = "test-secret-123";
|
||||||
|
|
||||||
|
// ── Helpers ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Create an `InMemorySecretsStore` backed by [`TEST_CRYPTO_KEY`].
|
||||||
|
///
|
||||||
|
/// Replaces the duplicated `test_store()` pattern found across multiple
|
||||||
|
/// test modules.
|
||||||
|
pub fn test_secrets_store() -> InMemorySecretsStore {
|
||||||
|
let crypto =
|
||||||
|
Arc::new(SecretsCrypto::new(SecretString::from(TEST_CRYPTO_KEY.to_string())).unwrap());
|
||||||
|
InMemorySecretsStore::new(crypto)
|
||||||
|
}
|
||||||
@@ -18,6 +18,8 @@
|
|||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
|
pub mod credentials;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
|
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
|
||||||
@@ -768,11 +768,11 @@ mod tests {
|
|||||||
/// Create a stub manager for schema tests (these don't call execute).
|
/// Create a stub manager for schema tests (these don't call execute).
|
||||||
fn test_manager_stub() -> Arc<ExtensionManager> {
|
fn test_manager_stub() -> Arc<ExtensionManager> {
|
||||||
use crate::secrets::{InMemorySecretsStore, SecretsCrypto};
|
use crate::secrets::{InMemorySecretsStore, SecretsCrypto};
|
||||||
|
use crate::testing::credentials::TEST_CRYPTO_KEY;
|
||||||
use crate::tools::ToolRegistry;
|
use crate::tools::ToolRegistry;
|
||||||
use crate::tools::mcp::session::McpSessionManager;
|
use crate::tools::mcp::session::McpSessionManager;
|
||||||
|
|
||||||
let master_key =
|
let master_key = secrecy::SecretString::from(TEST_CRYPTO_KEY.to_string());
|
||||||
secrecy::SecretString::from("0123456789abcdef0123456789abcdef".to_string());
|
|
||||||
let crypto = Arc::new(SecretsCrypto::new(master_key).unwrap());
|
let crypto = Arc::new(SecretsCrypto::new(master_key).unwrap());
|
||||||
|
|
||||||
Arc::new(ExtensionManager::new(
|
Arc::new(ExtensionManager::new(
|
||||||
|
|||||||
@@ -609,6 +609,7 @@ impl Tool for HttpTool {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::testing::credentials::{TEST_OPENAI_API_KEY, test_secrets_store};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_http_tool_schema_headers_is_array() {
|
fn test_http_tool_schema_headers_is_array() {
|
||||||
@@ -868,12 +869,7 @@ mod tests {
|
|||||||
let tool = HttpTool::new().with_credentials(
|
let tool = HttpTool::new().with_credentials(
|
||||||
registry,
|
registry,
|
||||||
// secrets_store is not used in requires_approval, just needs to be present
|
// secrets_store is not used in requires_approval, just needs to be present
|
||||||
Arc::new(crate::secrets::InMemorySecretsStore::new(Arc::new(
|
Arc::new(test_secrets_store()),
|
||||||
crate::secrets::SecretsCrypto::new(secrecy::SecretString::from(
|
|
||||||
"0123456789abcdef0123456789abcdef".to_string(),
|
|
||||||
))
|
|
||||||
.unwrap(),
|
|
||||||
))),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let params = serde_json::json!({
|
let params = serde_json::json!({
|
||||||
@@ -890,15 +886,7 @@ mod tests {
|
|||||||
let registry = Arc::new(SharedCredentialRegistry::new());
|
let registry = Arc::new(SharedCredentialRegistry::new());
|
||||||
// Empty registry - no credential mappings
|
// Empty registry - no credential mappings
|
||||||
|
|
||||||
let tool = HttpTool::new().with_credentials(
|
let tool = HttpTool::new().with_credentials(registry, Arc::new(test_secrets_store()));
|
||||||
registry,
|
|
||||||
Arc::new(crate::secrets::InMemorySecretsStore::new(Arc::new(
|
|
||||||
crate::secrets::SecretsCrypto::new(secrecy::SecretString::from(
|
|
||||||
"0123456789abcdef0123456789abcdef".to_string(),
|
|
||||||
))
|
|
||||||
.unwrap(),
|
|
||||||
))),
|
|
||||||
);
|
|
||||||
|
|
||||||
let params = serde_json::json!({
|
let params = serde_json::json!({
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
@@ -926,7 +914,7 @@ mod tests {
|
|||||||
let params = serde_json::json!({
|
let params = serde_json::json!({
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"url": "https://example.com",
|
"url": "https://example.com",
|
||||||
"headers": {"X-Custom": "Bearer sk-test123"}
|
"headers": {"X-Custom": format!("Bearer {TEST_OPENAI_API_KEY}")}
|
||||||
});
|
});
|
||||||
assert_eq!(tool.requires_approval(¶ms), ApprovalRequirement::Always);
|
assert_eq!(tool.requires_approval(¶ms), ApprovalRequirement::Always);
|
||||||
}
|
}
|
||||||
@@ -957,15 +945,7 @@ mod tests {
|
|||||||
let registry = Arc::new(SharedCredentialRegistry::new());
|
let registry = Arc::new(SharedCredentialRegistry::new());
|
||||||
registry.add_mappings(vec![CredentialMapping::bearer("test_key", "api.test.com")]);
|
registry.add_mappings(vec![CredentialMapping::bearer("test_key", "api.test.com")]);
|
||||||
|
|
||||||
let tool = HttpTool::new().with_credentials(
|
let tool = HttpTool::new().with_credentials(registry, Arc::new(test_secrets_store()));
|
||||||
registry,
|
|
||||||
Arc::new(crate::secrets::InMemorySecretsStore::new(Arc::new(
|
|
||||||
crate::secrets::SecretsCrypto::new(secrecy::SecretString::from(
|
|
||||||
"0123456789abcdef0123456789abcdef".to_string(),
|
|
||||||
))
|
|
||||||
.unwrap(),
|
|
||||||
))),
|
|
||||||
);
|
|
||||||
|
|
||||||
// These calls should not panic in multi-thread runtime
|
// These calls should not panic in multi-thread runtime
|
||||||
let params_no_auth = serde_json::json!({
|
let params_no_auth = serde_json::json!({
|
||||||
|
|||||||
@@ -1748,14 +1748,10 @@ mod tests {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_parse_credentials_missing_secret() {
|
async fn test_parse_credentials_missing_secret() {
|
||||||
use crate::secrets::{InMemorySecretsStore, SecretsCrypto};
|
use crate::testing::credentials::test_secrets_store;
|
||||||
use secrecy::SecretString;
|
|
||||||
|
|
||||||
let manager = Arc::new(ContextManager::new(5));
|
let manager = Arc::new(ContextManager::new(5));
|
||||||
let key = "0123456789abcdef0123456789abcdef";
|
let secrets: Arc<dyn SecretsStore + Send + Sync> = Arc::new(test_secrets_store());
|
||||||
let crypto = Arc::new(SecretsCrypto::new(SecretString::from(key.to_string())).unwrap());
|
|
||||||
let secrets: Arc<dyn SecretsStore + Send + Sync> =
|
|
||||||
Arc::new(InMemorySecretsStore::new(crypto));
|
|
||||||
|
|
||||||
let tool = CreateJobTool::new(manager).with_secrets(Arc::clone(&secrets));
|
let tool = CreateJobTool::new(manager).with_secrets(Arc::clone(&secrets));
|
||||||
|
|
||||||
@@ -1772,20 +1768,17 @@ mod tests {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_parse_credentials_valid() {
|
async fn test_parse_credentials_valid() {
|
||||||
use crate::secrets::{CreateSecretParams, InMemorySecretsStore, SecretsCrypto};
|
use crate::secrets::CreateSecretParams;
|
||||||
use secrecy::SecretString;
|
use crate::testing::credentials::{TEST_GITHUB_TOKEN, test_secrets_store};
|
||||||
|
|
||||||
let manager = Arc::new(ContextManager::new(5));
|
let manager = Arc::new(ContextManager::new(5));
|
||||||
let key = "0123456789abcdef0123456789abcdef";
|
let secrets: Arc<dyn SecretsStore + Send + Sync> = Arc::new(test_secrets_store());
|
||||||
let crypto = Arc::new(SecretsCrypto::new(SecretString::from(key.to_string())).unwrap());
|
|
||||||
let secrets: Arc<dyn SecretsStore + Send + Sync> =
|
|
||||||
Arc::new(InMemorySecretsStore::new(Arc::clone(&crypto)));
|
|
||||||
|
|
||||||
// Store a secret
|
// Store a secret
|
||||||
secrets
|
secrets
|
||||||
.create(
|
.create(
|
||||||
"user1",
|
"user1",
|
||||||
CreateSecretParams::new("github_token", "ghp_test123"),
|
CreateSecretParams::new("github_token", TEST_GITHUB_TOKEN),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|||||||
@@ -158,16 +158,13 @@ impl Tool for SecretDeleteTool {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use secrecy::SecretString;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::context::JobContext;
|
use crate::context::JobContext;
|
||||||
use crate::secrets::{CreateSecretParams, InMemorySecretsStore, SecretsCrypto};
|
use crate::secrets::CreateSecretParams;
|
||||||
|
use crate::testing::credentials::{TEST_OPENAI_API_KEY_SHORT, test_secrets_store};
|
||||||
|
|
||||||
fn test_store() -> Arc<InMemorySecretsStore> {
|
fn test_store() -> Arc<crate::secrets::InMemorySecretsStore> {
|
||||||
let key = "0123456789abcdef0123456789abcdef";
|
Arc::new(test_secrets_store())
|
||||||
let crypto = Arc::new(SecretsCrypto::new(SecretString::from(key.to_string())).unwrap());
|
|
||||||
Arc::new(InMemorySecretsStore::new(crypto))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_ctx() -> JobContext {
|
fn test_ctx() -> JobContext {
|
||||||
@@ -183,7 +180,7 @@ mod tests {
|
|||||||
store
|
store
|
||||||
.create(
|
.create(
|
||||||
&ctx.user_id,
|
&ctx.user_id,
|
||||||
CreateSecretParams::new("openai_key", "sk-test"),
|
CreateSecretParams::new("openai_key", TEST_OPENAI_API_KEY_SHORT),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|||||||
+3
-2
@@ -480,6 +480,7 @@ pub fn validate_tool_schema(schema: &serde_json::Value, path: &str) -> Vec<Strin
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::testing::credentials::TEST_REDACT_SECRET;
|
||||||
|
|
||||||
/// A simple no-op tool for testing.
|
/// A simple no-op tool for testing.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -602,12 +603,12 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_redact_params_replaces_sensitive_key() {
|
fn test_redact_params_replaces_sensitive_key() {
|
||||||
let params = serde_json::json!({"name": "openai_key", "value": "sk-secret"});
|
let params = serde_json::json!({"name": "openai_key", "value": TEST_REDACT_SECRET});
|
||||||
let redacted = redact_params(¶ms, &["value"]);
|
let redacted = redact_params(¶ms, &["value"]);
|
||||||
assert_eq!(redacted["name"], "openai_key");
|
assert_eq!(redacted["name"], "openai_key");
|
||||||
assert_eq!(redacted["value"], "[REDACTED]");
|
assert_eq!(redacted["value"], "[REDACTED]");
|
||||||
// Original unchanged
|
// Original unchanged
|
||||||
assert_eq!(params["value"], "sk-secret");
|
assert_eq!(params["value"], TEST_REDACT_SECRET);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -365,22 +365,18 @@ fn base64_encode(input: &[u8]) -> String {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use secrecy::SecretString;
|
|
||||||
|
|
||||||
use crate::secrets::{
|
use crate::secrets::{
|
||||||
CreateSecretParams, CredentialLocation, CredentialMapping, InMemorySecretsStore,
|
CreateSecretParams, CredentialLocation, CredentialMapping, InMemorySecretsStore,
|
||||||
SecretsCrypto, SecretsStore,
|
SecretsStore,
|
||||||
};
|
};
|
||||||
|
use crate::testing::credentials::{TEST_OPENAI_API_KEY, test_secrets_store};
|
||||||
use crate::tools::wasm::credential_injector::{
|
use crate::tools::wasm::credential_injector::{
|
||||||
CredentialInjector, base64_encode, host_matches_pattern,
|
CredentialInjector, base64_encode, host_matches_pattern,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn test_store() -> InMemorySecretsStore {
|
fn test_store() -> InMemorySecretsStore {
|
||||||
let key = "0123456789abcdef0123456789abcdef";
|
test_secrets_store()
|
||||||
let crypto = Arc::new(SecretsCrypto::new(SecretString::from(key.to_string())).unwrap());
|
|
||||||
InMemorySecretsStore::new(crypto)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -406,7 +402,10 @@ mod tests {
|
|||||||
async fn test_inject_bearer() {
|
async fn test_inject_bearer() {
|
||||||
let store = test_store();
|
let store = test_store();
|
||||||
store
|
store
|
||||||
.create("user1", CreateSecretParams::new("openai_key", "sk-test123"))
|
.create(
|
||||||
|
"user1",
|
||||||
|
CreateSecretParams::new("openai_key", TEST_OPENAI_API_KEY),
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -428,7 +427,7 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result.headers.get("Authorization"),
|
result.headers.get("Authorization"),
|
||||||
Some(&"Bearer sk-test123".to_string())
|
Some(&format!("Bearer {TEST_OPENAI_API_KEY}"))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -694,6 +694,7 @@ mod tests {
|
|||||||
|
|
||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
|
|
||||||
|
use crate::testing::credentials::{TEST_OAUTH_CLIENT_ID, TEST_OAUTH_CLIENT_SECRET};
|
||||||
use crate::tools::wasm::loader::{WasmLoadError, check_wit_version_compat, discover_tools};
|
use crate::tools::wasm::loader::{WasmLoadError, check_wit_version_compat, discover_tools};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -834,8 +835,8 @@ mod tests {
|
|||||||
oauth: Some(OAuthConfigSchema {
|
oauth: Some(OAuthConfigSchema {
|
||||||
authorization_url: "https://accounts.google.com/o/oauth2/v2/auth".to_string(),
|
authorization_url: "https://accounts.google.com/o/oauth2/v2/auth".to_string(),
|
||||||
token_url: "https://oauth2.googleapis.com/token".to_string(),
|
token_url: "https://oauth2.googleapis.com/token".to_string(),
|
||||||
client_id: Some("test-client-id".to_string()),
|
client_id: Some(TEST_OAUTH_CLIENT_ID.to_string()),
|
||||||
client_secret: Some("test-client-secret".to_string()),
|
client_secret: Some(TEST_OAUTH_CLIENT_SECRET.to_string()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@@ -848,8 +849,11 @@ mod tests {
|
|||||||
|
|
||||||
let config = config.unwrap();
|
let config = config.unwrap();
|
||||||
assert_eq!(config.token_url, "https://oauth2.googleapis.com/token");
|
assert_eq!(config.token_url, "https://oauth2.googleapis.com/token");
|
||||||
assert_eq!(config.client_id, "test-client-id");
|
assert_eq!(config.client_id, TEST_OAUTH_CLIENT_ID);
|
||||||
assert_eq!(config.client_secret, Some("test-client-secret".to_string()));
|
assert_eq!(
|
||||||
|
config.client_secret,
|
||||||
|
Some(TEST_OAUTH_CLIENT_SECRET.to_string())
|
||||||
|
);
|
||||||
assert_eq!(config.secret_name, "google_oauth_token");
|
assert_eq!(config.secret_name, "google_oauth_token");
|
||||||
assert_eq!(config.provider, Some("google".to_string()));
|
assert_eq!(config.provider, Some("google".to_string()));
|
||||||
}
|
}
|
||||||
|
|||||||
+29
-49
@@ -1223,6 +1223,11 @@ fn coerce_params_to_schema(
|
|||||||
mod tests {
|
mod tests {
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::testing::credentials::{
|
||||||
|
TEST_BEARER_TOKEN_123, TEST_GOOGLE_OAUTH_FRESH, TEST_GOOGLE_OAUTH_LEGACY,
|
||||||
|
TEST_GOOGLE_OAUTH_TOKEN, TEST_OAUTH_CLIENT_ID, TEST_OAUTH_CLIENT_SECRET,
|
||||||
|
test_secrets_store,
|
||||||
|
};
|
||||||
use crate::tools::wasm::capabilities::Capabilities;
|
use crate::tools::wasm::capabilities::Capabilities;
|
||||||
use crate::tools::wasm::runtime::{WasmRuntimeConfig, WasmToolRuntime};
|
use crate::tools::wasm::runtime::{WasmRuntimeConfig, WasmToolRuntime};
|
||||||
|
|
||||||
@@ -1290,12 +1295,12 @@ mod tests {
|
|||||||
let mut h = HashMap::new();
|
let mut h = HashMap::new();
|
||||||
h.insert(
|
h.insert(
|
||||||
"Authorization".to_string(),
|
"Authorization".to_string(),
|
||||||
"Bearer test-token-123".to_string(),
|
format!("Bearer {TEST_BEARER_TOKEN_123}"),
|
||||||
);
|
);
|
||||||
h
|
h
|
||||||
},
|
},
|
||||||
query_params: HashMap::new(),
|
query_params: HashMap::new(),
|
||||||
secret_value: "test-token-123".to_string(),
|
secret_value: TEST_BEARER_TOKEN_123.to_string(),
|
||||||
}];
|
}];
|
||||||
|
|
||||||
let store_data = StoreData::new(
|
let store_data = StoreData::new(
|
||||||
@@ -1311,7 +1316,7 @@ mod tests {
|
|||||||
store_data.inject_host_credentials("www.googleapis.com", &mut headers, &mut url);
|
store_data.inject_host_credentials("www.googleapis.com", &mut headers, &mut url);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
headers.get("Authorization"),
|
headers.get("Authorization"),
|
||||||
Some(&"Bearer test-token-123".to_string())
|
Some(&format!("Bearer {TEST_BEARER_TOKEN_123}"))
|
||||||
);
|
);
|
||||||
|
|
||||||
// Should not inject for non-matching host
|
// Should not inject for non-matching host
|
||||||
@@ -1387,13 +1392,9 @@ mod tests {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_resolve_host_credentials_no_http_cap() {
|
async fn test_resolve_host_credentials_no_http_cap() {
|
||||||
use crate::secrets::{InMemorySecretsStore, SecretsCrypto};
|
|
||||||
use crate::tools::wasm::wrapper::resolve_host_credentials;
|
use crate::tools::wasm::wrapper::resolve_host_credentials;
|
||||||
use secrecy::SecretString;
|
|
||||||
|
|
||||||
let key = "0123456789abcdef0123456789abcdef";
|
let store = test_secrets_store();
|
||||||
let crypto = Arc::new(SecretsCrypto::new(SecretString::from(key.to_string())).unwrap());
|
|
||||||
let store = InMemorySecretsStore::new(crypto);
|
|
||||||
|
|
||||||
let caps = Capabilities::default();
|
let caps = Capabilities::default();
|
||||||
let result = resolve_host_credentials(&caps, Some(&store), "user1", None).await;
|
let result = resolve_host_credentials(&caps, Some(&store), "user1", None).await;
|
||||||
@@ -1405,21 +1406,17 @@ mod tests {
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::secrets::{
|
use crate::secrets::{
|
||||||
CreateSecretParams, CredentialLocation, CredentialMapping, InMemorySecretsStore,
|
CreateSecretParams, CredentialLocation, CredentialMapping, SecretsStore,
|
||||||
SecretsCrypto, SecretsStore,
|
|
||||||
};
|
};
|
||||||
use crate::tools::wasm::capabilities::HttpCapability;
|
use crate::tools::wasm::capabilities::HttpCapability;
|
||||||
use crate::tools::wasm::wrapper::resolve_host_credentials;
|
use crate::tools::wasm::wrapper::resolve_host_credentials;
|
||||||
use secrecy::SecretString;
|
|
||||||
|
|
||||||
let key = "0123456789abcdef0123456789abcdef";
|
let store = test_secrets_store();
|
||||||
let crypto = Arc::new(SecretsCrypto::new(SecretString::from(key.to_string())).unwrap());
|
|
||||||
let store = InMemorySecretsStore::new(crypto);
|
|
||||||
|
|
||||||
store
|
store
|
||||||
.create(
|
.create(
|
||||||
"user1",
|
"user1",
|
||||||
CreateSecretParams::new("google_oauth_token", "ya29.test-token"),
|
CreateSecretParams::new("google_oauth_token", TEST_GOOGLE_OAUTH_TOKEN),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -1447,7 +1444,7 @@ mod tests {
|
|||||||
assert_eq!(result[0].host_patterns, vec!["www.googleapis.com"]);
|
assert_eq!(result[0].host_patterns, vec!["www.googleapis.com"]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result[0].headers.get("Authorization"),
|
result[0].headers.get("Authorization"),
|
||||||
Some(&"Bearer ya29.test-token".to_string())
|
Some(&format!("Bearer {TEST_GOOGLE_OAUTH_TOKEN}"))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1455,16 +1452,11 @@ mod tests {
|
|||||||
async fn test_resolve_host_credentials_missing_secret() {
|
async fn test_resolve_host_credentials_missing_secret() {
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::secrets::{
|
use crate::secrets::{CredentialLocation, CredentialMapping};
|
||||||
CredentialLocation, CredentialMapping, InMemorySecretsStore, SecretsCrypto,
|
|
||||||
};
|
|
||||||
use crate::tools::wasm::capabilities::HttpCapability;
|
use crate::tools::wasm::capabilities::HttpCapability;
|
||||||
use crate::tools::wasm::wrapper::resolve_host_credentials;
|
use crate::tools::wasm::wrapper::resolve_host_credentials;
|
||||||
use secrecy::SecretString;
|
|
||||||
|
|
||||||
let key = "0123456789abcdef0123456789abcdef";
|
let store = test_secrets_store();
|
||||||
let crypto = Arc::new(SecretsCrypto::new(SecretString::from(key.to_string())).unwrap());
|
|
||||||
let store = InMemorySecretsStore::new(crypto);
|
|
||||||
|
|
||||||
// No secret stored, should silently skip
|
// No secret stored, should silently skip
|
||||||
let mut credentials = HashMap::new();
|
let mut credentials = HashMap::new();
|
||||||
@@ -1494,23 +1486,19 @@ mod tests {
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::secrets::{
|
use crate::secrets::{
|
||||||
CreateSecretParams, CredentialLocation, CredentialMapping, InMemorySecretsStore,
|
CreateSecretParams, CredentialLocation, CredentialMapping, SecretsStore,
|
||||||
SecretsCrypto, SecretsStore,
|
|
||||||
};
|
};
|
||||||
use crate::tools::wasm::capabilities::HttpCapability;
|
use crate::tools::wasm::capabilities::HttpCapability;
|
||||||
use crate::tools::wasm::wrapper::{OAuthRefreshConfig, resolve_host_credentials};
|
use crate::tools::wasm::wrapper::{OAuthRefreshConfig, resolve_host_credentials};
|
||||||
use secrecy::SecretString;
|
|
||||||
|
|
||||||
let key = "0123456789abcdef0123456789abcdef";
|
let store = test_secrets_store();
|
||||||
let crypto = Arc::new(SecretsCrypto::new(SecretString::from(key.to_string())).unwrap());
|
|
||||||
let store = InMemorySecretsStore::new(crypto);
|
|
||||||
|
|
||||||
// Store a token that expires 2 hours from now (well within buffer)
|
// Store a token that expires 2 hours from now (well within buffer)
|
||||||
let expires_at = chrono::Utc::now() + chrono::Duration::hours(2);
|
let expires_at = chrono::Utc::now() + chrono::Duration::hours(2);
|
||||||
store
|
store
|
||||||
.create(
|
.create(
|
||||||
"user1",
|
"user1",
|
||||||
CreateSecretParams::new("google_oauth_token", "ya29.fresh-token")
|
CreateSecretParams::new("google_oauth_token", TEST_GOOGLE_OAUTH_FRESH)
|
||||||
.with_expiry(expires_at),
|
.with_expiry(expires_at),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
@@ -1536,8 +1524,8 @@ mod tests {
|
|||||||
|
|
||||||
let oauth_config = OAuthRefreshConfig {
|
let oauth_config = OAuthRefreshConfig {
|
||||||
token_url: "https://oauth2.googleapis.com/token".to_string(),
|
token_url: "https://oauth2.googleapis.com/token".to_string(),
|
||||||
client_id: "test-client-id".to_string(),
|
client_id: TEST_OAUTH_CLIENT_ID.to_string(),
|
||||||
client_secret: Some("test-client-secret".to_string()),
|
client_secret: Some(TEST_OAUTH_CLIENT_SECRET.to_string()),
|
||||||
secret_name: "google_oauth_token".to_string(),
|
secret_name: "google_oauth_token".to_string(),
|
||||||
provider: Some("google".to_string()),
|
provider: Some("google".to_string()),
|
||||||
};
|
};
|
||||||
@@ -1548,7 +1536,7 @@ mod tests {
|
|||||||
assert_eq!(result.len(), 1);
|
assert_eq!(result.len(), 1);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result[0].headers.get("Authorization"),
|
result[0].headers.get("Authorization"),
|
||||||
Some(&"Bearer ya29.fresh-token".to_string())
|
Some(&format!("Bearer {TEST_GOOGLE_OAUTH_FRESH}"))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1557,16 +1545,12 @@ mod tests {
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::secrets::{
|
use crate::secrets::{
|
||||||
CreateSecretParams, CredentialLocation, CredentialMapping, InMemorySecretsStore,
|
CreateSecretParams, CredentialLocation, CredentialMapping, SecretsStore,
|
||||||
SecretsCrypto, SecretsStore,
|
|
||||||
};
|
};
|
||||||
use crate::tools::wasm::capabilities::HttpCapability;
|
use crate::tools::wasm::capabilities::HttpCapability;
|
||||||
use crate::tools::wasm::wrapper::resolve_host_credentials;
|
use crate::tools::wasm::wrapper::resolve_host_credentials;
|
||||||
use secrecy::SecretString;
|
|
||||||
|
|
||||||
let key = "0123456789abcdef0123456789abcdef";
|
let store = test_secrets_store();
|
||||||
let crypto = Arc::new(SecretsCrypto::new(SecretString::from(key.to_string())).unwrap());
|
|
||||||
let store = InMemorySecretsStore::new(crypto);
|
|
||||||
|
|
||||||
// Store an expired token
|
// Store an expired token
|
||||||
let expires_at = chrono::Utc::now() - chrono::Duration::hours(1);
|
let expires_at = chrono::Utc::now() - chrono::Duration::hours(1);
|
||||||
@@ -1606,22 +1590,18 @@ mod tests {
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::secrets::{
|
use crate::secrets::{
|
||||||
CreateSecretParams, CredentialLocation, CredentialMapping, InMemorySecretsStore,
|
CreateSecretParams, CredentialLocation, CredentialMapping, SecretsStore,
|
||||||
SecretsCrypto, SecretsStore,
|
|
||||||
};
|
};
|
||||||
use crate::tools::wasm::capabilities::HttpCapability;
|
use crate::tools::wasm::capabilities::HttpCapability;
|
||||||
use crate::tools::wasm::wrapper::{OAuthRefreshConfig, resolve_host_credentials};
|
use crate::tools::wasm::wrapper::{OAuthRefreshConfig, resolve_host_credentials};
|
||||||
use secrecy::SecretString;
|
|
||||||
|
|
||||||
let key = "0123456789abcdef0123456789abcdef";
|
let store = test_secrets_store();
|
||||||
let crypto = Arc::new(SecretsCrypto::new(SecretString::from(key.to_string())).unwrap());
|
|
||||||
let store = InMemorySecretsStore::new(crypto);
|
|
||||||
|
|
||||||
// Legacy token: no expires_at set
|
// Legacy token: no expires_at set
|
||||||
store
|
store
|
||||||
.create(
|
.create(
|
||||||
"user1",
|
"user1",
|
||||||
CreateSecretParams::new("google_oauth_token", "ya29.legacy-token"),
|
CreateSecretParams::new("google_oauth_token", TEST_GOOGLE_OAUTH_LEGACY),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -1646,8 +1626,8 @@ mod tests {
|
|||||||
|
|
||||||
let oauth_config = OAuthRefreshConfig {
|
let oauth_config = OAuthRefreshConfig {
|
||||||
token_url: "https://oauth2.googleapis.com/token".to_string(),
|
token_url: "https://oauth2.googleapis.com/token".to_string(),
|
||||||
client_id: "test-client-id".to_string(),
|
client_id: TEST_OAUTH_CLIENT_ID.to_string(),
|
||||||
client_secret: Some("test-client-secret".to_string()),
|
client_secret: Some(TEST_OAUTH_CLIENT_SECRET.to_string()),
|
||||||
secret_name: "google_oauth_token".to_string(),
|
secret_name: "google_oauth_token".to_string(),
|
||||||
provider: Some("google".to_string()),
|
provider: Some("google".to_string()),
|
||||||
};
|
};
|
||||||
@@ -1658,7 +1638,7 @@ mod tests {
|
|||||||
assert_eq!(result.len(), 1);
|
assert_eq!(result.len(), 1);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result[0].headers.get("Authorization"),
|
result[0].headers.get("Authorization"),
|
||||||
Some(&"Bearer ya29.legacy-token".to_string())
|
Some(&format!("Bearer {TEST_GOOGLE_OAUTH_LEGACY}"))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -294,10 +294,11 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn factory_cloudflare_with_config_ok() {
|
fn factory_cloudflare_with_config_ok() {
|
||||||
|
use crate::testing::credentials::TEST_BEARER_TOKEN;
|
||||||
let cfg = TunnelProviderConfig {
|
let cfg = TunnelProviderConfig {
|
||||||
provider: "cloudflare".into(),
|
provider: "cloudflare".into(),
|
||||||
cloudflare: Some(CloudflareTunnelConfig {
|
cloudflare: Some(CloudflareTunnelConfig {
|
||||||
token: "test-token".into(),
|
token: TEST_BEARER_TOKEN.into(),
|
||||||
}),
|
}),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|||||||
+3
-2
@@ -419,13 +419,14 @@ fn parse_finish_reason(s: &str) -> FinishReason {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::testing::credentials::TEST_BEARER_TOKEN;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_url_construction() {
|
fn test_url_construction() {
|
||||||
let client = WorkerHttpClient::new(
|
let client = WorkerHttpClient::new(
|
||||||
"http://host.docker.internal:50051".to_string(),
|
"http://host.docker.internal:50051".to_string(),
|
||||||
Uuid::nil(),
|
Uuid::nil(),
|
||||||
"test-token".to_string(),
|
TEST_BEARER_TOKEN.to_string(),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -449,7 +450,7 @@ mod tests {
|
|||||||
let client = WorkerHttpClient::new(
|
let client = WorkerHttpClient::new(
|
||||||
"http://host.docker.internal:50051".to_string(),
|
"http://host.docker.internal:50051".to_string(),
|
||||||
Uuid::nil(),
|
Uuid::nil(),
|
||||||
"test-token".to_string(),
|
TEST_BEARER_TOKEN.to_string(),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
@@ -12,6 +12,11 @@ use tempfile::tempdir;
|
|||||||
|
|
||||||
use ironclaw::bootstrap::{save_bootstrap_env_to, upsert_bootstrap_var_to};
|
use ironclaw::bootstrap::{save_bootstrap_env_to, upsert_bootstrap_var_to};
|
||||||
|
|
||||||
|
/// Fake OpenAI API key for test use only. Mirrors the internal
|
||||||
|
/// `TEST_OPENAI_API_KEY_LONG` constant from the main crate, which is not
|
||||||
|
/// directly available to integration tests due to `#[cfg(test)]`.
|
||||||
|
const TEST_OPENAI_API_KEY_LONG: &str = "sk-test-key-1234567890";
|
||||||
|
|
||||||
/// Parse a .env file into a HashMap using dotenvy.
|
/// Parse a .env file into a HashMap using dotenvy.
|
||||||
fn read_env_map(path: &std::path::Path) -> HashMap<String, String> {
|
fn read_env_map(path: &std::path::Path) -> HashMap<String, String> {
|
||||||
dotenvy::from_path_iter(path)
|
dotenvy::from_path_iter(path)
|
||||||
@@ -77,7 +82,7 @@ fn bootstrap_env_round_trips_embedding_disabled() {
|
|||||||
&[
|
&[
|
||||||
("DATABASE_BACKEND", "libsql"),
|
("DATABASE_BACKEND", "libsql"),
|
||||||
("EMBEDDING_ENABLED", "false"),
|
("EMBEDDING_ENABLED", "false"),
|
||||||
("OPENAI_API_KEY", "sk-test-key-1234567890"),
|
("OPENAI_API_KEY", TEST_OPENAI_API_KEY_LONG),
|
||||||
("ONBOARD_COMPLETED", "true"),
|
("ONBOARD_COMPLETED", "true"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@@ -92,7 +97,7 @@ fn bootstrap_env_round_trips_embedding_disabled() {
|
|||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
map.get("OPENAI_API_KEY").map(String::as_str),
|
map.get("OPENAI_API_KEY").map(String::as_str),
|
||||||
Some("sk-test-key-1234567890"),
|
Some(TEST_OPENAI_API_KEY_LONG),
|
||||||
"OPENAI_API_KEY must be preserved alongside EMBEDDING_ENABLED"
|
"OPENAI_API_KEY must be preserved alongside EMBEDDING_ENABLED"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user