|
@@ -0,0 +1,209 @@
|
|
|
|
|
+--[[
|
|
|
|
|
+ tests/probes_config.lua — regression tests for the three testbed bugs:
|
|
|
|
|
+ 1. probes.all() must accept per-WAN tcp targets (tables or
|
|
|
|
|
+ "host:port" strings) instead of dereferencing a nil global.
|
|
|
|
|
+ 2. config.load() must find UCI wan sections named wan_a / wan_b
|
|
|
|
|
+ (hyphens are not valid UCI identifiers) and map them onto the
|
|
|
|
|
+ internal wan-a / wan-b ids.
|
|
|
|
|
+ 3. main.lua --dry-run: covered by the daemon dry-run smoke check
|
|
|
|
|
+ ("(DRY RUN)" banner + "exited after 1 cycles").
|
|
|
|
|
+
|
|
|
|
|
+ Pure-Lua, no root, no network (exec is not invoked by these paths
|
|
|
|
|
+ except via injected stubs / disabled probes).
|
|
|
|
|
+]]
|
|
|
|
|
+
|
|
|
|
|
+package.path = package.path .. ";./src/?.lua"
|
|
|
|
|
+
|
|
|
|
|
+local probes = require("balancerlite.probes")
|
|
|
|
|
+local config = require("balancerlite.config")
|
|
|
|
|
+
|
|
|
|
|
+local passed = 0
|
|
|
|
|
+local failed = 0
|
|
|
|
|
+local function check(name, ok, detail)
|
|
|
|
|
+ if ok then
|
|
|
|
|
+ passed = passed + 1
|
|
|
|
|
+ print(" PASS " .. name)
|
|
|
|
|
+ else
|
|
|
|
|
+ failed = failed + 1
|
|
|
|
|
+ print(" FAIL " .. name .. (detail and (" " .. detail) or ""))
|
|
|
|
|
+ end
|
|
|
|
|
+end
|
|
|
|
|
+
|
|
|
|
|
+-- -----------------------------------------------------------------
|
|
|
|
|
+-- TEST 1: probes.all with per-WAN tcp targets (the drive_cycle crash)
|
|
|
|
|
+-- -----------------------------------------------------------------
|
|
|
|
|
+print("\n[1] probes.all per-WAN tcp targets")
|
|
|
|
|
+do
|
|
|
|
|
+ -- All probe types disabled except TCP; use a dead local port so
|
|
|
|
|
+ -- nc fails fast (connect refused, no network dependency).
|
|
|
|
|
+ local P = probes.new({
|
|
|
|
|
+ tcp_targets = {}, -- global list intentionally empty
|
|
|
|
|
+ tcp_enabled = true,
|
|
|
|
|
+ icmp_enabled = false,
|
|
|
|
|
+ dns_enabled = false,
|
|
|
|
|
+ wg_enabled = false,
|
|
|
|
|
+ tcp_timeout = 1,
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ -- Table form
|
|
|
|
|
+ local r = probes.all(P, "lo", { { host = "127.0.0.1", port = 1 } }, nil)
|
|
|
|
|
+ check("returns 1 result", #r == 1, "got " .. tostring(#r))
|
|
|
|
|
+ check("probe is tcp", r[1] and r[1].name == "tcp")
|
|
|
|
|
+ check("no crash on empty global list", true)
|
|
|
|
|
+
|
|
|
|
|
+ -- String form
|
|
|
|
|
+ local P2 = probes.new({
|
|
|
|
|
+ tcp_targets = {},
|
|
|
|
|
+ tcp_enabled = true,
|
|
|
|
|
+ icmp_enabled = false,
|
|
|
|
|
+ dns_enabled = false,
|
|
|
|
|
+ wg_enabled = false,
|
|
|
|
|
+ tcp_timeout = 1,
|
|
|
|
|
+ })
|
|
|
|
|
+ local r2 = probes.all(P2, "lo", { "127.0.0.1:1" }, nil)
|
|
|
|
|
+ check("string form returns 1 result", #r2 == 1, "got " .. tostring(#r2))
|
|
|
|
|
+ check("string form probe is tcp", r2[1] and r2[1].name == "tcp")
|
|
|
|
|
+
|
|
|
|
|
+ -- nil override → falls back to global list
|
|
|
|
|
+ local P3 = probes.new({
|
|
|
|
|
+ tcp_targets = { { host = "127.0.0.1", port = 1 } },
|
|
|
|
|
+ tcp_enabled = true,
|
|
|
|
|
+ icmp_enabled = false,
|
|
|
|
|
+ dns_enabled = false,
|
|
|
|
|
+ wg_enabled = false,
|
|
|
|
|
+ tcp_timeout = 1,
|
|
|
|
|
+ })
|
|
|
|
|
+ local r3 = probes.all(P3, "lo", nil, nil)
|
|
|
|
|
+ check("nil override uses global", #r3 == 1, "got " .. tostring(#r3))
|
|
|
|
|
+
|
|
|
|
|
+ -- Empty per-WAN list → no tcp probes at all
|
|
|
|
|
+ local P4 = probes.new({
|
|
|
|
|
+ tcp_targets = { { host = "127.0.0.1", port = 1 } },
|
|
|
|
|
+ tcp_enabled = true,
|
|
|
|
|
+ icmp_enabled = false,
|
|
|
|
|
+ dns_enabled = false,
|
|
|
|
|
+ wg_enabled = false,
|
|
|
|
|
+ })
|
|
|
|
|
+ local r4 = probes.all(P4, "lo", {}, nil)
|
|
|
|
|
+ check("empty override yields no results", #r4 == 0, "got " .. tostring(#r4))
|
|
|
|
|
+end
|
|
|
|
|
+
|
|
|
|
|
+-- -----------------------------------------------------------------
|
|
|
|
|
+-- TEST 2: config.load maps UCI wan_a/wan_b sections to wan-a/wan-b
|
|
|
|
|
+-- -----------------------------------------------------------------
|
|
|
|
|
+print("\n[2] config.load wan section naming")
|
|
|
|
|
+do
|
|
|
|
|
+ local dir = "/tmp/bl_pcfg_" .. tostring(os.time()) .. "_" ..
|
|
|
|
|
+ tostring(math.random(1000000))
|
|
|
|
|
+ os.execute("mkdir -p " .. dir)
|
|
|
|
|
+ local p = dir .. "/balancerlite"
|
|
|
|
|
+ local f = io.open(p, "w")
|
|
|
|
|
+ f:write([[
|
|
|
|
|
+config general 'general'
|
|
|
|
|
+ option host 'unit-test'
|
|
|
|
|
+
|
|
|
|
|
+config health 'health'
|
|
|
|
|
+ option probe_interval '1s'
|
|
|
|
|
+
|
|
|
|
|
+config wan 'wan_a'
|
|
|
|
|
+ option interface 'eth10'
|
|
|
|
|
+ option address '192.0.2.10/24'
|
|
|
|
|
+ option gateway '192.0.2.1'
|
|
|
|
|
+ option preference '90'
|
|
|
|
|
+
|
|
|
|
|
+config wan 'wan_b'
|
|
|
|
|
+ option interface 'eth11'
|
|
|
|
|
+ option address '198.51.100.10/24'
|
|
|
|
|
+ option gateway '198.51.100.1'
|
|
|
|
|
+ option preference '10'
|
|
|
|
|
+]])
|
|
|
|
|
+ f:close()
|
|
|
|
|
+
|
|
|
|
|
+ local cfg, err = config.load(p)
|
|
|
|
|
+ check("config loads", cfg ~= nil, tostring(err))
|
|
|
|
|
+ check("two wans parsed", cfg and #cfg.wans == 2,
|
|
|
|
|
+ cfg and tostring(#cfg.wans))
|
|
|
|
|
+ check("wan-a interface from wan_a section",
|
|
|
|
|
+ cfg and cfg.wans[1].interface == "eth10",
|
|
|
|
|
+ cfg and tostring(cfg.wans[1].interface))
|
|
|
|
|
+ check("wan-a address from wan_a section",
|
|
|
|
|
+ cfg and cfg.wans[1].address == "192.0.2.10/24",
|
|
|
|
|
+ cfg and tostring(cfg.wans[1].address))
|
|
|
|
|
+ check("wan-a gateway from wan_a section",
|
|
|
|
|
+ cfg and cfg.wans[1].gateway == "192.0.2.1",
|
|
|
|
|
+ cfg and tostring(cfg.wans[1].gateway))
|
|
|
|
|
+ check("wan-a preference from wan_a section",
|
|
|
|
|
+ cfg and cfg.wans[1].preference == 90,
|
|
|
|
|
+ cfg and tostring(cfg.wans[1].preference))
|
|
|
|
|
+ check("wan-b interface from wan_b section",
|
|
|
|
|
+ cfg and cfg.wans[2].interface == "eth11",
|
|
|
|
|
+ cfg and tostring(cfg.wans[2].interface))
|
|
|
|
|
+ check("wan-b gateway from wan_b section",
|
|
|
|
|
+ cfg and cfg.wans[2].gateway == "198.51.100.1",
|
|
|
|
|
+ cfg and tostring(cfg.wans[2].gateway))
|
|
|
|
|
+ os.execute("rm -rf " .. dir)
|
|
|
|
|
+end
|
|
|
|
|
+
|
|
|
|
|
+-- -----------------------------------------------------------------
|
|
|
|
|
+-- TEST 3: config.load still accepts hyphenated wan-a/wan-b sections
|
|
|
|
|
+-- (back-compat)
|
|
|
|
|
+-- -----------------------------------------------------------------
|
|
|
|
|
+print("\n[3] config.load hyphenated sections (back-compat)")
|
|
|
|
|
+do
|
|
|
|
|
+ local dir = "/tmp/bl_pcfg2_" .. tostring(os.time()) .. "_" ..
|
|
|
|
|
+ tostring(math.random(1000000))
|
|
|
|
|
+ os.execute("mkdir -p " .. dir)
|
|
|
|
|
+ local p = dir .. "/balancerlite"
|
|
|
|
|
+ local f = io.open(p, "w")
|
|
|
|
|
+ f:write([[
|
|
|
|
|
+config wan 'wan-a'
|
|
|
|
|
+ option interface 'eth20'
|
|
|
|
|
+ option gateway '10.20.0.1'
|
|
|
|
|
+
|
|
|
|
|
+config wan 'wan-b'
|
|
|
|
|
+ option interface 'eth21'
|
|
|
|
|
+ option gateway '10.21.0.1'
|
|
|
|
|
+]])
|
|
|
|
|
+ f:close()
|
|
|
|
|
+
|
|
|
|
|
+ local cfg, err = config.load(p)
|
|
|
|
|
+ check("config loads", cfg ~= nil, tostring(err))
|
|
|
|
|
+ check("wan-a interface from wan-a section",
|
|
|
|
|
+ cfg and cfg.wans[1].interface == "eth20",
|
|
|
|
|
+ cfg and tostring(cfg.wans[1].interface))
|
|
|
|
|
+ check("wan-b interface from wan-b section",
|
|
|
|
|
+ cfg and cfg.wans[2].interface == "eth21",
|
|
|
|
|
+ cfg and tostring(cfg.wans[2].interface))
|
|
|
|
|
+ os.execute("rm -rf " .. dir)
|
|
|
|
|
+end
|
|
|
|
|
+
|
|
|
|
|
+-- -----------------------------------------------------------------
|
|
|
|
|
+-- TEST 4: no wan sections at all → defaults, no crash
|
|
|
|
|
+-- -----------------------------------------------------------------
|
|
|
|
|
+print("\n[4] config.load zero wan sections")
|
|
|
|
|
+do
|
|
|
|
|
+ local dir = "/tmp/bl_pcfg3_" .. tostring(os.time()) .. "_" ..
|
|
|
|
|
+ tostring(math.random(1000000))
|
|
|
|
|
+ os.execute("mkdir -p " .. dir)
|
|
|
|
|
+ local p = dir .. "/balancerlite"
|
|
|
|
|
+ local f = io.open(p, "w")
|
|
|
|
|
+ f:write([[
|
|
|
|
|
+config general 'general'
|
|
|
|
|
+ option host 'nowan'
|
|
|
|
|
+]])
|
|
|
|
|
+ f:close()
|
|
|
|
|
+
|
|
|
|
|
+ local cfg, err = config.load(p)
|
|
|
|
|
+ check("config loads", cfg ~= nil, tostring(err))
|
|
|
|
|
+ check("two wans defaulted", cfg and #cfg.wans == 2)
|
|
|
|
|
+ check("default iface eth0", cfg and cfg.wans[1].interface == "eth0",
|
|
|
|
|
+ cfg and tostring(cfg.wans[1].interface))
|
|
|
|
|
+ os.execute("rm -rf " .. dir)
|
|
|
|
|
+end
|
|
|
|
|
+
|
|
|
|
|
+-- -----------------------------------------------------------------
|
|
|
|
|
+-- Done
|
|
|
|
|
+-- -----------------------------------------------------------------
|
|
|
|
|
+print(string.format("\n=== probes_config: %d/%d passed ===",
|
|
|
|
|
+ passed, passed + failed))
|
|
|
|
|
+if failed > 0 then os.exit(1) end
|