use super::{build_openai_extractor, parse_chat_template_kwargs, CHAT_TEMPLATE_KWARGS_VAR};
#[test]
fn an_unset_variable_means_no_kwargs() {
assert_eq!(parse_chat_template_kwargs(None), Ok(None));
}
#[test]
fn an_empty_or_blank_value_means_no_kwargs() {
assert_eq!(parse_chat_template_kwargs(Some("")), Ok(None));
assert_eq!(parse_chat_template_kwargs(Some(" \t\n")), Ok(None));
}
#[test]
fn a_json_object_is_kept_as_written() {
let parsed = parse_chat_template_kwargs(Some(r#" {"enable_thinking": false, "n": [1]} "#))
.expect("an object is valid");
assert_eq!(
parsed,
Some(serde_json::json!({ "enable_thinking": false, "n": [1] }))
);
}
#[test]
fn an_empty_object_is_a_valid_value() {
assert_eq!(
parse_chat_template_kwargs(Some("{}")),
Ok(Some(serde_json::json!({})))
);
}
#[test]
fn invalid_json_is_refused_and_names_the_variable() {
let err = parse_chat_template_kwargs(Some(r#"{"enable_thinking": "#))
.expect_err("truncated JSON must not be silently dropped");
assert!(
err.starts_with(&format!(
"{CHAT_TEMPLATE_KWARGS_VAR} must be a JSON object: "
)),
"got: {err}"
);
assert!(
err.contains("line 1 column"),
"the parser's position is what makes the typo findable, got: {err}"
);
}
#[test]
fn a_json_value_that_is_not_an_object_is_refused() {
for raw in ["[1, 2]", "\"text\"", "42", "true", "null"] {
let err = parse_chat_template_kwargs(Some(raw))
.expect_err("only an object is a set of template arguments");
assert!(
err.starts_with(&format!(
"{CHAT_TEMPLATE_KWARGS_VAR} must be a JSON object: "
)),
"{raw} -> {err}"
);
}
}
#[test]
fn a_refusal_never_echoes_the_raw_value() {
for raw in [
"sk-live-hunter2",
"\"sk-live-hunter2\"",
"{\"k\": sk-live-hunter2}",
] {
let err = parse_chat_template_kwargs(Some(raw)).expect_err("not an object");
assert!(!err.contains("hunter2"), "{raw} leaked into: {err}");
}
}
static ENV: std::sync::Mutex<()> = std::sync::Mutex::new(());
fn configure_openai_extractor(kwargs: Option<&str>) {
std::env::set_var("VELESDB_MEMORY_EXTRACTOR_URL", "http://127.0.0.1:1");
std::env::set_var("VELESDB_MEMORY_EXTRACTOR_MODEL", "any-model");
std::env::remove_var("VELESDB_MEMORY_EXTRACTOR_API_TOKEN");
match kwargs {
Some(value) => std::env::set_var(CHAT_TEMPLATE_KWARGS_VAR, value),
None => std::env::remove_var(CHAT_TEMPLATE_KWARGS_VAR),
}
}
fn clear_extractor_variables() {
for key in [
"VELESDB_MEMORY_EXTRACTOR_URL",
"VELESDB_MEMORY_EXTRACTOR_MODEL",
CHAT_TEMPLATE_KWARGS_VAR,
] {
std::env::remove_var(key);
}
}
#[test]
fn the_daemon_builder_fails_at_startup_on_a_malformed_variable() {
let _env = ENV
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
configure_openai_extractor(Some("{not json"));
let outcome = build_openai_extractor();
clear_extractor_variables();
let err = outcome
.err()
.expect("a malformed variable must stop startup, like DEFAULT_TTL does");
assert!(
err.to_string().contains(CHAT_TEMPLATE_KWARGS_VAR),
"the operator must be told which variable to fix, got: {err}"
);
}
#[test]
fn the_daemon_builder_fails_at_startup_on_a_non_object_variable() {
let _env = ENV
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
configure_openai_extractor(Some("[true]"));
let outcome = build_openai_extractor();
clear_extractor_variables();
assert!(
outcome.is_err(),
"an array is not a set of template arguments"
);
}
#[test]
fn the_daemon_builder_accepts_a_valid_object_and_an_unset_variable() {
let _env = ENV
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
configure_openai_extractor(Some(r#"{"enable_thinking": false}"#));
let with_kwargs = build_openai_extractor();
configure_openai_extractor(None);
let without = build_openai_extractor();
clear_extractor_variables();
assert!(with_kwargs.is_ok());
assert!(without.is_ok());
}