1pub use uptrakit_shared_types::SecretString;
4
5pub use uptrakit_shared_types::PluginRole;
8
9pub mod access_catalog;
10pub mod access_grants;
11pub mod agents;
12pub mod api_tokens;
13pub mod audit_logs;
14pub mod auth;
15pub mod autodiscovery;
16pub mod batch_actions;
17pub mod command_validation;
18pub mod device_auth;
19pub mod discovery_allowlist;
20pub mod enrollment_tokens;
21pub mod error;
22pub mod events;
23pub mod host_tags;
24pub mod hosts;
25pub mod instance_config_state;
26pub mod instance_plugins;
27pub(crate) mod json_object;
28pub mod masked_url;
29pub mod mfa;
30pub mod notifications;
31pub mod oauth;
32pub mod oidc_auth;
33pub mod oidc_providers;
34pub mod pagination;
35pub mod plugin_config_test;
36pub mod plugin_configs;
37pub mod plugin_type_settings;
38pub mod prelude;
39pub mod profile;
40pub mod registration;
41pub mod roles;
42pub mod scheduler;
43pub mod server_cert;
44pub mod services;
45pub mod settings_access;
46pub mod settings_agent_certs;
47pub mod settings_ca;
48pub mod settings_combined;
49pub mod settings_network;
50pub mod settings_reset;
51pub mod system_services;
52pub use masked_url::MaskedUrl;
53pub mod settings_nats;
54pub mod settings_oauth;
55pub mod settings_provider_github;
56pub mod settings_zeroconf;
57pub mod software_items;
58pub mod surfaces;
59pub mod system_alerts;
60pub mod system_enrollment_tokens;
61pub mod update_batches;
62pub mod update_history;
63pub mod users;
64pub mod validation;
65
66pub fn default_enabled() -> bool {
71 true
72}
73
74pub fn default_featured() -> bool {
79 true
80}
81
82#[cfg(test)]
83mod tests {
84 #![expect(
85 clippy::assertions_on_result_states,
86 reason = "test assertions — is_ok/is_err provides readable failure messages"
87 )]
88 use crate::auth::{AuthResponse, AuthorityStatus, UserResponse};
89 use crate::error::ErrorResponse;
90 use crate::oidc_providers::CreateOidcProviderRequest;
91 use crate::plugin_configs::CreatePluginConfigRequest;
92 use crate::registration::RegistrationMode;
93 use crate::software_items::CreateSoftwareItemRequest;
94 use strum::IntoEnumIterator;
95 use uptrakit_shared_types::SecretString;
96 use uuid::Uuid;
97
98 #[test]
101 fn registration_mode_serde_round_trip() {
102 for mode in RegistrationMode::iter() {
103 let json = serde_json::to_string(&mode).unwrap();
104 let deserialized: RegistrationMode = serde_json::from_str(&json).unwrap();
105 assert_eq!(deserialized, mode);
106 }
107 }
108
109 #[test]
110 fn registration_mode_as_str_values() {
111 assert_eq!(RegistrationMode::Open.as_str(), "open");
112 assert_eq!(RegistrationMode::Invite.as_str(), "invite");
113 assert_eq!(RegistrationMode::Closed.as_str(), "closed");
114 }
115
116 #[test]
117 fn registration_mode_from_str_valid() {
118 assert_eq!(
119 "open".parse::<RegistrationMode>().ok(),
120 Some(RegistrationMode::Open)
121 );
122 assert_eq!(
123 "invite".parse::<RegistrationMode>().ok(),
124 Some(RegistrationMode::Invite)
125 );
126 assert_eq!(
127 "closed".parse::<RegistrationMode>().ok(),
128 Some(RegistrationMode::Closed)
129 );
130 }
131
132 #[test]
133 fn registration_mode_from_str_invalid_returns_none() {
134 assert!("disabled".parse::<RegistrationMode>().is_err());
135 assert!("".parse::<RegistrationMode>().is_err());
136 assert!("OPEN".parse::<RegistrationMode>().is_err());
137 }
138
139 #[test]
140 fn registration_mode_as_str_round_trips_through_from_str() {
141 for mode in RegistrationMode::iter() {
142 let s = mode.as_str();
143 let parsed: RegistrationMode = s
144 .parse()
145 .expect("from_str should succeed for as_str output");
146 assert_eq!(parsed, mode);
147 }
148 }
149
150 #[test]
151 fn registration_mode_display_matches_as_str() {
152 for mode in RegistrationMode::iter() {
153 assert_eq!(format!("{mode}"), mode.as_str());
154 }
155 }
156
157 #[test]
160 fn create_oidc_provider_request_default_scopes() {
161 let json = serde_json::json!({
162 "name": "Test Provider",
163 "slug": "test",
164 "issuer_url": "https://issuer.example.com",
165 "client_id": "client-id",
166 "client_secret": "client-secret"
167 });
168 let req: CreateOidcProviderRequest = serde_json::from_value(json).unwrap();
169 assert_eq!(req.scopes, "openid email profile groups");
170 }
171
172 #[test]
173 fn create_oidc_provider_request_default_auto_create() {
174 let json = serde_json::json!({
175 "name": "Test Provider",
176 "slug": "test",
177 "issuer_url": "https://issuer.example.com",
178 "client_id": "client-id",
179 "client_secret": "client-secret"
180 });
181 let req: CreateOidcProviderRequest = serde_json::from_value(json).unwrap();
182 assert!(req.auto_create_users);
183 }
184
185 #[test]
186 fn create_oidc_provider_request_explicit_overrides_defaults() {
187 let json = serde_json::json!({
188 "name": "Test Provider",
189 "slug": "test",
190 "issuer_url": "https://issuer.example.com",
191 "client_id": "client-id",
192 "client_secret": "client-secret",
193 "scopes": "openid",
194 "auto_create_users": false
195 });
196 let req: CreateOidcProviderRequest = serde_json::from_value(json).unwrap();
197 assert_eq!(req.scopes, "openid");
198 assert!(!req.auto_create_users);
199 }
200
201 #[test]
202 fn create_software_item_request_default_featured() {
203 let json = serde_json::json!({ "name": "Node.js" });
204 let req: CreateSoftwareItemRequest = serde_json::from_value(json).unwrap();
205 assert!(req.featured);
206 }
207
208 #[test]
209 fn create_software_item_request_explicit_featured_false() {
210 let json = serde_json::json!({
211 "name": "Node.js",
212 "featured": false
213 });
214 let req: CreateSoftwareItemRequest = serde_json::from_value(json).unwrap();
215 assert!(!req.featured);
216 }
217
218 #[test]
219 fn create_plugin_config_request_default_enabled() {
220 let json = serde_json::json!({
221 "name": "GitHub Releases",
222 "plugin_type": "releases.github",
223 "config": {}
224 });
225 let req: CreatePluginConfigRequest = serde_json::from_value(json).unwrap();
226 assert!(req.enabled);
227 }
228
229 #[test]
230 fn create_plugin_config_request_explicit_enabled_false() {
231 let json = serde_json::json!({
232 "name": "GitHub Releases",
233 "plugin_type": "releases.github",
234 "config": {},
235 "enabled": false
236 });
237 let req: CreatePluginConfigRequest = serde_json::from_value(json).unwrap();
238 assert!(!req.enabled);
239 }
240
241 #[test]
247 fn user_response_round_trip() {
248 let user_id = Uuid::parse_str("00000000-0000-0000-0000-000000000001").expect("valid uuid");
249 let user = UserResponse {
250 id: user_id,
251 email: "owner@example.com".to_string(),
252 first_name: "Owner".to_string(),
253 last_name: "User".to_string(),
254 actions: vec!["hosts:read".to_string()],
255 authority: AuthorityStatus::Ok,
256 has_pending_email_change: false,
257 };
258 let json = serde_json::to_string(&user).unwrap();
259 let deserialized: UserResponse = serde_json::from_str(&json).unwrap();
260
261 assert_eq!(deserialized.id, user_id);
262 assert_eq!(deserialized.email, "owner@example.com");
263 assert_eq!(deserialized.first_name, "Owner");
264 assert_eq!(deserialized.last_name, "User");
265 assert_eq!(deserialized.actions, vec!["hosts:read".to_string()]);
266 assert_eq!(deserialized.authority, AuthorityStatus::Ok);
267
268 let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
269 assert_eq!(parsed["authority"], "ok");
270 assert_eq!(parsed["actions"][0], "hosts:read");
271 assert!(
272 parsed.get("permissions").is_none(),
273 "permissions must not appear on the wire"
274 );
275 }
276
277 #[test]
278 fn user_response_empty_actions_with_unavailable_authority() {
279 let user_id = Uuid::parse_str("00000000-0000-0000-0000-000000000002").expect("valid uuid");
280 let user = UserResponse {
281 id: user_id,
282 email: "viewer@example.com".to_string(),
283 first_name: "View".to_string(),
284 last_name: "Only".to_string(),
285 actions: vec![],
286 authority: AuthorityStatus::Unavailable,
287 has_pending_email_change: false,
288 };
289 let json = serde_json::to_string(&user).unwrap();
290 let deserialized: UserResponse = serde_json::from_str(&json).unwrap();
291
292 assert!(deserialized.actions.is_empty());
293 assert_eq!(deserialized.authority, AuthorityStatus::Unavailable);
294 }
295
296 #[test]
297 fn user_response_empty_actions_with_ok_authority() {
298 let user_id = Uuid::parse_str("00000000-0000-0000-0000-000000000003").expect("valid uuid");
302 let user = UserResponse {
303 id: user_id,
304 email: "zero-grant@example.com".to_string(),
305 first_name: "Zero".to_string(),
306 last_name: "Grant".to_string(),
307 actions: vec![],
308 authority: AuthorityStatus::Ok,
309 has_pending_email_change: false,
310 };
311 let json = serde_json::to_string(&user).unwrap();
312 let deserialized: UserResponse = serde_json::from_str(&json).unwrap();
313
314 assert!(deserialized.actions.is_empty());
315 assert_eq!(deserialized.authority, AuthorityStatus::Ok);
316 }
317
318 #[test]
319 fn auth_response_round_trip() {
320 let user_id = Uuid::parse_str("00000000-0000-0000-0000-000000000001").expect("valid uuid");
321 let auth = AuthResponse {
322 access_token: SecretString::new("eyJhbGciOiJIUzI1NiJ9.test"),
323 refresh_token: SecretString::new("refresh-token-value"),
324 expires_in: 3600,
325 token_type: "Bearer".to_string(),
326 user: UserResponse {
327 id: user_id,
328 email: "admin@example.com".to_string(),
329 first_name: "Admin".to_string(),
330 last_name: "User".to_string(),
331 actions: vec!["settings:read".to_string(), "services:update".to_string()],
332 authority: AuthorityStatus::Ok,
333 has_pending_email_change: false,
334 },
335 };
336 let json = serde_json::to_string(&auth).unwrap();
337 let deserialized: AuthResponse = serde_json::from_str(&json).unwrap();
338
339 assert_eq!(
340 deserialized.access_token.expose_secret(),
341 "eyJhbGciOiJIUzI1NiJ9.test"
342 );
343 assert_eq!(
344 deserialized.refresh_token.expose_secret(),
345 "refresh-token-value"
346 );
347 assert_eq!(deserialized.expires_in, 3600);
348 assert_eq!(deserialized.token_type, "Bearer");
349 assert_eq!(deserialized.user.id, user_id);
350 assert_eq!(deserialized.user.email, "admin@example.com");
351 assert_eq!(deserialized.user.actions.len(), 2);
352 assert_eq!(deserialized.user.actions[0], "settings:read");
353 assert_eq!(deserialized.user.actions[1], "services:update");
354 }
355
356 #[test]
359 fn error_response_serialization_without_code() {
360 let resp = ErrorResponse {
361 error: "Something went wrong".to_string(),
362 code: None,
363 };
364 let json = serde_json::to_value(&resp).unwrap();
365 let obj = json.as_object().unwrap();
366
367 assert_eq!(obj.get("error").unwrap(), "Something went wrong");
368 assert!(
369 obj.get("code").unwrap().is_null(),
370 "code should serialize as null when None"
371 );
372 }
373
374 #[test]
375 fn error_response_serialization_with_code() {
376 let resp = ErrorResponse {
377 error: "Not found".to_string(),
378 code: Some("not_found".to_string()),
379 };
380 let json = serde_json::to_value(&resp).unwrap();
381 let obj = json.as_object().unwrap();
382
383 assert_eq!(obj.get("error").unwrap(), "Not found");
384 assert_eq!(obj.get("code").unwrap(), "not_found");
385 }
386
387 #[test]
388 fn error_response_round_trip_without_code() {
389 let resp = ErrorResponse {
390 error: "Bad request".to_string(),
391 code: None,
392 };
393 let json = serde_json::to_string(&resp).unwrap();
394 let deserialized: ErrorResponse = serde_json::from_str(&json).unwrap();
395
396 assert_eq!(deserialized.error, "Bad request");
397 assert!(deserialized.code.is_none());
398 }
399
400 #[test]
401 fn error_response_round_trip_with_code() {
402 let resp = ErrorResponse {
403 error: "Forbidden".to_string(),
404 code: Some("insufficient_permissions".to_string()),
405 };
406 let json = serde_json::to_string(&resp).unwrap();
407 let deserialized: ErrorResponse = serde_json::from_str(&json).unwrap();
408
409 assert_eq!(deserialized.error, "Forbidden");
410 assert_eq!(
411 deserialized.code.as_deref(),
412 Some("insufficient_permissions")
413 );
414 }
415
416 #[test]
417 fn error_response_deserialization_without_code_field() {
418 let json = r#"{"error":"test error"}"#;
419 let resp: ErrorResponse = serde_json::from_str(json).unwrap();
420
421 assert_eq!(resp.error, "test error");
422 assert!(resp.code.is_none());
423 }
424
425 #[test]
428 fn pagination_resolve_defaults() {
429 use crate::pagination::{DEFAULT_PER_PAGE, PaginationParams};
430 let params = PaginationParams {
431 page: None,
432 per_page: None,
433 };
434 let resolved = params.resolve();
435 assert_eq!(resolved.page, 1);
436 assert_eq!(resolved.per_page, DEFAULT_PER_PAGE);
437 }
438
439 #[test]
440 fn pagination_resolve_explicit_values() {
441 use crate::pagination::PaginationParams;
442 let params = PaginationParams {
443 page: Some(3),
444 per_page: Some(50),
445 };
446 let resolved = params.resolve();
447 assert_eq!(resolved.page, 3);
448 assert_eq!(resolved.per_page, 50);
449 }
450
451 #[test]
452 fn pagination_resolve_clamps_page_zero_to_one() {
453 use crate::pagination::PaginationParams;
454 let params = PaginationParams {
455 page: Some(0),
456 per_page: None,
457 };
458 let resolved = params.resolve();
459 assert_eq!(resolved.page, 1);
460 }
461
462 #[test]
463 fn pagination_resolve_clamps_per_page_zero_to_one() {
464 use crate::pagination::PaginationParams;
465 let params = PaginationParams {
466 page: None,
467 per_page: Some(0),
468 };
469 let resolved = params.resolve();
470 assert_eq!(resolved.per_page, 1);
471 }
472
473 #[test]
474 fn pagination_resolve_clamps_per_page_above_max() {
475 use crate::pagination::{MAX_PER_PAGE, PaginationParams};
476 let params = PaginationParams {
477 page: None,
478 per_page: Some(5000),
479 };
480 let resolved = params.resolve();
481 assert_eq!(resolved.per_page, MAX_PER_PAGE);
482 }
483
484 #[test]
485 fn pagination_offset_calculation() {
486 use crate::pagination::ResolvedPagination;
487 let p = ResolvedPagination {
488 page: 1,
489 per_page: 20,
490 };
491 assert_eq!(p.offset(), 0);
492
493 let p = ResolvedPagination {
494 page: 2,
495 per_page: 20,
496 };
497 assert_eq!(p.offset(), 20);
498
499 let p = ResolvedPagination {
500 page: 3,
501 per_page: 50,
502 };
503 assert_eq!(p.offset(), 100);
504 }
505
506 #[test]
507 fn pagination_total_pages_calculation() {
508 use crate::pagination::ResolvedPagination;
509 let p = ResolvedPagination {
510 page: 1,
511 per_page: 20,
512 };
513 assert_eq!(p.total_pages(0), 0);
514 assert_eq!(p.total_pages(1), 1);
515 assert_eq!(p.total_pages(20), 1);
516 assert_eq!(p.total_pages(21), 2);
517 assert_eq!(p.total_pages(100), 5);
518 }
519
520 #[test]
521 fn paginated_response_serialization_round_trip() {
522 use crate::pagination::{PaginatedResponse, ResolvedPagination};
523 let pagination = ResolvedPagination {
524 page: 2,
525 per_page: 10,
526 };
527 let resp = PaginatedResponse::new(vec!["a".to_string(), "b".to_string()], 25, pagination);
528
529 let json = serde_json::to_string(&resp).unwrap();
530 let deserialized: PaginatedResponse<String> = serde_json::from_str(&json).unwrap();
531
532 assert_eq!(deserialized.items, vec!["a", "b"]);
533 assert_eq!(deserialized.total, 25);
534 assert_eq!(deserialized.page, 2);
535 assert_eq!(deserialized.per_page, 10);
536 assert_eq!(deserialized.total_pages, 3);
537 }
538
539 #[test]
540 fn paginated_response_empty_items() {
541 use crate::pagination::{PaginatedResponse, ResolvedPagination};
542 let pagination = ResolvedPagination {
543 page: 1,
544 per_page: 20,
545 };
546 let resp = PaginatedResponse::<String>::new(vec![], 0, pagination);
547
548 assert!(resp.items.is_empty());
549 assert_eq!(resp.total, 0);
550 assert_eq!(resp.total_pages, 0);
551 }
552
553 #[test]
556 fn prelude_re_exports_resolve() {
557 use crate::prelude::*;
558
559 let _: AuthResponse;
561 let _: UserResponse;
562
563 let _ = ServiceStatus::Pending;
565
566 let _: HostResponse;
568
569 let _: SoftwareItemResponse;
571 let _ = TriggerUpdateStatus::Pending;
572
573 let _: PluginConfigResponse;
575
576 let _ = UpdateStatus::Pending;
578
579 let _: ApiTokenListResponse;
581 let _: CreateApiTokenResponse;
582
583 let _: OidcProviderResponse;
585 let _: AuthMethodsResponse;
586
587 let _ = NotificationEventType::UpdateAvailable;
589 let _ = NotificationDeliveryStatus::Pending;
590 let _: NotificationChannelResponse;
591 let _: NotificationRuleResponse;
592 let _: NotificationLogResponse;
593 let _: TestNotificationResponse;
594
595 let _: ErrorResponse;
597 let _: PaginatedResponse<String>;
598 let _: PaginationParams;
599 let _ = RegistrationMode::Open;
600 }
601
602 #[test]
605 fn register_request_valid() {
606 use crate::auth::RegisterRequest;
607 use crate::validation::Validate;
608 let req = RegisterRequest {
609 email: "user@example.com".to_string(),
610 first_name: "John".to_string(),
611 last_name: "Doe".to_string(),
612 password: SecretString::new("password12345678"),
613 registration_token: None,
614 };
615 assert!(req.validate().is_ok());
616 }
617
618 #[test]
619 fn register_request_email_missing_at() {
620 use crate::auth::RegisterRequest;
621 use crate::validation::Validate;
622 let req = RegisterRequest {
623 email: "invalid-email".to_string(),
624 first_name: "John".to_string(),
625 last_name: "Doe".to_string(),
626 password: SecretString::new("password12345678"),
627 registration_token: None,
628 };
629 let err = req.validate().unwrap_err();
630 assert_eq!(err.field, "email");
631 }
632
633 #[test]
634 fn register_request_password_too_short() {
635 use crate::auth::RegisterRequest;
636 use crate::validation::Validate;
637 let req = RegisterRequest {
638 email: "user@example.com".to_string(),
639 first_name: "John".to_string(),
640 last_name: "Doe".to_string(),
641 password: SecretString::new("short"),
642 registration_token: None,
643 };
644 let err = req.validate().unwrap_err();
645 assert_eq!(err.field, "password");
646 }
647
648 #[test]
649 fn register_request_empty_first_name() {
650 use crate::auth::RegisterRequest;
651 use crate::validation::Validate;
652 let req = RegisterRequest {
653 email: "user@example.com".to_string(),
654 first_name: "".to_string(),
655 last_name: "Doe".to_string(),
656 password: SecretString::new("password12345678"),
657 registration_token: None,
658 };
659 let err = req.validate().unwrap_err();
660 assert_eq!(err.field, "first_name");
661 }
662
663 #[test]
664 fn login_request_valid() {
665 use crate::auth::LoginRequest;
666 use crate::validation::Validate;
667 let req = LoginRequest {
668 email: "user@example.com".to_string(),
669 password: SecretString::new("any-password"),
670 };
671 assert!(req.validate().is_ok());
672 }
673
674 #[test]
675 fn login_request_empty_password() {
676 use crate::auth::LoginRequest;
677 use crate::validation::Validate;
678 let req = LoginRequest {
679 email: "user@example.com".to_string(),
680 password: SecretString::new(""),
681 };
682 let err = req.validate().unwrap_err();
683 assert_eq!(err.field, "password");
684 }
685
686 #[test]
687 fn create_oidc_provider_valid() {
688 use crate::validation::Validate;
689 let req = CreateOidcProviderRequest {
690 name: "Test Provider".to_string(),
691 slug: "test-provider".to_string(),
692 logo_url: None,
693 issuer_url: "https://issuer.example.com".to_string(),
694 client_id: "client-id".to_string(),
695 client_secret: SecretString::new("secret"),
696 scopes: "openid email".to_string(),
697 auto_create_users: true,
698 allow_private_network_issuers: Some(true),
699 role_claim_path: None,
700 role_mapping: Default::default(),
701 };
702 assert!(req.validate().is_ok());
703 }
704
705 #[test]
706 fn create_oidc_provider_invalid_slug() {
707 use crate::validation::Validate;
708 let req = CreateOidcProviderRequest {
709 name: "Test".to_string(),
710 slug: "INVALID_SLUG".to_string(),
711 logo_url: None,
712 issuer_url: "https://issuer.example.com".to_string(),
713 client_id: "client-id".to_string(),
714 client_secret: SecretString::new("secret"),
715 scopes: "openid".to_string(),
716 auto_create_users: true,
717 allow_private_network_issuers: Some(true),
718 role_claim_path: None,
719 role_mapping: Default::default(),
720 };
721 let err = req.validate().unwrap_err();
722 assert_eq!(err.field, "slug");
723 }
724
725 #[test]
726 fn create_oidc_provider_invalid_issuer_url() {
727 use crate::validation::Validate;
728 let req = CreateOidcProviderRequest {
729 name: "Test".to_string(),
730 slug: "test".to_string(),
731 logo_url: None,
732 issuer_url: "ftp://issuer.example.com".to_string(),
733 client_id: "client-id".to_string(),
734 client_secret: SecretString::new("secret"),
735 scopes: "openid".to_string(),
736 auto_create_users: true,
737 allow_private_network_issuers: Some(true),
738 role_claim_path: None,
739 role_mapping: Default::default(),
740 };
741 let err = req.validate().unwrap_err();
742 assert_eq!(err.field, "issuer_url");
743 }
744
745 #[test]
746 fn create_software_item_valid() {
747 use crate::validation::Validate;
748 let req = CreateSoftwareItemRequest {
749 name: "Node.js".to_string(),
750 featured: true,
751 icon_url: None,
752 };
753 assert!(req.validate().is_ok());
754 }
755
756 #[test]
757 fn create_software_item_empty_name_fails() {
758 use crate::validation::Validate;
759 let req = CreateSoftwareItemRequest {
760 name: "".to_string(),
761 featured: true,
762 icon_url: None,
763 };
764 let err = req.validate().unwrap_err();
765 assert_eq!(err.field, "name");
766 }
767
768 #[test]
769 fn create_plugin_config_valid() {
770 use crate::validation::Validate;
771 use uptrakit_shared_types::plugin_ids;
772 let req = CreatePluginConfigRequest {
773 name: "GitHub Releases".to_string(),
774 plugin_type: plugin_ids::RELEASES_GITHUB.clone(),
775 config: serde_json::json!({}),
776 enabled: true,
777 };
778 assert!(req.validate().is_ok());
779 }
780
781 #[test]
782 fn create_plugin_config_empty_name() {
783 use crate::validation::Validate;
784 use uptrakit_shared_types::plugin_ids;
785 let req = CreatePluginConfigRequest {
786 name: "".to_string(),
787 plugin_type: plugin_ids::RELEASES_GITHUB.clone(),
788 config: serde_json::json!({}),
789 enabled: true,
790 };
791 let err = req.validate().unwrap_err();
792 assert_eq!(err.field, "name");
793 }
794
795 #[test]
796 fn update_scheduled_task_valid_interval() {
797 use crate::scheduler::UpdateScheduledTaskRequest;
798 use crate::validation::Validate;
799 let req = UpdateScheduledTaskRequest {
800 interval_seconds: Some(300),
801 jitter_seconds: Some(30),
802 enabled: None,
803 task_config: None,
804 };
805 assert!(req.validate().is_ok());
806 }
807
808 #[test]
809 fn update_scheduled_task_invalid_interval() {
810 use crate::scheduler::UpdateScheduledTaskRequest;
811 use crate::validation::Validate;
812 let req = UpdateScheduledTaskRequest {
813 interval_seconds: Some(0),
814 jitter_seconds: None,
815 enabled: None,
816 task_config: None,
817 };
818 let err = req.validate().unwrap_err();
819 assert_eq!(err.field, "interval_seconds");
820 }
821
822 #[test]
823 fn update_network_settings_valid() {
824 use crate::settings_network::UpdateNetworkSettingsRequest;
825 use crate::validation::Validate;
826 let req = UpdateNetworkSettingsRequest {
827 trusted_proxies: Some(vec!["10.0.0.0/8".to_string()]),
828 real_ip_header: Some("X-Real-IP".to_string()),
829 sans: None,
830 https_addr: None,
831 forwarded_client_cert_info_header: None,
832 forwarded_client_cert_pem_header: None,
833 pki_addr: Some("https://pki.example.com".to_string()),
834 regenerate_cert: None,
835 };
836 assert!(req.validate().is_ok());
837 }
838
839 #[test]
840 fn update_network_settings_empty_trusted_proxy() {
841 use crate::settings_network::UpdateNetworkSettingsRequest;
842 use crate::validation::Validate;
843 let req = UpdateNetworkSettingsRequest {
844 trusted_proxies: Some(vec!["".to_string()]),
845 real_ip_header: None,
846 sans: None,
847 https_addr: None,
848 forwarded_client_cert_info_header: None,
849 forwarded_client_cert_pem_header: None,
850 pki_addr: None,
851 regenerate_cert: None,
852 };
853 let err = req.validate().unwrap_err();
854 assert_eq!(err.field, "trusted_proxies");
855 }
856
857 }