config_test.go 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. package conf
  2. import (
  3. "os"
  4. "testing"
  5. "github.com/stretchr/testify/assert"
  6. "github.com/zeromicro/go-zero/core/fs"
  7. "github.com/zeromicro/go-zero/core/hash"
  8. )
  9. func TestLoadConfig_notExists(t *testing.T) {
  10. assert.NotNil(t, Load("not_a_file", nil))
  11. }
  12. func TestLoadConfig_notRecogFile(t *testing.T) {
  13. filename, err := fs.TempFilenameWithText("hello")
  14. assert.Nil(t, err)
  15. defer os.Remove(filename)
  16. assert.NotNil(t, Load(filename, nil))
  17. }
  18. func TestConfigJson(t *testing.T) {
  19. tests := []string{
  20. ".json",
  21. ".yaml",
  22. ".yml",
  23. }
  24. text := `{
  25. "a": "foo",
  26. "b": 1,
  27. "c": "${FOO}",
  28. "d": "abcd!@#$112"
  29. }`
  30. for _, test := range tests {
  31. test := test
  32. t.Run(test, func(t *testing.T) {
  33. os.Setenv("FOO", "2")
  34. defer os.Unsetenv("FOO")
  35. tmpfile, err := createTempFile(test, text)
  36. assert.Nil(t, err)
  37. defer os.Remove(tmpfile)
  38. var val struct {
  39. A string `json:"a"`
  40. B int `json:"b"`
  41. C string `json:"c"`
  42. D string `json:"d"`
  43. }
  44. MustLoad(tmpfile, &val)
  45. assert.Equal(t, "foo", val.A)
  46. assert.Equal(t, 1, val.B)
  47. assert.Equal(t, "${FOO}", val.C)
  48. assert.Equal(t, "abcd!@#$112", val.D)
  49. })
  50. }
  51. }
  52. func TestLoadFromJsonBytesArray(t *testing.T) {
  53. input := []byte(`{"users": [{"name": "foo"}, {"Name": "bar"}]}`)
  54. var val struct {
  55. Users []struct {
  56. Name string
  57. }
  58. }
  59. assert.NoError(t, LoadFromJsonBytes(input, &val))
  60. var expect []string
  61. for _, user := range val.Users {
  62. expect = append(expect, user.Name)
  63. }
  64. assert.EqualValues(t, []string{"foo", "bar"}, expect)
  65. }
  66. func TestConfigToml(t *testing.T) {
  67. text := `a = "foo"
  68. b = 1
  69. c = "${FOO}"
  70. d = "abcd!@#$112"
  71. `
  72. os.Setenv("FOO", "2")
  73. defer os.Unsetenv("FOO")
  74. tmpfile, err := createTempFile(".toml", text)
  75. assert.Nil(t, err)
  76. defer os.Remove(tmpfile)
  77. var val struct {
  78. A string `json:"a"`
  79. B int `json:"b"`
  80. C string `json:"c"`
  81. D string `json:"d"`
  82. }
  83. MustLoad(tmpfile, &val)
  84. assert.Equal(t, "foo", val.A)
  85. assert.Equal(t, 1, val.B)
  86. assert.Equal(t, "${FOO}", val.C)
  87. assert.Equal(t, "abcd!@#$112", val.D)
  88. }
  89. func TestConfigJsonCanonical(t *testing.T) {
  90. text := []byte(`{"a": "foo", "B": "bar"}`)
  91. var val1 struct {
  92. A string `json:"a"`
  93. B string `json:"b"`
  94. }
  95. var val2 struct {
  96. A string
  97. B string
  98. }
  99. assert.NoError(t, LoadFromJsonBytes(text, &val1))
  100. assert.Equal(t, "foo", val1.A)
  101. assert.Equal(t, "bar", val1.B)
  102. assert.NoError(t, LoadFromJsonBytes(text, &val2))
  103. assert.Equal(t, "foo", val2.A)
  104. assert.Equal(t, "bar", val2.B)
  105. }
  106. func TestConfigTomlCanonical(t *testing.T) {
  107. text := []byte(`a = "foo"
  108. B = "bar"`)
  109. var val1 struct {
  110. A string `json:"a"`
  111. B string `json:"b"`
  112. }
  113. var val2 struct {
  114. A string
  115. B string
  116. }
  117. assert.NoError(t, LoadFromTomlBytes(text, &val1))
  118. assert.Equal(t, "foo", val1.A)
  119. assert.Equal(t, "bar", val1.B)
  120. assert.NoError(t, LoadFromTomlBytes(text, &val2))
  121. assert.Equal(t, "foo", val2.A)
  122. assert.Equal(t, "bar", val2.B)
  123. }
  124. func TestConfigYamlCanonical(t *testing.T) {
  125. text := []byte(`a: foo
  126. B: bar`)
  127. var val1 struct {
  128. A string `json:"a"`
  129. B string `json:"b"`
  130. }
  131. var val2 struct {
  132. A string
  133. B string
  134. }
  135. assert.NoError(t, LoadFromYamlBytes(text, &val1))
  136. assert.Equal(t, "foo", val1.A)
  137. assert.Equal(t, "bar", val1.B)
  138. assert.NoError(t, LoadFromYamlBytes(text, &val2))
  139. assert.Equal(t, "foo", val2.A)
  140. assert.Equal(t, "bar", val2.B)
  141. }
  142. func TestConfigTomlEnv(t *testing.T) {
  143. text := `a = "foo"
  144. b = 1
  145. c = "${FOO}"
  146. d = "abcd!@#112"
  147. `
  148. os.Setenv("FOO", "2")
  149. defer os.Unsetenv("FOO")
  150. tmpfile, err := createTempFile(".toml", text)
  151. assert.Nil(t, err)
  152. defer os.Remove(tmpfile)
  153. var val struct {
  154. A string `json:"a"`
  155. B int `json:"b"`
  156. C string `json:"c"`
  157. D string `json:"d"`
  158. }
  159. MustLoad(tmpfile, &val, UseEnv())
  160. assert.Equal(t, "foo", val.A)
  161. assert.Equal(t, 1, val.B)
  162. assert.Equal(t, "2", val.C)
  163. assert.Equal(t, "abcd!@#112", val.D)
  164. }
  165. func TestConfigJsonEnv(t *testing.T) {
  166. tests := []string{
  167. ".json",
  168. ".yaml",
  169. ".yml",
  170. }
  171. text := `{
  172. "a": "foo",
  173. "b": 1,
  174. "c": "${FOO}",
  175. "d": "abcd!@#$a12 3"
  176. }`
  177. for _, test := range tests {
  178. test := test
  179. t.Run(test, func(t *testing.T) {
  180. os.Setenv("FOO", "2")
  181. defer os.Unsetenv("FOO")
  182. tmpfile, err := createTempFile(test, text)
  183. assert.Nil(t, err)
  184. defer os.Remove(tmpfile)
  185. var val struct {
  186. A string `json:"a"`
  187. B int `json:"b"`
  188. C string `json:"c"`
  189. D string `json:"d"`
  190. }
  191. MustLoad(tmpfile, &val, UseEnv())
  192. assert.Equal(t, "foo", val.A)
  193. assert.Equal(t, 1, val.B)
  194. assert.Equal(t, "2", val.C)
  195. assert.Equal(t, "abcd!@# 3", val.D)
  196. })
  197. }
  198. }
  199. func TestToCamelCase(t *testing.T) {
  200. tests := []struct {
  201. input string
  202. expect string
  203. }{
  204. {
  205. input: "",
  206. expect: "",
  207. },
  208. {
  209. input: "A",
  210. expect: "a",
  211. },
  212. {
  213. input: "a",
  214. expect: "a",
  215. },
  216. {
  217. input: "hello_world",
  218. expect: "helloWorld",
  219. },
  220. {
  221. input: "Hello_world",
  222. expect: "helloWorld",
  223. },
  224. {
  225. input: "hello_World",
  226. expect: "helloWorld",
  227. },
  228. {
  229. input: "helloWorld",
  230. expect: "helloWorld",
  231. },
  232. {
  233. input: "HelloWorld",
  234. expect: "helloWorld",
  235. },
  236. {
  237. input: "hello World",
  238. expect: "hello world",
  239. },
  240. {
  241. input: "Hello World",
  242. expect: "hello world",
  243. },
  244. {
  245. input: "Hello World",
  246. expect: "hello world",
  247. },
  248. {
  249. input: "Hello World foo_bar",
  250. expect: "hello world fooBar",
  251. },
  252. {
  253. input: "Hello World foo_Bar",
  254. expect: "hello world fooBar",
  255. },
  256. {
  257. input: "Hello World Foo_bar",
  258. expect: "hello world fooBar",
  259. },
  260. {
  261. input: "Hello World Foo_Bar",
  262. expect: "hello world fooBar",
  263. },
  264. {
  265. input: "Hello.World Foo_Bar",
  266. expect: "hello.world fooBar",
  267. },
  268. {
  269. input: "你好 World Foo_Bar",
  270. expect: "你好 world fooBar",
  271. },
  272. }
  273. for _, test := range tests {
  274. test := test
  275. t.Run(test.input, func(t *testing.T) {
  276. assert.Equal(t, test.expect, toCamelCase(test.input))
  277. })
  278. }
  279. }
  280. func TestLoadFromJsonBytesError(t *testing.T) {
  281. var val struct{}
  282. assert.Error(t, LoadFromJsonBytes([]byte(`hello`), &val))
  283. }
  284. func TestLoadFromTomlBytesError(t *testing.T) {
  285. var val struct{}
  286. assert.Error(t, LoadFromTomlBytes([]byte(`hello`), &val))
  287. }
  288. func TestLoadFromYamlBytesError(t *testing.T) {
  289. var val struct{}
  290. assert.Error(t, LoadFromYamlBytes([]byte(`':hello`), &val))
  291. }
  292. func TestLoadFromYamlBytes(t *testing.T) {
  293. input := []byte(`layer1:
  294. layer2:
  295. layer3: foo`)
  296. var val struct {
  297. Layer1 struct {
  298. Layer2 struct {
  299. Layer3 string
  300. }
  301. }
  302. }
  303. assert.NoError(t, LoadFromYamlBytes(input, &val))
  304. assert.Equal(t, "foo", val.Layer1.Layer2.Layer3)
  305. }
  306. func TestLoadFromYamlBytesLayers(t *testing.T) {
  307. input := []byte(`layer1:
  308. layer2:
  309. layer3: foo`)
  310. var val struct {
  311. Value string `json:"Layer1.Layer2.Layer3"`
  312. }
  313. assert.NoError(t, LoadFromYamlBytes(input, &val))
  314. assert.Equal(t, "foo", val.Value)
  315. }
  316. func TestUnmarshalJsonBytesMap(t *testing.T) {
  317. input := []byte(`{"foo":{"/mtproto.RPCTos": "bff.bff","bar":"baz"}}`)
  318. var val struct {
  319. Foo map[string]string
  320. }
  321. assert.NoError(t, LoadFromJsonBytes(input, &val))
  322. assert.Equal(t, "bff.bff", val.Foo["/mtproto.RPCTos"])
  323. assert.Equal(t, "baz", val.Foo["bar"])
  324. }
  325. func TestUnmarshalJsonBytesMapWithSliceElements(t *testing.T) {
  326. input := []byte(`{"foo":{"/mtproto.RPCTos": ["bff.bff", "any"],"bar":["baz", "qux"]}}`)
  327. var val struct {
  328. Foo map[string][]string
  329. }
  330. assert.NoError(t, LoadFromJsonBytes(input, &val))
  331. assert.EqualValues(t, []string{"bff.bff", "any"}, val.Foo["/mtproto.RPCTos"])
  332. assert.EqualValues(t, []string{"baz", "qux"}, val.Foo["bar"])
  333. }
  334. func TestUnmarshalJsonBytesMapWithSliceOfStructs(t *testing.T) {
  335. input := []byte(`{"foo":{
  336. "/mtproto.RPCTos": [{"bar": "any"}],
  337. "bar":[{"bar": "qux"}, {"bar": "ever"}]}}`)
  338. var val struct {
  339. Foo map[string][]struct {
  340. Bar string
  341. }
  342. }
  343. assert.NoError(t, LoadFromJsonBytes(input, &val))
  344. assert.Equal(t, 1, len(val.Foo["/mtproto.RPCTos"]))
  345. assert.Equal(t, "any", val.Foo["/mtproto.RPCTos"][0].Bar)
  346. assert.Equal(t, 2, len(val.Foo["bar"]))
  347. assert.Equal(t, "qux", val.Foo["bar"][0].Bar)
  348. assert.Equal(t, "ever", val.Foo["bar"][1].Bar)
  349. }
  350. func TestUnmarshalJsonBytesWithAnonymousField(t *testing.T) {
  351. type (
  352. Int int
  353. InnerConf struct {
  354. Name string
  355. }
  356. Conf struct {
  357. Int
  358. InnerConf
  359. }
  360. )
  361. var (
  362. input = []byte(`{"Name": "hello", "int": 3}`)
  363. c Conf
  364. )
  365. assert.NoError(t, LoadFromJsonBytes(input, &c))
  366. assert.Equal(t, "hello", c.Name)
  367. assert.Equal(t, Int(3), c.Int)
  368. }
  369. func createTempFile(ext, text string) (string, error) {
  370. tmpfile, err := os.CreateTemp(os.TempDir(), hash.Md5Hex([]byte(text))+"*"+ext)
  371. if err != nil {
  372. return "", err
  373. }
  374. if err := os.WriteFile(tmpfile.Name(), []byte(text), os.ModeTemporary); err != nil {
  375. return "", err
  376. }
  377. filename := tmpfile.Name()
  378. if err = tmpfile.Close(); err != nil {
  379. return "", err
  380. }
  381. return filename, nil
  382. }