@@ -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