use nika::ast::completion::{
CompletionConfig, CompletionMode, ConfidenceConfig, LowConfidenceAction, OnLowConfidenceConfig,
PatternConfig, PatternType, SignalConfig, SignalFields,
};
use nika::ast::AgentParams;
use nika::event::EventLog;
use nika::runtime::builtin::{
is_completion_signal, parse_completion_response, BuiltinTool, CompleteTool, COMPLETION_MARKER,
};
use nika::runtime::{RigAgentLoop, RigAgentStatus};
use rustc_hash::FxHashMap;
#[test]
fn test_explicit_mode_generates_complete_tool_instruction() {
let config = CompletionConfig {
mode: CompletionMode::Explicit,
signal: Some(SignalConfig {
tool: "nika:complete".to_string(),
fields: SignalFields {
required: vec!["result".to_string()],
optional: vec!["confidence".to_string()],
},
}),
instruction: None,
confidence: None,
patterns: vec![],
};
let instruction = config.generate_system_instruction();
assert!(instruction.contains("nika:complete"));
assert!(instruction.contains("result"));
}
#[test]
fn test_pattern_mode_generates_pattern_instruction() {
let config = CompletionConfig {
mode: CompletionMode::Pattern,
patterns: vec![PatternConfig::new("DONE", PatternType::Exact)],
instruction: None,
confidence: None,
signal: None,
};
let instruction = config.generate_system_instruction();
assert!(
instruction.contains("DONE")
|| instruction.contains("complete")
|| instruction.contains("pattern")
);
}
#[test]
fn test_natural_mode_generates_minimal_instruction() {
let config = CompletionConfig {
mode: CompletionMode::Natural,
instruction: None,
confidence: None,
signal: None,
patterns: vec![],
};
let instruction = config.generate_system_instruction();
assert!(instruction.len() < 200);
}
#[test]
fn test_agent_params_with_completion_config() {
let params = AgentParams {
prompt: "Test agent".to_string(),
completion: Some(CompletionConfig {
mode: CompletionMode::Explicit,
signal: None,
patterns: vec![],
confidence: None,
instruction: None,
}),
..Default::default()
};
assert!(params.completion.is_some());
assert_eq!(
params.completion.as_ref().unwrap().mode,
CompletionMode::Explicit
);
}
#[test]
fn test_agent_params_effective_completion_with_config() {
let params = AgentParams {
prompt: "Test".to_string(),
completion: Some(CompletionConfig {
mode: CompletionMode::Explicit,
instruction: None,
confidence: None,
signal: None,
patterns: vec![],
}),
..Default::default()
};
let effective = params.effective_completion();
assert!(effective.is_some());
assert_eq!(effective.unwrap().mode, CompletionMode::Explicit);
}
#[test]
fn test_agent_params_completion_system_instruction() {
let params = AgentParams {
prompt: "Test".to_string(),
completion: Some(CompletionConfig {
mode: CompletionMode::Explicit,
signal: Some(SignalConfig {
tool: "nika:complete".to_string(),
fields: SignalFields {
required: vec!["result".to_string()],
optional: vec![],
},
}),
instruction: None,
confidence: None,
patterns: vec![],
}),
..Default::default()
};
let instruction = params.completion_system_instruction();
assert!(!instruction.is_empty());
assert!(instruction.contains("nika:complete"));
}
#[tokio::test]
async fn test_complete_tool_returns_marker() {
let tool = CompleteTool;
let result = tool
.call(r#"{"result": "Task completed successfully"}"#.to_string())
.await
.unwrap();
assert!(result.contains(COMPLETION_MARKER));
}
#[tokio::test]
async fn test_complete_tool_with_confidence() {
let tool = CompleteTool;
let result = tool
.call(r#"{"result": "Answer is 42", "confidence": 0.95}"#.to_string())
.await
.unwrap();
let response = parse_completion_response(&result).unwrap();
assert!(response.completed);
assert_eq!(response.confidence, Some(0.95));
}
#[tokio::test]
async fn test_complete_tool_validates_confidence_range() {
let tool = CompleteTool;
let result = tool
.call(r#"{"result": "x", "confidence": 1.5}"#.to_string())
.await;
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("confidence"));
}
#[test]
fn test_is_completion_signal_detects_marker() {
let response = format!(
r#"{{"completed": true, "marker": "{}"}}"#,
COMPLETION_MARKER
);
assert!(is_completion_signal("nika:complete", &response));
assert!(is_completion_signal("complete", &response));
}
#[test]
fn test_is_completion_signal_rejects_wrong_tool() {
let response = format!(r#"{{"marker": "{}"}}"#, COMPLETION_MARKER);
assert!(!is_completion_signal("nika:log", &response));
assert!(!is_completion_signal("other", &response));
}
#[test]
fn test_is_completion_signal_rejects_missing_marker() {
let response = r#"{"completed": true}"#;
assert!(!is_completion_signal("nika:complete", response));
}
#[test]
fn test_rig_agent_loop_explicit_completion_status() {
let params = AgentParams {
prompt: "Test".to_string(),
..Default::default()
};
let event_log = EventLog::new();
let mcp_clients = FxHashMap::default();
let agent = RigAgentLoop::new("test".to_string(), params, event_log, mcp_clients).unwrap();
let response = format!(r#"Result: {{"marker": "{}"}}"#, COMPLETION_MARKER);
let status = agent.determine_status(&response);
assert_eq!(status, RigAgentStatus::ExplicitCompletion);
}
#[test]
fn test_rig_agent_loop_natural_completion_status() {
let params = AgentParams {
prompt: "Test".to_string(),
..Default::default()
};
let event_log = EventLog::new();
let mcp_clients = FxHashMap::default();
let agent = RigAgentLoop::new("test".to_string(), params, event_log, mcp_clients).unwrap();
let status = agent.determine_status("Task completed normally.");
assert_eq!(status, RigAgentStatus::NaturalCompletion);
}
#[test]
fn test_rig_agent_loop_explicit_takes_priority() {
let params = AgentParams {
prompt: "Test".to_string(),
..Default::default()
};
let event_log = EventLog::new();
let mcp_clients = FxHashMap::default();
let agent = RigAgentLoop::new("test".to_string(), params, event_log, mcp_clients).unwrap();
let response = format!("Result with marker: {}", COMPLETION_MARKER);
let status = agent.determine_status(&response);
assert_eq!(status, RigAgentStatus::ExplicitCompletion);
}
#[test]
fn test_explicit_completion_canonical_str() {
assert_eq!(
RigAgentStatus::ExplicitCompletion.as_canonical_str(),
"tool_complete"
);
}
#[test]
fn test_all_status_canonical_strs() {
assert_eq!(
RigAgentStatus::NaturalCompletion.as_canonical_str(),
"end_turn"
);
assert_eq!(
RigAgentStatus::ExplicitCompletion.as_canonical_str(),
"tool_complete"
);
assert_eq!(
RigAgentStatus::MaxTurnsReached.as_canonical_str(),
"max_turns"
);
assert_eq!(
RigAgentStatus::TokenBudgetExceeded.as_canonical_str(),
"max_tokens"
);
assert_eq!(RigAgentStatus::Failed.as_canonical_str(), "error");
}
#[test]
fn test_completion_config_validation_valid() {
let config = CompletionConfig {
mode: CompletionMode::Explicit,
signal: Some(SignalConfig::default()),
patterns: vec![],
confidence: Some(ConfidenceConfig {
threshold: 0.8,
on_low: OnLowConfidenceConfig {
action: LowConfidenceAction::Retry,
max_retries: 3,
feedback: None,
},
routing: None,
}),
instruction: None,
};
assert!(config.validate().is_ok());
}
#[test]
fn test_completion_config_validation_invalid_threshold() {
let config = CompletionConfig {
mode: CompletionMode::Explicit,
signal: None,
patterns: vec![],
confidence: Some(ConfidenceConfig {
threshold: 1.5, on_low: OnLowConfidenceConfig::default(),
routing: None,
}),
instruction: None,
};
assert!(config.validate().is_err());
}
#[test]
fn test_pattern_mode_without_patterns_fails() {
let config = CompletionConfig {
mode: CompletionMode::Pattern,
signal: None,
patterns: vec![], confidence: None,
instruction: None,
};
assert!(config.validate().is_err());
}