From a34e860cb061172e92c2b33361a47751f4da6e6b Mon Sep 17 00:00:00 2001 From: Chrono Date: Thu, 6 Apr 2023 17:22:49 +0800 Subject: [PATCH] feat(*): add `testing=true` for resty cli testing (#24) Add a new option `testing=true` --- README.md | 9 ++ lualib/resty/events/init.lua | 11 +- lualib/resty/events/worker.lua | 21 ++- t/listening-off.t | 284 +++++++++++++++++++++++++++++++++ 4 files changed, 322 insertions(+), 3 deletions(-) create mode 100644 t/listening-off.t diff --git a/README.md b/README.md index a5770282..6fcc8e20 100644 --- a/README.md +++ b/README.md @@ -130,6 +130,13 @@ The `opts` parameter is a Lua table with named options: The return value will be the event object or `nil`. +There is a special parameter `testing`, which means the library will not enable +unix domain socket listening, and the events will only be propagated in the worker process internally. +In the meanwhile, `unique_timeout` will be meanless. + +This feature is very useful for testing, such as `resty cli`. +The default value for `testing` is `false`. + [Back to TOC](#table-of-contents) init_worker @@ -157,6 +164,8 @@ it must be called in `content_by_lua*`. `ev` object must be the same object returned by [new](#new). +Should not call it if `testing` is set to `true`. + [Back to TOC](#table-of-contents) publish diff --git a/lualib/resty/events/init.lua b/lualib/resty/events/init.lua index ae2d6050..0c6b3491 100644 --- a/lualib/resty/events/init.lua +++ b/lualib/resty/events/init.lua @@ -13,7 +13,7 @@ local str_sub = string.sub local worker_count = ngx.worker.count() local _M = { - _VERSION = '0.1.3', + _VERSION = '0.1.4', } local _MT = { __index = _M, } @@ -66,6 +66,12 @@ local function check_options(opts) return nil, '"max_queue_len" option is invalid' end + opts.testing = opts.testing or false + + if type(opts.testing) ~= "boolean" then + return nil, '"testing" option must be a boolean' + end + return true end @@ -87,7 +93,8 @@ function _M:init_worker() local worker_id = ngx_worker_id() or -1 - local is_broker = worker_id == opts.broker_id + local is_broker = opts.broker_id == worker_id or + opts.testing == true local ok, err diff --git a/lualib/resty/events/worker.lua b/lualib/resty/events/worker.lua index 3f9a15be..eb6d0229 100644 --- a/lualib/resty/events/worker.lua +++ b/lualib/resty/events/worker.lua @@ -53,7 +53,7 @@ local PAYLOAD_T = { local _worker_id = ngx.worker.id() or -1 local _M = { - _VERSION = '0.1.2', + _VERSION = '0.1.4', } local _MT = { __index = _M, } @@ -107,6 +107,12 @@ function _M:communicate(premature) return end + -- only for testing, skip read/write/events threads + if self._opts.testing == true then + self._connected = true + return + end + local listening = self._opts.listening if not check_sock_exist(listening) then @@ -306,6 +312,19 @@ function _M:publish(target, source, event, data) assert(type(source) == "string" and source ~= "", "source is required") assert(type(event) == "string" and event ~= "", "event is required") + -- fall back to local events + if self._opts.testing == true then + log(DEBUG, "event published to 1 workers") + + do_event(self, { + source = source, + event = event, + data = data, + }) + + return true + end + if target == "current" then ok, err = self._sub_queue:push({ source = source, diff --git a/t/listening-off.t b/t/listening-off.t new file mode 100644 index 00000000..835b6d9f --- /dev/null +++ b/t/listening-off.t @@ -0,0 +1,284 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use Test::Nginx::Socket::Lua; + +#worker_connections(1014); +#master_process_enabled(1); +#log_level('warn'); + +#repeat_each(2); + +plan tests => repeat_each() * (blocks() * 7); + +$ENV{TEST_NGINX_HTML_DIR} ||= html_dir(); + +#no_diff(); +#no_long_string(); +#master_on(); +#workers(2); +run_tests(); + +__DATA__ + +=== TEST 1: posting events and handling events, broadcast and local +--- http_config + lua_package_path "../lua-resty-core/lib/?.lua;lualib/?/init.lua;lualib/?.lua;;"; + init_worker_by_lua_block { + local opts = { + --broker_id = 0, + listening = "unix:$TEST_NGINX_HTML_DIR/nginx.sock", + testing = true, + } + + local ev = require("resty.events").new(opts) + if not ev then + ngx.log(ngx.ERR, "failed to new events") + end + + local ok, err = ev:init_worker() + if not ok then + ngx.log(ngx.ERR, "failed to init_worker events: ", err) + end + + assert(not ev:is_ready()) + + ev:subscribe("*", "*", function(data, event, source, wid) + ngx.log(ngx.DEBUG, "worker-events: handler event; ","source=",source,", event=",event, ", wid=", wid, + ", data=", data) + end) + + _G.ev = ev + } +--- config + location = /test { + content_by_lua_block { + local ev = _G.ev + + assert(ev:is_ready()) + + ev:publish("all", "content_by_lua","request1","01234567890") + ev:publish("current", "content_by_lua","request2","01234567890") + ev:publish("all", "content_by_lua","request3","01234567890") + + ngx.say("ok") + } + } +--- request +GET /test +--- response_body +ok +--- error_log +event published to 1 workers +--- no_error_log +[error] +[crit] +[alert] +--- grep_error_log eval: qr/worker-events: .*/ +--- grep_error_log_out eval +qr/^worker-events: handling event; source=content_by_lua, event=request1, wid=nil +worker-events: handler event; source=content_by_lua, event=request1, wid=nil, data=01234567890 +worker-events: handling event; source=content_by_lua, event=request2, wid=nil +worker-events: handler event; source=content_by_lua, event=request2, wid=nil, data=01234567890 +worker-events: handling event; source=content_by_lua, event=request3, wid=nil +worker-events: handler event; source=content_by_lua, event=request3, wid=nil, data=01234567890$/ + + +=== TEST 2: worker.events handling remote events +--- http_config + lua_package_path "../lua-resty-core/lib/?.lua;lualib/?/init.lua;lualib/?.lua;;"; + init_worker_by_lua_block { + local opts = { + --broker_id = 0, + listening = "unix:$TEST_NGINX_HTML_DIR/nginx.sock", + testing = true, + } + + local ev = require("resty.events").new(opts) + if not ev then + ngx.log(ngx.ERR, "failed to new events") + end + + local ok, err = ev:init_worker() + if not ok then + ngx.log(ngx.ERR, "failed to init_worker events: ", err) + end + + assert(not ev:is_ready()) + + ev:subscribe("*", "*", function(data, event, source, wid) + ngx.log(ngx.DEBUG, "worker-events: handler event; ","source=",source,", event=",event, ", wid=", wid, + ", data=", tostring(data)) + end) + + _G.ev = ev + } +--- config + location = /test { + content_by_lua_block { + local ev = _G.ev + + assert(ev:is_ready()) + + ev:publish("all", "content_by_lua","request1","01234567890") + ev:publish("current", "content_by_lua","request2","01234567890") + ev:publish("all", "content_by_lua","request3","01234567890") + + ngx.say("ok") + } + } +--- request +GET /test +--- response_body +ok +--- error_log +event published to 1 workers +--- no_error_log +[error] +[crit] +[alert] +--- grep_error_log eval: qr/worker-events: .*/ +--- grep_error_log_out eval +qr/^worker-events: handling event; source=content_by_lua, event=request1, wid=nil +worker-events: handler event; source=content_by_lua, event=request1, wid=nil, data=01234567890 +worker-events: handling event; source=content_by_lua, event=request2, wid=nil +worker-events: handler event; source=content_by_lua, event=request2, wid=nil, data=01234567890 +worker-events: handling event; source=content_by_lua, event=request3, wid=nil +worker-events: handler event; source=content_by_lua, event=request3, wid=nil, data=01234567890$/ + + +=== TEST 3: worker.events 'one' being done, and only once +--- http_config + lua_package_path "../lua-resty-core/lib/?.lua;lualib/?/init.lua;lualib/?.lua;;"; + init_worker_by_lua_block { + local opts = { + unique_timeout = 0.04, + --broker_id = 0, + listening = "unix:$TEST_NGINX_HTML_DIR/nginx.sock", + testing = true, + } + + local ev = require("resty.events").new(opts) + if not ev then + ngx.log(ngx.ERR, "failed to new events") + end + + local ok, err = ev:init_worker() + if not ok then + ngx.log(ngx.ERR, "failed to init_worker events: ", err) + end + + assert(not ev:is_ready()) + + ev:subscribe("*", "*", function(data, event, source, wid) + ngx.log(ngx.DEBUG, "worker-events: handler event; ","source=",source,", event=",event, ", wid=", wid, + ", data=", tostring(data)) + end) + + _G.ev = ev + } +--- config + location = /test { + content_by_lua_block { + local ev = _G.ev + + assert(ev:is_ready()) + + ev:publish("all", "content_by_lua","request1","01234567890") + ev:publish("unique_value", "content_by_lua","request2","01234567890") + ev:publish("unique_value", "content_by_lua","request3","01234567890") + + ngx.sleep(0.1) -- wait for unique timeout to expire + + ev:publish("unique_value", "content_by_lua","request4","01234567890") + ev:publish("unique_value", "content_by_lua","request5","01234567890") + ev:publish("all", "content_by_lua","request6","01234567890") + + ngx.say("ok") + } + } +--- request +GET /test +--- response_body +ok +--- error_log +event published to 1 workers +--- no_error_log +[error] +[crit] +[alert] +--- grep_error_log eval: qr/worker-events: .*/ +--- grep_error_log_out eval +qr/^worker-events: handling event; source=content_by_lua, event=request1, wid=nil +worker-events: handler event; source=content_by_lua, event=request1, wid=nil, data=01234567890 +worker-events: handling event; source=content_by_lua, event=request2, wid=nil +worker-events: handler event; source=content_by_lua, event=request2, wid=nil, data=01234567890 +worker-events: handling event; source=content_by_lua, event=request3, wid=nil +worker-events: handler event; source=content_by_lua, event=request3, wid=nil, data=01234567890 +worker-events: handling event; source=content_by_lua, event=request4, wid=nil +worker-events: handler event; source=content_by_lua, event=request4, wid=nil, data=01234567890 +worker-events: handling event; source=content_by_lua, event=request5, wid=nil +worker-events: handler event; source=content_by_lua, event=request5, wid=nil, data=01234567890 +worker-events: handling event; source=content_by_lua, event=request6, wid=nil +worker-events: handler event; source=content_by_lua, event=request6, wid=nil, data=01234567890$/ + + +=== TEST 4: publish events at anywhere +--- http_config + lua_package_path "../lua-resty-core/lib/?.lua;lualib/?/init.lua;lualib/?.lua;;"; + init_worker_by_lua_block { + local opts = { + --broker_id = 0, + listening = "unix:$TEST_NGINX_HTML_DIR/nginx.sock", + testing = true, + } + + local ev = require("resty.events").new(opts) + if not ev then + ngx.log(ngx.ERR, "failed to new events") + end + + ev:subscribe("*", "*", function(data, event, source, wid) + ngx.log(ngx.DEBUG, "worker-events: handler event; ","source=",source,", event=",event, ", wid=", wid, + ", data=", tostring(data)) + end) + + ev:publish("all", "content_by_lua","request1","01234567890") + + local ok, err = ev:init_worker() + if not ok then + ngx.log(ngx.ERR, "failed to init_worker events: ", err) + end + + assert(not ev:is_ready()) + + ev:publish("current", "content_by_lua","request2","01234567890") + + ev:publish("all", "content_by_lua","request3","01234567890") + + _G.ev = ev + } +--- config + location = /test { + content_by_lua_block { + ngx.say("ok") + } + } +--- request +GET /test +--- response_body +ok +--- error_log +event published to 1 workers +--- no_error_log +[error] +[crit] +[alert] +--- grep_error_log eval: qr/worker-events: .*/ +--- grep_error_log_out eval +qr/^worker-events: handling event; source=content_by_lua, event=request1, wid=nil +worker-events: handler event; source=content_by_lua, event=request1, wid=nil, data=01234567890 +worker-events: handling event; source=content_by_lua, event=request2, wid=nil +worker-events: handler event; source=content_by_lua, event=request2, wid=nil, data=01234567890 +worker-events: handling event; source=content_by_lua, event=request3, wid=nil +worker-events: handler event; source=content_by_lua, event=request3, wid=nil, data=01234567890$/ + +