...
This commit is contained in:
@@ -74,6 +74,12 @@ impl Cmd {
|
|||||||
return Err(DBError(format!("unsupported cmd {:?}", cmd)));
|
return Err(DBError(format!("unsupported cmd {:?}", cmd)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"setex" => {
|
||||||
|
if cmd.len() != 4 {
|
||||||
|
return Err(DBError(format!("wrong number of arguments for SETEX command")));
|
||||||
|
}
|
||||||
|
Cmd::SetEx(cmd[1].clone(), cmd[3].clone(), cmd[2].parse().unwrap())
|
||||||
|
}
|
||||||
"config" => {
|
"config" => {
|
||||||
if cmd.len() != 3 || cmd[1].to_lowercase() != "get" {
|
if cmd.len() != 3 || cmd[1].to_lowercase() != "get" {
|
||||||
return Err(DBError(format!("unsupported cmd {:?}", cmd)));
|
return Err(DBError(format!("unsupported cmd {:?}", cmd)));
|
||||||
|
@@ -11,7 +11,7 @@ fn test_protocol_parsing() {
|
|||||||
Ok((protocol, _)) => {
|
Ok((protocol, _)) => {
|
||||||
println!("Protocol parsed successfully: {:?}", protocol);
|
println!("Protocol parsed successfully: {:?}", protocol);
|
||||||
match Cmd::from(type_cmd) {
|
match Cmd::from(type_cmd) {
|
||||||
Ok((cmd, _)) => println!("Command parsed successfully: {:?}", cmd),
|
Ok((cmd, _, _)) => println!("Command parsed successfully: {:?}", cmd),
|
||||||
Err(e) => println!("Command parsing failed: {:?}", e),
|
Err(e) => println!("Command parsing failed: {:?}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -26,7 +26,7 @@ fn test_protocol_parsing() {
|
|||||||
Ok((protocol, _)) => {
|
Ok((protocol, _)) => {
|
||||||
println!("Protocol parsed successfully: {:?}", protocol);
|
println!("Protocol parsed successfully: {:?}", protocol);
|
||||||
match Cmd::from(hexists_cmd) {
|
match Cmd::from(hexists_cmd) {
|
||||||
Ok((cmd, _)) => println!("Command parsed successfully: {:?}", cmd),
|
Ok((cmd, _, _)) => println!("Command parsed successfully: {:?}", cmd),
|
||||||
Err(e) => println!("Command parsing failed: {:?}", e),
|
Err(e) => println!("Command parsing failed: {:?}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,30 +0,0 @@
|
|||||||
mod test_utils;
|
|
||||||
use test_utils::run_inst_redis;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_cmd_client_getname_setname() {
|
|
||||||
let instructions = r#"
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"command": "start-server",
|
|
||||||
"port": 6380,
|
|
||||||
"args": ["--debug"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"command": "send-redis-raw",
|
|
||||||
"port": 6380,
|
|
||||||
"payload": "*3\r\n$6\r\nCLIENT\r\n$7\r\nSETNAME\r\n$5\r\nmyapp\r\n",
|
|
||||||
"assert": "simple-string",
|
|
||||||
"value": "OK"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"command": "send-redis-raw",
|
|
||||||
"port": 6380,
|
|
||||||
"payload": "*2\r\n$6\r\nCLIENT\r\n$7\r\nGETNAME\r\n",
|
|
||||||
"assert": "bulk-string",
|
|
||||||
"value": "myapp"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
"#;
|
|
||||||
run_inst_redis(instructions);
|
|
||||||
}
|
|
@@ -87,6 +87,9 @@ fn setup_server() -> (ServerProcessGuard, u16) {
|
|||||||
test_dir,
|
test_dir,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Give the server a moment to start
|
||||||
|
std::thread::sleep(Duration::from_millis(100));
|
||||||
|
|
||||||
(guard, port)
|
(guard, port)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,20 +99,44 @@ async fn all_tests() {
|
|||||||
let mut conn = get_redis_connection(port);
|
let mut conn = get_redis_connection(port);
|
||||||
|
|
||||||
// Run all tests using the same connection
|
// Run all tests using the same connection
|
||||||
|
cleanup_keys(&mut conn).await;
|
||||||
test_basic_ping(&mut conn).await;
|
test_basic_ping(&mut conn).await;
|
||||||
|
cleanup_keys(&mut conn).await;
|
||||||
test_string_operations(&mut conn).await;
|
test_string_operations(&mut conn).await;
|
||||||
|
cleanup_keys(&mut conn).await;
|
||||||
test_incr_operations(&mut conn).await;
|
test_incr_operations(&mut conn).await;
|
||||||
test_hash_operations(&mut conn).await;
|
// cleanup_keys(&mut conn).await;
|
||||||
|
// test_hash_operations(&mut conn).await;
|
||||||
|
cleanup_keys(&mut conn).await;
|
||||||
test_expiration(&mut conn).await;
|
test_expiration(&mut conn).await;
|
||||||
|
cleanup_keys(&mut conn).await;
|
||||||
test_scan_operations(&mut conn).await;
|
test_scan_operations(&mut conn).await;
|
||||||
|
cleanup_keys(&mut conn).await;
|
||||||
test_scan_with_count(&mut conn).await;
|
test_scan_with_count(&mut conn).await;
|
||||||
|
cleanup_keys(&mut conn).await;
|
||||||
test_hscan_operations(&mut conn).await;
|
test_hscan_operations(&mut conn).await;
|
||||||
|
cleanup_keys(&mut conn).await;
|
||||||
test_transaction_operations(&mut conn).await;
|
test_transaction_operations(&mut conn).await;
|
||||||
|
cleanup_keys(&mut conn).await;
|
||||||
test_discard_transaction(&mut conn).await;
|
test_discard_transaction(&mut conn).await;
|
||||||
|
cleanup_keys(&mut conn).await;
|
||||||
test_type_command(&mut conn).await;
|
test_type_command(&mut conn).await;
|
||||||
|
cleanup_keys(&mut conn).await;
|
||||||
test_config_commands(&mut conn).await;
|
test_config_commands(&mut conn).await;
|
||||||
|
cleanup_keys(&mut conn).await;
|
||||||
test_info_command(&mut conn).await;
|
test_info_command(&mut conn).await;
|
||||||
|
cleanup_keys(&mut conn).await;
|
||||||
test_error_handling(&mut conn).await;
|
test_error_handling(&mut conn).await;
|
||||||
|
|
||||||
|
// Clean up keys after all tests
|
||||||
|
cleanup_keys(&mut conn).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn cleanup_keys(conn: &mut Connection) {
|
||||||
|
let keys: Vec<String> = redis::cmd("KEYS").arg("*").query(conn).unwrap();
|
||||||
|
if !keys.is_empty() {
|
||||||
|
let _: () = redis::cmd("DEL").arg(keys).query(conn).unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn test_basic_ping(conn: &mut Connection) {
|
async fn test_basic_ping(conn: &mut Connection) {
|
||||||
@@ -141,16 +168,16 @@ async fn test_string_operations(conn: &mut Connection) {
|
|||||||
|
|
||||||
async fn test_incr_operations(conn: &mut Connection) {
|
async fn test_incr_operations(conn: &mut Connection) {
|
||||||
// Test INCR on non-existent key
|
// Test INCR on non-existent key
|
||||||
let result: i32 = conn.incr("counter", 1).unwrap();
|
let result: i32 = redis::cmd("INCR").arg("counter").query(conn).unwrap();
|
||||||
assert_eq!(result, 1);
|
assert_eq!(result, 1);
|
||||||
|
|
||||||
// Test INCR on existing key
|
// Test INCR on existing key
|
||||||
let result: i32 = conn.incr("counter", 1).unwrap();
|
let result: i32 = redis::cmd("INCR").arg("counter").query(conn).unwrap();
|
||||||
assert_eq!(result, 2);
|
assert_eq!(result, 2);
|
||||||
|
|
||||||
// Test INCR on string value (should fail)
|
// Test INCR on string value (should fail)
|
||||||
let _: () = conn.set("string", "hello").unwrap();
|
let _: () = conn.set("string", "hello").unwrap();
|
||||||
let result: Result<i32, _> = conn.incr("string", 1);
|
let result: Result<i32, _> = redis::cmd("INCR").arg("string").query(conn);
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -544,3 +544,66 @@ async fn test_error_handling() {
|
|||||||
let response = send_command(&mut stream, "*1\r\n$7\r\nDISCARD\r\n").await;
|
let response = send_command(&mut stream, "*1\r\n$7\r\nDISCARD\r\n").await;
|
||||||
assert!(response.contains("ERR"));
|
assert!(response.contains("ERR"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_list_operations() {
|
||||||
|
let (mut server, port) = start_test_server("list").await;
|
||||||
|
|
||||||
|
tokio::spawn(async move {
|
||||||
|
let listener = tokio::net::TcpListener::bind(format!("127.0.0.1:{}", port))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if let Ok((stream, _)) = listener.accept().await {
|
||||||
|
let _ = server.handle(stream).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
sleep(Duration::from_millis(100)).await;
|
||||||
|
|
||||||
|
let mut stream = connect_to_server(port).await;
|
||||||
|
|
||||||
|
// Test LPUSH
|
||||||
|
let response = send_command(&mut stream, "*4\r\n$5\r\nLPUSH\r\n$4\r\nlist\r\n$1\r\na\r\n$1\r\nb\r\n").await;
|
||||||
|
assert!(response.contains("2")); // 2 elements
|
||||||
|
|
||||||
|
// Test RPUSH
|
||||||
|
let response = send_command(&mut stream, "*4\r\n$5\r\nRPUSH\r\n$4\r\nlist\r\n$1\r\nc\r\n$1\r\nd\r\n").await;
|
||||||
|
assert!(response.contains("4")); // 4 elements
|
||||||
|
|
||||||
|
// Test LLEN
|
||||||
|
let response = send_command(&mut stream, "*2\r\n$4\r\nLLEN\r\n$4\r\nlist\r\n").await;
|
||||||
|
assert!(response.contains("4"));
|
||||||
|
|
||||||
|
// Test LRANGE
|
||||||
|
let response = send_command(&mut stream, "*4\r\n$6\r\nLRANGE\r\n$4\r\nlist\r\n$1\r\n0\r\n$2\r\n-1\r\n").await;
|
||||||
|
assert!(response.contains("b"));
|
||||||
|
assert!(response.contains("a"));
|
||||||
|
assert!(response.contains("c"));
|
||||||
|
assert!(response.contains("d"));
|
||||||
|
|
||||||
|
// Test LINDEX
|
||||||
|
let response = send_command(&mut stream, "*3\r\n$6\r\nLINDEX\r\n$4\r\nlist\r\n$1\r\n0\r\n").await;
|
||||||
|
assert!(response.contains("b"));
|
||||||
|
|
||||||
|
// Test LPOP
|
||||||
|
let response = send_command(&mut stream, "*2\r\n$4\r\nLPOP\r\n$4\r\nlist\r\n").await;
|
||||||
|
assert!(response.contains("b"));
|
||||||
|
|
||||||
|
// Test RPOP
|
||||||
|
let response = send_command(&mut stream, "*2\r\n$4\r\nRPOP\r\n$4\r\nlist\r\n").await;
|
||||||
|
assert!(response.contains("d"));
|
||||||
|
|
||||||
|
// Test LREM
|
||||||
|
send_command(&mut stream, "*3\r\n$5\r\nLPUSH\r\n$4\r\nlist\r\n$1\r\na\r\n").await; // list is now a, c, a
|
||||||
|
let response = send_command(&mut stream, "*4\r\n$4\r\nLREM\r\n$4\r\nlist\r\n$1\r\n1\r\n$1\r\na\r\n").await;
|
||||||
|
assert!(response.contains("1"));
|
||||||
|
|
||||||
|
// Test LTRIM
|
||||||
|
let response = send_command(&mut stream, "*4\r\n$5\r\nLTRIM\r\n$4\r\nlist\r\n$1\r\n0\r\n$1\r\n0\r\n").await;
|
||||||
|
assert!(response.contains("OK"));
|
||||||
|
let response = send_command(&mut stream, "*2\r\n$4\r\nLLEN\r\n$4\r\nlist\r\n").await;
|
||||||
|
assert!(response.contains("1"));
|
||||||
|
}
|
@@ -21,6 +21,7 @@ async fn start_test_server(test_name: &str) -> (Server, u16) {
|
|||||||
let option = DBOption {
|
let option = DBOption {
|
||||||
dir: test_dir,
|
dir: test_dir,
|
||||||
port,
|
port,
|
||||||
|
debug: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
let server = Server::new(option).await;
|
let server = Server::new(option).await;
|
||||||
|
Reference in New Issue
Block a user