An AI agent built to do Ralph loops - plan mode for planning and ralph mode for implementing.
1use rustagent::llm::{Message, ToolDefinition};
2use serde_json::json;
3
4#[test]
5fn test_message_serialization() {
6 let msg = Message::user("Hello");
7
8 let json = serde_json::to_value(&msg).unwrap();
9 assert_eq!(json["role"], "user");
10 assert_eq!(json["content"], "Hello");
11}
12
13#[test]
14fn test_tool_definition_parameters() {
15 let tool = ToolDefinition {
16 name: "read_file".to_string(),
17 description: "Read a file".to_string(),
18 parameters: json!({
19 "type": "object",
20 "properties": {
21 "path": {"type": "string"}
22 },
23 "required": ["path"]
24 }),
25 };
26
27 assert_eq!(tool.name, "read_file");
28}
29
30use rustagent::llm::anthropic::AnthropicClient;
31
32#[tokio::test]
33async fn test_anthropic_message_format() {
34 // This test validates request structure, doesn't actually call API
35 let client = AnthropicClient::new("test-key".to_string(), "claude-sonnet-4".to_string(), 4096);
36
37 let messages = vec![Message::user("Hello")];
38
39 // We'll test this by mocking in future, for now just construct
40 assert!(client.format_request(&messages, &[]).is_ok());
41}
42
43#[test]
44fn test_format_request_with_system_message() {
45 let client = AnthropicClient::new("test-key".to_string(), "claude-sonnet-4".to_string(), 4096);
46
47 let messages = vec![
48 Message::system("You are a helpful assistant"),
49 Message::user("Hello"),
50 ];
51
52 let request = client.format_request(&messages, &[]).unwrap();
53
54 // Should have system field
55 assert!(request.get("system").is_some());
56 assert_eq!(
57 request.get("system").unwrap().as_str().unwrap(),
58 "You are a helpful assistant"
59 );
60
61 // Should not include system message in messages array
62 let msgs = request.get("messages").unwrap().as_array().unwrap();
63 assert_eq!(msgs.len(), 1);
64 assert_eq!(msgs[0].get("role").unwrap().as_str().unwrap(), "user");
65}
66
67use reqwest::StatusCode;
68use rustagent::llm::error::{ErrorKind, classify_status};
69use rustagent::llm::retry::parse_retry_from_message;
70
71#[test]
72fn test_classify_status_retryable() {
73 assert_eq!(
74 classify_status(StatusCode::TOO_MANY_REQUESTS),
75 ErrorKind::RateLimited
76 );
77 assert_eq!(
78 classify_status(StatusCode::BAD_GATEWAY),
79 ErrorKind::Transient
80 );
81 assert_eq!(
82 classify_status(StatusCode::SERVICE_UNAVAILABLE),
83 ErrorKind::Transient
84 );
85 assert_eq!(
86 classify_status(StatusCode::GATEWAY_TIMEOUT),
87 ErrorKind::Transient
88 );
89 assert_eq!(
90 classify_status(StatusCode::REQUEST_TIMEOUT),
91 ErrorKind::Transient
92 );
93}
94
95#[test]
96fn test_classify_status_non_retryable() {
97 assert_eq!(
98 classify_status(StatusCode::BAD_REQUEST),
99 ErrorKind::BadRequest
100 );
101 assert_eq!(classify_status(StatusCode::UNAUTHORIZED), ErrorKind::Auth);
102 assert_eq!(classify_status(StatusCode::FORBIDDEN), ErrorKind::Auth);
103}
104
105#[test]
106fn test_parse_retry_from_message() {
107 let msg = "Rate limit reached. Please try again in 45.622s.";
108 let dur = parse_retry_from_message(msg).unwrap();
109 assert!(dur.as_millis() >= 45000 && dur.as_millis() <= 46000);
110
111 let msg2 = "Try again in 500ms";
112 let dur2 = parse_retry_from_message(msg2).unwrap();
113 assert_eq!(dur2.as_millis(), 500);
114
115 assert!(parse_retry_from_message("Something went wrong").is_none());
116}