Skip to content

Commit ab3536a

Browse files
committed
test(lua): add comprehensive tests for shared_dict add and safe_add methods
- Add tests for shared_dict:add() Lua API - Add tests for shared_dict:safe_add() Lua API - Test LRU eviction behavior with add method - Verify OpenResty-compatible return values
1 parent 08bd0cf commit ab3536a

2 files changed

Lines changed: 1131 additions & 115 deletions

File tree

src/lua_engine/engine.rs

Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -848,4 +848,289 @@ mod tests {
848848
assert_eq!(value, "expired_value");
849849
assert!(stale);
850850
}
851+
852+
#[test]
853+
fn test_shared_dict_add_lua() {
854+
let engine = LuaEngine::new();
855+
engine.init_shared_dict("test", 1024 * 1024);
856+
857+
// 测试 add 方法
858+
let (success1, err1, forcible1, success2, err2): (bool, Option<String>, bool, bool, Option<String>) = engine
859+
.lua
860+
.load(r#"
861+
local test = ngx.shared.test
862+
-- 添加新键
863+
local succ, err, forcible = test:add("key1", "value1")
864+
local success1, err1, forcible1 = succ, err, forcible
865+
-- 再次添加相同键
866+
local succ, err, forcible = test:add("key1", "value2")
867+
return success1, err1, forcible1, succ, err
868+
"#)
869+
.eval()
870+
.unwrap();
871+
assert!(success1);
872+
assert!(err1.is_none());
873+
assert!(!forcible1);
874+
assert!(!success2);
875+
assert_eq!(err2, Some("exists".to_string()));
876+
}
877+
878+
#[test]
879+
fn test_shared_dict_add_with_lru_lua() {
880+
let engine = LuaEngine::new();
881+
engine.init_shared_dict("small", 100);
882+
883+
// 测试 add 的 LRU 淘汰
884+
let (success, forcible): (bool, bool) = engine
885+
.lua
886+
.load(r#"
887+
local small = ngx.shared.small
888+
-- 填满容量
889+
small:set("k1", "v1")
890+
small:set("k2", "v2")
891+
-- add 新键需要 LRU 淘汰
892+
local succ, err, forcible = small:add("k3", "v3")
893+
return succ, forcible
894+
"#)
895+
.eval()
896+
.unwrap();
897+
assert!(success);
898+
assert!(forcible);
899+
}
900+
901+
#[test]
902+
fn test_shared_dict_safe_add_lua() {
903+
let engine = LuaEngine::new();
904+
engine.init_shared_dict("small", 100);
905+
906+
// 测试 safe_add
907+
let (ok1, err1, ok2, err2): (Option<bool>, Option<String>, Option<bool>, Option<String>) = engine
908+
.lua
909+
.load(r#"
910+
local small = ngx.shared.small
911+
-- 添加新键
912+
local ok, err = small:safe_add("k1", "v1")
913+
local ok1, err1 = ok, err
914+
-- 再次添加相同键
915+
local ok, err = small:safe_add("k1", "v2")
916+
-- 内存不足时不淘汰,返回 nil
917+
small:safe_add("k2", "v2")
918+
local ok, err = small:safe_add("k3", "v3")
919+
return ok1, err1, ok, err
920+
"#)
921+
.eval()
922+
.unwrap();
923+
assert_eq!(ok1, Some(true));
924+
assert!(err1.is_none());
925+
assert!(ok2.is_none()); // 内存不足返回 nil
926+
assert_eq!(err2, Some("no memory".to_string()));
927+
}
928+
929+
#[test]
930+
fn test_shared_dict_replace_lua() {
931+
let engine = LuaEngine::new();
932+
engine.init_shared_dict("test", 1024 * 1024);
933+
934+
// 测试 replace 方法
935+
let (success1, err1, success2, err2, value): (bool, Option<String>, bool, Option<String>, String) = engine
936+
.lua
937+
.load(r#"
938+
local test = ngx.shared.test
939+
-- 替换不存在的键
940+
local succ, err, forcible = test:replace("key1", "value1")
941+
local success1, err1 = succ, err
942+
-- 设置后替换
943+
test:set("key1", "value1")
944+
local succ, err, forcible = test:replace("key1", "value2")
945+
local success2, err2 = succ, err
946+
-- 获取值
947+
local val = test:get("key1")
948+
return success1, err1, success2, err2, val
949+
"#)
950+
.eval()
951+
.unwrap();
952+
assert!(!success1);
953+
assert_eq!(err1, Some("not found".to_string()));
954+
assert!(success2);
955+
assert!(err2.is_none());
956+
assert_eq!(value, "value2");
957+
}
958+
959+
#[test]
960+
fn test_shared_dict_delete_lua() {
961+
let engine = LuaEngine::new();
962+
engine.init_shared_dict("test", 1024 * 1024);
963+
964+
// 测试 delete 方法
965+
let (result1, result2): (Option<bool>, Option<bool>) = engine
966+
.lua
967+
.load(r#"
968+
local test = ngx.shared.test
969+
test:set("key1", "value1")
970+
-- 删除存在的键
971+
local ok = test:delete("key1")
972+
local result1 = ok
973+
-- 删除不存在的键
974+
local ok = test:delete("key1")
975+
local result2 = ok
976+
return result1, result2
977+
"#)
978+
.eval()
979+
.unwrap();
980+
assert_eq!(result1, Some(true));
981+
assert!(result2.is_none()); // OpenResty 返回 nil 而不是 false
982+
}
983+
984+
#[test]
985+
fn test_shared_dict_incr_lua() {
986+
let engine = LuaEngine::new();
987+
engine.init_shared_dict("test", 1024 * 1024);
988+
989+
// 测试 incr 方法
990+
let (val1, err1, val2, val4): (Option<i64>, Option<String>, Option<i64>, Option<f64>) = engine
991+
.lua
992+
.load(r#"
993+
local test = ngx.shared.test
994+
-- 不存在的键,无初始值
995+
local val, err, forcible = test:incr("key1", 1)
996+
local val1, err1 = val, err
997+
-- 不存在的键,有初始值
998+
local val, err, forcible = test:incr("key2", 5, 0)
999+
local val2 = val
1000+
-- 已存在的键
1001+
local val, err, forcible = test:incr("key2", 3)
1002+
-- 浮点数
1003+
local val, err, forcible = test:incr("key2", 0.5)
1004+
local val4 = val
1005+
return val1, err1, val2, val4
1006+
"#)
1007+
.eval()
1008+
.unwrap();
1009+
assert!(val1.is_none());
1010+
assert_eq!(err1, Some("not found".to_string()));
1011+
assert_eq!(val2, Some(5));
1012+
assert_eq!(val4, Some(8.5));
1013+
}
1014+
1015+
#[test]
1016+
fn test_shared_dict_incr_with_forcible_lua() {
1017+
let engine = LuaEngine::new();
1018+
engine.init_shared_dict("small", 80);
1019+
1020+
// 测试 incr 的 forcible 返回值
1021+
let (forcible1, forcible2, forcible3): (Option<bool>, Option<bool>, Option<bool>) = engine
1022+
.lua
1023+
.load(r#"
1024+
local small = ngx.shared.small
1025+
-- 填满容量
1026+
small:set("k1", "v1")
1027+
small:set("k2", "v2")
1028+
-- incr 新键需要 LRU 淘汰
1029+
local val, err, forcible = small:incr("k3", 1, 0)
1030+
local forcible1 = forcible
1031+
-- 更新现有键,forcible = false
1032+
local val, err, forcible = small:incr("k3", 1)
1033+
local forcible2 = forcible
1034+
-- 不存在的键无 init,forcible = nil
1035+
local val, err, forcible = small:incr("nonexistent", 1)
1036+
local forcible3 = forcible
1037+
return forcible1, forcible2, forcible3
1038+
"#)
1039+
.eval()
1040+
.unwrap();
1041+
assert!(forcible1.unwrap()); // 创建新键时触发了 LRU
1042+
assert_eq!(forcible2, Some(false)); // 更新现有键,forcible = false
1043+
assert!(forcible3.is_none()); // 不存在的键无 init,forcible = nil
1044+
}
1045+
1046+
#[test]
1047+
fn test_shared_dict_lpush_rpush_lua() {
1048+
let engine = LuaEngine::new();
1049+
engine.init_shared_dict("test", 1024 * 1024);
1050+
1051+
// 测试 lpush/rpush
1052+
let (len1, len2, len3, len4): (Option<i64>, Option<i64>, Option<i64>, Option<i64>) = engine
1053+
.lua
1054+
.load(r#"
1055+
local test = ngx.shared.test
1056+
-- lpush 新列表
1057+
local len, err = test:lpush("mylist", "value1")
1058+
local len1 = len
1059+
-- rpush 添加到尾部
1060+
local len, err = test:rpush("mylist", "value2")
1061+
local len2 = len
1062+
-- lpush 添加到头部
1063+
local len, err = test:lpush("mylist", "value0")
1064+
local len3 = len
1065+
-- llen 获取长度
1066+
local len, err = test:llen("mylist")
1067+
local len4 = len
1068+
return len1, len2, len3, len4
1069+
"#)
1070+
.eval()
1071+
.unwrap();
1072+
assert_eq!(len1, Some(1));
1073+
assert_eq!(len2, Some(2));
1074+
assert_eq!(len3, Some(3));
1075+
assert_eq!(len4, Some(3));
1076+
}
1077+
1078+
#[test]
1079+
fn test_shared_dict_lpop_rpop_lua() {
1080+
let engine = LuaEngine::new();
1081+
engine.init_shared_dict("test", 1024 * 1024);
1082+
1083+
// 测试 lpop/rpop
1084+
let (val1, val2, val3, val4, len): (Option<String>, Option<String>, Option<String>, Option<String>, Option<i64>) = engine
1085+
.lua
1086+
.load(r#"
1087+
local test = ngx.shared.test
1088+
-- 创建列表: [a, b, c]
1089+
test:rpush("mylist", "a")
1090+
test:rpush("mylist", "b")
1091+
test:rpush("mylist", "c")
1092+
-- lpop 从头部弹出
1093+
local val1, err = test:lpop("mylist")
1094+
-- rpop 从尾部弹出
1095+
local val2, err = test:rpop("mylist")
1096+
-- 弹出最后一个
1097+
local val3, err = test:lpop("mylist")
1098+
-- 空列表弹出
1099+
local val4, err = test:lpop("mylist")
1100+
-- 获取长度
1101+
local len, err = test:llen("mylist")
1102+
return val1, val2, val3, val4, len
1103+
"#)
1104+
.eval()
1105+
.unwrap();
1106+
assert_eq!(val1, Some("a".to_string()));
1107+
assert_eq!(val2, Some("c".to_string()));
1108+
assert_eq!(val3, Some("b".to_string()));
1109+
assert!(val4.is_none()); // 空列表返回 nil
1110+
assert!(len.is_none()); // 键不存在
1111+
}
1112+
1113+
#[test]
1114+
fn test_shared_dict_list_not_a_list_lua() {
1115+
let engine = LuaEngine::new();
1116+
engine.init_shared_dict("test", 1024 * 1024);
1117+
1118+
// 测试对非列表值操作
1119+
let (err1, err2): (Option<String>, Option<String>) = engine
1120+
.lua
1121+
.load(r#"
1122+
local test = ngx.shared.test
1123+
-- 设置标量值
1124+
test:set("key", "value")
1125+
-- 尝试 lpush 到标量
1126+
local len, err1 = test:lpush("key", "item")
1127+
-- 尝试 lpop 标量
1128+
local val, err2 = test:lpop("key")
1129+
return err1, err2
1130+
"#)
1131+
.eval()
1132+
.unwrap();
1133+
assert_eq!(err1, Some("value not a list".to_string()));
1134+
assert_eq!(err2, Some("value not a list".to_string()));
1135+
}
8511136
}

0 commit comments

Comments
 (0)