feat: Add Redis client module and tests
- Add a new Redis client module to the SAL library. - Implement Rhai wrappers for Redis connection and operations. - Add comprehensive test suite for the Redis client module. - Update documentation to include Redis client module details. - Add .gitignore entries to exclude test logs and files.
This commit is contained in:
parent
32217b6545
commit
1286939608
1
.gitignore
vendored
1
.gitignore
vendored
@ -21,3 +21,4 @@ Cargo.lock
|
|||||||
/target
|
/target
|
||||||
/rhai_test_download
|
/rhai_test_download
|
||||||
/rhai_test_fs
|
/rhai_test_fs
|
||||||
|
run_rhai_tests.log
|
@ -33,6 +33,7 @@ SAL includes test scripts for verifying the functionality of its Rhai integratio
|
|||||||
- [OS Module Tests](os_module_tests.md): Tests for file system, download, and package management operations
|
- [OS Module Tests](os_module_tests.md): Tests for file system, download, and package management operations
|
||||||
- [Git Module Tests](git_module_tests.md): Tests for Git repository management and operations
|
- [Git Module Tests](git_module_tests.md): Tests for Git repository management and operations
|
||||||
- [Process Module Tests](process_module_tests.md): Tests for command execution and process management
|
- [Process Module Tests](process_module_tests.md): Tests for command execution and process management
|
||||||
|
- [Redis Client Module Tests](redisclient_module_tests.md): Tests for Redis connection and operations
|
||||||
- [Running Tests](running_tests.md): Instructions for running all Rhai tests
|
- [Running Tests](running_tests.md): Instructions for running all Rhai tests
|
||||||
- [CI Workflow](ci_workflow.md): Continuous integration workflow for Rhai tests
|
- [CI Workflow](ci_workflow.md): Continuous integration workflow for Rhai tests
|
||||||
|
|
||||||
|
97
docs/rhai/redisclient_module_tests.md
Normal file
97
docs/rhai/redisclient_module_tests.md
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
# Redis Client Module Tests
|
||||||
|
|
||||||
|
This document describes the test scripts for the Redis client module in the SAL library. These tests verify the functionality of the Redis client module's connection management and Redis operations.
|
||||||
|
|
||||||
|
## Test Structure
|
||||||
|
|
||||||
|
The tests are organized into two main scripts:
|
||||||
|
|
||||||
|
1. **Redis Connection** (`01_redis_connection.rhai`): Tests basic Redis connection and simple operations like PING, SET, GET, and DEL.
|
||||||
|
2. **Redis Operations** (`02_redis_operations.rhai`): Tests more advanced Redis operations like hash operations (HSET, HGET, HGETALL, HDEL) and list operations (RPUSH, LLEN, LRANGE).
|
||||||
|
|
||||||
|
Additionally, there's a runner script (`run_all_tests.rhai`) that executes all tests and reports results. The runner script contains simplified versions of the individual tests to avoid dependency issues.
|
||||||
|
|
||||||
|
## Running the Tests
|
||||||
|
|
||||||
|
To run all tests, execute the following command from the project root:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
herodo --path src/rhai_tests/redisclient/run_all_tests.rhai
|
||||||
|
```
|
||||||
|
|
||||||
|
To run individual test scripts:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
herodo --path src/rhai_tests/redisclient/01_redis_connection.rhai
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test Details
|
||||||
|
|
||||||
|
### Redis Connection Test
|
||||||
|
|
||||||
|
The Redis connection test (`01_redis_connection.rhai`) verifies the following functions:
|
||||||
|
|
||||||
|
- `redis_ping`: Checking if the Redis server is available
|
||||||
|
- `redis_set`: Setting a key-value pair
|
||||||
|
- `redis_get`: Getting a value by key
|
||||||
|
- `redis_del`: Deleting a key
|
||||||
|
|
||||||
|
The test creates a temporary key, performs operations on it, and then cleans up after itself.
|
||||||
|
|
||||||
|
### Redis Operations Test
|
||||||
|
|
||||||
|
The Redis operations test (`02_redis_operations.rhai`) verifies the following functions:
|
||||||
|
|
||||||
|
- Hash operations:
|
||||||
|
- `redis_hset`: Setting a field in a hash
|
||||||
|
- `redis_hget`: Getting a field from a hash
|
||||||
|
- `redis_hgetall`: Getting all fields and values from a hash
|
||||||
|
- `redis_hdel`: Deleting a field from a hash
|
||||||
|
|
||||||
|
- List operations:
|
||||||
|
- `redis_rpush`: Adding elements to a list
|
||||||
|
- `redis_llen`: Getting the length of a list
|
||||||
|
- `redis_lrange`: Getting a range of elements from a list
|
||||||
|
|
||||||
|
The test creates temporary keys with a unique prefix, performs operations on them, and then cleans up after itself.
|
||||||
|
|
||||||
|
## Test Runner
|
||||||
|
|
||||||
|
The test runner script (`run_all_tests.rhai`) provides a framework for executing all tests and reporting results. It:
|
||||||
|
|
||||||
|
1. Checks if Redis is available before running tests
|
||||||
|
2. Skips tests if Redis is not available
|
||||||
|
3. Contains simplified versions of each test
|
||||||
|
4. Runs each test in a try/catch block to handle errors
|
||||||
|
5. Catches and reports any errors
|
||||||
|
6. Provides a summary of passed, failed, and skipped tests
|
||||||
|
|
||||||
|
## Redis Server Requirements
|
||||||
|
|
||||||
|
These tests require a Redis server to be running and accessible. The tests will attempt to connect to Redis using the following strategy:
|
||||||
|
|
||||||
|
1. First, try to connect via Unix socket at `$HOME/hero/var/myredis.sock`
|
||||||
|
2. If that fails, try to connect via TCP to `127.0.0.1` on the default Redis port (6379)
|
||||||
|
|
||||||
|
If no Redis server is available, the tests will be skipped rather than failing.
|
||||||
|
|
||||||
|
## Adding New Tests
|
||||||
|
|
||||||
|
To add a new test:
|
||||||
|
|
||||||
|
1. Create a new Rhai script in the `src/rhai_tests/redisclient` directory
|
||||||
|
2. Add a new test section to the `run_all_tests.rhai` script
|
||||||
|
3. Update this documentation to include information about the new test
|
||||||
|
|
||||||
|
## Best Practices for Writing Tests
|
||||||
|
|
||||||
|
When writing tests for the Redis client module:
|
||||||
|
|
||||||
|
1. Always check if Redis is available before running tests
|
||||||
|
2. Use a unique prefix for test keys to avoid conflicts
|
||||||
|
3. Clean up any keys created during testing
|
||||||
|
4. Use assertions to verify expected behavior
|
||||||
|
5. Print clear messages about what's being tested
|
||||||
|
6. Handle errors gracefully
|
||||||
|
7. Make tests independent of each other
|
||||||
|
8. Keep tests focused on specific functionality
|
@ -1,131 +0,0 @@
|
|||||||
[0;34m=======================================[0m
|
|
||||||
[0;34m Running All Rhai Tests [0m
|
|
||||||
[0;34m=======================================[0m
|
|
||||||
|
|
||||||
[0;33mRunning tests for module: git[0m
|
|
||||||
[0;33m-------------------------------------[0m
|
|
||||||
Found 1 Rhai script to execute:
|
|
||||||
|
|
||||||
Executing: src/rhai_tests/git/run_all_tests.rhai
|
|
||||||
=== Running Git Module Tests ===
|
|
||||||
|
|
||||||
--- Running Basic Git Operations Tests ---
|
|
||||||
Created test directory: rhai_test_git
|
|
||||||
Testing GitTree constructor...
|
|
||||||
✓ GitTree created successfully
|
|
||||||
Testing GitTree.list() with empty directory...
|
|
||||||
✓ GitTree.list(): Found 0 repositories (expected 0)
|
|
||||||
Testing GitTree.find() with empty directory...
|
|
||||||
✓ GitTree.find(): Found 0 repositories (expected 0)
|
|
||||||
Cleaning up...
|
|
||||||
✓ Cleanup: Directory rhai_test_git removed
|
|
||||||
--- Basic Git Operations Tests completed successfully ---
|
|
||||||
|
|
||||||
--- Running Git Repository Operations Tests ---
|
|
||||||
Created test directory: rhai_test_git_ops
|
|
||||||
Creating GitTree...
|
|
||||||
✓ GitTree created successfully
|
|
||||||
Cleaning up...
|
|
||||||
✓ Cleanup: Directory rhai_test_git_ops removed
|
|
||||||
--- Git Repository Operations Tests completed successfully ---
|
|
||||||
|
|
||||||
=== Test Summary ===
|
|
||||||
Passed: 2
|
|
||||||
Failed: 0
|
|
||||||
Total: 2
|
|
||||||
|
|
||||||
✅ All tests passed!
|
|
||||||
Script executed successfully
|
|
||||||
Result: 0
|
|
||||||
|
|
||||||
All scripts executed
|
|
||||||
[0;32m✓ Module git tests passed[0m
|
|
||||||
|
|
||||||
[0;33mRunning tests for module: os[0m
|
|
||||||
[0;33m-------------------------------------[0m
|
|
||||||
Found 1 Rhai script to execute:
|
|
||||||
|
|
||||||
Executing: src/rhai_tests/os/run_all_tests.rhai
|
|
||||||
=== Running OS Module Tests ===
|
|
||||||
|
|
||||||
--- Running File Operations Tests ---
|
|
||||||
Testing mkdir...
|
|
||||||
✓ mkdir: Successfully created directory 'rhai_test_fs'
|
|
||||||
✓ mkdir (nested): Successfully created directory 'rhai_test_fs/subdir'
|
|
||||||
✓ file_write: Successfully wrote to file 'rhai_test_fs/test.txt'
|
|
||||||
✓ file_read: Content matches
|
|
||||||
✓ file_size: 48 bytes
|
|
||||||
✓ delete: Directory cleaned up
|
|
||||||
--- File Operations Tests completed successfully ---
|
|
||||||
|
|
||||||
--- Running Download Operations Tests ---
|
|
||||||
Created test directory: rhai_test_download
|
|
||||||
✓ which: curl found at /usr/bin/curl
|
|
||||||
✓ cmd_ensure_exists: Command 'curl' exists
|
|
||||||
Downloading https://raw.githubusercontent.com/rust-lang/rust/master/LICENSE-MIT...
|
|
||||||
Downloading https://raw.githubusercontent.com/rust-lang/rust/master/LICENSE-MIT to rhai_test_download/license.txt
|
|
||||||
Download complete! File size: 1.04 KB
|
|
||||||
✓ download_file: rhai_test_download/license.txt
|
|
||||||
✓ Downloaded file content verified
|
|
||||||
✓ Cleanup: Directory rhai_test_download removed
|
|
||||||
--- Download Operations Tests completed successfully ---
|
|
||||||
|
|
||||||
--- Running Package Operations Tests ---
|
|
||||||
Current platform: Ubuntu
|
|
||||||
✓ package_set_debug: Debug mode enabled
|
|
||||||
--- Package Operations Tests completed successfully ---
|
|
||||||
|
|
||||||
=== Test Summary ===
|
|
||||||
Passed: 3
|
|
||||||
Failed: 0
|
|
||||||
Total: 3
|
|
||||||
|
|
||||||
✅ All tests passed!
|
|
||||||
Script executed successfully
|
|
||||||
Result: 0
|
|
||||||
|
|
||||||
All scripts executed
|
|
||||||
[0;32m✓ Module os tests passed[0m
|
|
||||||
|
|
||||||
[0;33mRunning tests for module: process[0m
|
|
||||||
[0;33m-------------------------------------[0m
|
|
||||||
Found 1 Rhai script to execute:
|
|
||||||
|
|
||||||
Executing: src/rhai_tests/process/run_all_tests.rhai
|
|
||||||
=== Running Process Module Tests ===
|
|
||||||
|
|
||||||
--- Running Command Execution Tests ---
|
|
||||||
Testing run() with a simple command...
|
|
||||||
Hello, World!
|
|
||||||
✓ run().execute(): Command executed successfully
|
|
||||||
Testing which() function...
|
|
||||||
✓ which(): Found bash at /usr/bin/bash
|
|
||||||
--- Command Execution Tests completed successfully ---
|
|
||||||
|
|
||||||
--- Running Process Management Tests ---
|
|
||||||
Testing process_list() function...
|
|
||||||
✓ process_list(): Found 344 processes
|
|
||||||
Testing process properties...
|
|
||||||
✓ Process properties: PID=1, Name=systemd
|
|
||||||
--- Process Management Tests completed successfully ---
|
|
||||||
|
|
||||||
=== Test Summary ===
|
|
||||||
Passed: 2
|
|
||||||
Failed: 0
|
|
||||||
Total: 2
|
|
||||||
|
|
||||||
✅ All tests passed!
|
|
||||||
Script executed successfully
|
|
||||||
Result: 0
|
|
||||||
|
|
||||||
All scripts executed
|
|
||||||
[0;32m✓ Module process tests passed[0m
|
|
||||||
|
|
||||||
[0;34m=======================================[0m
|
|
||||||
[0;34m Test Summary [0m
|
|
||||||
[0;34m=======================================[0m
|
|
||||||
Total modules tested: 3
|
|
||||||
Passed: [0;32m3[0m
|
|
||||||
Failed: [0;31m0[0m
|
|
||||||
|
|
||||||
[0;32mAll tests passed![0m
|
|
@ -9,6 +9,7 @@ mod git;
|
|||||||
mod nerdctl;
|
mod nerdctl;
|
||||||
mod os;
|
mod os;
|
||||||
mod process;
|
mod process;
|
||||||
|
mod redisclient;
|
||||||
mod rfs;
|
mod rfs;
|
||||||
mod text;
|
mod text;
|
||||||
|
|
||||||
@ -39,6 +40,9 @@ pub use os::{
|
|||||||
rsync,
|
rsync,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Re-export Redis client module registration function
|
||||||
|
pub use redisclient::register_redisclient_module;
|
||||||
|
|
||||||
pub use process::{
|
pub use process::{
|
||||||
kill,
|
kill,
|
||||||
process_get,
|
process_get,
|
||||||
@ -140,6 +144,9 @@ pub fn register(engine: &mut Engine) -> Result<(), Box<rhai::EvalAltResult>> {
|
|||||||
// Register RFS module functions
|
// Register RFS module functions
|
||||||
rfs::register(engine)?;
|
rfs::register(engine)?;
|
||||||
|
|
||||||
|
// Register Redis client module functions
|
||||||
|
redisclient::register_redisclient_module(engine)?;
|
||||||
|
|
||||||
// Future modules can be registered here
|
// Future modules can be registered here
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
323
src/rhai/redisclient.rs
Normal file
323
src/rhai/redisclient.rs
Normal file
@ -0,0 +1,323 @@
|
|||||||
|
//! Rhai wrappers for Redis client module functions
|
||||||
|
//!
|
||||||
|
//! This module provides Rhai wrappers for the functions in the Redis client module.
|
||||||
|
|
||||||
|
use crate::redisclient;
|
||||||
|
use rhai::{Engine, EvalAltResult, Map};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
/// Register Redis client module functions with the Rhai engine
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `engine` - The Rhai engine to register the functions with
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Result<(), Box<EvalAltResult>>` - Ok if registration was successful, Err otherwise
|
||||||
|
pub fn register_redisclient_module(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> {
|
||||||
|
// Register basic Redis operations
|
||||||
|
engine.register_fn("redis_ping", redis_ping);
|
||||||
|
engine.register_fn("redis_set", redis_set);
|
||||||
|
engine.register_fn("redis_get", redis_get);
|
||||||
|
engine.register_fn("redis_del", redis_del);
|
||||||
|
|
||||||
|
// Register hash operations
|
||||||
|
engine.register_fn("redis_hset", redis_hset);
|
||||||
|
engine.register_fn("redis_hget", redis_hget);
|
||||||
|
engine.register_fn("redis_hgetall", redis_hgetall);
|
||||||
|
engine.register_fn("redis_hdel", redis_hdel);
|
||||||
|
|
||||||
|
// Register list operations
|
||||||
|
engine.register_fn("redis_rpush", redis_rpush);
|
||||||
|
engine.register_fn("redis_lpush", redis_lpush);
|
||||||
|
engine.register_fn("redis_llen", redis_llen);
|
||||||
|
engine.register_fn("redis_lrange", redis_lrange);
|
||||||
|
|
||||||
|
// Register other operations
|
||||||
|
engine.register_fn("redis_reset", redis_reset);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ping the Redis server
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Result<String, Box<EvalAltResult>>` - "PONG" if successful, error otherwise
|
||||||
|
pub fn redis_ping() -> Result<String, Box<EvalAltResult>> {
|
||||||
|
let mut cmd = redis::cmd("PING");
|
||||||
|
redisclient::execute(&mut cmd).map_err(|e| {
|
||||||
|
Box::new(EvalAltResult::ErrorRuntime(
|
||||||
|
format!("Redis error: {}", e).into(),
|
||||||
|
rhai::Position::NONE,
|
||||||
|
))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set a key-value pair in Redis
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `key` - The key to set
|
||||||
|
/// * `value` - The value to set
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Result<bool, Box<EvalAltResult>>` - true if successful, error otherwise
|
||||||
|
pub fn redis_set(key: &str, value: &str) -> Result<bool, Box<EvalAltResult>> {
|
||||||
|
let mut cmd = redis::cmd("SET");
|
||||||
|
cmd.arg(key).arg(value);
|
||||||
|
let result: redis::RedisResult<String> = redisclient::execute(&mut cmd);
|
||||||
|
match result {
|
||||||
|
Ok(s) if s == "OK" => Ok(true),
|
||||||
|
Ok(_) => Ok(false),
|
||||||
|
Err(e) => Err(Box::new(EvalAltResult::ErrorRuntime(
|
||||||
|
format!("Redis error: {}", e).into(),
|
||||||
|
rhai::Position::NONE,
|
||||||
|
))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a value from Redis by key
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `key` - The key to get
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Result<String, Box<EvalAltResult>>` - The value if found, empty string if not found, error otherwise
|
||||||
|
pub fn redis_get(key: &str) -> Result<String, Box<EvalAltResult>> {
|
||||||
|
let mut cmd = redis::cmd("GET");
|
||||||
|
cmd.arg(key);
|
||||||
|
let result: redis::RedisResult<Option<String>> = redisclient::execute(&mut cmd);
|
||||||
|
match result {
|
||||||
|
Ok(Some(value)) => Ok(value),
|
||||||
|
Ok(None) => Ok(String::new()),
|
||||||
|
Err(e) => Err(Box::new(EvalAltResult::ErrorRuntime(
|
||||||
|
format!("Redis error: {}", e).into(),
|
||||||
|
rhai::Position::NONE,
|
||||||
|
))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delete a key from Redis
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `key` - The key to delete
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Result<bool, Box<EvalAltResult>>` - true if successful, error otherwise
|
||||||
|
pub fn redis_del(key: &str) -> Result<bool, Box<EvalAltResult>> {
|
||||||
|
let mut cmd = redis::cmd("DEL");
|
||||||
|
cmd.arg(key);
|
||||||
|
let result: redis::RedisResult<i64> = redisclient::execute(&mut cmd);
|
||||||
|
match result {
|
||||||
|
Ok(n) => Ok(n > 0),
|
||||||
|
Err(e) => Err(Box::new(EvalAltResult::ErrorRuntime(
|
||||||
|
format!("Redis error: {}", e).into(),
|
||||||
|
rhai::Position::NONE,
|
||||||
|
))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set a field in a hash
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `key` - The hash key
|
||||||
|
/// * `field` - The field to set
|
||||||
|
/// * `value` - The value to set
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Result<bool, Box<EvalAltResult>>` - true if successful, error otherwise
|
||||||
|
pub fn redis_hset(key: &str, field: &str, value: &str) -> Result<bool, Box<EvalAltResult>> {
|
||||||
|
let mut cmd = redis::cmd("HSET");
|
||||||
|
cmd.arg(key).arg(field).arg(value);
|
||||||
|
let result: redis::RedisResult<i64> = redisclient::execute(&mut cmd);
|
||||||
|
match result {
|
||||||
|
Ok(_) => Ok(true),
|
||||||
|
Err(e) => Err(Box::new(EvalAltResult::ErrorRuntime(
|
||||||
|
format!("Redis error: {}", e).into(),
|
||||||
|
rhai::Position::NONE,
|
||||||
|
))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a field from a hash
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `key` - The hash key
|
||||||
|
/// * `field` - The field to get
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Result<String, Box<EvalAltResult>>` - The value if found, empty string if not found, error otherwise
|
||||||
|
pub fn redis_hget(key: &str, field: &str) -> Result<String, Box<EvalAltResult>> {
|
||||||
|
let mut cmd = redis::cmd("HGET");
|
||||||
|
cmd.arg(key).arg(field);
|
||||||
|
let result: redis::RedisResult<Option<String>> = redisclient::execute(&mut cmd);
|
||||||
|
match result {
|
||||||
|
Ok(Some(value)) => Ok(value),
|
||||||
|
Ok(None) => Ok(String::new()),
|
||||||
|
Err(e) => Err(Box::new(EvalAltResult::ErrorRuntime(
|
||||||
|
format!("Redis error: {}", e).into(),
|
||||||
|
rhai::Position::NONE,
|
||||||
|
))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get all fields and values from a hash
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `key` - The hash key
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Result<Map, Box<EvalAltResult>>` - A map of field-value pairs, error otherwise
|
||||||
|
pub fn redis_hgetall(key: &str) -> Result<Map, Box<EvalAltResult>> {
|
||||||
|
let mut cmd = redis::cmd("HGETALL");
|
||||||
|
cmd.arg(key);
|
||||||
|
let result: redis::RedisResult<HashMap<String, String>> = redisclient::execute(&mut cmd);
|
||||||
|
match result {
|
||||||
|
Ok(hash_map) => {
|
||||||
|
let mut map = Map::new();
|
||||||
|
for (k, v) in hash_map {
|
||||||
|
map.insert(k.into(), v.into());
|
||||||
|
}
|
||||||
|
Ok(map)
|
||||||
|
}
|
||||||
|
Err(e) => Err(Box::new(EvalAltResult::ErrorRuntime(
|
||||||
|
format!("Redis error: {}", e).into(),
|
||||||
|
rhai::Position::NONE,
|
||||||
|
))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delete a field from a hash
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `key` - The hash key
|
||||||
|
/// * `field` - The field to delete
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Result<bool, Box<EvalAltResult>>` - true if successful, error otherwise
|
||||||
|
pub fn redis_hdel(key: &str, field: &str) -> Result<bool, Box<EvalAltResult>> {
|
||||||
|
let mut cmd = redis::cmd("HDEL");
|
||||||
|
cmd.arg(key).arg(field);
|
||||||
|
let result: redis::RedisResult<i64> = redisclient::execute(&mut cmd);
|
||||||
|
match result {
|
||||||
|
Ok(n) => Ok(n > 0),
|
||||||
|
Err(e) => Err(Box::new(EvalAltResult::ErrorRuntime(
|
||||||
|
format!("Redis error: {}", e).into(),
|
||||||
|
rhai::Position::NONE,
|
||||||
|
))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Push an element to the end of a list
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `key` - The list key
|
||||||
|
/// * `value` - The value to push
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Result<i64, Box<EvalAltResult>>` - The new length of the list, error otherwise
|
||||||
|
pub fn redis_rpush(key: &str, value: &str) -> Result<i64, Box<EvalAltResult>> {
|
||||||
|
let mut cmd = redis::cmd("RPUSH");
|
||||||
|
cmd.arg(key).arg(value);
|
||||||
|
redisclient::execute(&mut cmd).map_err(|e| {
|
||||||
|
Box::new(EvalAltResult::ErrorRuntime(
|
||||||
|
format!("Redis error: {}", e).into(),
|
||||||
|
rhai::Position::NONE,
|
||||||
|
))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Push an element to the beginning of a list
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `key` - The list key
|
||||||
|
/// * `value` - The value to push
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Result<i64, Box<EvalAltResult>>` - The new length of the list, error otherwise
|
||||||
|
pub fn redis_lpush(key: &str, value: &str) -> Result<i64, Box<EvalAltResult>> {
|
||||||
|
let mut cmd = redis::cmd("LPUSH");
|
||||||
|
cmd.arg(key).arg(value);
|
||||||
|
redisclient::execute(&mut cmd).map_err(|e| {
|
||||||
|
Box::new(EvalAltResult::ErrorRuntime(
|
||||||
|
format!("Redis error: {}", e).into(),
|
||||||
|
rhai::Position::NONE,
|
||||||
|
))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the length of a list
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `key` - The list key
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Result<i64, Box<EvalAltResult>>` - The length of the list, error otherwise
|
||||||
|
pub fn redis_llen(key: &str) -> Result<i64, Box<EvalAltResult>> {
|
||||||
|
let mut cmd = redis::cmd("LLEN");
|
||||||
|
cmd.arg(key);
|
||||||
|
redisclient::execute(&mut cmd).map_err(|e| {
|
||||||
|
Box::new(EvalAltResult::ErrorRuntime(
|
||||||
|
format!("Redis error: {}", e).into(),
|
||||||
|
rhai::Position::NONE,
|
||||||
|
))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a range of elements from a list
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `key` - The list key
|
||||||
|
/// * `start` - The start index
|
||||||
|
/// * `stop` - The stop index
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Result<Vec<String>, Box<EvalAltResult>>` - The elements in the range, error otherwise
|
||||||
|
pub fn redis_lrange(key: &str, start: i64, stop: i64) -> Result<Vec<String>, Box<EvalAltResult>> {
|
||||||
|
let mut cmd = redis::cmd("LRANGE");
|
||||||
|
cmd.arg(key).arg(start).arg(stop);
|
||||||
|
redisclient::execute(&mut cmd).map_err(|e| {
|
||||||
|
Box::new(EvalAltResult::ErrorRuntime(
|
||||||
|
format!("Redis error: {}", e).into(),
|
||||||
|
rhai::Position::NONE,
|
||||||
|
))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reset the Redis client connection
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Result<bool, Box<EvalAltResult>>` - true if successful, error otherwise
|
||||||
|
pub fn redis_reset() -> Result<bool, Box<EvalAltResult>> {
|
||||||
|
match redisclient::reset() {
|
||||||
|
Ok(_) => Ok(true),
|
||||||
|
Err(e) => Err(Box::new(EvalAltResult::ErrorRuntime(
|
||||||
|
format!("Redis error: {}", e).into(),
|
||||||
|
rhai::Position::NONE,
|
||||||
|
))),
|
||||||
|
}
|
||||||
|
}
|
68
src/rhai_tests/redisclient/01_redis_connection.rhai
Normal file
68
src/rhai_tests/redisclient/01_redis_connection.rhai
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
// 01_redis_connection.rhai
|
||||||
|
// Tests for Redis client connection and basic operations
|
||||||
|
|
||||||
|
// Custom assert function
|
||||||
|
fn assert_true(condition, message) {
|
||||||
|
if !condition {
|
||||||
|
print(`ASSERTION FAILED: ${message}`);
|
||||||
|
throw message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to check if Redis is available
|
||||||
|
fn is_redis_available() {
|
||||||
|
try {
|
||||||
|
// Try to execute a simple PING command
|
||||||
|
let ping_result = redis_ping();
|
||||||
|
return ping_result == "PONG";
|
||||||
|
} catch(err) {
|
||||||
|
print(`Redis connection error: ${err}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print("=== Testing Redis Client Connection ===");
|
||||||
|
|
||||||
|
// Check if Redis is available
|
||||||
|
let redis_available = is_redis_available();
|
||||||
|
if !redis_available {
|
||||||
|
print("Redis server is not available. Skipping Redis tests.");
|
||||||
|
// Exit gracefully without error
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
print("✓ Redis server is available");
|
||||||
|
|
||||||
|
// Test redis_ping function
|
||||||
|
print("Testing redis_ping()...");
|
||||||
|
let ping_result = redis_ping();
|
||||||
|
assert_true(ping_result == "PONG", "PING should return PONG");
|
||||||
|
print(`✓ redis_ping(): Returned ${ping_result}`);
|
||||||
|
|
||||||
|
// Test redis_set and redis_get functions
|
||||||
|
print("Testing redis_set() and redis_get()...");
|
||||||
|
let test_key = "rhai_test_key";
|
||||||
|
let test_value = "Hello from Rhai test";
|
||||||
|
|
||||||
|
// Set a value
|
||||||
|
let set_result = redis_set(test_key, test_value);
|
||||||
|
assert_true(set_result, "SET operation should succeed");
|
||||||
|
print(`✓ redis_set(): Successfully set key ${test_key}`);
|
||||||
|
|
||||||
|
// Get the value back
|
||||||
|
let get_result = redis_get(test_key);
|
||||||
|
assert_true(get_result == test_value, "GET should return the value we set");
|
||||||
|
print(`✓ redis_get(): Successfully retrieved value for key ${test_key}`);
|
||||||
|
|
||||||
|
// Test redis_del function
|
||||||
|
print("Testing redis_del()...");
|
||||||
|
let del_result = redis_del(test_key);
|
||||||
|
assert_true(del_result, "DEL operation should succeed");
|
||||||
|
print(`✓ redis_del(): Successfully deleted key ${test_key}`);
|
||||||
|
|
||||||
|
// Verify the key was deleted
|
||||||
|
let get_after_del = redis_get(test_key);
|
||||||
|
assert_true(get_after_del == "", "Key should not exist after deletion");
|
||||||
|
print("✓ Key was successfully deleted");
|
||||||
|
|
||||||
|
print("All Redis connection tests completed successfully!");
|
109
src/rhai_tests/redisclient/02_redis_operations.rhai
Normal file
109
src/rhai_tests/redisclient/02_redis_operations.rhai
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
// 02_redis_operations.rhai
|
||||||
|
// Tests for advanced Redis operations
|
||||||
|
|
||||||
|
// Custom assert function
|
||||||
|
fn assert_true(condition, message) {
|
||||||
|
if !condition {
|
||||||
|
print(`ASSERTION FAILED: ${message}`);
|
||||||
|
throw message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to check if Redis is available
|
||||||
|
fn is_redis_available() {
|
||||||
|
try {
|
||||||
|
// Try to execute a simple PING command
|
||||||
|
let ping_result = redis_ping();
|
||||||
|
return ping_result == "PONG";
|
||||||
|
} catch(err) {
|
||||||
|
print(`Redis connection error: ${err}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print("=== Testing Advanced Redis Operations ===");
|
||||||
|
|
||||||
|
// Check if Redis is available
|
||||||
|
let redis_available = is_redis_available();
|
||||||
|
if !redis_available {
|
||||||
|
print("Redis server is not available. Skipping Redis tests.");
|
||||||
|
// Exit gracefully without error
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
print("✓ Redis server is available");
|
||||||
|
|
||||||
|
// Test prefix for all keys to avoid conflicts
|
||||||
|
let prefix = "rhai_test_";
|
||||||
|
|
||||||
|
// Test redis_hset and redis_hget functions
|
||||||
|
print("Testing redis_hset() and redis_hget()...");
|
||||||
|
let hash_key = prefix + "hash";
|
||||||
|
let field1 = "field1";
|
||||||
|
let value1 = "value1";
|
||||||
|
let field2 = "field2";
|
||||||
|
let value2 = "value2";
|
||||||
|
|
||||||
|
// Set hash fields
|
||||||
|
let hset_result1 = redis_hset(hash_key, field1, value1);
|
||||||
|
assert_true(hset_result1, "HSET operation should succeed for field1");
|
||||||
|
let hset_result2 = redis_hset(hash_key, field2, value2);
|
||||||
|
assert_true(hset_result2, "HSET operation should succeed for field2");
|
||||||
|
print(`✓ redis_hset(): Successfully set fields in hash ${hash_key}`);
|
||||||
|
|
||||||
|
// Get hash fields
|
||||||
|
let hget_result1 = redis_hget(hash_key, field1);
|
||||||
|
assert_true(hget_result1 == value1, "HGET should return the value we set for field1");
|
||||||
|
let hget_result2 = redis_hget(hash_key, field2);
|
||||||
|
assert_true(hget_result2 == value2, "HGET should return the value we set for field2");
|
||||||
|
print(`✓ redis_hget(): Successfully retrieved values from hash ${hash_key}`);
|
||||||
|
|
||||||
|
// Test redis_hgetall function
|
||||||
|
print("Testing redis_hgetall()...");
|
||||||
|
let hgetall_result = redis_hgetall(hash_key);
|
||||||
|
assert_true(hgetall_result.len() == 2, "HGETALL should return 2 fields");
|
||||||
|
assert_true(hgetall_result[field1] == value1, "HGETALL should include field1 with correct value");
|
||||||
|
assert_true(hgetall_result[field2] == value2, "HGETALL should include field2 with correct value");
|
||||||
|
print(`✓ redis_hgetall(): Successfully retrieved all fields from hash ${hash_key}`);
|
||||||
|
|
||||||
|
// Test redis_hdel function
|
||||||
|
print("Testing redis_hdel()...");
|
||||||
|
let hdel_result = redis_hdel(hash_key, field1);
|
||||||
|
assert_true(hdel_result, "HDEL operation should succeed");
|
||||||
|
print(`✓ redis_hdel(): Successfully deleted field from hash ${hash_key}`);
|
||||||
|
|
||||||
|
// Verify the field was deleted
|
||||||
|
let hget_after_del = redis_hget(hash_key, field1);
|
||||||
|
assert_true(hget_after_del == "", "Field should not exist after deletion");
|
||||||
|
print("✓ Field was successfully deleted from hash");
|
||||||
|
|
||||||
|
// Test redis_list operations
|
||||||
|
print("Testing redis list operations...");
|
||||||
|
let list_key = prefix + "list";
|
||||||
|
|
||||||
|
// Push items to list
|
||||||
|
let rpush_result = redis_rpush(list_key, "item1");
|
||||||
|
assert_true(rpush_result > 0, "RPUSH operation should succeed");
|
||||||
|
redis_rpush(list_key, "item2");
|
||||||
|
redis_rpush(list_key, "item3");
|
||||||
|
print(`✓ redis_rpush(): Successfully pushed items to list ${list_key}`);
|
||||||
|
|
||||||
|
// Get list length
|
||||||
|
let llen_result = redis_llen(list_key);
|
||||||
|
assert_true(llen_result == 3, "List should have 3 items");
|
||||||
|
print(`✓ redis_llen(): List has ${llen_result} items`);
|
||||||
|
|
||||||
|
// Get list range
|
||||||
|
let lrange_result = redis_lrange(list_key, 0, -1);
|
||||||
|
assert_true(lrange_result.len() == 3, "LRANGE should return 3 items");
|
||||||
|
assert_true(lrange_result[0] == "item1", "First item should be 'item1'");
|
||||||
|
assert_true(lrange_result[2] == "item3", "Last item should be 'item3'");
|
||||||
|
print(`✓ redis_lrange(): Successfully retrieved all items from list ${list_key}`);
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
print("Cleaning up...");
|
||||||
|
redis_del(hash_key);
|
||||||
|
redis_del(list_key);
|
||||||
|
print("✓ Cleanup: All test keys removed");
|
||||||
|
|
||||||
|
print("All Redis operations tests completed successfully!");
|
121
src/rhai_tests/redisclient/run_all_tests.rhai
Normal file
121
src/rhai_tests/redisclient/run_all_tests.rhai
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
// run_all_tests.rhai
|
||||||
|
// Runs all Redis client module tests
|
||||||
|
|
||||||
|
print("=== Running Redis Client Module Tests ===");
|
||||||
|
|
||||||
|
// Custom assert function
|
||||||
|
fn assert_true(condition, message) {
|
||||||
|
if !condition {
|
||||||
|
print(`ASSERTION FAILED: ${message}`);
|
||||||
|
throw message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to check if Redis is available
|
||||||
|
fn is_redis_available() {
|
||||||
|
try {
|
||||||
|
// Try to execute a simple PING command
|
||||||
|
let ping_result = redis_ping();
|
||||||
|
return ping_result == "PONG";
|
||||||
|
} catch(err) {
|
||||||
|
print(`Redis connection error: ${err}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run each test directly
|
||||||
|
let passed = 0;
|
||||||
|
let failed = 0;
|
||||||
|
let skipped = 0;
|
||||||
|
|
||||||
|
// Check if Redis is available
|
||||||
|
let redis_available = is_redis_available();
|
||||||
|
if !redis_available {
|
||||||
|
print("Redis server is not available. Skipping all Redis tests.");
|
||||||
|
skipped = 2; // Skip both tests
|
||||||
|
} else {
|
||||||
|
// Test 1: Redis Connection
|
||||||
|
print("\n--- Running Redis Connection Tests ---");
|
||||||
|
try {
|
||||||
|
// Test redis_ping function
|
||||||
|
print("Testing redis_ping()...");
|
||||||
|
let ping_result = redis_ping();
|
||||||
|
assert_true(ping_result == "PONG", "PING should return PONG");
|
||||||
|
print(`✓ redis_ping(): Returned ${ping_result}`);
|
||||||
|
|
||||||
|
// Test redis_set and redis_get functions
|
||||||
|
print("Testing redis_set() and redis_get()...");
|
||||||
|
let test_key = "rhai_test_key";
|
||||||
|
let test_value = "Hello from Rhai test";
|
||||||
|
|
||||||
|
// Set a value
|
||||||
|
let set_result = redis_set(test_key, test_value);
|
||||||
|
assert_true(set_result, "SET operation should succeed");
|
||||||
|
print(`✓ redis_set(): Successfully set key ${test_key}`);
|
||||||
|
|
||||||
|
// Get the value back
|
||||||
|
let get_result = redis_get(test_key);
|
||||||
|
assert_true(get_result == test_value, "GET should return the value we set");
|
||||||
|
print(`✓ redis_get(): Successfully retrieved value for key ${test_key}`);
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
redis_del(test_key);
|
||||||
|
|
||||||
|
print("--- Redis Connection Tests completed successfully ---");
|
||||||
|
passed += 1;
|
||||||
|
} catch(err) {
|
||||||
|
print(`!!! Error in Redis Connection Tests: ${err}`);
|
||||||
|
failed += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 2: Redis Operations
|
||||||
|
print("\n--- Running Redis Operations Tests ---");
|
||||||
|
try {
|
||||||
|
// Test prefix for all keys to avoid conflicts
|
||||||
|
let prefix = "rhai_test_";
|
||||||
|
|
||||||
|
// Test redis_hset and redis_hget functions
|
||||||
|
print("Testing redis_hset() and redis_hget()...");
|
||||||
|
let hash_key = prefix + "hash";
|
||||||
|
let field = "field1";
|
||||||
|
let value = "value1";
|
||||||
|
|
||||||
|
// Set hash field
|
||||||
|
let hset_result = redis_hset(hash_key, field, value);
|
||||||
|
assert_true(hset_result, "HSET operation should succeed");
|
||||||
|
print(`✓ redis_hset(): Successfully set field in hash ${hash_key}`);
|
||||||
|
|
||||||
|
// Get hash field
|
||||||
|
let hget_result = redis_hget(hash_key, field);
|
||||||
|
assert_true(hget_result == value, "HGET should return the value we set");
|
||||||
|
print(`✓ redis_hget(): Successfully retrieved value from hash ${hash_key}`);
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
redis_del(hash_key);
|
||||||
|
|
||||||
|
print("--- Redis Operations Tests completed successfully ---");
|
||||||
|
passed += 1;
|
||||||
|
} catch(err) {
|
||||||
|
print(`!!! Error in Redis Operations Tests: ${err}`);
|
||||||
|
failed += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print("\n=== Test Summary ===");
|
||||||
|
print(`Passed: ${passed}`);
|
||||||
|
print(`Failed: ${failed}`);
|
||||||
|
print(`Skipped: ${skipped}`);
|
||||||
|
print(`Total: ${passed + failed + skipped}`);
|
||||||
|
|
||||||
|
if failed == 0 {
|
||||||
|
if skipped > 0 {
|
||||||
|
print("\n⚠️ All tests skipped or passed!");
|
||||||
|
} else {
|
||||||
|
print("\n✅ All tests passed!");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print("\n❌ Some tests failed!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the number of failed tests (0 means success)
|
||||||
|
failed;
|
Loading…
Reference in New Issue
Block a user