1use super::types::{Permission, RateLimits};
4use chrono::{DateTime, Utc};
5use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct CreateKeyRequest {
11 pub key_alias: Option<String>,
13 pub user_id: String,
15 pub team_id: Option<String>,
17 pub models: Vec<String>,
19 pub max_budget: Option<f64>,
21 pub budget_duration: Option<String>,
23 pub rate_limits: Option<RateLimits>,
25 pub permissions: Vec<Permission>,
27 pub metadata: HashMap<String, String>,
29 pub expires_at: Option<DateTime<Utc>>,
31 pub tags: Vec<String>,
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize)]
37pub struct UpdateKeyRequest {
38 pub key_alias: Option<String>,
40 pub models: Option<Vec<String>>,
42 pub max_budget: Option<f64>,
44 pub budget_duration: Option<String>,
46 pub rate_limits: Option<RateLimits>,
48 pub permissions: Option<Vec<Permission>>,
50 pub metadata: Option<HashMap<String, String>>,
52 pub expires_at: Option<DateTime<Utc>>,
54 pub is_active: Option<bool>,
56 pub tags: Option<Vec<String>>,
58}
59
60#[cfg(test)]
61mod tests {
62 use super::*;
63 use chrono::{Duration, Utc};
64
65 fn create_test_rate_limits() -> RateLimits {
68 RateLimits {
69 rpm: Some(60),
70 rph: Some(3600),
71 rpd: Some(86400),
72 tpm: Some(100000),
73 tph: Some(6000000),
74 tpd: Some(144000000),
75 max_parallel_requests: Some(10),
76 }
77 }
78
79 #[test]
82 fn test_create_key_request_minimal() {
83 let request = CreateKeyRequest {
84 key_alias: None,
85 user_id: "user-001".to_string(),
86 team_id: None,
87 models: vec![],
88 max_budget: None,
89 budget_duration: None,
90 rate_limits: None,
91 permissions: vec![],
92 metadata: HashMap::new(),
93 expires_at: None,
94 tags: vec![],
95 };
96
97 assert_eq!(request.user_id, "user-001");
98 assert!(request.key_alias.is_none());
99 assert!(request.models.is_empty());
100 assert!(request.permissions.is_empty());
101 }
102
103 #[test]
104 fn test_create_key_request_full() {
105 let expires = Utc::now() + Duration::days(365);
106 let mut metadata = HashMap::new();
107 metadata.insert("env".to_string(), "production".to_string());
108
109 let request = CreateKeyRequest {
110 key_alias: Some("my-api-key".to_string()),
111 user_id: "user-001".to_string(),
112 team_id: Some("team-001".to_string()),
113 models: vec!["gpt-4".to_string(), "gpt-3.5-turbo".to_string()],
114 max_budget: Some(100.0),
115 budget_duration: Some("1m".to_string()),
116 rate_limits: Some(create_test_rate_limits()),
117 permissions: vec![Permission::ChatCompletion, Permission::Embedding],
118 metadata,
119 expires_at: Some(expires),
120 tags: vec!["production".to_string(), "api".to_string()],
121 };
122
123 assert_eq!(request.key_alias, Some("my-api-key".to_string()));
124 assert_eq!(request.user_id, "user-001");
125 assert_eq!(request.team_id, Some("team-001".to_string()));
126 assert_eq!(request.models.len(), 2);
127 assert_eq!(request.max_budget, Some(100.0));
128 assert_eq!(request.permissions.len(), 2);
129 assert_eq!(request.tags.len(), 2);
130 }
131
132 #[test]
133 fn test_create_key_request_with_models() {
134 let request = CreateKeyRequest {
135 key_alias: None,
136 user_id: "user-001".to_string(),
137 team_id: None,
138 models: vec![
139 "gpt-4".to_string(),
140 "gpt-3.5-turbo".to_string(),
141 "claude-3-opus".to_string(),
142 ],
143 max_budget: None,
144 budget_duration: None,
145 rate_limits: None,
146 permissions: vec![],
147 metadata: HashMap::new(),
148 expires_at: None,
149 tags: vec![],
150 };
151
152 assert_eq!(request.models.len(), 3);
153 assert!(request.models.contains(&"gpt-4".to_string()));
154 assert!(request.models.contains(&"claude-3-opus".to_string()));
155 }
156
157 #[test]
158 fn test_create_key_request_with_budget() {
159 let request = CreateKeyRequest {
160 key_alias: None,
161 user_id: "user-001".to_string(),
162 team_id: None,
163 models: vec![],
164 max_budget: Some(500.0),
165 budget_duration: Some("1w".to_string()),
166 rate_limits: None,
167 permissions: vec![],
168 metadata: HashMap::new(),
169 expires_at: None,
170 tags: vec![],
171 };
172
173 assert_eq!(request.max_budget, Some(500.0));
174 assert_eq!(request.budget_duration, Some("1w".to_string()));
175 }
176
177 #[test]
178 fn test_create_key_request_with_rate_limits() {
179 let request = CreateKeyRequest {
180 key_alias: None,
181 user_id: "user-001".to_string(),
182 team_id: None,
183 models: vec![],
184 max_budget: None,
185 budget_duration: None,
186 rate_limits: Some(create_test_rate_limits()),
187 permissions: vec![],
188 metadata: HashMap::new(),
189 expires_at: None,
190 tags: vec![],
191 };
192
193 assert!(request.rate_limits.is_some());
194 let limits = request.rate_limits.unwrap();
195 assert_eq!(limits.rpm, Some(60));
196 assert_eq!(limits.tpm, Some(100000));
197 }
198
199 #[test]
200 fn test_create_key_request_with_permissions() {
201 let request = CreateKeyRequest {
202 key_alias: None,
203 user_id: "user-001".to_string(),
204 team_id: None,
205 models: vec![],
206 max_budget: None,
207 budget_duration: None,
208 rate_limits: None,
209 permissions: vec![
210 Permission::ChatCompletion,
211 Permission::TextCompletion,
212 Permission::Embedding,
213 Permission::Admin,
214 ],
215 metadata: HashMap::new(),
216 expires_at: None,
217 tags: vec![],
218 };
219
220 assert_eq!(request.permissions.len(), 4);
221 assert!(request.permissions.contains(&Permission::Admin));
222 }
223
224 #[test]
225 fn test_create_key_request_with_metadata() {
226 let mut metadata = HashMap::new();
227 metadata.insert("environment".to_string(), "production".to_string());
228 metadata.insert("version".to_string(), "v1".to_string());
229 metadata.insert("owner".to_string(), "team-a".to_string());
230
231 let request = CreateKeyRequest {
232 key_alias: None,
233 user_id: "user-001".to_string(),
234 team_id: None,
235 models: vec![],
236 max_budget: None,
237 budget_duration: None,
238 rate_limits: None,
239 permissions: vec![],
240 metadata,
241 expires_at: None,
242 tags: vec![],
243 };
244
245 assert_eq!(request.metadata.len(), 3);
246 assert_eq!(
247 request.metadata.get("environment"),
248 Some(&"production".to_string())
249 );
250 }
251
252 #[test]
253 fn test_create_key_request_with_expiration() {
254 let expires = Utc::now() + Duration::days(30);
255
256 let request = CreateKeyRequest {
257 key_alias: None,
258 user_id: "user-001".to_string(),
259 team_id: None,
260 models: vec![],
261 max_budget: None,
262 budget_duration: None,
263 rate_limits: None,
264 permissions: vec![],
265 metadata: HashMap::new(),
266 expires_at: Some(expires),
267 tags: vec![],
268 };
269
270 assert!(request.expires_at.is_some());
271 assert!(request.expires_at.unwrap() > Utc::now());
272 }
273
274 #[test]
275 fn test_create_key_request_clone() {
276 let request = CreateKeyRequest {
277 key_alias: Some("test-key".to_string()),
278 user_id: "user-001".to_string(),
279 team_id: Some("team-001".to_string()),
280 models: vec!["gpt-4".to_string()],
281 max_budget: Some(100.0),
282 budget_duration: Some("1m".to_string()),
283 rate_limits: None,
284 permissions: vec![Permission::ChatCompletion],
285 metadata: HashMap::new(),
286 expires_at: None,
287 tags: vec!["test".to_string()],
288 };
289
290 let cloned = request.clone();
291 assert_eq!(cloned.key_alias, request.key_alias);
292 assert_eq!(cloned.user_id, request.user_id);
293 assert_eq!(cloned.models, request.models);
294 }
295
296 #[test]
297 fn test_create_key_request_debug() {
298 let request = CreateKeyRequest {
299 key_alias: Some("debug-key".to_string()),
300 user_id: "user-001".to_string(),
301 team_id: None,
302 models: vec![],
303 max_budget: None,
304 budget_duration: None,
305 rate_limits: None,
306 permissions: vec![],
307 metadata: HashMap::new(),
308 expires_at: None,
309 tags: vec![],
310 };
311
312 let debug_str = format!("{:?}", request);
313 assert!(debug_str.contains("CreateKeyRequest"));
314 assert!(debug_str.contains("debug-key"));
315 }
316
317 #[test]
318 fn test_create_key_request_serialization() {
319 let request = CreateKeyRequest {
320 key_alias: Some("ser-key".to_string()),
321 user_id: "user-001".to_string(),
322 team_id: None,
323 models: vec!["gpt-4".to_string()],
324 max_budget: Some(100.0),
325 budget_duration: None,
326 rate_limits: None,
327 permissions: vec![Permission::ChatCompletion],
328 metadata: HashMap::new(),
329 expires_at: None,
330 tags: vec![],
331 };
332
333 let json = serde_json::to_string(&request).unwrap();
334 assert!(json.contains("ser-key"));
335 assert!(json.contains("user-001"));
336 assert!(json.contains("gpt-4"));
337
338 let parsed: CreateKeyRequest = serde_json::from_str(&json).unwrap();
339 assert_eq!(parsed.key_alias, request.key_alias);
340 assert_eq!(parsed.user_id, request.user_id);
341 }
342
343 #[test]
346 fn test_update_key_request_minimal() {
347 let request = UpdateKeyRequest {
348 key_alias: None,
349 models: None,
350 max_budget: None,
351 budget_duration: None,
352 rate_limits: None,
353 permissions: None,
354 metadata: None,
355 expires_at: None,
356 is_active: None,
357 tags: None,
358 };
359
360 assert!(request.key_alias.is_none());
361 assert!(request.models.is_none());
362 assert!(request.is_active.is_none());
363 }
364
365 #[test]
366 fn test_update_key_request_full() {
367 let expires = Utc::now() + Duration::days(90);
368 let mut metadata = HashMap::new();
369 metadata.insert("updated".to_string(), "true".to_string());
370
371 let request = UpdateKeyRequest {
372 key_alias: Some("updated-key".to_string()),
373 models: Some(vec!["gpt-4".to_string()]),
374 max_budget: Some(200.0),
375 budget_duration: Some("1m".to_string()),
376 rate_limits: Some(create_test_rate_limits()),
377 permissions: Some(vec![Permission::ChatCompletion]),
378 metadata: Some(metadata),
379 expires_at: Some(expires),
380 is_active: Some(true),
381 tags: Some(vec!["updated".to_string()]),
382 };
383
384 assert_eq!(request.key_alias, Some("updated-key".to_string()));
385 assert_eq!(request.max_budget, Some(200.0));
386 assert_eq!(request.is_active, Some(true));
387 }
388
389 #[test]
390 fn test_update_key_request_partial_update() {
391 let request = UpdateKeyRequest {
392 key_alias: Some("new-alias".to_string()),
393 models: None,
394 max_budget: Some(300.0),
395 budget_duration: None,
396 rate_limits: None,
397 permissions: None,
398 metadata: None,
399 expires_at: None,
400 is_active: None,
401 tags: None,
402 };
403
404 assert_eq!(request.key_alias, Some("new-alias".to_string()));
405 assert!(request.models.is_none());
406 assert_eq!(request.max_budget, Some(300.0));
407 }
408
409 #[test]
410 fn test_update_key_request_deactivate() {
411 let request = UpdateKeyRequest {
412 key_alias: None,
413 models: None,
414 max_budget: None,
415 budget_duration: None,
416 rate_limits: None,
417 permissions: None,
418 metadata: None,
419 expires_at: None,
420 is_active: Some(false),
421 tags: None,
422 };
423
424 assert_eq!(request.is_active, Some(false));
425 }
426
427 #[test]
428 fn test_update_key_request_update_models() {
429 let request = UpdateKeyRequest {
430 key_alias: None,
431 models: Some(vec![
432 "gpt-4".to_string(),
433 "gpt-4-turbo".to_string(),
434 "claude-3-opus".to_string(),
435 ]),
436 max_budget: None,
437 budget_duration: None,
438 rate_limits: None,
439 permissions: None,
440 metadata: None,
441 expires_at: None,
442 is_active: None,
443 tags: None,
444 };
445
446 assert!(request.models.is_some());
447 let models = request.models.unwrap();
448 assert_eq!(models.len(), 3);
449 }
450
451 #[test]
452 fn test_update_key_request_update_rate_limits() {
453 let request = UpdateKeyRequest {
454 key_alias: None,
455 models: None,
456 max_budget: None,
457 budget_duration: None,
458 rate_limits: Some(RateLimits {
459 rpm: Some(120),
460 rph: None,
461 rpd: None,
462 tpm: Some(200000),
463 tph: None,
464 tpd: None,
465 max_parallel_requests: Some(20),
466 }),
467 permissions: None,
468 metadata: None,
469 expires_at: None,
470 is_active: None,
471 tags: None,
472 };
473
474 assert!(request.rate_limits.is_some());
475 let limits = request.rate_limits.unwrap();
476 assert_eq!(limits.rpm, Some(120));
477 assert_eq!(limits.tpm, Some(200000));
478 }
479
480 #[test]
481 fn test_update_key_request_update_permissions() {
482 let request = UpdateKeyRequest {
483 key_alias: None,
484 models: None,
485 max_budget: None,
486 budget_duration: None,
487 rate_limits: None,
488 permissions: Some(vec![
489 Permission::ChatCompletion,
490 Permission::Admin,
491 Permission::KeyManagement,
492 ]),
493 metadata: None,
494 expires_at: None,
495 is_active: None,
496 tags: None,
497 };
498
499 assert!(request.permissions.is_some());
500 let perms = request.permissions.unwrap();
501 assert_eq!(perms.len(), 3);
502 assert!(perms.contains(&Permission::Admin));
503 }
504
505 #[test]
506 fn test_update_key_request_update_metadata() {
507 let mut metadata = HashMap::new();
508 metadata.insert("status".to_string(), "upgraded".to_string());
509 metadata.insert("tier".to_string(), "premium".to_string());
510
511 let request = UpdateKeyRequest {
512 key_alias: None,
513 models: None,
514 max_budget: None,
515 budget_duration: None,
516 rate_limits: None,
517 permissions: None,
518 metadata: Some(metadata),
519 expires_at: None,
520 is_active: None,
521 tags: None,
522 };
523
524 assert!(request.metadata.is_some());
525 let meta = request.metadata.unwrap();
526 assert_eq!(meta.len(), 2);
527 assert_eq!(meta.get("tier"), Some(&"premium".to_string()));
528 }
529
530 #[test]
531 fn test_update_key_request_extend_expiration() {
532 let new_expires = Utc::now() + Duration::days(365);
533
534 let request = UpdateKeyRequest {
535 key_alias: None,
536 models: None,
537 max_budget: None,
538 budget_duration: None,
539 rate_limits: None,
540 permissions: None,
541 metadata: None,
542 expires_at: Some(new_expires),
543 is_active: None,
544 tags: None,
545 };
546
547 assert!(request.expires_at.is_some());
548 assert!(request.expires_at.unwrap() > Utc::now());
549 }
550
551 #[test]
552 fn test_update_key_request_clone() {
553 let request = UpdateKeyRequest {
554 key_alias: Some("clone-test".to_string()),
555 models: Some(vec!["gpt-4".to_string()]),
556 max_budget: Some(100.0),
557 budget_duration: None,
558 rate_limits: None,
559 permissions: None,
560 metadata: None,
561 expires_at: None,
562 is_active: Some(true),
563 tags: None,
564 };
565
566 let cloned = request.clone();
567 assert_eq!(cloned.key_alias, request.key_alias);
568 assert_eq!(cloned.max_budget, request.max_budget);
569 assert_eq!(cloned.is_active, request.is_active);
570 }
571
572 #[test]
573 fn test_update_key_request_debug() {
574 let request = UpdateKeyRequest {
575 key_alias: Some("debug-test".to_string()),
576 models: None,
577 max_budget: None,
578 budget_duration: None,
579 rate_limits: None,
580 permissions: None,
581 metadata: None,
582 expires_at: None,
583 is_active: None,
584 tags: None,
585 };
586
587 let debug_str = format!("{:?}", request);
588 assert!(debug_str.contains("UpdateKeyRequest"));
589 assert!(debug_str.contains("debug-test"));
590 }
591
592 #[test]
593 fn test_update_key_request_serialization() {
594 let request = UpdateKeyRequest {
595 key_alias: Some("ser-test".to_string()),
596 models: Some(vec!["gpt-4".to_string()]),
597 max_budget: Some(150.0),
598 budget_duration: None,
599 rate_limits: None,
600 permissions: None,
601 metadata: None,
602 expires_at: None,
603 is_active: Some(true),
604 tags: None,
605 };
606
607 let json = serde_json::to_string(&request).unwrap();
608 assert!(json.contains("ser-test"));
609 assert!(json.contains("150"));
610
611 let parsed: UpdateKeyRequest = serde_json::from_str(&json).unwrap();
612 assert_eq!(parsed.key_alias, request.key_alias);
613 assert_eq!(parsed.max_budget, request.max_budget);
614 }
615
616 #[test]
619 fn test_key_lifecycle_create_and_update() {
620 let create_request = CreateKeyRequest {
622 key_alias: Some("lifecycle-key".to_string()),
623 user_id: "user-001".to_string(),
624 team_id: None,
625 models: vec!["gpt-3.5-turbo".to_string()],
626 max_budget: Some(50.0),
627 budget_duration: None,
628 rate_limits: None,
629 permissions: vec![Permission::ChatCompletion],
630 metadata: HashMap::new(),
631 expires_at: None,
632 tags: vec!["test".to_string()],
633 };
634
635 assert_eq!(create_request.max_budget, Some(50.0));
636 assert_eq!(create_request.models.len(), 1);
637
638 let update_request = UpdateKeyRequest {
640 key_alias: None,
641 models: Some(vec!["gpt-3.5-turbo".to_string(), "gpt-4".to_string()]),
642 max_budget: Some(200.0),
643 budget_duration: None,
644 rate_limits: Some(create_test_rate_limits()),
645 permissions: Some(vec![Permission::ChatCompletion, Permission::Embedding]),
646 metadata: None,
647 expires_at: None,
648 is_active: None,
649 tags: None,
650 };
651
652 assert_eq!(update_request.max_budget, Some(200.0));
653 assert_eq!(update_request.models.as_ref().unwrap().len(), 2);
654 }
655
656 #[test]
657 fn test_budget_duration_formats() {
658 let durations = vec!["1d", "1w", "1m", "3m", "1y"];
659
660 for duration in durations {
661 let request = CreateKeyRequest {
662 key_alias: None,
663 user_id: "user-001".to_string(),
664 team_id: None,
665 models: vec![],
666 max_budget: Some(100.0),
667 budget_duration: Some(duration.to_string()),
668 rate_limits: None,
669 permissions: vec![],
670 metadata: HashMap::new(),
671 expires_at: None,
672 tags: vec![],
673 };
674
675 assert_eq!(request.budget_duration, Some(duration.to_string()));
676 }
677 }
678
679 #[test]
680 fn test_permission_combinations() {
681 let basic_perms = [Permission::ChatCompletion];
682 let standard_perms = [
683 Permission::ChatCompletion,
684 Permission::TextCompletion,
685 Permission::Embedding,
686 ];
687 let admin_perms = [
688 Permission::ChatCompletion,
689 Permission::TextCompletion,
690 Permission::Embedding,
691 Permission::Admin,
692 Permission::KeyManagement,
693 ];
694
695 assert_eq!(basic_perms.len(), 1);
696 assert_eq!(standard_perms.len(), 3);
697 assert_eq!(admin_perms.len(), 5);
698 assert!(admin_perms.contains(&Permission::Admin));
699 }
700
701 #[test]
702 fn test_empty_update_request() {
703 let request = UpdateKeyRequest {
704 key_alias: None,
705 models: None,
706 max_budget: None,
707 budget_duration: None,
708 rate_limits: None,
709 permissions: None,
710 metadata: None,
711 expires_at: None,
712 is_active: None,
713 tags: None,
714 };
715
716 assert!(request.key_alias.is_none());
718 assert!(request.models.is_none());
719 assert!(request.max_budget.is_none());
720 assert!(request.budget_duration.is_none());
721 assert!(request.rate_limits.is_none());
722 assert!(request.permissions.is_none());
723 assert!(request.metadata.is_none());
724 assert!(request.expires_at.is_none());
725 assert!(request.is_active.is_none());
726 assert!(request.tags.is_none());
727 }
728
729 #[test]
730 fn test_update_tags() {
731 let request = UpdateKeyRequest {
732 key_alias: None,
733 models: None,
734 max_budget: None,
735 budget_duration: None,
736 rate_limits: None,
737 permissions: None,
738 metadata: None,
739 expires_at: None,
740 is_active: None,
741 tags: Some(vec!["production".to_string(), "high-priority".to_string()]),
742 };
743
744 assert!(request.tags.is_some());
745 let tags = request.tags.unwrap();
746 assert_eq!(tags.len(), 2);
747 assert!(tags.contains(&"production".to_string()));
748 }
749}