1use serde::Serialize;
2
3#[derive(Debug, Clone)]
5pub struct JellyfinConfig {
6 pub server_url: String,
7 pub access_token: String,
8 pub device_id: String,
9}
10
11#[derive(Debug, Serialize)]
13#[serde(rename_all = "PascalCase")]
14pub struct PlaybackStartRequest {
15 pub item_id: String,
16 pub position_ticks: i64,
17 #[serde(skip_serializing_if = "Option::is_none")]
18 pub play_session_id: Option<String>,
19 pub play_command: String,
20 pub is_paused: bool,
21}
22
23#[derive(Debug, Serialize)]
25#[serde(rename_all = "PascalCase")]
26pub struct PlaybackStoppedRequest {
27 pub item_id: String,
28 pub position_ticks: i64,
29 #[serde(skip_serializing_if = "Option::is_none")]
30 pub play_session_id: Option<String>,
31}
32
33#[derive(Debug, Serialize)]
35#[serde(rename_all = "PascalCase")]
36#[allow(dead_code)] pub struct PlaybackProgressRequest {
38 pub item_id: String,
39 pub position_ticks: i64,
40 pub is_paused: bool,
41 #[serde(skip_serializing_if = "Option::is_none")]
42 pub play_session_id: Option<String>,
43}
44
45#[cfg(test)]
46mod tests {
47 use super::*;
48
49 #[test]
50 fn test_jellyfin_config_creation() {
51 let config = JellyfinConfig {
52 server_url: "https://jellyfin.example.com".to_string(),
53 access_token: "token-123".to_string(),
54 device_id: "device-456".to_string(),
55 };
56
57 assert_eq!(config.server_url, "https://jellyfin.example.com");
58 assert_eq!(config.access_token, "token-123");
59 assert_eq!(config.device_id, "device-456");
60 }
61
62 #[test]
63 fn test_jellyfin_config_clone() {
64 let config = JellyfinConfig {
65 server_url: "https://server.local".to_string(),
66 access_token: "token-abc".to_string(),
67 device_id: "device-xyz".to_string(),
68 };
69
70 let cloned = config.clone();
71 assert_eq!(config.server_url, cloned.server_url);
72 assert_eq!(config.access_token, cloned.access_token);
73 assert_eq!(config.device_id, cloned.device_id);
74 }
75
76 #[test]
77 fn test_playback_start_request_serialization() {
78 let request = PlaybackStartRequest {
79 item_id: "item-123".to_string(),
80 position_ticks: 0,
81 play_session_id: Some("session-456".to_string()),
82 play_command: "PlayNow".to_string(),
83 is_paused: false,
84 };
85
86 let json = serde_json::to_string(&request);
87 assert!(json.is_ok());
88 let serialized = json.unwrap();
89 assert!(serialized.contains("item-123"));
90 assert!(serialized.contains("PlayNow"));
91 }
92
93 #[test]
94 fn test_playback_start_request_without_session() {
95 let request = PlaybackStartRequest {
96 item_id: "item-789".to_string(),
97 position_ticks: 1_000_000,
98 play_session_id: None,
99 play_command: "Resume".to_string(),
100 is_paused: true,
101 };
102
103 let json = serde_json::to_string(&request).unwrap();
104 assert!(json.contains("true"));
105 assert!(!json.contains("playSessionId"));
106 }
107
108 #[test]
109 fn test_playback_start_request_pascal_case() {
110 let request = PlaybackStartRequest {
111 item_id: "item-1".to_string(),
112 position_ticks: 100,
113 play_session_id: Some("session-1".to_string()),
114 play_command: "Play".to_string(),
115 is_paused: false,
116 };
117
118 let json = serde_json::to_string(&request).unwrap();
119 assert!(json.contains("ItemId"));
121 assert!(json.contains("PositionTicks"));
122 assert!(json.contains("PlaySessionId"));
123 assert!(json.contains("PlayCommand"));
124 assert!(json.contains("IsPaused"));
125 }
126
127 #[test]
128 fn test_playback_stopped_request_serialization() {
129 let request = PlaybackStoppedRequest {
130 item_id: "item-555".to_string(),
131 position_ticks: 5_000_000,
132 play_session_id: Some("session-789".to_string()),
133 };
134
135 let json = serde_json::to_string(&request);
136 assert!(json.is_ok());
137 let serialized = json.unwrap();
138 assert!(serialized.contains("item-555"));
139 assert!(serialized.contains("5000000"));
140 }
141
142 #[test]
143 fn test_playback_stopped_request_without_session() {
144 let request = PlaybackStoppedRequest {
145 item_id: "item-000".to_string(),
146 position_ticks: 0,
147 play_session_id: None,
148 };
149
150 let json = serde_json::to_string(&request).unwrap();
151 assert!(json.contains("item-000"));
152 assert!(!json.contains("playSessionId"));
153 }
154
155 #[test]
156 fn test_playback_stopped_request_pascal_case() {
157 let request = PlaybackStoppedRequest {
158 item_id: "i1".to_string(),
159 position_ticks: 500,
160 play_session_id: Some("s1".to_string()),
161 };
162
163 let json = serde_json::to_string(&request).unwrap();
164 assert!(json.contains("ItemId"));
165 assert!(json.contains("PositionTicks"));
166 assert!(json.contains("PlaySessionId"));
167 }
168
169 #[test]
170 fn test_playback_progress_request_serialization() {
171 let request = PlaybackProgressRequest {
172 item_id: "item-999".to_string(),
173 position_ticks: 150_000_000,
174 is_paused: false,
175 play_session_id: Some("session-111".to_string()),
176 };
177
178 let json = serde_json::to_string(&request);
179 assert!(json.is_ok());
180 let serialized = json.unwrap();
181 assert!(serialized.contains("item-999"));
182 assert!(serialized.contains("150000000"));
183 assert!(serialized.contains("false"));
184 }
185
186 #[test]
187 fn test_playback_progress_request_paused() {
188 let request = PlaybackProgressRequest {
189 item_id: "item-paused".to_string(),
190 position_ticks: 75_000_000,
191 is_paused: true,
192 play_session_id: None,
193 };
194
195 let json = serde_json::to_string(&request).unwrap();
196 assert!(json.contains("true"));
197 assert!(json.contains("item-paused"));
198 }
199
200 #[test]
201 fn test_playback_progress_request_pascal_case() {
202 let request = PlaybackProgressRequest {
203 item_id: "i2".to_string(),
204 position_ticks: 200,
205 is_paused: true,
206 play_session_id: Some("s2".to_string()),
207 };
208
209 let json = serde_json::to_string(&request).unwrap();
210 assert!(json.contains("ItemId"));
211 assert!(json.contains("PositionTicks"));
212 assert!(json.contains("IsPaused"));
213 assert!(json.contains("PlaySessionId"));
214 }
215
216 #[test]
217 fn test_playback_start_request_various_position_values() {
218 let positions = vec![0, 1_000_000, 10_000_000, 100_000_000, 1_000_000_000];
219
220 for pos in positions {
221 let request = PlaybackStartRequest {
222 item_id: "item-test".to_string(),
223 position_ticks: pos,
224 play_session_id: None,
225 play_command: "Play".to_string(),
226 is_paused: false,
227 };
228
229 let json = serde_json::to_string(&request).unwrap();
230 assert!(json.contains("item-test"));
231 assert!(json.contains(&pos.to_string()));
232 }
233 }
234
235 #[test]
236 fn test_jellyfin_config_debug() {
237 let config = JellyfinConfig {
238 server_url: "https://debug.example.com".to_string(),
239 access_token: "token-debug".to_string(),
240 device_id: "device-debug".to_string(),
241 };
242
243 let debug_str = format!("{:?}", config);
244 assert!(debug_str.contains("JellyfinConfig"));
245 assert!(debug_str.contains("https://debug.example.com"));
246 }
247
248 #[test]
249 fn test_playback_requests_creation_patterns() {
250 let start = PlaybackStartRequest {
252 item_id: "i1".to_string(),
253 position_ticks: 0,
254 play_session_id: None,
255 play_command: "PlayNow".to_string(),
256 is_paused: false,
257 };
258
259 let stopped = PlaybackStoppedRequest {
260 item_id: "i1".to_string(),
261 position_ticks: 1_000_000,
262 play_session_id: None,
263 };
264
265 let progress = PlaybackProgressRequest {
266 item_id: "i1".to_string(),
267 position_ticks: 500_000,
268 is_paused: false,
269 play_session_id: None,
270 };
271
272 assert!(serde_json::to_string(&start).is_ok());
274 assert!(serde_json::to_string(&stopped).is_ok());
275 assert!(serde_json::to_string(&progress).is_ok());
276 }
277}