use picobot::providers::{ChatCompletionRequest, Message}; use picobot::protocol::{SessionSummary, WsInbound, WsOutbound}; /// Test that message with special characters is properly escaped #[test] fn test_message_special_characters() { let msg = Message::user("Hello \"world\"\nNew line\tTab"); let json = serde_json::to_string(&msg).unwrap(); let deserialized: Message = serde_json::from_str(&json).unwrap(); assert_eq!(deserialized.role, "user"); assert_eq!(deserialized.content.len(), 1); let encoded = serde_json::to_string(&deserialized.content).unwrap(); assert!(encoded.contains("Hello \\\"world\\\"\\nNew line\\tTab")); } /// Test that multi-line system prompt is preserved #[test] fn test_multiline_system_prompt() { let messages = vec![ Message::system("You are a helpful assistant.\n\nFollow these rules:\n1. Be kind\n2. Be accurate"), Message::user("Hi"), ]; let json = serde_json::to_string(&messages[0]).unwrap(); assert!(json.contains("helpful assistant")); assert!(json.contains("rules")); assert!(json.contains("1. Be kind")); } /// Test ChatCompletionRequest serialization (without model field) #[test] fn test_chat_request_serialization() { let request = ChatCompletionRequest { messages: vec![ Message::system("You are helpful"), Message::user("Hello"), ], temperature: Some(0.7), max_tokens: Some(100), tools: None, }; let json = serde_json::to_string(&request).unwrap(); // Verify structure assert!(json.contains(r#""role":"system""#)); assert!(json.contains(r#""role":"user""#)); assert!(json.contains("You are helpful")); assert!(json.contains("Hello")); assert!(json.contains(r#""temperature":0.7"#)); assert!(json.contains(r#""max_tokens":100"#)); } #[test] fn test_session_inbound_serialization() { let msg = WsInbound::CreateSession { title: Some("demo".to_string()), }; let json = serde_json::to_string(&msg).unwrap(); assert!(json.contains(r#""type":"create_session""#)); assert!(json.contains(r#""title":"demo""#)); let decoded: WsInbound = serde_json::from_str(&json).unwrap(); match decoded { WsInbound::CreateSession { title } => { assert_eq!(title.as_deref(), Some("demo")); } other => panic!("unexpected decoded variant: {:?}", other), } } #[test] fn test_session_list_outbound_serialization() { let msg = WsOutbound::SessionList { sessions: vec![SessionSummary { session_id: "session-1".to_string(), title: "demo".to_string(), channel_name: "cli".to_string(), chat_id: "session-1".to_string(), message_count: 2, last_active_at: 123, archived_at: None, }], current_session_id: Some("session-1".to_string()), }; let json = serde_json::to_string(&msg).unwrap(); assert!(json.contains(r#""type":"session_list""#)); assert!(json.contains(r#""session_id":"session-1""#)); assert!(json.contains(r#""message_count":2"#)); let decoded: WsOutbound = serde_json::from_str(&json).unwrap(); match decoded { WsOutbound::SessionList { sessions, current_session_id, } => { assert_eq!(sessions.len(), 1); assert_eq!(sessions[0].title, "demo"); assert_eq!(current_session_id.as_deref(), Some("session-1")); } other => panic!("unexpected decoded variant: {:?}", other), } } #[test] fn test_clear_history_with_session_id_serialization() { let msg = WsInbound::ClearHistory { chat_id: None, session_id: Some("session-1".to_string()), }; let json = serde_json::to_string(&msg).unwrap(); assert!(json.contains(r#""type":"clear_history""#)); assert!(json.contains(r#""session_id":"session-1""#)); }