This commit is contained in:
2025-08-16 08:50:28 +02:00
parent bec9b20ec7
commit 0511dddd99
6 changed files with 103 additions and 36 deletions

View File

@@ -74,6 +74,12 @@ impl 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" => {
if cmd.len() != 3 || cmd[1].to_lowercase() != "get" {
return Err(DBError(format!("unsupported cmd {:?}", cmd)));

View File

@@ -11,7 +11,7 @@ fn test_protocol_parsing() {
Ok((protocol, _)) => {
println!("Protocol parsed successfully: {:?}", protocol);
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),
}
}
@@ -26,7 +26,7 @@ fn test_protocol_parsing() {
Ok((protocol, _)) => {
println!("Protocol parsed successfully: {:?}", protocol);
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),
}
}

View File

@@ -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);
}

View File

@@ -87,6 +87,9 @@ fn setup_server() -> (ServerProcessGuard, u16) {
test_dir,
};
// Give the server a moment to start
std::thread::sleep(Duration::from_millis(100));
(guard, port)
}
@@ -96,20 +99,44 @@ async fn all_tests() {
let mut conn = get_redis_connection(port);
// Run all tests using the same connection
cleanup_keys(&mut conn).await;
test_basic_ping(&mut conn).await;
cleanup_keys(&mut conn).await;
test_string_operations(&mut conn).await;
cleanup_keys(&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;
cleanup_keys(&mut conn).await;
test_scan_operations(&mut conn).await;
cleanup_keys(&mut conn).await;
test_scan_with_count(&mut conn).await;
cleanup_keys(&mut conn).await;
test_hscan_operations(&mut conn).await;
cleanup_keys(&mut conn).await;
test_transaction_operations(&mut conn).await;
cleanup_keys(&mut conn).await;
test_discard_transaction(&mut conn).await;
cleanup_keys(&mut conn).await;
test_type_command(&mut conn).await;
cleanup_keys(&mut conn).await;
test_config_commands(&mut conn).await;
cleanup_keys(&mut conn).await;
test_info_command(&mut conn).await;
cleanup_keys(&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) {
@@ -141,16 +168,16 @@ async fn test_string_operations(conn: &mut Connection) {
async fn test_incr_operations(conn: &mut Connection) {
// 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);
// 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);
// Test INCR on string value (should fail)
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());
}

View File

@@ -544,3 +544,66 @@ async fn test_error_handling() {
let response = send_command(&mut stream, "*1\r\n$7\r\nDISCARD\r\n").await;
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"));
}

View File

@@ -21,6 +21,7 @@ async fn start_test_server(test_name: &str) -> (Server, u16) {
let option = DBOption {
dir: test_dir,
port,
debug: true,
};
let server = Server::new(option).await;