#[ allow( unused_imports ) ]
use super::*;
#[ cfg( feature = "integration" ) ]
#[ tokio::test ]
async fn test_ap_11_invalid_key_returns_auth_error()
{
let invalid_secret = the_module::Secret::new_unchecked(
"sk-ant-api03-".to_string() + &"x".repeat( 80 )
);
let client = the_module::Client::new( invalid_secret );
let request = the_module::CreateMessageRequest::builder()
.model( "claude-haiku-4-5-20251001" )
.max_tokens( 10 )
.message( the_module::Message::user( "auth test" ) )
.build_validated()
.unwrap();
let result = client.create_message( request ).await;
assert!( result.is_err(), "Invalid API key must produce an error" );
if let Err( the_module::AnthropicError::Api( api_error ) ) = result
{
assert_eq!(
api_error.r#type, "authentication_error",
"401 from Anthropic must surface as authentication_error type"
);
}
}
#[ test ]
#[ allow( clippy::similar_names ) ]
fn test_workspace_credential_scoping()
{
let key_a = "sk-ant-api03-".to_string() + &"a".repeat( 80 );
let key_b = "sk-ant-api03-".to_string() + &"b".repeat( 80 );
let secret_a = the_module::Secret::new( key_a.clone() ).expect( "valid key" );
let secret_b = the_module::Secret::new( key_b.clone() ).expect( "valid key" );
let client_a = the_module::Client::new( secret_a );
let client_b = the_module::Client::new( secret_b );
assert_ne!(
client_a.secret().ANTHROPIC_API_KEY,
client_b.secret().ANTHROPIC_API_KEY,
"Clients with different secrets must hold different keys"
);
}
#[ cfg( feature = "integration" ) ]
#[ tokio::test ]
async fn test_authentication_failure_recovery()
{
let invalid_secret = the_module::Secret::new_unchecked(
"sk-ant-api03-".to_string() + &"x".repeat( 80 )
);
let client = the_module::Client::new( invalid_secret );
let request = the_module::CreateMessageRequest::builder()
.model( "claude-haiku-4-5-20251001" )
.max_tokens( 50 )
.message( the_module::Message::user( "Auth failure test" ) )
.build_validated()
.unwrap();
let result = client.create_message( request ).await;
assert!( result.is_err(), "Invalid API key must produce an error" );
if let Err( the_module::AnthropicError::Api( api_error ) ) = result
{
assert!(
api_error.r#type == "authentication_error" || api_error.r#type == "error",
"Invalid key must surface as authentication_error, got: {}",
api_error.r#type
);
}
}
#[ test ]
fn test_extended_api_key_format_validation()
{
let test_cases = vec![
( "", false ), ( "bad-key", false ), ( "SK-ANT-test", false ), ( "openai-sk-abcdef", false ), ( "sk-ant-api03-test-value", true ), ( "sk-ant-any-body-value", true ), ];
for ( api_key, should_be_valid ) in test_cases
{
let result = the_module::Secret::new( api_key.to_string() );
match result
{
Ok( _ ) =>
{
assert!( should_be_valid, "Expected validation to fail for : {api_key:?}" );
},
Err( err ) =>
{
assert!(
!should_be_valid,
"Expected validation to succeed for {api_key:?} but got: {err}"
);
}
}
}
}
#[ test ]
fn test_environment_variable_precedence()
{
let saved = std::env::var( "ANTHROPIC_API_KEY" ).ok();
std::env::remove_var( "ANTHROPIC_API_KEY" );
let absent_result = the_module::Secret::load_from_env( "ANTHROPIC_API_KEY" );
assert!( absent_result.is_err(), "load_from_env must fail when var is absent" );
let test_key = "sk-ant-api03-test-key-value";
std::env::set_var( "ANTHROPIC_API_KEY", test_key );
let present_result = the_module::Secret::load_from_env( "ANTHROPIC_API_KEY" );
assert!( present_result.is_ok(), "load_from_env must succeed when var is set" );
assert_eq!( present_result.unwrap().ANTHROPIC_API_KEY, test_key );
std::env::remove_var( "ANTHROPIC_API_KEY" );
if let Some( key ) = saved
{
std::env::set_var( "ANTHROPIC_API_KEY", key );
}
}
#[ cfg( feature = "integration" ) ]
#[ test ]
fn test_workspace_tools_secret_loading()
{
let workspace_result = the_module::Secret::from_workspace();
let secret = workspace_result
.expect( "INTEGRATION TEST FAILURE: Workspace secret loading MUST work - check ../../secret/-secrets.sh contains ANTHROPIC_API_KEY" );
let client = the_module::Client::new( secret );
assert!(
!client.secret().ANTHROPIC_API_KEY.is_empty(),
"INTEGRATION TEST FAILURE: Secret loaded but API key is empty"
);
assert!(
client.secret().ANTHROPIC_API_KEY.starts_with( "sk-ant-" ),
"INTEGRATION TEST FAILURE: API key format invalid - must start with sk-ant-"
);
let client_from_workspace = the_module::Client::from_workspace()
.expect( "INTEGRATION TEST FAILURE: Client::from_workspace() MUST work when Secret::from_workspace() works" );
assert!(
!client_from_workspace.secret().ANTHROPIC_API_KEY.is_empty(),
"INTEGRATION TEST FAILURE: Client workspace secret is empty"
);
assert_eq!(
client.secret().ANTHROPIC_API_KEY,
client_from_workspace.secret().ANTHROPIC_API_KEY,
"INTEGRATION TEST FAILURE: Inconsistent secrets between Secret::from_workspace() and Client::from_workspace()"
);
}
#[ cfg( feature = "integration" ) ]
#[ test ]
fn test_workspace_secret_fallback_to_environment()
{
let workspace_result = the_module::Secret::load_from_workspace( "ANTHROPIC_API_KEY", "-secrets.sh" );
let env_result = the_module::Secret::load_from_env( "ANTHROPIC_API_KEY" );
match ( workspace_result, env_result )
{
( Ok( ws_secret ), Ok( env_secret ) ) =>
{
let client_ws = the_module::Client::new( ws_secret );
let client_env = the_module::Client::new( env_secret );
assert!(
!client_ws.secret().ANTHROPIC_API_KEY.is_empty(),
"INTEGRATION TEST FAILURE: Workspace secret is empty"
);
assert!(
!client_env.secret().ANTHROPIC_API_KEY.is_empty(),
"INTEGRATION TEST FAILURE: Environment secret is empty"
);
assert!(
client_ws.secret().ANTHROPIC_API_KEY.starts_with( "sk-ant-" ),
"INTEGRATION TEST FAILURE: Workspace secret format invalid"
);
assert!(
client_env.secret().ANTHROPIC_API_KEY.starts_with( "sk-ant-" ),
"INTEGRATION TEST FAILURE: Environment secret format invalid"
);
},
( Ok( ws_secret ), Err( _env_err ) ) =>
{
let client = the_module::Client::new( ws_secret );
assert!(
!client.secret().ANTHROPIC_API_KEY.is_empty(),
"INTEGRATION TEST FAILURE: Workspace secret is empty"
);
assert!(
client.secret().ANTHROPIC_API_KEY.starts_with( "sk-ant-" ),
"INTEGRATION TEST FAILURE: Workspace secret format invalid"
);
},
( Err( _ws_err ), Ok( env_secret ) ) =>
{
let client = the_module::Client::new( env_secret );
assert!(
!client.secret().ANTHROPIC_API_KEY.is_empty(),
"INTEGRATION TEST FAILURE: Environment secret is empty"
);
assert!(
client.secret().ANTHROPIC_API_KEY.starts_with( "sk-ant-" ),
"INTEGRATION TEST FAILURE: Environment secret format invalid"
);
},
( Err( ws_err ), Err( env_err ) ) =>
{
panic!(
"INTEGRATION TEST FAILURE: No API secrets available. Workspace error : {ws_err} Environment error : {env_err}. \
Set ANTHROPIC_API_KEY environment variable or create ../../secret/-secrets.sh"
);
}
}
}
#[ test ]
fn test_secret_single_quote_parsing_via_shell_file()
{
use std::io::Write;
let mut tmp = tempfile::NamedTempFile::new().expect( "Failed to create temp file" );
writeln!( tmp, "export ANTHROPIC_API_KEY='sk-ant-api03-single-quoted-value'" )
.expect( "Failed to write temp file" );
let result = the_module::Secret::load_from_shell_file(
tmp.path(),
"ANTHROPIC_API_KEY",
);
assert!( result.is_ok(), "Single-quoted value must parse correctly, got : {result:?}" );
assert_eq!(
result.unwrap().ANTHROPIC_API_KEY,
"sk-ant-api03-single-quoted-value",
"Single quotes must be stripped from parsed value"
);
}
#[ test ]
fn test_secret_single_quote_bare_assignment()
{
use std::io::Write;
let mut tmp = tempfile::NamedTempFile::new().expect( "Failed to create temp file" );
writeln!( tmp, "ANTHROPIC_API_KEY='sk-ant-api03-bare-single-quoted'" )
.expect( "Failed to write temp file" );
let result = the_module::Secret::load_from_shell_file(
tmp.path(),
"ANTHROPIC_API_KEY",
);
assert!( result.is_ok(), "Bare single-quoted assignment must parse correctly" );
assert_eq!(
result.unwrap().ANTHROPIC_API_KEY,
"sk-ant-api03-bare-single-quoted",
);
}
#[ test ]
fn test_secret_load_from_shell_file_missing_key()
{
use std::io::Write;
let mut tmp = tempfile::NamedTempFile::new().expect( "Failed to create temp file" );
writeln!( tmp, "export OTHER_KEY=\"sk-ant-api03-some-value\"" )
.expect( "Failed to write temp file" );
let result = the_module::Secret::load_from_shell_file(
tmp.path(),
"ANTHROPIC_API_KEY",
);
assert!( result.is_err(), "Missing key must return an error" );
}
#[ test ]
fn test_secret_load_from_file_success()
{
use std::io::Write;
let mut tmp = tempfile::NamedTempFile::new().expect( "Failed to create temp file" );
write!( tmp, "sk-ant-api03-plain-file-key" ).expect( "Failed to write temp file" );
let result = the_module::Secret::load_from_file( tmp.path() );
assert!( result.is_ok(), "Plain-text key file must load successfully, got : {result:?}" );
assert_eq!(
result.unwrap().ANTHROPIC_API_KEY,
"sk-ant-api03-plain-file-key",
);
}
#[ test ]
fn test_secret_load_from_file_whitespace_trimming()
{
use std::io::Write;
let mut tmp = tempfile::NamedTempFile::new().expect( "Failed to create temp file" );
writeln!( tmp, " sk-ant-api03-whitespace-key " ).expect( "Failed to write temp file" );
let result = the_module::Secret::load_from_file( tmp.path() );
assert!( result.is_ok(), "Whitespace around key must be trimmed, got : {result:?}" );
assert_eq!(
result.unwrap().ANTHROPIC_API_KEY,
"sk-ant-api03-whitespace-key",
"Key must be trimmed of surrounding whitespace"
);
}
#[ test ]
fn test_secret_load_from_file_nonexistent()
{
let nonexistent = std::path::Path::new( "/tmp/does-not-exist-api-claude-test.txt" );
let result = the_module::Secret::load_from_file( nonexistent );
assert!( result.is_err(), "Nonexistent file must return an error" );
}
#[ test ]
fn test_secret_debug_redaction()
{
let secret = the_module::Secret::new_unchecked( "sk-ant-api03-super-secret-do-not-reveal".to_string() );
let debug_output = format!( "{secret:?}" );
assert!(
debug_output.contains( "REDACTED" ),
"Debug output must contain REDACTED placeholder, got : {debug_output}"
);
assert!(
!debug_output.contains( "sk-ant-api03-super-secret-do-not-reveal" ),
"Debug output must NOT reveal the actual API key, got : {debug_output}"
);
}
#[ cfg( feature = "integration" ) ]
#[ tokio::test ]
async fn test_real_api_call_must_work_no_graceful_fallbacks()
{
let client = the_module::Client::from_workspace()
.expect( "INTEGRATION TEST FAILURE: Must have valid workspace secret for real API testing" );
let request = the_module::CreateMessageRequest::builder()
.model( "claude-haiku-4-5-20251001" )
.max_tokens( 10 )
.message( the_module::Message::user( "Hi" ) )
.build_validated()
.expect( "INTEGRATION TEST FAILURE: Request construction failed" );
let response = match client.create_message( request ).await
{
Ok( response ) => response,
Err( the_module::AnthropicError::Api( ref api_err ) )
if api_err.message.contains( "credit balance is too low" ) =>
{
panic!(
"INTEGRATION: credit balance exhausted - real API call succeeded but account has no credits. \
Test must fail per Loud Failure Mandate: {}",
api_err.message
)
},
Err( err ) =>
{
panic!(
"INTEGRATION TEST FAILURE: Real API call MUST work - check network connectivity and API key validity : {err}"
);
}
};
assert!( !response.id.is_empty(), "INTEGRATION TEST FAILURE: Response ID is empty - not a real API response" );
assert!( response.r#type == "message", "INTEGRATION TEST FAILURE: Response type incorrect - not a real API response" );
assert!( response.role == "assistant", "INTEGRATION TEST FAILURE: Response role incorrect - not a real API response" );
assert!( !response.content.is_empty(), "INTEGRATION TEST FAILURE: Response content is empty - not a real API response" );
}