Update bottest

This commit is contained in:
Rodrigo Rodriguez (Pragmatismo) 2025-12-21 23:40:44 -03:00
parent 706391b272
commit 7c6c48be3a
10 changed files with 222 additions and 115 deletions

View file

@ -5,6 +5,9 @@ edition = "2021"
description = "Comprehensive test suite for General Bots - Unit, Integration, and E2E testing" description = "Comprehensive test suite for General Bots - Unit, Integration, and E2E testing"
license = "AGPL-3.0" license = "AGPL-3.0"
repository = "https://github.com/GeneralBots/BotServer" repository = "https://github.com/GeneralBots/BotServer"
readme = "README.md"
keywords = ["testing", "bot", "integration-testing", "e2e", "general-bots"]
categories = ["development-tools::testing"]
[dependencies] [dependencies]
# The server we're testing - include drive and cache for required deps # The server we're testing - include drive and cache for required deps
@ -105,3 +108,20 @@ required-features = ["e2e"]
[[bin]] [[bin]]
name = "bottest" name = "bottest"
path = "src/main.rs" 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"

View file

@ -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 ## Weekly Maintenance - EVERY MONDAY
### Package Review Checklist ### Package Review Checklist
@ -214,3 +292,17 @@ Never create .md files at:
- ✗ Any project root - ✗ Any project root
All non-PROMPT.md documentation belongs in botbook. 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.

View file

@ -312,28 +312,122 @@ impl BotRunner {
} }
} }
/// Execute bot logic (placeholder for actual implementation)
async fn execute_bot_logic( async fn execute_bot_logic(
&self, &self,
_session_id: Uuid, session_id: Uuid,
message: &str, message: &str,
_state: &SessionState, state: &SessionState,
) -> Result<BotResponse> { ) -> Result<BotResponse> {
// In a real implementation, this would: let start = Instant::now();
// 1. Load the bot's BASIC script
// 2. Execute it with the message as input let bot = self.bot.as_ref().context("No bot configured")?;
// 3. Return the bot's response
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 { Ok(BotResponse {
id: Uuid::new_v4(), id: Uuid::new_v4(),
content: format!("Echo: {}", message), content: response_content,
content_type: ResponseContentType::Text, content_type: ResponseContentType::Text,
metadata: HashMap::new(), metadata: HashMap::from([
latency_ms: 50, (
"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<String, serde_json::Value>,
) -> Result<String> {
let mut output = String::new();
let mut variables: HashMap<String, String> = 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, String>) -> 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 /// Execute a BASIC script directly
pub async fn execute_script( pub async fn execute_script(
&mut self, &mut self,
@ -379,7 +473,6 @@ impl BotRunner {
metrics.script_executions += 1; metrics.script_executions += 1;
} }
// Execute script (placeholder)
let result = self.execute_script_internal(&script, input).await; let result = self.execute_script_internal(&script, input).await;
let execution_time = start.elapsed(); 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<String> {
async fn execute_script_internal(&self, _script: &str, input: &str) -> Result<String> { let context = HashMap::new();
// In a real implementation, this would parse and execute the BASIC script self.evaluate_basic_script(script, input, &context).await
// For now, just echo the input
Ok(format!("Script output for: {}", input))
} }
/// Get current metrics /// Get current metrics

View file

@ -296,9 +296,7 @@ impl Default for QueueStatus {
} }
} }
// =============================================================================
// Factory Functions // Factory Functions
// =============================================================================
/// Create an admin user /// Create an admin user
pub fn admin_user() -> User { pub fn admin_user() -> User {

View file

@ -25,9 +25,7 @@ pub trait Page {
async fn wait_for_load(&self, browser: &Browser) -> Result<()>; async fn wait_for_load(&self, browser: &Browser) -> Result<()>;
} }
// =============================================================================
// Login Page // Login Page
// =============================================================================
/// Login page object /// Login page object
pub struct LoginPage { pub struct LoginPage {
@ -120,9 +118,7 @@ impl Page for LoginPage {
} }
} }
// =============================================================================
// Dashboard Page // Dashboard Page
// =============================================================================
/// Dashboard home page object /// Dashboard home page object
pub struct DashboardPage { pub struct DashboardPage {
@ -199,9 +195,7 @@ impl Page for DashboardPage {
} }
} }
// =============================================================================
// Chat Page // Chat Page
// =============================================================================
/// Chat interface page object /// Chat interface page object
pub struct ChatPage { pub struct ChatPage {
@ -359,9 +353,7 @@ impl Page for ChatPage {
} }
} }
// =============================================================================
// Queue Panel Page // Queue Panel Page
// =============================================================================
/// Queue management panel page object /// Queue management panel page object
pub struct QueuePage { pub struct QueuePage {
@ -431,9 +423,7 @@ impl Page for QueuePage {
} }
} }
// =============================================================================
// Bot Management Page // Bot Management Page
// =============================================================================
/// Bot management page object /// Bot management page object
pub struct BotManagementPage { pub struct BotManagementPage {
@ -528,9 +518,7 @@ impl Page for BotManagementPage {
} }
} }
// =============================================================================
// Knowledge Base Page // Knowledge Base Page
// =============================================================================
/// Knowledge base management page object /// Knowledge base management page object
pub struct KnowledgeBasePage { pub struct KnowledgeBasePage {
@ -599,9 +587,7 @@ impl Page for KnowledgeBasePage {
} }
} }
// =============================================================================
// Analytics Page // Analytics Page
// =============================================================================
/// Analytics dashboard page object /// Analytics dashboard page object
pub struct AnalyticsPage { pub struct AnalyticsPage {
@ -656,9 +642,7 @@ impl Page for AnalyticsPage {
} }
} }
// =============================================================================
// Tests // Tests
// =============================================================================
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View file

@ -1,9 +1,7 @@
use rhai::Engine; use rhai::Engine;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
// =============================================================================
// Test Utilities // Test Utilities
// =============================================================================
/// Create a Rhai engine with BASIC-like functions registered /// Create a Rhai engine with BASIC-like functions registered
fn create_basic_engine() -> Engine { fn create_basic_engine() -> Engine {
@ -158,9 +156,7 @@ fn create_conversation_engine(output: OutputCollector, input: InputProvider) ->
engine engine
} }
// =============================================================================
// String Function Tests with Engine // String Function Tests with Engine
// =============================================================================
#[test] #[test]
fn test_string_concatenation_in_engine() { fn test_string_concatenation_in_engine() {
@ -229,9 +225,7 @@ fn test_replace_function() {
assert_eq!(result, "bbb"); assert_eq!(result, "bbb");
} }
// =============================================================================
// Math Function Tests with Engine // Math Function Tests with Engine
// =============================================================================
#[test] #[test]
fn test_math_operations_chain() { fn test_math_operations_chain() {
@ -300,9 +294,7 @@ fn test_val_function() {
assert!((result - 0.0).abs() < f64::EPSILON); assert!((result - 0.0).abs() < f64::EPSILON);
} }
// =============================================================================
// TALK/HEAR Conversation Tests // TALK/HEAR Conversation Tests
// =============================================================================
#[test] #[test]
fn test_talk_output() { fn test_talk_output() {
@ -426,9 +418,7 @@ fn test_keyword_detection() {
assert_eq!(messages[0], "I can help you! What do you need?"); assert_eq!(messages[0], "I can help you! What do you need?");
} }
// =============================================================================
// Variable and Expression Tests // Variable and Expression Tests
// =============================================================================
#[test] #[test]
fn test_variable_assignment() { fn test_variable_assignment() {
@ -480,9 +470,7 @@ fn test_numeric_expressions() {
assert_eq!(result, 12); assert_eq!(result, 12);
} }
// =============================================================================
// Loop and Control Flow Tests // Loop and Control Flow Tests
// =============================================================================
#[test] #[test]
fn test_for_loop() { fn test_for_loop() {
@ -527,9 +515,7 @@ fn test_while_loop() {
assert_eq!(result, 10); // 0 + 1 + 2 + 3 + 4 = 10 assert_eq!(result, 10); // 0 + 1 + 2 + 3 + 4 = 10
} }
// =============================================================================
// Error Handling Tests // Error Handling Tests
// =============================================================================
#[test] #[test]
fn test_division_by_zero() { fn test_division_by_zero() {
@ -561,9 +547,7 @@ fn test_type_mismatch() {
assert!(result.is_err()); assert!(result.is_err());
} }
// =============================================================================
// Script Fixture Tests // Script Fixture Tests
// =============================================================================
#[test] #[test]
fn test_greeting_script_logic() { fn test_greeting_script_logic() {
@ -659,9 +643,7 @@ fn test_echo_bot_logic() {
assert_eq!(messages[2], "You said: How are you?"); assert_eq!(messages[2], "You said: How are you?");
} }
// =============================================================================
// Complex Scenario Tests // Complex Scenario Tests
// =============================================================================
#[test] #[test]
fn test_order_lookup_simulation() { fn test_order_lookup_simulation() {

View file

@ -174,7 +174,7 @@ async fn test_query_result_types() {
9223372036854775807::bigint as bigint_val, 9223372036854775807::bigint as bigint_val,
'hello' as text_val, 'hello' as text_val,
true as bool_val, true as bool_val,
3.14159 as float_val", 3.125 as float_val",
) )
.load(&mut conn) .load(&mut conn)
.expect("Query failed"); .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].bigint_val, 9223372036854775807_i64);
assert_eq!(result[0].text_val, "hello"); assert_eq!(result[0].text_val, "hello");
assert!(result[0].bool_val); 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] #[tokio::test]

View file

@ -66,9 +66,7 @@ fn get_next_in_queue(entries: &[QueueEntry]) -> Option<&QueueEntry> {
Some(best) Some(best)
} }
// =============================================================================
// Priority Comparison Tests // Priority Comparison Tests
// =============================================================================
#[test] #[test]
fn test_priority_ordering() { fn test_priority_ordering() {
@ -83,9 +81,7 @@ fn test_priority_equality() {
assert_ne!(Priority::Normal, Priority::High); assert_ne!(Priority::Normal, Priority::High);
} }
// =============================================================================
// Queue Entry Comparison Tests // Queue Entry Comparison Tests
// =============================================================================
#[test] #[test]
fn test_higher_priority_comes_first() { fn test_higher_priority_comes_first() {
@ -125,9 +121,7 @@ fn test_urgent_beats_everything() {
assert_eq!(compare_queue_entries(&urgent, &low), Ordering::Less); assert_eq!(compare_queue_entries(&urgent, &low), Ordering::Less);
} }
// =============================================================================
// Queue Sorting Tests // Queue Sorting Tests
// =============================================================================
#[test] #[test]
fn test_sort_queue_by_priority() { fn test_sort_queue_by_priority() {
@ -180,9 +174,7 @@ fn test_sort_single_entry() {
assert_eq!(queue[0].id, 1); assert_eq!(queue[0].id, 1);
} }
// =============================================================================
// Get Next in Queue Tests // Get Next in Queue Tests
// =============================================================================
#[test] #[test]
fn test_get_next_returns_highest_priority() { fn test_get_next_returns_highest_priority() {
@ -222,9 +214,7 @@ fn test_get_next_single_entry() {
assert_eq!(next.id, 42); assert_eq!(next.id, 42);
} }
// =============================================================================
// Real-world Scenario Tests // Real-world Scenario Tests
// =============================================================================
#[test] #[test]
fn test_scenario_customer_support_queue() { 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 assert_eq!(estimated_wait, 10); // 2 people * 5 minutes each
} }
// =============================================================================
// Edge Case Tests // Edge Case Tests
// =============================================================================
#[test] #[test]
fn test_large_queue() { fn test_large_queue() {

View file

@ -5,9 +5,7 @@
use rhai::Engine; use rhai::Engine;
// =============================================================================
// ABS Function Tests // ABS Function Tests
// =============================================================================
#[test] #[test]
fn test_abs_positive() { fn test_abs_positive() {
@ -46,9 +44,7 @@ fn test_abs_float() {
assert!((result - 3.14).abs() < f64::EPSILON); assert!((result - 3.14).abs() < f64::EPSILON);
} }
// =============================================================================
// ROUND Function Tests // ROUND Function Tests
// =============================================================================
#[test] #[test]
fn test_round_up() { fn test_round_up() {
@ -86,9 +82,7 @@ fn test_round_negative() {
assert_eq!(result, -4); assert_eq!(result, -4);
} }
// =============================================================================
// INT / FIX Function Tests (Truncation) // INT / FIX Function Tests (Truncation)
// =============================================================================
#[test] #[test]
fn test_int_positive() { fn test_int_positive() {
@ -117,9 +111,7 @@ fn test_fix_alias() {
assert_eq!(result, 7); assert_eq!(result, 7);
} }
// =============================================================================
// FLOOR / CEIL Function Tests // FLOOR / CEIL Function Tests
// =============================================================================
#[test] #[test]
fn test_floor_positive() { fn test_floor_positive() {
@ -157,9 +149,7 @@ fn test_ceil_negative() {
assert_eq!(result, -3); assert_eq!(result, -3);
} }
// =============================================================================
// MIN / MAX Function Tests // MIN / MAX Function Tests
// =============================================================================
#[test] #[test]
fn test_max_basic() { fn test_max_basic() {
@ -224,9 +214,7 @@ fn test_min_negative() {
assert_eq!(result, -10); assert_eq!(result, -10);
} }
// =============================================================================
// MOD Function Tests // MOD Function Tests
// =============================================================================
#[test] #[test]
fn test_mod_basic() { fn test_mod_basic() {
@ -255,9 +243,7 @@ fn test_mod_smaller_dividend() {
assert_eq!(result, 3); assert_eq!(result, 3);
} }
// =============================================================================
// SGN Function Tests // SGN Function Tests
// =============================================================================
#[test] #[test]
fn test_sgn_positive() { fn test_sgn_positive() {
@ -286,9 +272,7 @@ fn test_sgn_zero() {
assert_eq!(result, 0); assert_eq!(result, 0);
} }
// =============================================================================
// SQRT / SQR Function Tests // SQRT / SQR Function Tests
// =============================================================================
#[test] #[test]
fn test_sqrt_perfect_square() { fn test_sqrt_perfect_square() {
@ -317,9 +301,7 @@ fn test_sqr_alias() {
assert!((result - 5.0).abs() < f64::EPSILON); assert!((result - 5.0).abs() < f64::EPSILON);
} }
// =============================================================================
// POW Function Tests // POW Function Tests
// =============================================================================
#[test] #[test]
fn test_pow_basic() { fn test_pow_basic() {
@ -348,9 +330,7 @@ fn test_pow_square_root() {
assert!((result - 3.0).abs() < 0.00001); assert!((result - 3.0).abs() < 0.00001);
} }
// =============================================================================
// LOG / LOG10 / EXP Function Tests // LOG / LOG10 / EXP Function Tests
// =============================================================================
#[test] #[test]
fn test_log_e() { fn test_log_e() {
@ -389,9 +369,7 @@ fn test_exp_one() {
assert!((result - std::f64::consts::E).abs() < 0.00001); assert!((result - std::f64::consts::E).abs() < 0.00001);
} }
// =============================================================================
// Trigonometric Function Tests // Trigonometric Function Tests
// =============================================================================
#[test] #[test]
fn test_sin_zero() { fn test_sin_zero() {
@ -429,9 +407,7 @@ fn test_pi_constant() {
assert!((result - std::f64::consts::PI).abs() < f64::EPSILON); assert!((result - std::f64::consts::PI).abs() < f64::EPSILON);
} }
// =============================================================================
// VAL Function Tests (String to Number) // VAL Function Tests (String to Number)
// =============================================================================
#[test] #[test]
fn test_val_integer() { fn test_val_integer() {
@ -488,9 +464,7 @@ fn test_val_with_whitespace() {
assert!((result - 42.0).abs() < f64::EPSILON); assert!((result - 42.0).abs() < f64::EPSILON);
} }
// =============================================================================
// Combined Math Expression Tests // Combined Math Expression Tests
// =============================================================================
#[test] #[test]
fn test_combined_abs_sqrt() { fn test_combined_abs_sqrt() {

View file

@ -8,9 +8,7 @@
use rhai::Engine; use rhai::Engine;
// =============================================================================
// INSTR Function Tests - Testing the actual behavior // INSTR Function Tests - Testing the actual behavior
// =============================================================================
#[test] #[test]
fn test_instr_finds_substring() { fn test_instr_finds_substring() {
@ -67,9 +65,7 @@ fn test_instr_case_sensitive() {
assert_eq!(result, 0); // Case sensitive, so not found assert_eq!(result, 0); // Case sensitive, so not found
} }
// =============================================================================
// UPPER / UCASE Function Tests // UPPER / UCASE Function Tests
// =============================================================================
#[test] #[test]
fn test_upper_basic() { fn test_upper_basic() {
@ -98,9 +94,7 @@ fn test_ucase_alias() {
assert_eq!(result, "TEST"); assert_eq!(result, "TEST");
} }
// =============================================================================
// LOWER / LCASE Function Tests // LOWER / LCASE Function Tests
// =============================================================================
#[test] #[test]
fn test_lower_basic() { fn test_lower_basic() {
@ -120,9 +114,7 @@ fn test_lcase_alias() {
assert_eq!(result, "test"); assert_eq!(result, "test");
} }
// =============================================================================
// LEN Function Tests // LEN Function Tests
// =============================================================================
#[test] #[test]
fn test_len_basic() { fn test_len_basic() {
@ -151,9 +143,7 @@ fn test_len_with_spaces() {
assert_eq!(result, 11); assert_eq!(result, 11);
} }
// =============================================================================
// TRIM / LTRIM / RTRIM Function Tests // TRIM / LTRIM / RTRIM Function Tests
// =============================================================================
#[test] #[test]
fn test_trim_both_sides() { fn test_trim_both_sides() {
@ -182,9 +172,7 @@ fn test_rtrim() {
assert_eq!(result, " hello"); assert_eq!(result, " hello");
} }
// =============================================================================
// LEFT Function Tests // LEFT Function Tests
// =============================================================================
#[test] #[test]
fn test_left_basic() { fn test_left_basic() {
@ -222,9 +210,7 @@ fn test_left_zero() {
assert_eq!(result, ""); assert_eq!(result, "");
} }
// =============================================================================
// RIGHT Function Tests // RIGHT Function Tests
// =============================================================================
#[test] #[test]
fn test_right_basic() { fn test_right_basic() {
@ -260,9 +246,7 @@ fn test_right_exceeds_length() {
assert_eq!(result, "Hi"); assert_eq!(result, "Hi");
} }
// =============================================================================
// MID Function Tests // MID Function Tests
// =============================================================================
#[test] #[test]
fn test_mid_with_length() { fn test_mid_with_length() {
@ -294,9 +278,7 @@ fn test_mid_one_based_index() {
assert_eq!(result, "C"); assert_eq!(result, "C");
} }
// =============================================================================
// REPLACE Function Tests // REPLACE Function Tests
// =============================================================================
#[test] #[test]
fn test_replace_basic() { fn test_replace_basic() {
@ -333,9 +315,7 @@ fn test_replace_not_found() {
assert_eq!(result, "Hello"); assert_eq!(result, "Hello");
} }
// =============================================================================
// IS_NUMERIC Function Tests // IS_NUMERIC Function Tests
// =============================================================================
#[test] #[test]
fn test_is_numeric_integer() { fn test_is_numeric_integer() {
@ -397,9 +377,7 @@ fn test_is_numeric_empty() {
assert!(!result); assert!(!result);
} }
// =============================================================================
// Combined Expression Tests // Combined Expression Tests
// =============================================================================
#[test] #[test]
fn test_combined_string_operations() { fn test_combined_string_operations() {