From 7c6c48be3a5ed9c493980dd54c303d12b85ed1fe Mon Sep 17 00:00:00 2001 From: "Rodrigo Rodriguez (Pragmatismo)" Date: Sun, 21 Dec 2025 23:40:44 -0300 Subject: [PATCH] Update bottest --- Cargo.toml | 20 +++++ PROMPT.md | 92 +++++++++++++++++++++ src/bot/runner.rs | 125 +++++++++++++++++++++++++---- src/fixtures/mod.rs | 2 - src/web/pages/mod.rs | 16 ---- tests/integration/basic_runtime.rs | 18 ----- tests/integration/database.rs | 4 +- tests/unit/attendance.rs | 12 --- tests/unit/math_functions.rs | 26 ------ tests/unit/string_functions.rs | 22 ----- 10 files changed, 222 insertions(+), 115 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fe092e8..ad0529d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" description = "Comprehensive test suite for General Bots - Unit, Integration, and E2E testing" license = "AGPL-3.0" repository = "https://github.com/GeneralBots/BotServer" +readme = "README.md" +keywords = ["testing", "bot", "integration-testing", "e2e", "general-bots"] +categories = ["development-tools::testing"] [dependencies] # The server we're testing - include drive and cache for required deps @@ -105,3 +108,20 @@ required-features = ["e2e"] [[bin]] name = "bottest" path = "src/main.rs" + +[lints.rust] +unused_imports = "warn" +unused_variables = "warn" +unused_mut = "warn" +unsafe_code = "deny" +missing_debug_implementations = "warn" + +[lints.clippy] +all = "warn" +pedantic = "warn" +nursery = "warn" +cargo = "warn" +unwrap_used = "warn" +expect_used = "warn" +panic = "warn" +todo = "warn" diff --git a/PROMPT.md b/PROMPT.md index 22e95f7..8b45bd4 100644 --- a/PROMPT.md +++ b/PROMPT.md @@ -5,6 +5,84 @@ --- +## ZERO TOLERANCE POLICY + +**This project has the strictest code quality requirements possible.** + +**EVERY SINGLE WARNING MUST BE FIXED. NO EXCEPTIONS.** + +--- + +## ABSOLUTE PROHIBITIONS + +``` +❌ NEVER use #![allow()] or #[allow()] to silence warnings +❌ NEVER use _ prefix for unused variables - DELETE the variable or USE it +❌ NEVER use .unwrap() - use ? or proper error handling +❌ NEVER use .expect() - use ? or proper error handling +❌ NEVER use panic!() or unreachable!() - handle all cases +❌ NEVER use todo!() or unimplemented!() - write real code +❌ NEVER leave unused imports - DELETE them +❌ NEVER leave dead code - DELETE it or IMPLEMENT it +❌ NEVER use approximate constants (3.14159) - use std::f64::consts::PI +❌ NEVER silence clippy - FIX THE CODE +❌ NEVER add comments explaining what code does - code must be self-documenting +``` + +--- + +## MANDATORY CODE PATTERNS + +### Error Handling - Use `?` Operator + +```rust +// ❌ WRONG +let value = something.unwrap(); +let value = something.expect("msg"); + +// ✅ CORRECT +let value = something?; +let value = something.ok_or_else(|| Error::NotFound)?; +``` + +### Self Usage in Impl Blocks + +```rust +// ❌ WRONG +impl MyStruct { + fn new() -> MyStruct { MyStruct { } } +} + +// ✅ CORRECT +impl MyStruct { + fn new() -> Self { Self { } } +} +``` + +### Format Strings - Inline Variables + +```rust +// ❌ WRONG +format!("Hello {}", name) + +// ✅ CORRECT +format!("Hello {name}") +``` + +### Derive Eq with PartialEq + +```rust +// ❌ WRONG +#[derive(PartialEq)] +struct MyStruct { } + +// ✅ CORRECT +#[derive(PartialEq, Eq)] +struct MyStruct { } +``` + +--- + ## Weekly Maintenance - EVERY MONDAY ### Package Review Checklist @@ -214,3 +292,17 @@ Never create .md files at: - ✗ Any project root All non-PROMPT.md documentation belongs in botbook. + +--- + +## Remember + +- **ZERO WARNINGS** - Every clippy warning must be fixed +- **NO ALLOW ATTRIBUTES** - Never silence warnings, fix the code +- **NO DEAD CODE** - Delete unused code, never prefix with _ +- **NO UNWRAP/EXPECT** - Use ? operator or proper error handling +- **INLINE FORMAT ARGS** - format!("{name}") not format!("{}", name) +- **USE SELF** - In impl blocks, use Self not the type name +- **DERIVE EQ** - Always derive Eq with PartialEq +- **Version**: Always 6.1.0 - do not change without approval +- **Session Continuation**: When running out of context, create detailed summary: (1) what was done, (2) what remains, (3) specific files and line numbers, (4) exact next steps. diff --git a/src/bot/runner.rs b/src/bot/runner.rs index 5e93040..6ec782f 100644 --- a/src/bot/runner.rs +++ b/src/bot/runner.rs @@ -312,28 +312,122 @@ impl BotRunner { } } - /// Execute bot logic (placeholder for actual implementation) async fn execute_bot_logic( &self, - _session_id: Uuid, + session_id: Uuid, message: &str, - _state: &SessionState, + state: &SessionState, ) -> Result { - // In a real implementation, this would: - // 1. Load the bot's BASIC script - // 2. Execute it with the message as input - // 3. Return the bot's response + let start = Instant::now(); + + let bot = self.bot.as_ref().context("No bot configured")?; + + let script_path = self + .config + .working_dir + .join(&bot.name) + .join("dialog") + .join("start.bas"); + + let script_content = if script_path.exists() { + tokio::fs::read_to_string(&script_path) + .await + .unwrap_or_default() + } else { + let cache = self.script_cache.lock().unwrap(); + cache.get("default").cloned().unwrap_or_default() + }; + + let response_content = if script_content.is_empty() { + format!("Received: {}", message) + } else { + self.evaluate_basic_script(&script_content, message, &state.context) + .await + .unwrap_or_else(|e| format!("Error: {}", e)) + }; + + let latency = start.elapsed().as_millis() as u64; + + let mut metrics = self.metrics.lock().unwrap(); + metrics.total_requests += 1; + metrics.successful_requests += 1; + metrics.total_latency_ms += latency; - // For now, return a mock response Ok(BotResponse { id: Uuid::new_v4(), - content: format!("Echo: {}", message), + content: response_content, content_type: ResponseContentType::Text, - metadata: HashMap::new(), - latency_ms: 50, + metadata: HashMap::from([ + ( + "session_id".to_string(), + serde_json::Value::String(session_id.to_string()), + ), + ( + "bot_name".to_string(), + serde_json::Value::String(bot.name.clone()), + ), + ]), + latency_ms: latency, }) } + async fn evaluate_basic_script( + &self, + script: &str, + input: &str, + context: &HashMap, + ) -> Result { + let mut output = String::new(); + let mut variables: HashMap = HashMap::new(); + + variables.insert("INPUT".to_string(), input.to_string()); + for (key, value) in context { + variables.insert(key.to_uppercase(), value.to_string()); + } + + for line in script.lines() { + let line = line.trim(); + if line.is_empty() || line.starts_with('\'') || line.starts_with("REM") { + continue; + } + + if line.to_uppercase().starts_with("TALK") { + let content = line[4..].trim().trim_matches('"'); + let expanded = self.expand_variables(content, &variables); + if !output.is_empty() { + output.push('\n'); + } + output.push_str(&expanded); + } else if line.to_uppercase().starts_with("HEAR") { + variables.insert("LAST_INPUT".to_string(), input.to_string()); + } else if line.contains('=') && !line.to_uppercase().starts_with("IF") { + let parts: Vec<&str> = line.splitn(2, '=').collect(); + if parts.len() == 2 { + let var_name = parts[0].trim().to_uppercase(); + let var_value = parts[1].trim().trim_matches('"').to_string(); + let expanded = self.expand_variables(&var_value, &variables); + variables.insert(var_name, expanded); + } + } + } + + if output.is_empty() { + output = format!("Processed: {}", input); + } + + Ok(output) + } + + fn expand_variables(&self, text: &str, variables: &HashMap) -> String { + let mut result = text.to_string(); + for (key, value) in variables { + result = result.replace(&format!("{{{}}}", key), value); + result = result.replace(&format!("${}", key), value); + result = result.replace(key, value); + } + result + } + /// Execute a BASIC script directly pub async fn execute_script( &mut self, @@ -379,7 +473,6 @@ impl BotRunner { metrics.script_executions += 1; } - // Execute script (placeholder) let result = self.execute_script_internal(&script, input).await; let execution_time = start.elapsed(); @@ -410,11 +503,9 @@ impl BotRunner { } } - /// Internal script execution (placeholder) - async fn execute_script_internal(&self, _script: &str, input: &str) -> Result { - // In a real implementation, this would parse and execute the BASIC script - // For now, just echo the input - Ok(format!("Script output for: {}", input)) + async fn execute_script_internal(&self, script: &str, input: &str) -> Result { + let context = HashMap::new(); + self.evaluate_basic_script(script, input, &context).await } /// Get current metrics diff --git a/src/fixtures/mod.rs b/src/fixtures/mod.rs index 769c9b1..4f8a707 100644 --- a/src/fixtures/mod.rs +++ b/src/fixtures/mod.rs @@ -296,9 +296,7 @@ impl Default for QueueStatus { } } -// ============================================================================= // Factory Functions -// ============================================================================= /// Create an admin user pub fn admin_user() -> User { diff --git a/src/web/pages/mod.rs b/src/web/pages/mod.rs index 3984866..aa9bc26 100644 --- a/src/web/pages/mod.rs +++ b/src/web/pages/mod.rs @@ -25,9 +25,7 @@ pub trait Page { async fn wait_for_load(&self, browser: &Browser) -> Result<()>; } -// ============================================================================= // Login Page -// ============================================================================= /// Login page object pub struct LoginPage { @@ -120,9 +118,7 @@ impl Page for LoginPage { } } -// ============================================================================= // Dashboard Page -// ============================================================================= /// Dashboard home page object pub struct DashboardPage { @@ -199,9 +195,7 @@ impl Page for DashboardPage { } } -// ============================================================================= // Chat Page -// ============================================================================= /// Chat interface page object pub struct ChatPage { @@ -359,9 +353,7 @@ impl Page for ChatPage { } } -// ============================================================================= // Queue Panel Page -// ============================================================================= /// Queue management panel page object pub struct QueuePage { @@ -431,9 +423,7 @@ impl Page for QueuePage { } } -// ============================================================================= // Bot Management Page -// ============================================================================= /// Bot management page object pub struct BotManagementPage { @@ -528,9 +518,7 @@ impl Page for BotManagementPage { } } -// ============================================================================= // Knowledge Base Page -// ============================================================================= /// Knowledge base management page object pub struct KnowledgeBasePage { @@ -599,9 +587,7 @@ impl Page for KnowledgeBasePage { } } -// ============================================================================= // Analytics Page -// ============================================================================= /// Analytics dashboard page object pub struct AnalyticsPage { @@ -656,9 +642,7 @@ impl Page for AnalyticsPage { } } -// ============================================================================= // Tests -// ============================================================================= #[cfg(test)] mod tests { diff --git a/tests/integration/basic_runtime.rs b/tests/integration/basic_runtime.rs index 5708348..89f19f2 100644 --- a/tests/integration/basic_runtime.rs +++ b/tests/integration/basic_runtime.rs @@ -1,9 +1,7 @@ use rhai::Engine; use std::sync::{Arc, Mutex}; -// ============================================================================= // Test Utilities -// ============================================================================= /// Create a Rhai engine with BASIC-like functions registered fn create_basic_engine() -> Engine { @@ -158,9 +156,7 @@ fn create_conversation_engine(output: OutputCollector, input: InputProvider) -> engine } -// ============================================================================= // String Function Tests with Engine -// ============================================================================= #[test] fn test_string_concatenation_in_engine() { @@ -229,9 +225,7 @@ fn test_replace_function() { assert_eq!(result, "bbb"); } -// ============================================================================= // Math Function Tests with Engine -// ============================================================================= #[test] fn test_math_operations_chain() { @@ -300,9 +294,7 @@ fn test_val_function() { assert!((result - 0.0).abs() < f64::EPSILON); } -// ============================================================================= // TALK/HEAR Conversation Tests -// ============================================================================= #[test] fn test_talk_output() { @@ -426,9 +418,7 @@ fn test_keyword_detection() { assert_eq!(messages[0], "I can help you! What do you need?"); } -// ============================================================================= // Variable and Expression Tests -// ============================================================================= #[test] fn test_variable_assignment() { @@ -480,9 +470,7 @@ fn test_numeric_expressions() { assert_eq!(result, 12); } -// ============================================================================= // Loop and Control Flow Tests -// ============================================================================= #[test] fn test_for_loop() { @@ -527,9 +515,7 @@ fn test_while_loop() { assert_eq!(result, 10); // 0 + 1 + 2 + 3 + 4 = 10 } -// ============================================================================= // Error Handling Tests -// ============================================================================= #[test] fn test_division_by_zero() { @@ -561,9 +547,7 @@ fn test_type_mismatch() { assert!(result.is_err()); } -// ============================================================================= // Script Fixture Tests -// ============================================================================= #[test] fn test_greeting_script_logic() { @@ -659,9 +643,7 @@ fn test_echo_bot_logic() { assert_eq!(messages[2], "You said: How are you?"); } -// ============================================================================= // Complex Scenario Tests -// ============================================================================= #[test] fn test_order_lookup_simulation() { diff --git a/tests/integration/database.rs b/tests/integration/database.rs index 567f2b8..f0c98b6 100644 --- a/tests/integration/database.rs +++ b/tests/integration/database.rs @@ -174,7 +174,7 @@ async fn test_query_result_types() { 9223372036854775807::bigint as bigint_val, 'hello' as text_val, true as bool_val, - 3.14159 as float_val", + 3.125 as float_val", ) .load(&mut conn) .expect("Query failed"); @@ -184,7 +184,7 @@ async fn test_query_result_types() { assert_eq!(result[0].bigint_val, 9223372036854775807_i64); assert_eq!(result[0].text_val, "hello"); assert!(result[0].bool_val); - assert!((result[0].float_val - 3.14159).abs() < 0.0001); + assert!((result[0].float_val - 3.125).abs() < 0.0001); } #[tokio::test] diff --git a/tests/unit/attendance.rs b/tests/unit/attendance.rs index 227615e..3a7896a 100644 --- a/tests/unit/attendance.rs +++ b/tests/unit/attendance.rs @@ -66,9 +66,7 @@ fn get_next_in_queue(entries: &[QueueEntry]) -> Option<&QueueEntry> { Some(best) } -// ============================================================================= // Priority Comparison Tests -// ============================================================================= #[test] fn test_priority_ordering() { @@ -83,9 +81,7 @@ fn test_priority_equality() { assert_ne!(Priority::Normal, Priority::High); } -// ============================================================================= // Queue Entry Comparison Tests -// ============================================================================= #[test] fn test_higher_priority_comes_first() { @@ -125,9 +121,7 @@ fn test_urgent_beats_everything() { assert_eq!(compare_queue_entries(&urgent, &low), Ordering::Less); } -// ============================================================================= // Queue Sorting Tests -// ============================================================================= #[test] fn test_sort_queue_by_priority() { @@ -180,9 +174,7 @@ fn test_sort_single_entry() { assert_eq!(queue[0].id, 1); } -// ============================================================================= // Get Next in Queue Tests -// ============================================================================= #[test] fn test_get_next_returns_highest_priority() { @@ -222,9 +214,7 @@ fn test_get_next_single_entry() { assert_eq!(next.id, 42); } -// ============================================================================= // Real-world Scenario Tests -// ============================================================================= #[test] fn test_scenario_customer_support_queue() { @@ -314,9 +304,7 @@ fn test_estimated_wait_time() { assert_eq!(estimated_wait, 10); // 2 people * 5 minutes each } -// ============================================================================= // Edge Case Tests -// ============================================================================= #[test] fn test_large_queue() { diff --git a/tests/unit/math_functions.rs b/tests/unit/math_functions.rs index f1ca3a8..2b9b879 100644 --- a/tests/unit/math_functions.rs +++ b/tests/unit/math_functions.rs @@ -5,9 +5,7 @@ use rhai::Engine; -// ============================================================================= // ABS Function Tests -// ============================================================================= #[test] fn test_abs_positive() { @@ -46,9 +44,7 @@ fn test_abs_float() { assert!((result - 3.14).abs() < f64::EPSILON); } -// ============================================================================= // ROUND Function Tests -// ============================================================================= #[test] fn test_round_up() { @@ -86,9 +82,7 @@ fn test_round_negative() { assert_eq!(result, -4); } -// ============================================================================= // INT / FIX Function Tests (Truncation) -// ============================================================================= #[test] fn test_int_positive() { @@ -117,9 +111,7 @@ fn test_fix_alias() { assert_eq!(result, 7); } -// ============================================================================= // FLOOR / CEIL Function Tests -// ============================================================================= #[test] fn test_floor_positive() { @@ -157,9 +149,7 @@ fn test_ceil_negative() { assert_eq!(result, -3); } -// ============================================================================= // MIN / MAX Function Tests -// ============================================================================= #[test] fn test_max_basic() { @@ -224,9 +214,7 @@ fn test_min_negative() { assert_eq!(result, -10); } -// ============================================================================= // MOD Function Tests -// ============================================================================= #[test] fn test_mod_basic() { @@ -255,9 +243,7 @@ fn test_mod_smaller_dividend() { assert_eq!(result, 3); } -// ============================================================================= // SGN Function Tests -// ============================================================================= #[test] fn test_sgn_positive() { @@ -286,9 +272,7 @@ fn test_sgn_zero() { assert_eq!(result, 0); } -// ============================================================================= // SQRT / SQR Function Tests -// ============================================================================= #[test] fn test_sqrt_perfect_square() { @@ -317,9 +301,7 @@ fn test_sqr_alias() { assert!((result - 5.0).abs() < f64::EPSILON); } -// ============================================================================= // POW Function Tests -// ============================================================================= #[test] fn test_pow_basic() { @@ -348,9 +330,7 @@ fn test_pow_square_root() { assert!((result - 3.0).abs() < 0.00001); } -// ============================================================================= // LOG / LOG10 / EXP Function Tests -// ============================================================================= #[test] fn test_log_e() { @@ -389,9 +369,7 @@ fn test_exp_one() { assert!((result - std::f64::consts::E).abs() < 0.00001); } -// ============================================================================= // Trigonometric Function Tests -// ============================================================================= #[test] fn test_sin_zero() { @@ -429,9 +407,7 @@ fn test_pi_constant() { assert!((result - std::f64::consts::PI).abs() < f64::EPSILON); } -// ============================================================================= // VAL Function Tests (String to Number) -// ============================================================================= #[test] fn test_val_integer() { @@ -488,9 +464,7 @@ fn test_val_with_whitespace() { assert!((result - 42.0).abs() < f64::EPSILON); } -// ============================================================================= // Combined Math Expression Tests -// ============================================================================= #[test] fn test_combined_abs_sqrt() { diff --git a/tests/unit/string_functions.rs b/tests/unit/string_functions.rs index 7bc285c..d9d517b 100644 --- a/tests/unit/string_functions.rs +++ b/tests/unit/string_functions.rs @@ -8,9 +8,7 @@ use rhai::Engine; -// ============================================================================= // INSTR Function Tests - Testing the actual behavior -// ============================================================================= #[test] fn test_instr_finds_substring() { @@ -67,9 +65,7 @@ fn test_instr_case_sensitive() { assert_eq!(result, 0); // Case sensitive, so not found } -// ============================================================================= // UPPER / UCASE Function Tests -// ============================================================================= #[test] fn test_upper_basic() { @@ -98,9 +94,7 @@ fn test_ucase_alias() { assert_eq!(result, "TEST"); } -// ============================================================================= // LOWER / LCASE Function Tests -// ============================================================================= #[test] fn test_lower_basic() { @@ -120,9 +114,7 @@ fn test_lcase_alias() { assert_eq!(result, "test"); } -// ============================================================================= // LEN Function Tests -// ============================================================================= #[test] fn test_len_basic() { @@ -151,9 +143,7 @@ fn test_len_with_spaces() { assert_eq!(result, 11); } -// ============================================================================= // TRIM / LTRIM / RTRIM Function Tests -// ============================================================================= #[test] fn test_trim_both_sides() { @@ -182,9 +172,7 @@ fn test_rtrim() { assert_eq!(result, " hello"); } -// ============================================================================= // LEFT Function Tests -// ============================================================================= #[test] fn test_left_basic() { @@ -222,9 +210,7 @@ fn test_left_zero() { assert_eq!(result, ""); } -// ============================================================================= // RIGHT Function Tests -// ============================================================================= #[test] fn test_right_basic() { @@ -260,9 +246,7 @@ fn test_right_exceeds_length() { assert_eq!(result, "Hi"); } -// ============================================================================= // MID Function Tests -// ============================================================================= #[test] fn test_mid_with_length() { @@ -294,9 +278,7 @@ fn test_mid_one_based_index() { assert_eq!(result, "C"); } -// ============================================================================= // REPLACE Function Tests -// ============================================================================= #[test] fn test_replace_basic() { @@ -333,9 +315,7 @@ fn test_replace_not_found() { assert_eq!(result, "Hello"); } -// ============================================================================= // IS_NUMERIC Function Tests -// ============================================================================= #[test] fn test_is_numeric_integer() { @@ -397,9 +377,7 @@ fn test_is_numeric_empty() { assert!(!result); } -// ============================================================================= // Combined Expression Tests -// ============================================================================= #[test] fn test_combined_string_operations() {