Some checks failed
BotServer CI / build (push) Failing after 1m34s
Split 20+ files over 1000 lines into focused subdirectories for better maintainability and code organization. All changes maintain backward compatibility through re-export wrappers. Major splits: - attendance/llm_assist.rs (2074→7 modules) - basic/keywords/face_api.rs → face_api/ (7 modules) - basic/keywords/file_operations.rs → file_ops/ (8 modules) - basic/keywords/hear_talk.rs → hearing/ (6 modules) - channels/wechat.rs → wechat/ (10 modules) - channels/youtube.rs → youtube/ (5 modules) - contacts/mod.rs → contacts_api/ (6 modules) - core/bootstrap/mod.rs → bootstrap/ (5 modules) - core/shared/admin.rs → admin_*.rs (5 modules) - designer/canvas.rs → canvas_api/ (6 modules) - designer/mod.rs → designer_api/ (6 modules) - docs/handlers.rs → handlers_api/ (11 modules) - drive/mod.rs → drive_handlers.rs, drive_types.rs - learn/mod.rs → types.rs - main.rs → main_module/ (7 modules) - meet/webinar.rs → webinar_api/ (8 modules) - paper/mod.rs → (10 modules) - security/auth.rs → auth_api/ (7 modules) - security/passkey.rs → (4 modules) - sources/mod.rs → sources_api/ (5 modules) - tasks/mod.rs → task_api/ (5 modules) Stats: 38,040 deletions, 1,315 additions across 318 files Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
355 lines
8.6 KiB
Rust
355 lines
8.6 KiB
Rust
//! Tests for basic module
|
|
//!
|
|
//! Extracted from mod.rs to reduce file size
|
|
|
|
#[cfg(test)]
|
|
use std::collections::HashMap;
|
|
use std::time::Duration;
|
|
|
|
// Test script constants from bottest/fixtures/scripts/mod.rs
|
|
|
|
const GREETING_SCRIPT: &str = r#"
|
|
' Greeting Flow Script
|
|
' Simple greeting and response pattern
|
|
|
|
REM Initialize greeting
|
|
greeting$ = "Hello! Welcome to our service."
|
|
TALK greeting$
|
|
|
|
REM Wait for user response
|
|
HEAR userInput$
|
|
|
|
REM Check for specific keywords
|
|
IF INSTR(UCASE$(userInput$), "HELP") > 0 THEN
|
|
TALK "I can help you with: Products, Support, or Billing. What would you like to know?"
|
|
ELSEIF INSTR(UCASE$(userInput$), "BYE") > 0 THEN
|
|
TALK "Goodbye! Have a great day!"
|
|
END
|
|
ELSE
|
|
TALK "Thank you for your message. How can I assist you today?"
|
|
END IF
|
|
"#;
|
|
|
|
const SIMPLE_ECHO_SCRIPT: &str = r#"
|
|
' Simple Echo Script
|
|
' Echoes back whatever user says
|
|
|
|
TALK "Echo Bot: I will repeat everything you say. Type 'quit' to exit."
|
|
|
|
echo_loop:
|
|
HEAR input$
|
|
|
|
IF UCASE$(input$) = "QUIT" THEN
|
|
TALK "Goodbye!"
|
|
END
|
|
END IF
|
|
|
|
TALK "You said: " + input$
|
|
GOTO echo_loop
|
|
"#;
|
|
|
|
const VARIABLES_SCRIPT: &str = r#"
|
|
' Variables and Expressions Script
|
|
' Demonstrates variable types and operations
|
|
|
|
REM String variables
|
|
firstName$ = "John"
|
|
lastName$ = "Doe"
|
|
fullName$ = firstName$ + " " + lastName$
|
|
TALK "Full name: " + fullName$
|
|
|
|
REM Numeric variables
|
|
price = 99.99
|
|
quantity = 3
|
|
subtotal = price * quantity
|
|
tax = subtotal * 0.08
|
|
total = subtotal + tax
|
|
TALK "Total: $" + STR$(total)
|
|
"#;
|
|
|
|
fn get_script(name: &str) -> Option<&'static str> {
|
|
match name {
|
|
"greeting" => Some(GREETING_SCRIPT),
|
|
"simple_echo" => Some(SIMPLE_ECHO_SCRIPT),
|
|
"variables" => Some(VARIABLES_SCRIPT),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
fn available_scripts() -> Vec<&'static str> {
|
|
vec!["greeting", "simple_echo", "variables"]
|
|
}
|
|
|
|
fn all_scripts() -> HashMap<&'static str, &'static str> {
|
|
let mut scripts = HashMap::new();
|
|
for name in available_scripts() {
|
|
if let Some(content) = get_script(name) {
|
|
scripts.insert(name, content);
|
|
}
|
|
}
|
|
scripts
|
|
}
|
|
|
|
// Runner types from bottest/bot/runner.rs
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct BotRunnerConfig {
|
|
pub working_dir: std::path::PathBuf,
|
|
pub timeout: Duration,
|
|
pub use_mocks: bool,
|
|
pub env_vars: HashMap<String, String>,
|
|
pub capture_logs: bool,
|
|
pub log_level: LogLevel,
|
|
}
|
|
|
|
impl BotRunnerConfig {
|
|
pub const fn log_level(&self) -> LogLevel {
|
|
self.log_level
|
|
}
|
|
}
|
|
|
|
impl Default for BotRunnerConfig {
|
|
fn default() -> Self {
|
|
Self {
|
|
working_dir: std::env::temp_dir().join("bottest"),
|
|
timeout: Duration::from_secs(30),
|
|
use_mocks: true,
|
|
env_vars: HashMap::new(),
|
|
capture_logs: true,
|
|
log_level: LogLevel::Info,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
|
pub enum LogLevel {
|
|
Trace,
|
|
Debug,
|
|
#[default]
|
|
Info,
|
|
Warn,
|
|
Error,
|
|
}
|
|
|
|
#[derive(Debug, Default, Clone)]
|
|
pub struct RunnerMetrics {
|
|
pub total_requests: u64,
|
|
pub successful_requests: u64,
|
|
pub failed_requests: u64,
|
|
pub total_latency_ms: u64,
|
|
pub min_latency_ms: u64,
|
|
pub max_latency_ms: u64,
|
|
pub script_executions: u64,
|
|
pub transfer_to_human_count: u64,
|
|
}
|
|
|
|
impl RunnerMetrics {
|
|
pub const fn avg_latency_ms(&self) -> u64 {
|
|
if self.total_requests > 0 {
|
|
self.total_latency_ms / self.total_requests
|
|
} else {
|
|
0
|
|
}
|
|
}
|
|
|
|
pub fn success_rate(&self) -> f64 {
|
|
if self.total_requests > 0 {
|
|
(self.successful_requests as f64 / self.total_requests as f64) * 100.0
|
|
} else {
|
|
0.0
|
|
}
|
|
}
|
|
|
|
pub const fn min_latency(&self) -> u64 {
|
|
self.min_latency_ms
|
|
}
|
|
|
|
pub const fn max_latency(&self) -> u64 {
|
|
self.max_latency_ms
|
|
}
|
|
|
|
pub const fn latency_range(&self) -> u64 {
|
|
self.max_latency_ms.saturating_sub(self.min_latency_ms)
|
|
}
|
|
}
|
|
|
|
// Tests
|
|
|
|
#[test]
|
|
fn test_get_script() {
|
|
assert!(get_script("greeting").is_some());
|
|
assert!(get_script("simple_echo").is_some());
|
|
assert!(get_script("nonexistent").is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn test_available_scripts() {
|
|
let scripts = available_scripts();
|
|
assert!(!scripts.is_empty());
|
|
assert!(scripts.contains(&"greeting"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_all_scripts() {
|
|
let scripts = all_scripts();
|
|
assert_eq!(scripts.len(), available_scripts().len());
|
|
}
|
|
|
|
#[test]
|
|
fn test_greeting_script_content() {
|
|
let script = get_script("greeting").unwrap();
|
|
assert!(script.contains("TALK"));
|
|
assert!(script.contains("HEAR"));
|
|
assert!(script.contains("greeting"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_simple_echo_script_content() {
|
|
let script = get_script("simple_echo").unwrap();
|
|
assert!(script.contains("HEAR"));
|
|
assert!(script.contains("TALK"));
|
|
assert!(script.contains("GOTO"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_variables_script_content() {
|
|
let script = get_script("variables").unwrap();
|
|
assert!(script.contains("firstName$"));
|
|
assert!(script.contains("price"));
|
|
assert!(script.contains("STR$"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_bot_runner_config_default() {
|
|
let config = BotRunnerConfig::default();
|
|
assert_eq!(config.timeout, Duration::from_secs(30));
|
|
assert!(config.use_mocks);
|
|
assert!(config.capture_logs);
|
|
}
|
|
|
|
#[test]
|
|
fn test_runner_metrics_avg_latency() {
|
|
let metrics = RunnerMetrics {
|
|
total_requests: 10,
|
|
total_latency_ms: 1000,
|
|
..RunnerMetrics::default()
|
|
};
|
|
|
|
assert_eq!(metrics.avg_latency_ms(), 100);
|
|
}
|
|
|
|
#[test]
|
|
fn test_runner_metrics_success_rate() {
|
|
let metrics = RunnerMetrics {
|
|
total_requests: 100,
|
|
successful_requests: 95,
|
|
..RunnerMetrics::default()
|
|
};
|
|
|
|
assert!((metrics.success_rate() - 95.0).abs() < f64::EPSILON);
|
|
}
|
|
|
|
#[test]
|
|
fn test_runner_metrics_zero_requests() {
|
|
let metrics = RunnerMetrics::default();
|
|
assert_eq!(metrics.avg_latency_ms(), 0);
|
|
assert!(metrics.success_rate().abs() < f64::EPSILON);
|
|
}
|
|
|
|
#[test]
|
|
fn test_log_level_default() {
|
|
let level = LogLevel::default();
|
|
assert_eq!(level, LogLevel::Info);
|
|
}
|
|
|
|
#[test]
|
|
fn test_runner_config_env_vars() {
|
|
let mut env_vars = HashMap::new();
|
|
env_vars.insert("API_KEY".to_string(), "test123".to_string());
|
|
env_vars.insert("DEBUG".to_string(), "true".to_string());
|
|
|
|
let config = BotRunnerConfig {
|
|
env_vars,
|
|
..BotRunnerConfig::default()
|
|
};
|
|
|
|
assert_eq!(config.env_vars.get("API_KEY"), Some(&"test123".to_string()));
|
|
assert_eq!(config.env_vars.get("DEBUG"), Some(&"true".to_string()));
|
|
}
|
|
|
|
#[test]
|
|
fn test_runner_config_timeout() {
|
|
let config = BotRunnerConfig {
|
|
timeout: Duration::from_secs(60),
|
|
..BotRunnerConfig::default()
|
|
};
|
|
|
|
assert_eq!(config.timeout, Duration::from_secs(60));
|
|
}
|
|
|
|
#[test]
|
|
fn test_metrics_tracking() {
|
|
let metrics = RunnerMetrics {
|
|
total_requests: 50,
|
|
successful_requests: 45,
|
|
failed_requests: 5,
|
|
total_latency_ms: 5000,
|
|
min_latency_ms: 10,
|
|
max_latency_ms: 500,
|
|
..RunnerMetrics::default()
|
|
};
|
|
|
|
assert_eq!(metrics.avg_latency_ms(), 100);
|
|
assert!((metrics.success_rate() - 90.0).abs() < f64::EPSILON);
|
|
assert_eq!(
|
|
metrics.total_requests,
|
|
metrics.successful_requests + metrics.failed_requests
|
|
);
|
|
assert_eq!(metrics.min_latency(), 10);
|
|
assert_eq!(metrics.max_latency(), 500);
|
|
assert_eq!(metrics.latency_range(), 490);
|
|
}
|
|
|
|
#[test]
|
|
fn test_script_execution_tracking() {
|
|
let metrics = RunnerMetrics {
|
|
script_executions: 25,
|
|
transfer_to_human_count: 3,
|
|
..RunnerMetrics::default()
|
|
};
|
|
|
|
assert_eq!(metrics.script_executions, 25);
|
|
assert_eq!(metrics.transfer_to_human_count, 3);
|
|
}
|
|
|
|
#[test]
|
|
fn test_log_level_accessor() {
|
|
let config = BotRunnerConfig::default();
|
|
assert_eq!(config.log_level(), LogLevel::Info);
|
|
}
|
|
|
|
#[test]
|
|
fn test_log_levels() {
|
|
assert!(matches!(LogLevel::Trace, LogLevel::Trace));
|
|
assert!(matches!(LogLevel::Debug, LogLevel::Debug));
|
|
assert!(matches!(LogLevel::Info, LogLevel::Info));
|
|
assert!(matches!(LogLevel::Warn, LogLevel::Warn));
|
|
assert!(matches!(LogLevel::Error, LogLevel::Error));
|
|
}
|
|
|
|
#[test]
|
|
fn test_script_contains_basic_keywords() {
|
|
for name in available_scripts() {
|
|
if let Some(script) = get_script(name) {
|
|
// All scripts should have some form of output
|
|
let has_output = script.contains("TALK") || script.contains("PRINT");
|
|
assert!(has_output, "Script {} should have output keyword", name);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_runner_config_working_dir() {
|
|
let config = BotRunnerConfig::default();
|
|
assert!(config.working_dir.to_str().unwrap_or_default().contains("bottest"));
|
|
}
|