gemini_genai_rs/protocol/messages/
mod.rs1pub mod client;
4pub mod server;
5
6pub use client::*;
7pub use server::*;
8
9#[cfg(test)]
10mod tests {
11 use super::*;
12 use crate::protocol::types::*;
13
14 #[test]
15 fn setup_message_serialization() {
16 let config = SessionConfig::new("test-key")
17 .model(ModelId::from_static("models/gemini-2.0-flash-live-001"))
18 .voice(Voice::Kore)
19 .system_instruction("You are a helpful assistant.");
20
21 let json = config.to_setup_json();
22 assert!(json.contains("\"setup\""));
23 assert!(json.contains("\"generationConfig\""));
24 assert!(json.contains("\"Kore\""));
25 assert!(json.contains("\"systemInstruction\""));
26 }
27
28 #[test]
29 fn parse_setup_complete() {
30 let json = r#"{"setupComplete":{"sessionResumption":{"handle":"abc123"}}}"#;
31 let msg = ServerMessage::parse(json).unwrap();
32 match msg {
33 ServerMessage::SetupComplete(sc) => {
34 let handle = sc.setup_complete.session_resumption.unwrap().handle;
35 assert_eq!(handle, Some("abc123".to_string()));
36 }
37 _ => panic!("Expected SetupComplete"),
38 }
39 }
40
41 #[test]
42 fn parse_server_content_text() {
43 let json = r#"{
44 "serverContent": {
45 "modelTurn": {
46 "parts": [{"text": "Hello! How can I help?"}]
47 },
48 "turnComplete": true
49 }
50 }"#;
51 let msg = ServerMessage::parse(json).unwrap();
52 match msg {
53 ServerMessage::ServerContent(sc) => {
54 assert!(sc.server_content.turn_complete.unwrap_or(false));
55 let turn = sc.server_content.model_turn.unwrap();
56 assert_eq!(turn.parts.len(), 1);
57 match &turn.parts[0] {
58 Part::Text { text } => assert_eq!(text, "Hello! How can I help?"),
59 _ => panic!("Expected text part"),
60 }
61 }
62 _ => panic!("Expected ServerContent"),
63 }
64 }
65
66 #[test]
67 fn parse_server_content_audio() {
68 let json = r#"{
69 "serverContent": {
70 "modelTurn": {
71 "parts": [{"inlineData": {"mimeType": "audio/pcm", "data": "AAAA"}}]
72 }
73 }
74 }"#;
75 let msg = ServerMessage::parse(json).unwrap();
76 match msg {
77 ServerMessage::ServerContent(sc) => {
78 let turn = sc.server_content.model_turn.unwrap();
79 match &turn.parts[0] {
80 Part::InlineData { inline_data } => {
81 assert_eq!(inline_data.mime_type, "audio/pcm");
82 }
83 _ => panic!("Expected inline data part"),
84 }
85 }
86 _ => panic!("Expected ServerContent"),
87 }
88 }
89
90 #[test]
91 fn parse_tool_call() {
92 let json = r#"{
93 "toolCall": {
94 "functionCalls": [
95 {"name": "get_weather", "args": {"city": "London"}, "id": "call-1"}
96 ]
97 }
98 }"#;
99 let msg = ServerMessage::parse(json).unwrap();
100 match msg {
101 ServerMessage::ToolCall(tc) => {
102 assert_eq!(tc.tool_call.function_calls.len(), 1);
103 assert_eq!(tc.tool_call.function_calls[0].name, "get_weather");
104 }
105 _ => panic!("Expected ToolCall"),
106 }
107 }
108
109 #[test]
110 fn parse_tool_call_cancellation() {
111 let json = r#"{"toolCallCancellation": {"ids": ["call-1", "call-2"]}}"#;
112 let msg = ServerMessage::parse(json).unwrap();
113 match msg {
114 ServerMessage::ToolCallCancellation(tc) => {
115 assert_eq!(tc.tool_call_cancellation.ids, vec!["call-1", "call-2"]);
116 }
117 _ => panic!("Expected ToolCallCancellation"),
118 }
119 }
120
121 #[test]
122 fn parse_go_away() {
123 let json = r#"{"goAway": {"timeLeft": "30s"}}"#;
124 let msg = ServerMessage::parse(json).unwrap();
125 match msg {
126 ServerMessage::GoAway(ga) => {
127 assert_eq!(
128 ga.go_away.time_left,
129 Some(std::time::Duration::from_secs(30))
130 );
131 }
132 _ => panic!("Expected GoAway"),
133 }
134 }
135
136 #[test]
137 fn parse_interrupted() {
138 let json = r#"{"serverContent": {"interrupted": true}}"#;
139 let msg = ServerMessage::parse(json).unwrap();
140 match msg {
141 ServerMessage::ServerContent(sc) => {
142 assert!(sc.server_content.interrupted.unwrap_or(false));
143 }
144 _ => panic!("Expected ServerContent"),
145 }
146 }
147
148 #[test]
149 fn parse_unknown_message() {
150 let json = r#"{"newFeature": {"value": 42}}"#;
151 let msg = ServerMessage::parse(json).unwrap();
152 assert!(matches!(msg, ServerMessage::Unknown(_)));
153 }
154
155 #[test]
156 fn realtime_input_serialization_audio() {
157 let msg = RealtimeInputMessage {
158 realtime_input: RealtimeInputPayload {
159 media_chunks: Vec::new(),
160 audio: Some(Blob {
161 mime_type: "audio/pcm".to_string(),
162 data: "AQIDBA==".to_string(),
163 }),
164 video: None,
165 audio_stream_end: None,
166 text: None,
167 },
168 };
169 let json = serde_json::to_string(&msg).unwrap();
170 assert!(json.contains("\"realtimeInput\""));
171 assert!(json.contains("\"audio\""));
172 assert!(json.contains("\"mimeType\""));
173 assert!(!json.contains("\"mediaChunks\""));
175 }
176
177 #[test]
178 fn realtime_input_serialization_legacy() {
179 let msg = RealtimeInputMessage {
180 realtime_input: RealtimeInputPayload {
181 media_chunks: vec![MediaChunk {
182 mime_type: "audio/pcm".to_string(),
183 data: "AQIDBA==".to_string(),
184 }],
185 audio: None,
186 video: None,
187 audio_stream_end: None,
188 text: None,
189 },
190 };
191 let json = serde_json::to_string(&msg).unwrap();
192 assert!(json.contains("\"mediaChunks\""));
193 }
194
195 #[test]
196 fn parse_session_resumption_update() {
197 let json = r#"{"sessionResumptionUpdate": {"newHandle": "handle-xyz", "resumable": true}}"#;
198 let msg = ServerMessage::parse(json).unwrap();
199 match msg {
200 ServerMessage::SessionResumptionUpdate(sru) => {
201 assert_eq!(
202 sru.session_resumption_update.new_handle,
203 Some("handle-xyz".to_string())
204 );
205 assert_eq!(sru.session_resumption_update.resumable, Some(true));
206 }
207 _ => panic!("Expected SessionResumptionUpdate"),
208 }
209 }
210
211 #[test]
212 fn tool_response_serialization() {
213 let msg = ToolResponseMessage {
214 tool_response: ToolResponsePayload {
215 function_responses: vec![FunctionResponse {
216 name: "get_weather".to_string(),
217 response: serde_json::json!({"temp": 22}),
218 id: Some("call-1".to_string()),
219 scheduling: None,
220 }],
221 },
222 };
223 let json = serde_json::to_string(&msg).unwrap();
224 assert!(json.contains("\"toolResponse\""));
225 assert!(json.contains("\"functionResponses\""));
226 }
227
228 #[test]
229 fn client_content_serialization() {
230 let msg = ClientContentMessage {
231 client_content: ClientContentPayload {
232 turns: vec![Content::user("Hello")],
233 turn_complete: Some(true),
234 },
235 };
236 let json = serde_json::to_string(&msg).unwrap();
237 assert!(json.contains("\"clientContent\""));
238 assert!(json.contains("\"turnComplete\""));
239 }
240
241 #[test]
242 fn activity_signal_serialization() {
243 let msg = ActivitySignalMessage {
244 realtime_input: ActivitySignalPayload {
245 activity_start: Some(ActivityStart {}),
246 activity_end: None,
247 },
248 };
249 let json = serde_json::to_string(&msg).unwrap();
250 assert!(json.contains("\"activityStart\""));
251 }
252
253 #[test]
254 fn voice_activity_type_serialization() {
255 let start = VoiceActivityType::VoiceActivityStart;
256 let json = serde_json::to_string(&start).unwrap();
257 assert_eq!(json, "\"VOICE_ACTIVITY_START\"");
258 let parsed: VoiceActivityType = serde_json::from_str(&json).unwrap();
259 assert_eq!(parsed, start);
260
261 let end = VoiceActivityType::VoiceActivityEnd;
262 let json = serde_json::to_string(&end).unwrap();
263 assert_eq!(json, "\"VOICE_ACTIVITY_END\"");
264 let parsed: VoiceActivityType = serde_json::from_str(&json).unwrap();
265 assert_eq!(parsed, end);
266 }
267
268 #[test]
269 fn parse_voice_activity_message() {
270 let json = r#"{"voiceActivity":{"voiceActivityType":"VOICE_ACTIVITY_START"}}"#;
271 let msg = ServerMessage::parse(json).unwrap();
272 match msg {
273 ServerMessage::VoiceActivity(va) => {
274 assert_eq!(
275 va.voice_activity.voice_activity_type,
276 Some(VoiceActivityType::VoiceActivityStart)
277 );
278 }
279 _ => panic!("Expected VoiceActivity"),
280 }
281
282 let json = r#"{"voiceActivity":{"voiceActivityType":"VOICE_ACTIVITY_END"}}"#;
283 let msg = ServerMessage::parse(json).unwrap();
284 match msg {
285 ServerMessage::VoiceActivity(va) => {
286 assert_eq!(
287 va.voice_activity.voice_activity_type,
288 Some(VoiceActivityType::VoiceActivityEnd)
289 );
290 }
291 _ => panic!("Expected VoiceActivity"),
292 }
293 }
294
295 #[test]
296 fn parse_input_transcription() {
297 let json = r#"{
298 "serverContent": {
299 "inputTranscription": {"text": "Hello world"}
300 }
301 }"#;
302 let msg = ServerMessage::parse(json).unwrap();
303 match msg {
304 ServerMessage::ServerContent(sc) => {
305 let text = sc.server_content.input_transcription.unwrap().text.unwrap();
306 assert_eq!(text, "Hello world");
307 }
308 _ => panic!("Expected ServerContent"),
309 }
310 }
311}