config_test.go 22 KB

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