You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

417 lines
9.1 KiB

  1. /*
  2. * Minio Cloud Storage, (C) 2016, 2017 Minio, Inc.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package cmd
  17. import (
  18. "fmt"
  19. "net"
  20. "net/http"
  21. "net/url"
  22. "reflect"
  23. "runtime"
  24. "testing"
  25. )
  26. // Tests http.Header clone.
  27. func TestCloneHeader(t *testing.T) {
  28. headers := []http.Header{
  29. {
  30. "Content-Type": {"text/html; charset=UTF-8"},
  31. "Content-Length": {"0"},
  32. },
  33. {
  34. "Content-Length": {"0", "1", "2"},
  35. },
  36. {
  37. "Expires": {"-1"},
  38. "Content-Length": {"0"},
  39. "Content-Encoding": {"gzip"},
  40. },
  41. }
  42. for i, header := range headers {
  43. clonedHeader := cloneHeader(header)
  44. if !reflect.DeepEqual(header, clonedHeader) {
  45. t.Errorf("Test %d failed", i+1)
  46. }
  47. }
  48. }
  49. // Tests check duplicates function.
  50. func TestCheckDuplicates(t *testing.T) {
  51. tests := []struct {
  52. list []string
  53. err error
  54. shouldPass bool
  55. }{
  56. // Test 1 - for '/tmp/1' repeated twice.
  57. {
  58. list: []string{"/tmp/1", "/tmp/1", "/tmp/2", "/tmp/3"},
  59. err: fmt.Errorf("Duplicate key: \"/tmp/1\" found of count: \"2\""),
  60. shouldPass: false,
  61. },
  62. // Test 2 - for '/tmp/1' repeated thrice.
  63. {
  64. list: []string{"/tmp/1", "/tmp/1", "/tmp/1", "/tmp/3"},
  65. err: fmt.Errorf("Duplicate key: \"/tmp/1\" found of count: \"3\""),
  66. shouldPass: false,
  67. },
  68. // Test 3 - empty string.
  69. {
  70. list: []string{""},
  71. err: errInvalidArgument,
  72. shouldPass: false,
  73. },
  74. // Test 4 - empty string.
  75. {
  76. list: nil,
  77. err: errInvalidArgument,
  78. shouldPass: false,
  79. },
  80. // Test 5 - non repeated strings.
  81. {
  82. list: []string{"/tmp/1", "/tmp/2", "/tmp/3"},
  83. err: nil,
  84. shouldPass: true,
  85. },
  86. }
  87. // Validate if function runs as expected.
  88. for i, test := range tests {
  89. err := checkDuplicateStrings(test.list)
  90. if test.shouldPass && err != test.err {
  91. t.Errorf("Test: %d, Expected %s got %s", i+1, test.err, err)
  92. }
  93. if !test.shouldPass && err.Error() != test.err.Error() {
  94. t.Errorf("Test: %d, Expected %s got %s", i+1, test.err, err)
  95. }
  96. }
  97. }
  98. // Tests maximum object size.
  99. func TestMaxObjectSize(t *testing.T) {
  100. sizes := []struct {
  101. isMax bool
  102. size int64
  103. }{
  104. // Test - 1 - maximum object size.
  105. {
  106. true,
  107. globalMaxObjectSize + 1,
  108. },
  109. // Test - 2 - not maximum object size.
  110. {
  111. false,
  112. globalMaxObjectSize - 1,
  113. },
  114. }
  115. for i, s := range sizes {
  116. isMax := isMaxObjectSize(s.size)
  117. if isMax != s.isMax {
  118. t.Errorf("Test %d: Expected %t, got %t", i+1, s.isMax, isMax)
  119. }
  120. }
  121. }
  122. // Tests minimum allowed part size.
  123. func TestMinAllowedPartSize(t *testing.T) {
  124. sizes := []struct {
  125. isMin bool
  126. size int64
  127. }{
  128. // Test - 1 - within minimum part size.
  129. {
  130. true,
  131. globalMinPartSize + 1,
  132. },
  133. // Test - 2 - smaller than minimum part size.
  134. {
  135. false,
  136. globalMinPartSize - 1,
  137. },
  138. }
  139. for i, s := range sizes {
  140. isMin := isMinAllowedPartSize(s.size)
  141. if isMin != s.isMin {
  142. t.Errorf("Test %d: Expected %t, got %t", i+1, s.isMin, isMin)
  143. }
  144. }
  145. }
  146. // Tests maximum allowed part number.
  147. func TestMaxPartID(t *testing.T) {
  148. sizes := []struct {
  149. isMax bool
  150. partN int
  151. }{
  152. // Test - 1 part number within max part number.
  153. {
  154. false,
  155. globalMaxPartID - 1,
  156. },
  157. // Test - 2 part number bigger than max part number.
  158. {
  159. true,
  160. globalMaxPartID + 1,
  161. },
  162. }
  163. for i, s := range sizes {
  164. isMax := isMaxPartID(s.partN)
  165. if isMax != s.isMax {
  166. t.Errorf("Test %d: Expected %t, got %t", i+1, s.isMax, isMax)
  167. }
  168. }
  169. }
  170. // Tests extracting bucket and objectname from various types of URL paths.
  171. func TestURL2BucketObjectName(t *testing.T) {
  172. testCases := []struct {
  173. u *url.URL
  174. bucket, object string
  175. }{
  176. // Test case 1 normal case.
  177. {
  178. u: &url.URL{
  179. Path: "/bucket/object",
  180. },
  181. bucket: "bucket",
  182. object: "object",
  183. },
  184. // Test case 2 where url only has separator.
  185. {
  186. u: &url.URL{
  187. Path: "/",
  188. },
  189. bucket: "",
  190. object: "",
  191. },
  192. // Test case 3 only bucket is present.
  193. {
  194. u: &url.URL{
  195. Path: "/bucket",
  196. },
  197. bucket: "bucket",
  198. object: "",
  199. },
  200. // Test case 4 many separators and object is a directory.
  201. {
  202. u: &url.URL{
  203. Path: "/bucket/object/1/",
  204. },
  205. bucket: "bucket",
  206. object: "object/1/",
  207. },
  208. // Test case 5 object has many trailing separators.
  209. {
  210. u: &url.URL{
  211. Path: "/bucket/object/1///",
  212. },
  213. bucket: "bucket",
  214. object: "object/1///",
  215. },
  216. // Test case 6 object has only trailing separators.
  217. {
  218. u: &url.URL{
  219. Path: "/bucket/object///////",
  220. },
  221. bucket: "bucket",
  222. object: "object///////",
  223. },
  224. // Test case 7 object has preceding separators.
  225. {
  226. u: &url.URL{
  227. Path: "/bucket////object////",
  228. },
  229. bucket: "bucket",
  230. object: "///object////",
  231. },
  232. // Test case 8 url is not allocated.
  233. {
  234. u: nil,
  235. bucket: "",
  236. object: "",
  237. },
  238. // Test case 9 url path is empty.
  239. {
  240. u: &url.URL{},
  241. bucket: "",
  242. object: "",
  243. },
  244. }
  245. // Validate all test cases.
  246. for i, testCase := range testCases {
  247. bucketName, objectName := urlPath2BucketObjectName(testCase.u)
  248. if bucketName != testCase.bucket {
  249. t.Errorf("Test %d: failed expected bucket name \"%s\", got \"%s\"", i+1, testCase.bucket, bucketName)
  250. }
  251. if objectName != testCase.object {
  252. t.Errorf("Test %d: failed expected bucket name \"%s\", got \"%s\"", i+1, testCase.object, objectName)
  253. }
  254. }
  255. }
  256. // Add tests for starting and stopping different profilers.
  257. func TestStartProfiler(t *testing.T) {
  258. if startProfiler("") != nil {
  259. t.Fatal("Expected nil, but non-nil value returned for invalid profiler.")
  260. }
  261. }
  262. // Tests fetch local address.
  263. func TestLocalAddress(t *testing.T) {
  264. if runtime.GOOS == globalWindowsOSName {
  265. return
  266. }
  267. currentIsDistXL := globalIsDistXL
  268. defer func() {
  269. globalIsDistXL = currentIsDistXL
  270. }()
  271. // need to set this to avoid stale values from other tests.
  272. globalMinioPort = "9000"
  273. globalMinioHost = ""
  274. testCases := []struct {
  275. isDistXL bool
  276. srvCmdConfig serverCmdConfig
  277. localAddr string
  278. }{
  279. // Test 1 - local address is found.
  280. {
  281. isDistXL: true,
  282. srvCmdConfig: serverCmdConfig{
  283. endpoints: []*url.URL{{
  284. Scheme: httpScheme,
  285. Host: "localhost:9000",
  286. Path: "/mnt/disk1",
  287. }, {
  288. Scheme: httpScheme,
  289. Host: "1.1.1.2:9000",
  290. Path: "/mnt/disk2",
  291. }, {
  292. Scheme: httpScheme,
  293. Host: "1.1.2.1:9000",
  294. Path: "/mnt/disk3",
  295. }, {
  296. Scheme: httpScheme,
  297. Host: "1.1.2.2:9000",
  298. Path: "/mnt/disk4",
  299. }},
  300. },
  301. localAddr: net.JoinHostPort("localhost", globalMinioPort),
  302. },
  303. // Test 2 - local address is everything.
  304. {
  305. isDistXL: false,
  306. srvCmdConfig: serverCmdConfig{
  307. serverAddr: net.JoinHostPort("", globalMinioPort),
  308. endpoints: []*url.URL{{
  309. Path: "/mnt/disk1",
  310. }, {
  311. Path: "/mnt/disk2",
  312. }, {
  313. Path: "/mnt/disk3",
  314. }, {
  315. Path: "/mnt/disk4",
  316. }},
  317. },
  318. localAddr: net.JoinHostPort("", globalMinioPort),
  319. },
  320. // Test 3 - local address is not found.
  321. {
  322. isDistXL: true,
  323. srvCmdConfig: serverCmdConfig{
  324. endpoints: []*url.URL{{
  325. Scheme: httpScheme,
  326. Host: "1.1.1.1:9000",
  327. Path: "/mnt/disk2",
  328. }, {
  329. Scheme: httpScheme,
  330. Host: "1.1.1.2:9000",
  331. Path: "/mnt/disk2",
  332. }, {
  333. Scheme: httpScheme,
  334. Host: "1.1.2.1:9000",
  335. Path: "/mnt/disk3",
  336. }, {
  337. Scheme: httpScheme,
  338. Host: "1.1.2.2:9000",
  339. Path: "/mnt/disk4",
  340. }},
  341. },
  342. localAddr: "",
  343. },
  344. // Test 4 - in case of FS mode, with SSL, the host
  345. // name is specified in the --address option on the
  346. // server command line.
  347. {
  348. isDistXL: false,
  349. srvCmdConfig: serverCmdConfig{
  350. serverAddr: "play.minio.io:9000",
  351. endpoints: []*url.URL{{
  352. Path: "/mnt/disk1",
  353. }, {
  354. Path: "/mnt/disk2",
  355. }, {
  356. Path: "/mnt/disk3",
  357. }, {
  358. Path: "/mnt/disk4",
  359. }},
  360. },
  361. localAddr: "play.minio.io:9000",
  362. },
  363. }
  364. // Validates fetching local address.
  365. for i, testCase := range testCases {
  366. globalIsDistXL = testCase.isDistXL
  367. localAddr := getLocalAddress(testCase.srvCmdConfig)
  368. if localAddr != testCase.localAddr {
  369. t.Fatalf("Test %d: Expected %s, got %s", i+1, testCase.localAddr, localAddr)
  370. }
  371. }
  372. }
  373. // TestCheckURL tests valid address
  374. func TestCheckURL(t *testing.T) {
  375. testCases := []struct {
  376. addr string
  377. shouldPass bool
  378. }{
  379. {"", false},
  380. {":", false},
  381. {"localhost", true},
  382. {"127.0.0.1", true},
  383. {"http://localhost/", true},
  384. {"http://127.0.0.1/", true},
  385. {"proto://myhostname/path", true},
  386. }
  387. // Validates fetching local address.
  388. for i, testCase := range testCases {
  389. _, err := checkURL(testCase.addr)
  390. if testCase.shouldPass && err != nil {
  391. t.Errorf("Test %d: expected to pass but got an error: %v\n", i+1, err)
  392. }
  393. if !testCase.shouldPass && err == nil {
  394. t.Errorf("Test %d: expected to fail but passed.", i+1)
  395. }
  396. }
  397. }