1use crate::commands::{Command, OutputFormat};
6use crate::config::Config;
7use crate::constants::ui::emoji;
8use crate::errors::prelude::{CliError, Result as CliResult};
9use crate::file_utils;
10use crate::output::{CliResponse, OutputFormatter};
11use crate::utils::output::print_success_result;
12use crate::utils::{validate_directory_path, validate_file_id};
13use async_trait::async_trait;
14use clap::{Subcommand, ValueHint};
15use colored::Colorize;
16use serde_json::json;
17use tracing::{debug, info};
18use vkteams_bot::prelude::*;
19
20#[derive(Subcommand, Debug, Clone)]
22pub enum DiagnosticCommands {
23 GetSelf {
25 #[arg(short, long)]
27 detailed: bool,
28 },
29 GetEvents {
31 #[arg(short, long, required = false, value_name = "LISTEN")]
32 listen: Option<bool>,
33 },
34 GetFile {
36 #[arg(short = 'f', long, required = true, value_name = "FILE_ID")]
37 file_id: String,
38 #[arg(short = 'p', long, required = false, value_name = "FILE_PATH", value_hint = ValueHint::DirPath)]
39 file_path: String,
40 },
41 HealthCheck,
43 NetworkTest,
45 SystemInfo,
47 RateLimitTest {
49 #[arg(short = 'n', long, default_value = "10")]
51 requests: u32,
52 #[arg(short = 'd', long, default_value = "100")]
54 delay_ms: u64,
55 },
56}
57
58#[async_trait]
59impl Command for DiagnosticCommands {
60 async fn execute(&self, bot: &Bot) -> CliResult<()> {
61 match self {
62 DiagnosticCommands::GetSelf { detailed } => execute_get_self(bot, *detailed).await,
63 DiagnosticCommands::GetEvents { listen } => {
64 execute_get_events(bot, listen.unwrap_or(false)).await
65 }
66 DiagnosticCommands::GetFile { file_id, file_path } => {
67 execute_get_file(bot, file_id, file_path).await
68 }
69 DiagnosticCommands::HealthCheck => execute_health_check(bot).await,
70 DiagnosticCommands::NetworkTest => execute_network_test(bot).await,
71 DiagnosticCommands::SystemInfo => execute_system_info().await,
72 DiagnosticCommands::RateLimitTest { requests, delay_ms } => {
73 execute_rate_limit_test(bot, *requests, *delay_ms).await
74 }
75 }
76 }
77
78 fn name(&self) -> &'static str {
79 match self {
80 DiagnosticCommands::GetSelf { .. } => "get-self",
81 DiagnosticCommands::GetEvents { .. } => "get-events",
82 DiagnosticCommands::GetFile { .. } => "get-file",
83 DiagnosticCommands::HealthCheck => "health-check",
84 DiagnosticCommands::NetworkTest => "network-test",
85 DiagnosticCommands::SystemInfo => "system-info",
86 DiagnosticCommands::RateLimitTest { .. } => "rate-limit-test",
87 }
88 }
89
90 fn validate(&self) -> CliResult<()> {
91 match self {
92 DiagnosticCommands::GetFile { file_id, file_path } => {
93 validate_file_id(file_id)?;
94 if !file_path.is_empty() {
95 validate_directory_path(file_path)?;
96 }
97 }
98 DiagnosticCommands::RateLimitTest {
99 requests,
100 delay_ms: _,
101 } => {
102 if *requests == 0 || *requests > 1000 {
103 return Err(CliError::InputError(
104 "Number of requests must be between 1 and 1000".to_string(),
105 ));
106 }
107 }
108 _ => {} }
110 Ok(())
111 }
112
113 async fn execute_with_output(&self, bot: &Bot, output_format: &OutputFormat) -> CliResult<()> {
115 let response = match self {
116 DiagnosticCommands::GetSelf { detailed } => {
117 execute_get_self_structured(bot, *detailed).await
118 }
119 DiagnosticCommands::GetEvents { listen } => {
120 execute_get_events_structured(bot, listen.unwrap_or(false)).await
121 }
122 DiagnosticCommands::GetFile { file_id, file_path } => {
123 execute_get_file_structured(bot, file_id, file_path).await
124 }
125 DiagnosticCommands::HealthCheck => execute_health_check_structured(bot).await,
126 DiagnosticCommands::NetworkTest => execute_network_test_structured(bot).await,
127 DiagnosticCommands::SystemInfo => execute_system_info_structured().await,
128 DiagnosticCommands::RateLimitTest { requests, delay_ms } => {
129 execute_rate_limit_test_structured(bot, *requests, *delay_ms).await
130 }
131 };
132
133 OutputFormatter::print(&response, output_format)?;
134
135 if !response.success {
136 return Err(CliError::UnexpectedError("Command failed".to_string()));
137 }
138
139 Ok(())
140 }
141}
142
143async fn execute_get_self(bot: &Bot, detailed: bool) -> CliResult<()> {
146 debug!("Getting bot information");
147
148 let request = RequestSelfGet::new(());
149 let result = bot
150 .send_api_request(request)
151 .await
152 .map_err(CliError::ApiError)?;
153
154 if detailed {
155 info!("Bot information retrieved successfully");
156 print_success_result(&result, &OutputFormat::Pretty)?;
157 } else {
158 println!("{} Bot is configured and accessible", emoji::CHECK);
160 if let Ok(json_str) = serde_json::to_string_pretty(&result) {
161 println!("{}", json_str.green());
162 }
163 }
164
165 Ok(())
166}
167
168async fn execute_get_events(bot: &Bot, listen: bool) -> CliResult<()> {
169 debug!("Getting events, listen mode: {}", listen);
170
171 if listen {
172 info!("Starting event listener (long polling)...");
173 println!(
174 "{} Starting event listener. Press Ctrl+C to stop.",
175 emoji::ROCKET
176 );
177
178 match bot.event_listener(handle_event).await {
179 Ok(()) => (),
180 Err(e) => return Err(CliError::ApiError(e)),
181 }
182 } else {
183 let result = bot
184 .send_api_request(RequestEventsGet::new(bot.get_last_event_id()).with_poll_time(30))
185 .await
186 .map_err(CliError::ApiError)?;
187
188 info!("Successfully retrieved events");
189 print_success_result(&result, &OutputFormat::Pretty)?;
190 }
191
192 Ok(())
193}
194
195async fn execute_get_file(bot: &Bot, file_id: &str, file_path: &str) -> CliResult<()> {
196 debug!("Downloading file {} to {}", file_id, file_path);
197
198 let downloaded_path = file_utils::download_and_save_file(bot, file_id, file_path).await?;
199
200 info!("Successfully downloaded file with ID: {}", file_id);
201 println!(
202 "{} File downloaded to: {}",
203 emoji::CHECK,
204 downloaded_path.display().to_string().green()
205 );
206
207 Ok(())
208}
209
210async fn execute_health_check(bot: &Bot) -> CliResult<()> {
211 println!(
212 "{} Performing comprehensive health check...",
213 emoji::TEST_TUBE.bold().blue()
214 );
215 println!();
216
217 let mut all_passed = true;
218
219 print!("{} Testing basic API connectivity... ", emoji::GEAR);
221 match bot.send_api_request(RequestSelfGet::new(())).await {
222 Ok(_) => println!("{}", "PASS".green()),
223 Err(e) => {
224 println!("{} - {}", "FAIL".red(), e);
225 all_passed = false;
226 }
227 }
228
229 print!("{} Checking configuration... ", emoji::GEAR);
231 match Config::from_file() {
232 Ok(config) => {
233 if config.api.token.is_some() && config.api.url.is_some() {
234 println!("{}", "PASS".green());
235 } else {
236 println!("{} - Missing required configuration", "FAIL".red());
237 all_passed = false;
238 }
239 }
240 Err(_) => {
241 println!("{} - Configuration file not found", "FAIL".red());
242 all_passed = false;
243 }
244 }
245
246 print!("{} Testing network latency... ", emoji::GEAR);
248 let start = std::time::Instant::now();
249 match bot.send_api_request(RequestSelfGet::new(())).await {
250 Ok(_) => {
251 let latency = start.elapsed();
252 if latency.as_millis() < 1000 {
253 println!("{} - {}ms", "PASS".green(), latency.as_millis());
254 } else {
255 println!(
256 "{} - High latency: {}ms",
257 "WARN".yellow(),
258 latency.as_millis()
259 );
260 }
261 }
262 Err(e) => {
263 println!("{} - {}", "FAIL".red(), e);
264 all_passed = false;
265 }
266 }
267
268 println!();
269 if all_passed {
270 println!("{} All health checks passed!", emoji::CHECK.bold().green());
271 } else {
272 println!(
273 "{} Some health checks failed. Check configuration and network connectivity.",
274 emoji::WARNING.bold().yellow()
275 );
276 }
277
278 Ok(())
279}
280
281async fn execute_network_test(bot: &Bot) -> CliResult<()> {
282 println!(
283 "{} Testing network connectivity...",
284 emoji::GEAR.bold().blue()
285 );
286 println!();
287
288 let endpoints = vec![("Bot Info", RequestSelfGet::new(()))];
290
291 for (name, request) in endpoints {
292 print!("Testing {name}: ");
293 let start = std::time::Instant::now();
294
295 match bot.send_api_request(request).await {
296 Ok(_) => {
297 let duration = start.elapsed();
298 println!("{} ({}ms)", "OK".green(), duration.as_millis());
299 }
300 Err(e) => {
301 println!("{} - {}", "FAILED".red(), e);
302 }
303 }
304 }
305
306 println!();
307 println!("{} Network test completed", emoji::CHECK);
308
309 Ok(())
310}
311
312async fn execute_system_info() -> CliResult<()> {
313 println!("{} System Information", emoji::INFO.bold().blue());
314 println!();
315
316 println!("{}", "Runtime:".bold().green());
318 println!(" OS: {}", std::env::consts::OS);
319 println!(" Architecture: {}", std::env::consts::ARCH);
320 println!(" Family: {}", std::env::consts::FAMILY);
321
322 if let Ok(current_dir) = std::env::current_dir() {
324 println!(" Current directory: {}", current_dir.display());
325 }
326
327 println!("\n{}", "Environment:".bold().green());
329 let env_vars = [
330 "VKTEAMS_BOT_API_TOKEN",
331 "VKTEAMS_BOT_API_URL",
332 "VKTEAMS_PROXY",
333 "VKTEAMS_LOG_LEVEL",
334 ];
335
336 for var in &env_vars {
337 match std::env::var(var) {
338 Ok(value) => {
339 if var.contains("TOKEN") {
340 println!(" {}: {}***", var, &value[..8.min(value.len())]);
341 } else {
342 println!(" {var}: {value}");
343 }
344 }
345 Err(_) => println!(" {}: {}", var, "Not set".dimmed()),
346 }
347 }
348
349 println!("\n{}", "Configuration:".bold().green());
351 match Config::from_file() {
352 Ok(_) => println!(" Configuration file: {}", "Found".green()),
353 Err(_) => println!(" Configuration file: {}", "Not found".red()),
354 }
355
356 Ok(())
357}
358
359async fn execute_rate_limit_test(bot: &Bot, requests: u32, delay_ms: u64) -> CliResult<()> {
360 println!(
361 "{} Testing rate limits with {} requests ({}ms delay)...",
362 emoji::ROCKET.bold().blue(),
363 requests,
364 delay_ms
365 );
366 println!();
367
368 let mut successful = 0;
369 let mut failed = 0;
370 let start_time = std::time::Instant::now();
371
372 for i in 1..=requests {
373 let request_start = std::time::Instant::now();
374
375 match bot.send_api_request(RequestSelfGet::new(())).await {
376 Ok(_) => {
377 successful += 1;
378 let duration = request_start.elapsed();
379 println!(
380 "Request {}/{}: {} ({}ms)",
381 i,
382 requests,
383 "OK".green(),
384 duration.as_millis()
385 );
386 }
387 Err(e) => {
388 failed += 1;
389 println!("Request {}/{}: {} - {}", i, requests, "FAILED".red(), e);
390 }
391 }
392
393 if i < requests {
394 tokio::time::sleep(tokio::time::Duration::from_millis(delay_ms)).await;
395 }
396 }
397
398 let total_time = start_time.elapsed();
399
400 println!();
401 println!("{}", "Rate Limit Test Results:".bold().green());
402 println!(" Total requests: {requests}");
403 println!(" Successful: {}", successful.to_string().green());
404 println!(" Failed: {}", failed.to_string().red());
405 println!(
406 " Success rate: {:.1}%",
407 (successful as f64 / requests as f64) * 100.0
408 );
409 println!(" Total time: {:.2}s", total_time.as_secs_f64());
410 println!(
411 " Average rate: {:.1} req/s",
412 requests as f64 / total_time.as_secs_f64()
413 );
414
415 Ok(())
416}
417
418async fn handle_event<T>(
420 bot: Bot,
421 result: T,
422) -> std::result::Result<(), vkteams_bot::error::BotError>
423where
424 T: serde::Serialize + std::fmt::Debug,
425{
426 debug!("Last event id: {:?}", bot.get_last_event_id());
427
428 if let Ok(json_str) = serde_json::to_string_pretty(&result) {
429 println!("{}", json_str.green());
430 } else {
431 println!("Event: {result:?}");
432 }
433
434 Ok(())
435}
436
437async fn execute_get_self_structured(bot: &Bot, detailed: bool) -> CliResponse<serde_json::Value> {
442 debug!("Getting bot information (structured)");
443
444 let request = RequestSelfGet::new(());
445 match bot.send_api_request(request).await {
446 Ok(result) => {
447 info!("Bot information retrieved successfully");
448 let data = if detailed {
449 serde_json::to_value(&result).unwrap_or(json!({}))
450 } else {
451 json!({
452 "bot_id": result.user_id,
453 "nickname": result.nick,
454 "first_name": result.first_name,
455 "about": result.about,
456 "photo": result.photo
457 })
458 };
459 CliResponse::success("get-self", data)
460 }
461 Err(e) => CliResponse::error("get-self", format!("Failed to get bot info: {e}")),
462 }
463}
464
465async fn execute_get_events_structured(bot: &Bot, listen: bool) -> CliResponse<serde_json::Value> {
466 debug!("Getting events, listen mode: {}", listen);
467
468 if listen {
469 CliResponse::success(
472 "get-events",
473 json!({
474 "mode": "listen",
475 "message": "Event listener started. Press Ctrl+C to stop.",
476 "note": "Use regular execute mode for event listening"
477 }),
478 )
479 } else {
480 match bot
481 .send_api_request(RequestEventsGet::new(bot.get_last_event_id()).with_poll_time(30))
482 .await
483 {
484 Ok(result) => {
485 info!("Successfully retrieved events");
486 let data = serde_json::to_value(&result).unwrap_or(json!({}));
487 CliResponse::success("get-events", data)
488 }
489 Err(e) => CliResponse::error("get-events", format!("Failed to get events: {e}")),
490 }
491 }
492}
493
494async fn execute_get_file_structured(
495 bot: &Bot,
496 file_id: &str,
497 file_path: &str,
498) -> CliResponse<serde_json::Value> {
499 debug!("Downloading file {} to {}", file_id, file_path);
500
501 match file_utils::download_and_save_file(bot, file_id, file_path).await {
502 Ok(downloaded_path) => {
503 info!("Successfully downloaded file with ID: {}", file_id);
504 let data = json!({
505 "file_id": file_id,
506 "download_path": downloaded_path.display().to_string(),
507 "status": "downloaded"
508 });
509 CliResponse::success("get-file", data)
510 }
511 Err(e) => CliResponse::error("get-file", format!("Failed to download file: {e}")),
512 }
513}
514
515async fn execute_health_check_structured(bot: &Bot) -> CliResponse<serde_json::Value> {
516 debug!("Performing health check");
517
518 let mut tests = Vec::new();
519 let mut all_passed = true;
520
521 let start = std::time::Instant::now();
523 let connectivity_result = match bot.send_api_request(RequestSelfGet::new(())).await {
524 Ok(_) => {
525 json!({
526 "name": "API Connectivity",
527 "status": "pass",
528 "latency_ms": start.elapsed().as_millis()
529 })
530 }
531 Err(e) => {
532 all_passed = false;
533 json!({
534 "name": "API Connectivity",
535 "status": "fail",
536 "error": e.to_string()
537 })
538 }
539 };
540 tests.push(connectivity_result);
541
542 let config_result = match Config::from_file() {
544 Ok(config) => {
545 if config.api.token.is_some() && config.api.url.is_some() {
546 json!({
547 "name": "Configuration",
548 "status": "pass",
549 "details": "All required fields present"
550 })
551 } else {
552 all_passed = false;
553 json!({
554 "name": "Configuration",
555 "status": "fail",
556 "error": "Missing required configuration"
557 })
558 }
559 }
560 Err(_) => {
561 all_passed = false;
562 json!({
563 "name": "Configuration",
564 "status": "fail",
565 "error": "Configuration file not found"
566 })
567 }
568 };
569 tests.push(config_result);
570
571 let latency_start = std::time::Instant::now();
573 let latency_result = match bot.send_api_request(RequestSelfGet::new(())).await {
574 Ok(_) => {
575 let latency = latency_start.elapsed();
576 let status = if latency.as_millis() < 1000 {
577 "pass"
578 } else {
579 "warn"
580 };
581 json!({
582 "name": "Network Latency",
583 "status": status,
584 "latency_ms": latency.as_millis()
585 })
586 }
587 Err(e) => {
588 all_passed = false;
589 json!({
590 "name": "Network Latency",
591 "status": "fail",
592 "error": e.to_string()
593 })
594 }
595 };
596 tests.push(latency_result);
597
598 let data = json!({
599 "overall_status": if all_passed { "healthy" } else { "unhealthy" },
600 "tests": tests,
601 "timestamp": chrono::Utc::now().to_rfc3339()
602 });
603
604 CliResponse::success("health-check", data)
605}
606
607async fn execute_network_test_structured(bot: &Bot) -> CliResponse<serde_json::Value> {
608 debug!("Testing network connectivity");
609
610 let mut results = Vec::new();
611
612 let endpoints = vec![("Bot Info", RequestSelfGet::new(()))];
614
615 for (name, request) in endpoints {
616 let start = std::time::Instant::now();
617
618 let result = match bot.send_api_request(request).await {
619 Ok(_) => {
620 json!({
621 "endpoint": name,
622 "status": "ok",
623 "latency_ms": start.elapsed().as_millis()
624 })
625 }
626 Err(e) => {
627 json!({
628 "endpoint": name,
629 "status": "failed",
630 "error": e.to_string()
631 })
632 }
633 };
634 results.push(result);
635 }
636
637 let data = json!({
638 "test_results": results,
639 "timestamp": chrono::Utc::now().to_rfc3339()
640 });
641
642 CliResponse::success("network-test", data)
643}
644
645async fn execute_system_info_structured() -> CliResponse<serde_json::Value> {
646 debug!("Gathering system information");
647
648 let mut env_vars = serde_json::Map::new();
649 let vars = [
650 "VKTEAMS_BOT_API_TOKEN",
651 "VKTEAMS_BOT_API_URL",
652 "VKTEAMS_PROXY",
653 "VKTEAMS_LOG_LEVEL",
654 ];
655
656 for var in &vars {
657 match std::env::var(var) {
658 Ok(value) => {
659 if var.contains("TOKEN") {
660 env_vars.insert(
661 var.to_string(),
662 json!(format!("{}***", &value[..8.min(value.len())])),
663 );
664 } else {
665 env_vars.insert(var.to_string(), json!(value));
666 }
667 }
668 Err(_) => {
669 env_vars.insert(var.to_string(), json!("Not set"));
670 }
671 }
672 }
673
674 let config_status = match Config::from_file() {
675 Ok(_) => "found",
676 Err(_) => "not_found",
677 };
678
679 let data = json!({
680 "runtime": {
681 "os": std::env::consts::OS,
682 "architecture": std::env::consts::ARCH,
683 "family": std::env::consts::FAMILY,
684 "current_directory": std::env::current_dir().ok().map(|p| p.display().to_string())
685 },
686 "environment": env_vars,
687 "configuration": {
688 "status": config_status
689 }
690 });
691
692 CliResponse::success("system-info", data)
693}
694
695async fn execute_rate_limit_test_structured(
696 bot: &Bot,
697 requests: u32,
698 delay_ms: u64,
699) -> CliResponse<serde_json::Value> {
700 debug!("Testing rate limits with {} requests", requests);
701
702 let mut successful = 0;
703 let mut failed = 0;
704 let mut request_results = Vec::new();
705 let start_time = std::time::Instant::now();
706
707 for i in 1..=requests {
708 let request_start = std::time::Instant::now();
709
710 let result = match bot.send_api_request(RequestSelfGet::new(())).await {
711 Ok(_) => {
712 successful += 1;
713 json!({
714 "request_number": i,
715 "status": "success",
716 "latency_ms": request_start.elapsed().as_millis()
717 })
718 }
719 Err(e) => {
720 failed += 1;
721 json!({
722 "request_number": i,
723 "status": "failed",
724 "error": e.to_string()
725 })
726 }
727 };
728 request_results.push(result);
729
730 if i < requests {
731 tokio::time::sleep(tokio::time::Duration::from_millis(delay_ms)).await;
732 }
733 }
734
735 let total_time = start_time.elapsed();
736 let success_rate = (successful as f64 / requests as f64) * 100.0;
737 let average_rate = requests as f64 / total_time.as_secs_f64();
738
739 let data = json!({
740 "summary": {
741 "total_requests": requests,
742 "successful": successful,
743 "failed": failed,
744 "success_rate_percent": success_rate,
745 "total_time_seconds": total_time.as_secs_f64(),
746 "average_rate_per_second": average_rate,
747 "delay_between_requests_ms": delay_ms
748 },
749 "request_details": request_results
750 });
751
752 CliResponse::success("rate-limit-test", data)
753}
754
755#[cfg(test)]
758mod tests {
759 use super::*;
760 use tokio::runtime::Runtime;
761
762 fn dummy_bot() -> Bot {
763 Bot::with_params(&APIVersionUrl::V1, "dummy_token", "https://dummy.api.com").unwrap()
764 }
765
766 #[test]
767 fn test_validate_get_file_empty_file_id() {
768 let cmd = DiagnosticCommands::GetFile {
769 file_id: "".to_string(),
770 file_path: "/tmp".to_string(),
771 };
772 let res = cmd.validate();
773 assert!(res.is_err());
774 }
775
776 #[test]
777 fn test_validate_get_file_invalid_path() {
778 let cmd = DiagnosticCommands::GetFile {
779 file_id: "fileid".to_string(),
780 file_path: "".to_string(),
781 };
782 let res = cmd.validate();
783 assert!(res.is_ok()); }
785
786 #[test]
787 fn test_validate_rate_limit_zero() {
788 let cmd = DiagnosticCommands::RateLimitTest {
789 requests: 0,
790 delay_ms: 100,
791 };
792 let res = cmd.validate();
793 assert!(res.is_err());
794 }
795
796 #[test]
797 fn test_validate_rate_limit_too_many() {
798 let cmd = DiagnosticCommands::RateLimitTest {
799 requests: 1001,
800 delay_ms: 100,
801 };
802 let res = cmd.validate();
803 assert!(res.is_err());
804 }
805
806 #[test]
807 fn test_validate_rate_limit_valid() {
808 let cmd = DiagnosticCommands::RateLimitTest {
809 requests: 10,
810 delay_ms: 100,
811 };
812 let res = cmd.validate();
813 assert!(res.is_ok());
814 }
815
816 #[test]
817 fn test_execute_get_self_api_error() {
818 let cmd = DiagnosticCommands::GetSelf { detailed: true };
819 let bot = dummy_bot();
820 let rt = Runtime::new().unwrap();
821 let res = rt.block_on(cmd.execute(&bot));
822 assert!(res.is_err());
823 }
824
825 #[test]
826 fn test_execute_get_events_api_error() {
827 let cmd = DiagnosticCommands::GetEvents {
828 listen: Some(false),
829 };
830 let bot = dummy_bot();
831 let rt = Runtime::new().unwrap();
832 let res = rt.block_on(cmd.execute(&bot));
833 assert!(res.is_err());
834 }
835
836 #[test]
837 fn test_execute_get_file_api_error() {
838 let cmd = DiagnosticCommands::GetFile {
839 file_id: "fileid".to_string(),
840 file_path: "/tmp".to_string(),
841 };
842 let bot = dummy_bot();
843 let rt = Runtime::new().unwrap();
844 let res = rt.block_on(cmd.execute(&bot));
845 assert!(res.is_err());
846 }
847
848 #[test]
849 fn test_execute_health_check_api_error() {
850 let cmd = DiagnosticCommands::HealthCheck;
851 let bot = dummy_bot();
852 let rt = Runtime::new().unwrap();
853 let res = rt.block_on(cmd.execute(&bot));
854 assert!(res.is_ok());
855 }
856
857 #[test]
858 fn test_execute_network_test_api_error() {
859 let cmd = DiagnosticCommands::NetworkTest;
860 let bot = dummy_bot();
861 let rt = Runtime::new().unwrap();
862 let res = rt.block_on(cmd.execute(&bot));
863 assert!(res.is_ok());
864 }
865
866 #[test]
867 fn test_execute_system_info() {
868 let cmd = DiagnosticCommands::SystemInfo;
869 let bot = dummy_bot();
870 let rt = Runtime::new().unwrap();
871 let res = rt.block_on(cmd.execute(&bot));
873 assert!(res.is_ok());
874 }
875
876 #[test]
877 fn test_execute_rate_limit_test_api_error() {
878 let cmd = DiagnosticCommands::RateLimitTest {
879 requests: 2,
880 delay_ms: 10,
881 };
882 let bot = dummy_bot();
883 let rt = Runtime::new().unwrap();
884 let res = rt.block_on(cmd.execute(&bot));
885 assert!(res.is_ok());
886 }
887
888 #[test]
889 fn test_diagnostic_commands_variants() {
890 let get_self = DiagnosticCommands::GetSelf { detailed: true };
891 assert_eq!(get_self.name(), "get-self");
892 if let DiagnosticCommands::GetSelf { detailed } = get_self {
893 assert!(detailed);
894 }
895
896 let get_events = DiagnosticCommands::GetEvents { listen: Some(true) };
897 assert_eq!(get_events.name(), "get-events");
898 if let DiagnosticCommands::GetEvents { listen } = get_events {
899 assert_eq!(listen, Some(true));
900 }
901
902 let get_file = DiagnosticCommands::GetFile {
903 file_id: "file123".to_string(),
904 file_path: "/tmp".to_string(),
905 };
906 assert_eq!(get_file.name(), "get-file");
907 if let DiagnosticCommands::GetFile { file_id, file_path } = get_file {
908 assert_eq!(file_id, "file123");
909 assert_eq!(file_path, "/tmp");
910 }
911
912 let health = DiagnosticCommands::HealthCheck;
913 assert_eq!(health.name(), "health-check");
914
915 let net = DiagnosticCommands::NetworkTest;
916 assert_eq!(net.name(), "network-test");
917
918 let sys = DiagnosticCommands::SystemInfo;
919 assert_eq!(sys.name(), "system-info");
920
921 let rate = DiagnosticCommands::RateLimitTest {
922 requests: 10,
923 delay_ms: 100,
924 };
925 assert_eq!(rate.name(), "rate-limit-test");
926 if let DiagnosticCommands::RateLimitTest { requests, delay_ms } = rate {
927 assert_eq!(requests, 10);
928 assert_eq!(delay_ms, 100);
929 }
930 }
931}