use std::path::PathBuf;
use pixforge::config::LoadedConfig;
fn parse(s: &str) -> Result<LoadedConfig, String> {
LoadedConfig::parse(s, PathBuf::from("/tmp/test-config.toml")).map_err(|e| format!("{e:#}"))
}
const LEAKED_KEY_LIKE: &str =
"PLACEHOLDERkeyPLACEHOLDERkeyPLACEHOLDERkeyPLACEHOLDERkeyPLACEHOLDERkeyPLACEHOLDER123";
#[test]
fn pasted_secret_in_api_key_env_is_rejected_and_never_echoed() {
let txt = format!(
r#"
[profile.azure-mai]
provider = "azure-mai"
endpoint = "https://x.services.ai.azure.com"
model = "MAI-Image-2"
api_key_env = "{LEAKED_KEY_LIKE}"
"#
);
let err = parse(&txt).expect_err("must reject");
assert!(
!err.contains(LEAKED_KEY_LIKE),
"ERROR MUST NOT CONTAIN THE LEAKED VALUE.\nGot error: {err}"
);
assert!(
err.contains("does not look like an environment variable name")
|| err.contains("suspiciously long"),
"should explain what's wrong: {err}"
);
assert!(
err.contains("rotate")
|| err.contains("compromised")
|| err.contains("paste"),
"should warn about secret rotation: {err}"
);
}
#[test]
fn long_value_with_only_valid_chars_is_still_rejected_as_too_long() {
let suspicious = "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZAB";
assert_eq!(suspicious.len(), 80);
let txt = format!(
r#"
[profile.azure-mai]
provider = "azure-mai"
endpoint = "https://x.services.ai.azure.com"
model = "MAI-Image-2"
api_key_env = "{suspicious}"
"#
);
let err = parse(&txt).expect_err("80-char alphanum must fail");
assert!(
!err.contains(suspicious),
"MUST NOT echo the value, got: {err}"
);
assert!(err.contains("suspiciously long"), "got: {err}");
}
#[test]
fn value_with_special_chars_is_rejected() {
let bad = "sk-proj-abc123-def456";
let txt = format!(
r#"
[profile.openai]
provider = "openai-compat"
endpoint = "https://api.openai.com/v1"
model = "gpt-image-1"
api_key_env = "{bad}"
"#
);
let err = parse(&txt).expect_err("`-` in name must fail");
assert!(!err.contains(bad), "MUST NOT echo the value, got: {err}");
}
#[test]
fn value_starting_with_digit_is_rejected() {
let bad = "1AZURE_KEY";
let txt = format!(
r#"
[profile.azure-mai]
provider = "azure-mai"
endpoint = "https://x.services.ai.azure.com"
model = "MAI-Image-2"
api_key_env = "{bad}"
"#
);
let err = parse(&txt).expect_err("leading digit must fail");
assert!(!err.contains(bad), "MUST NOT echo the value, got: {err}");
}
#[test]
fn empty_api_key_env_is_rejected() {
let txt = r#"
[profile.azure-mai]
provider = "azure-mai"
endpoint = "https://x.services.ai.azure.com"
model = "MAI-Image-2"
api_key_env = ""
"#;
let err = parse(txt).expect_err("empty must fail");
assert!(err.contains("empty"), "got: {err}");
}
#[test]
fn normal_env_var_names_are_accepted() {
let names = [
"AZURE_API_KEY",
"OPENAI_API_KEY",
"GEMINI_API_KEY",
"_PRIVATE_VAR",
"MY_KEY_2",
"X",
];
for name in names {
let txt = format!(
r#"
[profile.p]
provider = "openai-compat"
endpoint = "https://api.openai.com/v1"
model = "gpt-image-1"
api_key_env = "{name}"
"#
);
parse(&txt).unwrap_or_else(|e| panic!("name {name:?} should be accepted, got: {e}"));
}
}