use crate::{
Result,
client::ClientPreparedCommand,
commands::{
FlushingMode, FunctionListOptions, LibraryInfo, ScriptingCommands, ServerCommands,
StringCommands,
},
error::{Error, RedisErrorKind},
sleep, spawn,
tests::get_test_client,
};
use serial_test::serial;
#[cfg_attr(feature = "tokio-runtime", tokio::test)]
#[cfg_attr(feature = "async-std-runtime", async_std::test)]
#[serial]
async fn eval() -> Result<()> {
let client = get_test_client().await?;
let result: String = client.eval("return ARGV[1]", (), "hello").await?;
assert_eq!("hello", result);
client.set("key", "hello").await?;
let result: String = client
.eval("return redis.call('GET', KEYS[1])", "key", ())
.await?;
assert_eq!("hello", result);
client.set("key", "hello").await?;
let result: String = client
.eval(
"return redis.call('GET', KEYS[1])..\" \"..ARGV[1]..\"!\"",
"key",
"world",
)
.await?;
assert_eq!("hello world!", result);
Ok(())
}
#[cfg_attr(feature = "tokio-runtime", tokio::test)]
#[cfg_attr(feature = "async-std-runtime", async_std::test)]
#[serial]
async fn eval_tuple_response() -> Result<()> {
let client = get_test_client().await?;
let lua_script = r#"
redis.call("DEL", "key");
redis.call("SADD", "key", 1, 2, 3, 4);
local arr = redis.call("SMEMBERS", "key");
redis.call("DEL", "key");
return { ARGV[1], ARGV[2], 42, arr }
"#;
let result: (String, String, i32, Vec<i64>) =
client.eval(lua_script, (), ["Hello", "world"]).await?;
assert_eq!(result.0, "Hello");
assert_eq!(result.1, "world");
assert_eq!(result.2, 42);
assert_eq!(result.3, vec![1, 2, 3, 4]);
Ok(())
}
#[cfg_attr(feature = "tokio-runtime", tokio::test)]
#[cfg_attr(feature = "async-std-runtime", async_std::test)]
#[serial]
async fn evalsha_noscript() -> Result<()> {
let client = get_test_client().await?;
let result = client
.evalsha::<()>("da39a3ee5e6b4b0d3255bfef95601890afd80709", (), ())
.await
.unwrap_err();
let Error::Redis(error) = result else {
unreachable!();
};
assert_eq!(error.kind, RedisErrorKind::NoScript);
Ok(())
}
#[cfg_attr(feature = "tokio-runtime", tokio::test)]
#[cfg_attr(feature = "async-std-runtime", async_std::test)]
#[serial]
async fn evalsha() -> Result<()> {
let client = get_test_client().await?;
let sha1: String = client.script_load("return ARGV[1]").await?;
let result: String = client.evalsha(sha1, (), "hello").await?;
assert_eq!("hello", result);
Ok(())
}
#[cfg_attr(feature = "tokio-runtime", tokio::test)]
#[cfg_attr(feature = "async-std-runtime", async_std::test)]
#[serial]
async fn fcall() -> Result<()> {
let client = get_test_client().await?;
let library: String = client.function_load(true, "#!lua name=mylib \n redis.register_function('myfunc', function(keys, args) return args[1] end)").await?;
assert_eq!("mylib", library);
let result: String = client.fcall("myfunc", (), "hello").await?;
assert_eq!("hello", result);
Ok(())
}
#[cfg_attr(feature = "tokio-runtime", tokio::test)]
#[cfg_attr(feature = "async-std-runtime", async_std::test)]
#[serial]
async fn fcall_tuple_response() -> Result<()> {
let client = get_test_client().await?;
let lua_lib = r#"#!lua name=mylib
redis.register_function('myfunc', function(keys, args)
redis.call("DEL", "key");
redis.call("SADD", "key", 1, 2, 3, 4);
local arr = redis.call("SMEMBERS", "key");
redis.call("DEL", "key");
return { args[1], args[2], 42, arr }
end)
"#;
let library: String = client.function_load(true, lua_lib).await?;
assert_eq!("mylib", library);
let result: (String, String, i32, Vec<i64>) =
client.fcall("myfunc", (), ["Hello", "world"]).await?;
assert_eq!(result.0, "Hello");
assert_eq!(result.1, "world");
assert_eq!(result.2, 42);
assert_eq!(result.3, vec![1, 2, 3, 4]);
Ok(())
}
#[cfg_attr(feature = "tokio-runtime", tokio::test)]
#[cfg_attr(feature = "async-std-runtime", async_std::test)]
#[serial]
async fn function_delete() -> Result<()> {
let client = get_test_client().await?;
let library: String = client.function_load(true, "#!lua name=mylib \n redis.register_function('myfunc', function(keys, args) return args[1] end)").await?;
assert_eq!("mylib", library);
let result: String = client.fcall("myfunc", (), "hello").await?;
assert_eq!("hello", result);
client.function_delete("mylib").await?;
let result: Result<String> = client.fcall("myfunc", (), "hello").await;
assert!(result.is_err());
Ok(())
}
#[cfg_attr(feature = "tokio-runtime", tokio::test)]
#[cfg_attr(feature = "async-std-runtime", async_std::test)]
#[serial]
async fn function_dump() -> Result<()> {
let client = get_test_client().await?;
client.flushdb(FlushingMode::Sync).await?;
let library: String = client.function_load(true, "#!lua name=mylib \n redis.register_function('myfunc', function(keys, args) return args[1] end)").await?;
assert_eq!("mylib", library);
let result: String = client.fcall("myfunc", (), "hello").await?;
assert_eq!("hello", result);
let serialized_payload = client.function_dump().await?;
assert!(!serialized_payload.is_empty());
client.function_delete("mylib").await?;
client.function_restore(&serialized_payload, None).await?;
let result: String = client.fcall("myfunc", (), "hello").await?;
assert_eq!("hello", result);
Ok(())
}
#[cfg_attr(feature = "tokio-runtime", tokio::test)]
#[cfg_attr(feature = "async-std-runtime", async_std::test)]
#[serial]
async fn function_flush() -> Result<()> {
let client = get_test_client().await?;
let library: String = client.function_load(true, "#!lua name=mylib \n redis.register_function('myfunc', function(keys, args) return args[1] end)").await?;
assert_eq!("mylib", library);
client.function_flush(FlushingMode::Sync).await?;
let list: Vec<LibraryInfo> = client.function_list(FunctionListOptions::default()).await?;
assert_eq!(0, list.len());
Ok(())
}
#[cfg_attr(feature = "tokio-runtime", tokio::test)]
#[cfg_attr(feature = "async-std-runtime", async_std::test)]
#[serial]
async fn function_list() -> Result<()> {
let client = get_test_client().await?;
client.function_flush(FlushingMode::Sync).await?;
let code = "#!lua name=mylib \n redis.register_function{function_name='myfunc', callback=function(keys, args) return args[1] end, flags={ 'no-writes' }, description='My description'}";
let library: String = client.function_load(true, code).await?;
assert_eq!("mylib", library);
let libs: Vec<LibraryInfo> = client.function_list(FunctionListOptions::default()).await?;
assert_eq!(1, libs.len());
assert_eq!("mylib", libs[0].library_name);
assert_eq!("LUA", libs[0].engine);
assert_eq!(1, libs[0].functions.len());
assert_eq!("myfunc", libs[0].functions[0].name);
assert_eq!("My description", libs[0].functions[0].description);
assert_eq!(1, libs[0].functions[0].flags.len());
assert_eq!("no-writes", libs[0].functions[0].flags[0]);
assert_eq!(None, libs[0].library_code);
let libs: Vec<LibraryInfo> = client
.function_list(FunctionListOptions::default().with_code())
.await?;
assert_eq!(1, libs.len());
assert_eq!("mylib", libs[0].library_name);
assert_eq!("LUA", libs[0].engine);
assert_eq!(1, libs[0].functions.len());
assert_eq!("myfunc", libs[0].functions[0].name);
assert_eq!("My description", libs[0].functions[0].description);
assert_eq!(1, libs[0].functions[0].flags.len());
assert_eq!("no-writes", libs[0].functions[0].flags[0]);
assert_eq!(Some(code.to_owned()), libs[0].library_code);
Ok(())
}
#[cfg_attr(feature = "tokio-runtime", tokio::test)]
#[cfg_attr(feature = "async-std-runtime", async_std::test)]
#[serial]
async fn function_stats() -> Result<()> {
let client = get_test_client().await?;
client.function_kill().forget()?;
client.function_flush(FlushingMode::Sync).await?;
let code = "#!lua name=mylib \n redis.register_function{function_name='myfunc', callback=function(keys, args) while (true) do end return args[1] end, flags={ 'no-writes' }, description='My description'}";
let library: String = client.function_load(true, code).await?;
assert_eq!("mylib", library);
spawn(async move {
async fn blocking_fcall() -> Result<()> {
let client = get_test_client().await?;
let _ = client.fcall::<String>("myfunc", (), "hello").await?;
Ok(())
}
let _ = blocking_fcall().await;
});
sleep(std::time::Duration::from_millis(100)).await;
let function_stat = client.function_stats().await?;
assert!(function_stat.running_script.is_some());
if let Some(running_script) = function_stat.running_script {
assert_eq!("myfunc", running_script.name);
assert_eq!(4, running_script.command.len());
assert_eq!("FCALL", running_script.command[0]);
assert_eq!("myfunc", running_script.command[1]);
assert_eq!("0", running_script.command[2]);
assert_eq!("hello", running_script.command[3]);
assert!(running_script.duration_ms > 100);
}
assert!(function_stat.engines.contains_key("LUA"));
assert_eq!(1, function_stat.engines["LUA"].libraries_count);
assert_eq!(1, function_stat.engines["LUA"].functions_count);
client.function_kill().await?;
Ok(())
}
#[cfg_attr(feature = "tokio-runtime", tokio::test)]
#[cfg_attr(feature = "async-std-runtime", async_std::test)]
#[serial]
async fn script_exists() -> Result<()> {
let client = get_test_client().await?;
let sha11: String = client.script_load("return ARGV[1]").await?;
let sha12: String = client
.script_load("return redis.call('GET', KEYS[1])")
.await?;
let result = client
.script_exists([sha11, sha12, "unknwon".to_owned()])
.await?;
assert_eq!([true, true, false], &result[..]);
Ok(())
}
#[cfg_attr(feature = "tokio-runtime", tokio::test)]
#[cfg_attr(feature = "async-std-runtime", async_std::test)]
#[serial]
async fn script_flush() -> Result<()> {
let client = get_test_client().await?;
let sha11: String = client.script_load("return ARGV[1]").await?;
let sha12: String = client
.script_load("return redis.call('GET', KEYS[1])")
.await?;
client.script_flush(FlushingMode::Sync).await?;
let result = client.script_exists([sha11, sha12]).await?;
assert_eq!([false, false], &result[..]);
Ok(())
}
#[cfg_attr(feature = "tokio-runtime", tokio::test)]
#[cfg_attr(feature = "async-std-runtime", async_std::test)]
#[serial]
async fn script_kill() -> Result<()> {
let client = get_test_client().await?;
let _ = client.script_kill().await;
let sha1: String = client
.script_load("while (true) do end return ARGV[1]")
.await?;
spawn(async move {
async fn blocking_script(sha1: String) -> Result<()> {
let client = get_test_client().await?;
let _ = client.evalsha::<String>(sha1, (), "hello").await?;
Ok(())
}
let _ = blocking_script(sha1).await;
});
sleep(std::time::Duration::from_millis(100)).await;
client.script_kill().await?;
Ok(())
}