use super::*;
#[test]
fn test_system_prompt_mentions_agent_consumer() {
assert!(
SYSTEM_PROMPT.contains("agent") || SYSTEM_PROMPT.contains("LLM"),
"SYSTEM_PROMPT must mention that the consumer is an LLM agent; got:\n{SYSTEM_PROMPT}"
);
}
#[test]
fn test_system_prompt_penalises_empty_output() {
let lower = SYSTEM_PROMPT.to_lowercase();
assert!(
lower.contains("empty") || lower.contains("nothing") || lower.contains("worst"),
"SYSTEM_PROMPT must warn that an empty/no summary is the worst outcome; got:\n{SYSTEM_PROMPT}"
);
}
#[test]
fn test_system_prompt_contains_toml_schema() {
assert!(
SYSTEM_PROMPT.contains("command_match"),
"SYSTEM_PROMPT must show the TOML schema (command_match field); got:\n{SYSTEM_PROMPT}"
);
}
#[test]
fn test_system_prompt_explains_tier_system() {
let lower = SYSTEM_PROMPT.to_lowercase();
assert!(
lower.contains("passthrough") || lower.contains("large") || lower.contains("tier"),
"SYSTEM_PROMPT must describe oo's tier system; got:\n{SYSTEM_PROMPT}"
);
}
#[test]
fn test_system_prompt_under_2000_chars() {
assert!(
SYSTEM_PROMPT.len() < 2000,
"SYSTEM_PROMPT must be under 2000 characters; actual length: {}",
SYSTEM_PROMPT.len()
);
}
#[test]
fn test_system_prompt_contains_named_group_instruction() {
assert!(
SYSTEM_PROMPT.contains("(?P<name>") || SYSTEM_PROMPT.contains("named capture"),
"SYSTEM_PROMPT must contain instruction about named capture groups; got:\n{SYSTEM_PROMPT}"
);
}
#[test]
fn test_system_prompt_contains_examples() {
let success_count = SYSTEM_PROMPT.matches("[success]").count();
assert!(
success_count >= 2,
"SYSTEM_PROMPT must contain at least 2 TOML [success] sections (one per example); found {success_count}"
);
}
#[test]
fn test_call_anthropic_success() {
let mut server = mockito::Server::new();
let mock = server
.mock("POST", "/v1/messages")
.with_status(200)
.with_header("content-type", "application/json")
.with_body(include_str!("../tests/fixtures/anthropic_success.json"))
.create();
let result = call_anthropic(
&format!("{}/v1/messages", server.url()),
"test-key",
"test-model",
"test prompt",
);
assert!(result.is_ok(), "expected Ok, got: {result:?}");
assert!(
result.unwrap().contains("command_match"),
"response must contain pattern content"
);
mock.assert();
}
#[test]
fn test_user_message_without_hint() {
let dir = tempfile::TempDir::new().expect("tempdir");
let patterns_dir = dir.path().join("patterns");
let learn_status_path = dir.path().join("learn-status.log");
let config = LearnConfig {
provider: "anthropic".into(),
model: "claude-haiku-4-5".into(),
api_key_env: "ANTHROPIC_API_KEY".into(),
};
let _params = LearnParams {
config: &config,
api_key: "test-key",
base_url: "https://api.anthropic.com/v1/messages",
patterns_dir: &patterns_dir,
learn_status_path: &learn_status_path,
hint: None,
};
let user_msg = format!(
"Command: cargo test\nExit code: 0\nOutput:\n{}",
truncate_for_prompt("test result: ok. 5 passed")
);
assert!(
!user_msg.contains("Hint:"),
"user message without hint should not contain 'Hint:'"
);
assert!(
user_msg.contains("Command:"),
"user message should contain 'Command:'"
);
assert!(
user_msg.contains("Exit code:"),
"user message should contain 'Exit code:'"
);
assert!(
user_msg.contains("Output:"),
"user message should contain 'Output:'"
);
}
#[test]
fn test_user_message_with_hint() {
let dir = tempfile::TempDir::new().expect("tempdir");
let patterns_dir = dir.path().join("patterns");
let learn_status_path = dir.path().join("learn-status.log");
let config = LearnConfig {
provider: "anthropic".into(),
model: "claude-haiku-4-5".into(),
api_key_env: "ANTHROPIC_API_KEY".into(),
};
let _params = LearnParams {
config: &config,
api_key: "test-key",
base_url: "https://api.anthropic.com/v1/messages",
patterns_dir: &patterns_dir,
learn_status_path: &learn_status_path,
hint: Some("capture summary line only"),
};
let user_msg = format!(
"Command: cargo test\nExit code: 0\nHint: capture summary line only\nOutput:\n{}",
truncate_for_prompt("test result: ok. 5 passed")
);
assert!(
user_msg.contains("Hint: capture summary line only"),
"user message with hint should contain the hint text"
);
assert!(
user_msg.contains("Command:"),
"user message should contain 'Command:'"
);
assert!(
user_msg.contains("Exit code:"),
"user message should contain 'Exit code:'"
);
assert!(
user_msg.contains("Output:"),
"user message should contain 'Output:'"
);
}
#[test]
fn test_call_anthropic_malformed_response() {
let mut server = mockito::Server::new();
let mock = server
.mock("POST", "/v1/messages")
.with_status(200)
.with_header("content-type", "application/json")
.with_body(r#"{"id":"msg-test","type":"message","content":[]}"#)
.create();
let result = call_anthropic(
&format!("{}/v1/messages", server.url()),
"test-key",
"test-model",
"test prompt",
);
assert!(
result.is_err(),
"expected Err on malformed response, got Ok"
);
mock.assert();
}