config_test.go 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746
  1. package config
  2. import (
  3. "reflect"
  4. "slices"
  5. "strings"
  6. "testing"
  7. )
  8. func TestParseListenersCfg(t *testing.T) {
  9. tests := []struct {
  10. name string
  11. bosListeners []string
  12. bosAdvertisedListeners []string
  13. bosAdvertisedHostsSSL []string
  14. kerberosListeners []string
  15. want []ListenerGroup
  16. wantErr bool
  17. errContains string
  18. bosListenersSSL []string
  19. }{
  20. {
  21. name: "valid single listener with kerberos",
  22. bosListeners: []string{"LOCAL://0.0.0.0:5190"},
  23. bosAdvertisedListeners: []string{"LOCAL://127.0.0.1:5190"},
  24. bosAdvertisedHostsSSL: []string{},
  25. kerberosListeners: []string{"LOCAL://0.0.0.0:1088"},
  26. want: []ListenerGroup{
  27. {
  28. Name: "local",
  29. BOSListenAddress: "0.0.0.0:5190",
  30. BOSAdvertisedHostPlain: "127.0.0.1:5190",
  31. KerberosListenAddress: "0.0.0.0:1088",
  32. },
  33. },
  34. wantErr: false,
  35. },
  36. {
  37. name: "valid single listener without kerberos",
  38. bosListeners: []string{"LOCAL://0.0.0.0:5190"},
  39. bosAdvertisedListeners: []string{"LOCAL://127.0.0.1:5190"},
  40. bosAdvertisedHostsSSL: []string{},
  41. kerberosListeners: []string{},
  42. want: []ListenerGroup{
  43. {
  44. Name: "local",
  45. BOSListenAddress: "0.0.0.0:5190",
  46. BOSAdvertisedHostPlain: "127.0.0.1:5190",
  47. KerberosListenAddress: "",
  48. },
  49. },
  50. wantErr: false,
  51. },
  52. {
  53. name: "valid multiple listeners with mixed kerberos",
  54. bosListeners: []string{"LAN://192.168.1.10:5190", "WAN://0.0.0.0:5191"},
  55. bosAdvertisedListeners: []string{"LAN://192.168.1.10:5190", "WAN://example.com:5191"},
  56. bosAdvertisedHostsSSL: []string{},
  57. kerberosListeners: []string{"LAN://192.168.1.10:1088"},
  58. want: []ListenerGroup{
  59. {
  60. Name: "lan",
  61. BOSListenAddress: "192.168.1.10:5190",
  62. BOSAdvertisedHostPlain: "192.168.1.10:5190",
  63. KerberosListenAddress: "192.168.1.10:1088",
  64. },
  65. {
  66. Name: "wan",
  67. BOSListenAddress: "0.0.0.0:5191",
  68. BOSAdvertisedHostPlain: "example.com:5191",
  69. KerberosListenAddress: "",
  70. },
  71. },
  72. wantErr: false,
  73. },
  74. {
  75. name: "missing BOS advertised host",
  76. bosListeners: []string{"LOCAL://0.0.0.0:5190"},
  77. bosAdvertisedListeners: []string{},
  78. bosAdvertisedHostsSSL: []string{},
  79. kerberosListeners: []string{},
  80. want: nil,
  81. wantErr: true,
  82. errContains: "missing BOS advertise address",
  83. },
  84. {
  85. name: "missing BOS listen address",
  86. bosListeners: []string{},
  87. bosAdvertisedListeners: []string{"LOCAL://127.0.0.1:5190"},
  88. bosAdvertisedHostsSSL: []string{},
  89. kerberosListeners: []string{},
  90. want: nil,
  91. wantErr: true,
  92. errContains: "missing BOS listen address for listener `local://`",
  93. },
  94. {
  95. name: "duplicate listener definition in BOS",
  96. bosListeners: []string{"LOCAL://0.0.0.0:5190", "LOCAL://0.0.0.0:5191"},
  97. bosAdvertisedListeners: []string{"LOCAL://127.0.0.1:5190"},
  98. bosAdvertisedHostsSSL: []string{},
  99. kerberosListeners: []string{},
  100. want: nil,
  101. wantErr: true,
  102. errContains: "duplicate listener definition",
  103. },
  104. {
  105. name: "duplicate listener definition in advertised",
  106. bosListeners: []string{"LOCAL://0.0.0.0:5190"},
  107. bosAdvertisedListeners: []string{"LOCAL://127.0.0.1:5190", "LOCAL://127.0.0.1:5191"},
  108. bosAdvertisedHostsSSL: []string{},
  109. kerberosListeners: []string{},
  110. want: nil,
  111. wantErr: true,
  112. errContains: "duplicate listener definition",
  113. },
  114. {
  115. name: "duplicate listener definition in kerberos",
  116. bosListeners: []string{"LOCAL://0.0.0.0:5190"},
  117. bosAdvertisedListeners: []string{"LOCAL://127.0.0.1:5190"},
  118. bosAdvertisedHostsSSL: []string{},
  119. kerberosListeners: []string{"LOCAL://0.0.0.0:1088", "LOCAL://0.0.0.0:1089"},
  120. want: nil,
  121. wantErr: true,
  122. errContains: "duplicate listener definition",
  123. },
  124. {
  125. name: "invalid URI format in BOS",
  126. bosListeners: []string{"invalid-uri"},
  127. bosAdvertisedListeners: []string{"LOCAL://127.0.0.1:5190"},
  128. bosAdvertisedHostsSSL: []string{},
  129. kerberosListeners: []string{},
  130. want: nil,
  131. wantErr: true,
  132. errContains: "missing scheme. Valid format",
  133. },
  134. {
  135. name: "invalid URI format in advertised",
  136. bosListeners: []string{"LOCAL://0.0.0.0:5190"},
  137. bosAdvertisedListeners: []string{"invalid-uri"},
  138. bosAdvertisedHostsSSL: []string{},
  139. kerberosListeners: []string{},
  140. want: nil,
  141. wantErr: true,
  142. errContains: "missing scheme. Valid format",
  143. },
  144. {
  145. name: "invalid URI format in kerberos",
  146. bosListeners: []string{"LOCAL://0.0.0.0:5190"},
  147. bosAdvertisedListeners: []string{"LOCAL://127.0.0.1:5190"},
  148. bosAdvertisedHostsSSL: []string{},
  149. kerberosListeners: []string{"invalid-uri"},
  150. want: nil,
  151. wantErr: true,
  152. errContains: "missing scheme. Valid format",
  153. },
  154. {
  155. name: "URI with underscore in scheme",
  156. bosListeners: []string{"LOCAL_://0.0.0.0:5190"},
  157. bosAdvertisedListeners: []string{"LOCAL://127.0.0.1:5190"},
  158. bosAdvertisedHostsSSL: []string{},
  159. kerberosListeners: []string{},
  160. want: nil,
  161. wantErr: true,
  162. errContains: "Valid format: SCHEME://HOST:PORT",
  163. },
  164. {
  165. name: "BOS listener missing port",
  166. bosListeners: []string{"LOCAL://0.0.0.0"},
  167. bosAdvertisedListeners: []string{"LOCAL://127.0.0.1:5190"},
  168. bosAdvertisedHostsSSL: []string{},
  169. kerberosListeners: []string{},
  170. want: nil,
  171. wantErr: true,
  172. errContains: "missing port",
  173. },
  174. {
  175. name: "BOS listener missing host",
  176. bosListeners: []string{"LOCAL://:5190"},
  177. bosAdvertisedListeners: []string{"LOCAL://127.0.0.1:5190"},
  178. bosAdvertisedHostsSSL: []string{},
  179. kerberosListeners: []string{},
  180. want: nil,
  181. wantErr: true,
  182. errContains: "missing host",
  183. },
  184. {
  185. name: "complex multi-listener setup",
  186. bosListeners: []string{"LAN://192.168.1.10:5190", "WAN://0.0.0.0:5191", "DOCKER://172.17.0.1:5192"},
  187. bosAdvertisedListeners: []string{"DOCKER://172.17.0.1:5192", "LAN://192.168.1.10:5190", "WAN://example.com:5191"},
  188. bosAdvertisedHostsSSL: []string{},
  189. kerberosListeners: []string{"WAN://0.0.0.0:1089", "LAN://192.168.1.10:1088"},
  190. want: []ListenerGroup{
  191. {
  192. Name: "docker",
  193. BOSListenAddress: "172.17.0.1:5192",
  194. BOSAdvertisedHostPlain: "172.17.0.1:5192",
  195. BOSAdvertisedHostSSL: "",
  196. KerberosListenAddress: "",
  197. },
  198. {
  199. Name: "lan",
  200. BOSListenAddress: "192.168.1.10:5190",
  201. BOSAdvertisedHostPlain: "192.168.1.10:5190",
  202. BOSAdvertisedHostSSL: "",
  203. KerberosListenAddress: "192.168.1.10:1088",
  204. },
  205. {
  206. Name: "wan",
  207. BOSListenAddress: "0.0.0.0:5191",
  208. BOSAdvertisedHostPlain: "example.com:5191",
  209. BOSAdvertisedHostSSL: "",
  210. KerberosListenAddress: "0.0.0.0:1089",
  211. },
  212. },
  213. wantErr: false,
  214. },
  215. {
  216. name: "empty strings for all inputs",
  217. bosListeners: []string{},
  218. bosAdvertisedListeners: []string{},
  219. bosAdvertisedHostsSSL: []string{},
  220. kerberosListeners: []string{},
  221. want: nil,
  222. wantErr: true,
  223. errContains: "at least one BOS listener is required",
  224. },
  225. {
  226. name: "whitespace-only strings",
  227. bosListeners: []string{" "},
  228. bosAdvertisedListeners: []string{" "},
  229. bosAdvertisedHostsSSL: []string{},
  230. kerberosListeners: []string{" "},
  231. want: nil,
  232. wantErr: true,
  233. errContains: "at least one BOS listener is required",
  234. },
  235. {
  236. name: "only kerberos listeners provided",
  237. bosListeners: []string{},
  238. bosAdvertisedListeners: []string{},
  239. bosAdvertisedHostsSSL: []string{},
  240. kerberosListeners: []string{"LOCAL://0.0.0.0:1088"},
  241. want: nil,
  242. wantErr: true,
  243. errContains: "missing BOS advertise address for listener `local://`",
  244. },
  245. {
  246. name: "valid single listener with SSL",
  247. bosListeners: []string{"LOCAL://0.0.0.0:5190"},
  248. bosListenersSSL: []string{"LOCAL://0.0.0.0:5191"},
  249. bosAdvertisedListeners: []string{"LOCAL://127.0.0.1:5190"},
  250. bosAdvertisedHostsSSL: []string{"LOCAL://127.0.0.1:5193"},
  251. kerberosListeners: []string{},
  252. want: []ListenerGroup{
  253. {
  254. Name: "local",
  255. BOSListenAddress: "0.0.0.0:5190",
  256. BOSListenAddressSSL: "0.0.0.0:5191",
  257. BOSAdvertisedHostPlain: "127.0.0.1:5190",
  258. BOSAdvertisedHostSSL: "127.0.0.1:5193",
  259. KerberosListenAddress: "",
  260. },
  261. },
  262. wantErr: false,
  263. },
  264. {
  265. name: "valid multiple listeners with mixed SSL",
  266. bosListeners: []string{"LAN://192.168.1.10:5190", "WAN://0.0.0.0:5191"},
  267. bosListenersSSL: []string{"LAN://192.168.1.10:5195"},
  268. bosAdvertisedListeners: []string{"LAN://192.168.1.10:5190", "WAN://example.com:5191"},
  269. bosAdvertisedHostsSSL: []string{"LAN://192.168.1.10:5193"},
  270. kerberosListeners: []string{},
  271. want: []ListenerGroup{
  272. {
  273. Name: "lan",
  274. BOSListenAddress: "192.168.1.10:5190",
  275. BOSListenAddressSSL: "192.168.1.10:5195",
  276. BOSAdvertisedHostPlain: "192.168.1.10:5190",
  277. BOSAdvertisedHostSSL: "192.168.1.10:5193",
  278. KerberosListenAddress: "",
  279. },
  280. {
  281. Name: "wan",
  282. BOSListenAddress: "0.0.0.0:5191",
  283. BOSAdvertisedHostPlain: "example.com:5191",
  284. BOSAdvertisedHostSSL: "",
  285. KerberosListenAddress: "",
  286. },
  287. },
  288. wantErr: false,
  289. },
  290. {
  291. name: "advertised SSL host without an SSL listen address",
  292. bosListeners: []string{"LOCAL://0.0.0.0:5190"},
  293. bosAdvertisedListeners: []string{"LOCAL://127.0.0.1:5190"},
  294. bosAdvertisedHostsSSL: []string{"LOCAL://127.0.0.1:5193"},
  295. kerberosListeners: []string{},
  296. want: nil,
  297. wantErr: true,
  298. errContains: "missing SSL BOS listen address for listener `local://`",
  299. },
  300. {
  301. name: "SSL listen address without advertised SSL host",
  302. bosListeners: []string{"LOCAL://0.0.0.0:5190"},
  303. bosListenersSSL: []string{"LOCAL://0.0.0.0:5191"},
  304. bosAdvertisedListeners: []string{"LOCAL://127.0.0.1:5190"},
  305. bosAdvertisedHostsSSL: []string{},
  306. kerberosListeners: []string{},
  307. want: []ListenerGroup{
  308. {
  309. Name: "local",
  310. BOSListenAddress: "0.0.0.0:5190",
  311. BOSListenAddressSSL: "0.0.0.0:5191",
  312. BOSAdvertisedHostPlain: "127.0.0.1:5190",
  313. BOSAdvertisedHostSSL: "",
  314. KerberosListenAddress: "",
  315. },
  316. },
  317. wantErr: false,
  318. },
  319. {
  320. name: "SSL listener with kerberos",
  321. bosListeners: []string{"LOCAL://0.0.0.0:5190"},
  322. bosListenersSSL: []string{"LOCAL://0.0.0.0:5191"},
  323. bosAdvertisedListeners: []string{"LOCAL://127.0.0.1:5190"},
  324. bosAdvertisedHostsSSL: []string{"LOCAL://127.0.0.1:5193"},
  325. kerberosListeners: []string{"LOCAL://0.0.0.0:1088"},
  326. want: []ListenerGroup{
  327. {
  328. Name: "local",
  329. BOSListenAddress: "0.0.0.0:5190",
  330. BOSListenAddressSSL: "0.0.0.0:5191",
  331. BOSAdvertisedHostPlain: "127.0.0.1:5190",
  332. BOSAdvertisedHostSSL: "127.0.0.1:5193",
  333. KerberosListenAddress: "0.0.0.0:1088",
  334. },
  335. },
  336. wantErr: false,
  337. },
  338. {
  339. name: "duplicate SSL BOS listen address",
  340. bosListeners: []string{"LOCAL://0.0.0.0:5190"},
  341. bosListenersSSL: []string{"LOCAL://0.0.0.0:5191", "LOCAL://0.0.0.0:5192"},
  342. bosAdvertisedListeners: []string{"LOCAL://127.0.0.1:5190"},
  343. bosAdvertisedHostsSSL: []string{"LOCAL://127.0.0.1:5193"},
  344. kerberosListeners: []string{},
  345. want: nil,
  346. wantErr: true,
  347. errContains: "duplicate listener definition",
  348. },
  349. {
  350. name: "plaintext and SSL BOS listeners share an address",
  351. bosListeners: []string{"LOCAL://0.0.0.0:5190"},
  352. bosListenersSSL: []string{"LOCAL://0.0.0.0:5190"},
  353. bosAdvertisedListeners: []string{"LOCAL://127.0.0.1:5190"},
  354. bosAdvertisedHostsSSL: []string{"LOCAL://127.0.0.1:5193"},
  355. kerberosListeners: []string{},
  356. want: nil,
  357. wantErr: true,
  358. errContains: "listen address 0.0.0.0:5190 is configured for both OSCAR_LISTENERS `local://` and OSCAR_LISTENERS_SSL `local://`",
  359. },
  360. {
  361. name: "BOS listeners in different groups share an address",
  362. bosListeners: []string{"LAN://0.0.0.0:5190", "WAN://0.0.0.0:5190"},
  363. bosAdvertisedListeners: []string{"LAN://192.168.1.10:5190", "WAN://example.com:5190"},
  364. bosAdvertisedHostsSSL: []string{},
  365. kerberosListeners: []string{},
  366. want: nil,
  367. wantErr: true,
  368. errContains: "listen address 0.0.0.0:5190 is configured for both OSCAR_LISTENERS `lan://` and OSCAR_LISTENERS `wan://`",
  369. },
  370. {
  371. name: "BOS and kerberos listeners share an address",
  372. bosListeners: []string{"LOCAL://0.0.0.0:5190"},
  373. bosAdvertisedListeners: []string{"LOCAL://127.0.0.1:5190"},
  374. bosAdvertisedHostsSSL: []string{},
  375. kerberosListeners: []string{"LOCAL://0.0.0.0:5190"},
  376. want: nil,
  377. wantErr: true,
  378. errContains: "listen address 0.0.0.0:5190 is configured for both OSCAR_LISTENERS `local://` and KERBEROS_LISTENERS `local://`",
  379. },
  380. {
  381. name: "SSL BOS listener missing port",
  382. bosListeners: []string{"LOCAL://0.0.0.0:5190"},
  383. bosListenersSSL: []string{"LOCAL://0.0.0.0"},
  384. bosAdvertisedListeners: []string{"LOCAL://127.0.0.1:5190"},
  385. bosAdvertisedHostsSSL: []string{"LOCAL://127.0.0.1:5193"},
  386. kerberosListeners: []string{},
  387. want: nil,
  388. wantErr: true,
  389. errContains: "missing port",
  390. },
  391. {
  392. name: "SSL host without corresponding BOS listener",
  393. bosListeners: []string{"LOCAL://0.0.0.0:5190"},
  394. bosAdvertisedListeners: []string{"LOCAL://127.0.0.1:5190"},
  395. bosAdvertisedHostsSSL: []string{"WAN://ssl.example.com:5193"},
  396. kerberosListeners: []string{},
  397. want: nil,
  398. wantErr: true,
  399. errContains: "missing BOS advertise address for listener `wan://`",
  400. },
  401. {
  402. name: "duplicate SSL listener definition",
  403. bosListeners: []string{"LOCAL://0.0.0.0:5190"},
  404. bosAdvertisedListeners: []string{"LOCAL://127.0.0.1:5190"},
  405. bosAdvertisedHostsSSL: []string{"LOCAL://127.0.0.1:5193", "LOCAL://127.0.0.1:5194"},
  406. kerberosListeners: []string{},
  407. want: nil,
  408. wantErr: true,
  409. errContains: "duplicate listener definition",
  410. },
  411. {
  412. name: "invalid URI format in SSL",
  413. bosListeners: []string{"LOCAL://0.0.0.0:5190"},
  414. bosAdvertisedListeners: []string{"LOCAL://127.0.0.1:5190"},
  415. bosAdvertisedHostsSSL: []string{"invalid-uri"},
  416. kerberosListeners: []string{},
  417. want: nil,
  418. wantErr: true,
  419. errContains: "missing scheme. Valid format",
  420. },
  421. {
  422. name: "SSL listener missing port",
  423. bosListeners: []string{"LOCAL://0.0.0.0:5190"},
  424. bosAdvertisedListeners: []string{"LOCAL://127.0.0.1:5190"},
  425. bosAdvertisedHostsSSL: []string{"LOCAL://127.0.0.1"},
  426. kerberosListeners: []string{},
  427. want: nil,
  428. wantErr: true,
  429. errContains: "missing port",
  430. },
  431. {
  432. name: "SSL listener missing host",
  433. bosListeners: []string{"LOCAL://0.0.0.0:5190"},
  434. bosAdvertisedListeners: []string{"LOCAL://127.0.0.1:5190"},
  435. bosAdvertisedHostsSSL: []string{"LOCAL://:5193"},
  436. kerberosListeners: []string{},
  437. want: nil,
  438. wantErr: true,
  439. errContains: "missing host",
  440. },
  441. {
  442. name: "complex multi-listener setup with SSL",
  443. bosListeners: []string{"LAN://192.168.1.10:5190", "WAN://0.0.0.0:5191", "DOCKER://172.17.0.1:5192"},
  444. bosListenersSSL: []string{"LAN://192.168.1.10:5195", "WAN://0.0.0.0:5196"},
  445. bosAdvertisedListeners: []string{"DOCKER://172.17.0.1:5192", "LAN://192.168.1.10:5190", "WAN://example.com:5191"},
  446. bosAdvertisedHostsSSL: []string{"LAN://192.168.1.10:5193", "WAN://ssl.example.com:5194"},
  447. kerberosListeners: []string{"WAN://0.0.0.0:1089", "LAN://192.168.1.10:1088"},
  448. want: []ListenerGroup{
  449. {
  450. Name: "docker",
  451. BOSListenAddress: "172.17.0.1:5192",
  452. BOSAdvertisedHostPlain: "172.17.0.1:5192",
  453. BOSAdvertisedHostSSL: "",
  454. KerberosListenAddress: "",
  455. },
  456. {
  457. Name: "lan",
  458. BOSListenAddress: "192.168.1.10:5190",
  459. BOSListenAddressSSL: "192.168.1.10:5195",
  460. BOSAdvertisedHostPlain: "192.168.1.10:5190",
  461. BOSAdvertisedHostSSL: "192.168.1.10:5193",
  462. KerberosListenAddress: "192.168.1.10:1088",
  463. },
  464. {
  465. Name: "wan",
  466. BOSListenAddress: "0.0.0.0:5191",
  467. BOSListenAddressSSL: "0.0.0.0:5196",
  468. BOSAdvertisedHostPlain: "example.com:5191",
  469. BOSAdvertisedHostSSL: "ssl.example.com:5194",
  470. KerberosListenAddress: "0.0.0.0:1089",
  471. },
  472. },
  473. wantErr: false,
  474. },
  475. }
  476. for _, tt := range tests {
  477. t.Run(tt.name, func(t *testing.T) {
  478. config := &Config{
  479. BOSListeners: tt.bosListeners,
  480. BOSListenersSSL: tt.bosListenersSSL,
  481. BOSAdvertisedHostsPlain: tt.bosAdvertisedListeners,
  482. BOSAdvertisedHostsSSL: tt.bosAdvertisedHostsSSL,
  483. KerberosListeners: tt.kerberosListeners,
  484. }
  485. got, err := config.ParseListenersCfg()
  486. if tt.wantErr {
  487. if err == nil {
  488. t.Errorf("ParseListenersCfg() expected error but got none")
  489. return
  490. }
  491. if tt.errContains != "" && !contains(err.Error(), tt.errContains) {
  492. t.Errorf("ParseListenersCfg() error = %v, want error containing %q", err, tt.errContains)
  493. }
  494. return
  495. }
  496. if err != nil {
  497. t.Errorf("ParseListenersCfg() unexpected error = %v", err)
  498. return
  499. }
  500. // groups come back in map order; want is listed by name
  501. slices.SortFunc(got, func(a, b ListenerGroup) int {
  502. return strings.Compare(a.Name, b.Name)
  503. })
  504. if !reflect.DeepEqual(got, tt.want) {
  505. t.Errorf("ParseListenersCfg() = %+v, want %+v", got, tt.want)
  506. }
  507. })
  508. }
  509. }
  510. func TestConfigValidate(t *testing.T) {
  511. tests := []struct {
  512. name string
  513. config Config
  514. wantErr bool
  515. errContains string
  516. }{
  517. {
  518. name: "valid config with all fields",
  519. config: Config{
  520. TOCListeners: []string{"0.0.0.0:9898", "192.168.1.10:9899"},
  521. APIListener: "127.0.0.1:8080",
  522. WebAPIListeners: []string{"0.0.0.0:8081"},
  523. },
  524. wantErr: false,
  525. },
  526. {
  527. name: "valid config with single TOC listener",
  528. config: Config{
  529. TOCListeners: []string{"0.0.0.0:9898"},
  530. APIListener: "127.0.0.1:8080",
  531. WebAPIListeners: []string{"0.0.0.0:8081"},
  532. },
  533. wantErr: false,
  534. },
  535. {
  536. name: "valid config with empty TOC listeners",
  537. config: Config{
  538. TOCListeners: []string{},
  539. APIListener: "127.0.0.1:8080",
  540. WebAPIListeners: []string{"0.0.0.0:8081"},
  541. },
  542. wantErr: false,
  543. },
  544. {
  545. name: "valid config with empty API listener",
  546. config: Config{
  547. TOCListeners: []string{"0.0.0.0:9898"},
  548. APIListener: "",
  549. },
  550. wantErr: true,
  551. errContains: "APIListener is required and cannot be empty",
  552. },
  553. {
  554. name: "valid config with all empty",
  555. config: Config{
  556. TOCListeners: []string{},
  557. APIListener: "",
  558. },
  559. wantErr: true,
  560. errContains: "APIListener is required and cannot be empty",
  561. },
  562. {
  563. name: "invalid TOC listener - missing port",
  564. config: Config{
  565. TOCListeners: []string{"0.0.0.0"},
  566. APIListener: "127.0.0.1:8080",
  567. },
  568. wantErr: true,
  569. errContains: "invalid TOC listener \"0.0.0.0\": address 0.0.0.0: missing port in address",
  570. },
  571. {
  572. name: "invalid TOC listener - missing host",
  573. config: Config{
  574. TOCListeners: []string{":9898"},
  575. APIListener: "127.0.0.1:8080",
  576. },
  577. wantErr: true,
  578. errContains: "invalid TOC listener \":9898\": missing host",
  579. },
  580. {
  581. name: "invalid TOC listener - malformed",
  582. config: Config{
  583. TOCListeners: []string{"invalid-format"},
  584. APIListener: "127.0.0.1:8080",
  585. },
  586. wantErr: true,
  587. errContains: "invalid TOC listener \"invalid-format\": address invalid-format: missing port in address",
  588. },
  589. {
  590. name: "invalid TOC listener in comma-separated list",
  591. config: Config{
  592. TOCListeners: []string{"0.0.0.0:9898", "invalid-format", "192.168.1.10:9899"},
  593. APIListener: "127.0.0.1:8080",
  594. },
  595. wantErr: true,
  596. errContains: "invalid TOC listener \"invalid-format\": address invalid-format: missing port in address",
  597. },
  598. {
  599. name: "invalid API listener - missing port",
  600. config: Config{
  601. TOCListeners: []string{"0.0.0.0:9898"},
  602. APIListener: "127.0.0.1",
  603. },
  604. wantErr: true,
  605. errContains: "invalid API listener \"127.0.0.1\": address 127.0.0.1: missing port in address",
  606. },
  607. {
  608. name: "invalid API listener - missing host",
  609. config: Config{
  610. TOCListeners: []string{"0.0.0.0:9898"},
  611. APIListener: ":8080",
  612. },
  613. wantErr: true,
  614. errContains: "invalid API listener \":8080\": missing host",
  615. },
  616. {
  617. name: "invalid API listener - malformed",
  618. config: Config{
  619. TOCListeners: []string{"0.0.0.0:9898"},
  620. APIListener: "invalid-format",
  621. },
  622. wantErr: true,
  623. errContains: "invalid API listener \"invalid-format\": address invalid-format: missing port in address",
  624. },
  625. {
  626. name: "whitespace-only TOC listeners",
  627. config: Config{
  628. TOCListeners: []string{" ", " ", " "},
  629. APIListener: "127.0.0.1:8080",
  630. WebAPIListeners: []string{"0.0.0.0:8081"},
  631. },
  632. wantErr: false,
  633. },
  634. {
  635. name: "whitespace-only API listener",
  636. config: Config{
  637. TOCListeners: []string{"0.0.0.0:9898"},
  638. APIListener: " ",
  639. WebAPIListeners: []string{"0.0.0.0:8081"},
  640. },
  641. wantErr: true,
  642. errContains: "APIListener is required and cannot be empty",
  643. },
  644. {
  645. name: "valid multiple web API listeners",
  646. config: Config{
  647. TOCListeners: []string{"0.0.0.0:9898"},
  648. APIListener: "127.0.0.1:8080",
  649. WebAPIListeners: []string{"0.0.0.0:8081", "192.168.1.10:8082"},
  650. },
  651. wantErr: false,
  652. },
  653. {
  654. name: "whitespace-only web API listeners",
  655. config: Config{
  656. TOCListeners: []string{"0.0.0.0:9898"},
  657. APIListener: "127.0.0.1:8080",
  658. WebAPIListeners: []string{" ", " "},
  659. },
  660. wantErr: false,
  661. },
  662. {
  663. name: "invalid web API listener - missing port",
  664. config: Config{
  665. TOCListeners: []string{"0.0.0.0:9898"},
  666. APIListener: "127.0.0.1:8080",
  667. WebAPIListeners: []string{"0.0.0.0"},
  668. },
  669. wantErr: true,
  670. errContains: "invalid web API listener \"0.0.0.0\": address 0.0.0.0: missing port in address",
  671. },
  672. {
  673. name: "invalid web API listener - missing host",
  674. config: Config{
  675. TOCListeners: []string{"0.0.0.0:9898"},
  676. APIListener: "127.0.0.1:8080",
  677. WebAPIListeners: []string{":8081"},
  678. },
  679. wantErr: true,
  680. errContains: "invalid web API listener \":8081\": missing host",
  681. },
  682. {
  683. name: "invalid web API listener - malformed",
  684. config: Config{
  685. TOCListeners: []string{"0.0.0.0:9898"},
  686. APIListener: "127.0.0.1:8080",
  687. WebAPIListeners: []string{"invalid-format"},
  688. },
  689. wantErr: true,
  690. errContains: "invalid web API listener \"invalid-format\": address invalid-format: missing port in address",
  691. },
  692. {
  693. name: "invalid web API listener in comma-separated list",
  694. config: Config{
  695. TOCListeners: []string{"0.0.0.0:9898"},
  696. APIListener: "127.0.0.1:8080",
  697. WebAPIListeners: []string{"0.0.0.0:8081", "invalid-format", "192.168.1.10:8082"},
  698. },
  699. wantErr: true,
  700. errContains: "invalid web API listener \"invalid-format\": address invalid-format: missing port in address",
  701. },
  702. }
  703. for _, tt := range tests {
  704. t.Run(tt.name, func(t *testing.T) {
  705. err := tt.config.Validate()
  706. if tt.wantErr {
  707. if err == nil {
  708. t.Errorf("Config.Validate() expected error but got none")
  709. return
  710. }
  711. if tt.errContains != "" && !contains(err.Error(), tt.errContains) {
  712. t.Errorf("Config.Validate() error = %v, want error containing %q", err, tt.errContains)
  713. }
  714. return
  715. }
  716. if err != nil {
  717. t.Errorf("Config.Validate() unexpected error = %v", err)
  718. }
  719. })
  720. }
  721. }
  722. // Helper function to check if a string contains a substring
  723. func contains(s, substr string) bool {
  724. return len(s) >= len(substr) && (s == substr || len(substr) == 0 ||
  725. (len(s) > len(substr) && (s[:len(substr)] == substr || s[len(s)-len(substr):] == substr ||
  726. func() bool {
  727. for i := 1; i <= len(s)-len(substr); i++ {
  728. if s[i:i+len(substr)] == substr {
  729. return true
  730. }
  731. }
  732. return false
  733. }())))
  734. }