probes_config.lua 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. --[[
  2. tests/probes_config.lua — regression tests for the three testbed bugs:
  3. 1. probes.all() must accept per-WAN tcp targets (tables or
  4. "host:port" strings) instead of dereferencing a nil global.
  5. 2. config.load() must find UCI wan sections named wan_a / wan_b
  6. (hyphens are not valid UCI identifiers) and map them onto the
  7. internal wan-a / wan-b ids.
  8. 3. main.lua --dry-run: covered by the daemon dry-run smoke check
  9. ("(DRY RUN)" banner + "exited after 1 cycles").
  10. Pure-Lua, no root, no network (exec is not invoked by these paths
  11. except via injected stubs / disabled probes).
  12. ]]
  13. package.path = package.path .. ";./src/?.lua"
  14. local probes = require("balancerlite.probes")
  15. local config = require("balancerlite.config")
  16. local passed = 0
  17. local failed = 0
  18. local function check(name, ok, detail)
  19. if ok then
  20. passed = passed + 1
  21. print(" PASS " .. name)
  22. else
  23. failed = failed + 1
  24. print(" FAIL " .. name .. (detail and (" " .. detail) or ""))
  25. end
  26. end
  27. -- -----------------------------------------------------------------
  28. -- TEST 1: probes.all with per-WAN tcp targets (the drive_cycle crash)
  29. -- -----------------------------------------------------------------
  30. print("\n[1] probes.all per-WAN tcp targets")
  31. do
  32. -- All probe types disabled except TCP; use a dead local port so
  33. -- nc fails fast (connect refused, no network dependency).
  34. local P = probes.new({
  35. tcp_targets = {}, -- global list intentionally empty
  36. tcp_enabled = true,
  37. icmp_enabled = false,
  38. dns_enabled = false,
  39. wg_enabled = false,
  40. tcp_timeout = 1,
  41. })
  42. -- Table form
  43. local r = probes.all(P, "lo", { { host = "127.0.0.1", port = 1 } }, nil)
  44. check("returns 1 result", #r == 1, "got " .. tostring(#r))
  45. check("probe is tcp", r[1] and r[1].name == "tcp")
  46. check("no crash on empty global list", true)
  47. -- String form
  48. local P2 = probes.new({
  49. tcp_targets = {},
  50. tcp_enabled = true,
  51. icmp_enabled = false,
  52. dns_enabled = false,
  53. wg_enabled = false,
  54. tcp_timeout = 1,
  55. })
  56. local r2 = probes.all(P2, "lo", { "127.0.0.1:1" }, nil)
  57. check("string form returns 1 result", #r2 == 1, "got " .. tostring(#r2))
  58. check("string form probe is tcp", r2[1] and r2[1].name == "tcp")
  59. -- nil override → falls back to global list
  60. local P3 = probes.new({
  61. tcp_targets = { { host = "127.0.0.1", port = 1 } },
  62. tcp_enabled = true,
  63. icmp_enabled = false,
  64. dns_enabled = false,
  65. wg_enabled = false,
  66. tcp_timeout = 1,
  67. })
  68. local r3 = probes.all(P3, "lo", nil, nil)
  69. check("nil override uses global", #r3 == 1, "got " .. tostring(#r3))
  70. -- Empty per-WAN list → no tcp probes at all
  71. local P4 = probes.new({
  72. tcp_targets = { { host = "127.0.0.1", port = 1 } },
  73. tcp_enabled = true,
  74. icmp_enabled = false,
  75. dns_enabled = false,
  76. wg_enabled = false,
  77. })
  78. local r4 = probes.all(P4, "lo", {}, nil)
  79. check("empty override yields no results", #r4 == 0, "got " .. tostring(#r4))
  80. end
  81. -- -----------------------------------------------------------------
  82. -- TEST 2: config.load maps UCI wan_a/wan_b sections to wan-a/wan-b
  83. -- -----------------------------------------------------------------
  84. print("\n[2] config.load wan section naming")
  85. do
  86. local dir = "/tmp/bl_pcfg_" .. tostring(os.time()) .. "_" ..
  87. tostring(math.random(1000000))
  88. os.execute("mkdir -p " .. dir)
  89. local p = dir .. "/balancerlite"
  90. local f = io.open(p, "w")
  91. f:write([[
  92. config general 'general'
  93. option host 'unit-test'
  94. config health 'health'
  95. option probe_interval '1s'
  96. config wan 'wan_a'
  97. option interface 'eth10'
  98. option address '192.0.2.10/24'
  99. option gateway '192.0.2.1'
  100. option preference '90'
  101. config wan 'wan_b'
  102. option interface 'eth11'
  103. option address '198.51.100.10/24'
  104. option gateway '198.51.100.1'
  105. option preference '10'
  106. ]])
  107. f:close()
  108. local cfg, err = config.load(p)
  109. check("config loads", cfg ~= nil, tostring(err))
  110. check("two wans parsed", cfg and #cfg.wans == 2,
  111. cfg and tostring(#cfg.wans))
  112. check("wan-a interface from wan_a section",
  113. cfg and cfg.wans[1].interface == "eth10",
  114. cfg and tostring(cfg.wans[1].interface))
  115. check("wan-a address from wan_a section",
  116. cfg and cfg.wans[1].address == "192.0.2.10/24",
  117. cfg and tostring(cfg.wans[1].address))
  118. check("wan-a gateway from wan_a section",
  119. cfg and cfg.wans[1].gateway == "192.0.2.1",
  120. cfg and tostring(cfg.wans[1].gateway))
  121. check("wan-a preference from wan_a section",
  122. cfg and cfg.wans[1].preference == 90,
  123. cfg and tostring(cfg.wans[1].preference))
  124. check("wan-b interface from wan_b section",
  125. cfg and cfg.wans[2].interface == "eth11",
  126. cfg and tostring(cfg.wans[2].interface))
  127. check("wan-b gateway from wan_b section",
  128. cfg and cfg.wans[2].gateway == "198.51.100.1",
  129. cfg and tostring(cfg.wans[2].gateway))
  130. os.execute("rm -rf " .. dir)
  131. end
  132. -- -----------------------------------------------------------------
  133. -- TEST 3: config.load still accepts hyphenated wan-a/wan-b sections
  134. -- (back-compat)
  135. -- -----------------------------------------------------------------
  136. print("\n[3] config.load hyphenated sections (back-compat)")
  137. do
  138. local dir = "/tmp/bl_pcfg2_" .. tostring(os.time()) .. "_" ..
  139. tostring(math.random(1000000))
  140. os.execute("mkdir -p " .. dir)
  141. local p = dir .. "/balancerlite"
  142. local f = io.open(p, "w")
  143. f:write([[
  144. config wan 'wan-a'
  145. option interface 'eth20'
  146. option gateway '10.20.0.1'
  147. config wan 'wan-b'
  148. option interface 'eth21'
  149. option gateway '10.21.0.1'
  150. ]])
  151. f:close()
  152. local cfg, err = config.load(p)
  153. check("config loads", cfg ~= nil, tostring(err))
  154. check("wan-a interface from wan-a section",
  155. cfg and cfg.wans[1].interface == "eth20",
  156. cfg and tostring(cfg.wans[1].interface))
  157. check("wan-b interface from wan-b section",
  158. cfg and cfg.wans[2].interface == "eth21",
  159. cfg and tostring(cfg.wans[2].interface))
  160. os.execute("rm -rf " .. dir)
  161. end
  162. -- -----------------------------------------------------------------
  163. -- TEST 4: no wan sections at all → defaults, no crash
  164. -- -----------------------------------------------------------------
  165. print("\n[4] config.load zero wan sections")
  166. do
  167. local dir = "/tmp/bl_pcfg3_" .. tostring(os.time()) .. "_" ..
  168. tostring(math.random(1000000))
  169. os.execute("mkdir -p " .. dir)
  170. local p = dir .. "/balancerlite"
  171. local f = io.open(p, "w")
  172. f:write([[
  173. config general 'general'
  174. option host 'nowan'
  175. ]])
  176. f:close()
  177. local cfg, err = config.load(p)
  178. check("config loads", cfg ~= nil, tostring(err))
  179. check("two wans defaulted", cfg and #cfg.wans == 2)
  180. check("default iface eth0", cfg and cfg.wans[1].interface == "eth0",
  181. cfg and tostring(cfg.wans[1].interface))
  182. os.execute("rm -rf " .. dir)
  183. end
  184. -- -----------------------------------------------------------------
  185. -- Done
  186. -- -----------------------------------------------------------------
  187. print(string.format("\n=== probes_config: %d/%d passed ===",
  188. passed, passed + failed))
  189. if failed > 0 then os.exit(1) end