PageRenderTime 51ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/api_unit_test.go

https://gitlab.com/JamesClonk/minio-go
Go | 583 lines | 500 code | 44 blank | 39 comment | 141 complexity | 54d26f5ed42a849e510fa3b301a64c8c MD5 | raw file
  1. /*
  2. * Minio Go Library for Amazon S3 Compatible Cloud Storage (C) 2015 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 minio
  17. import (
  18. "bytes"
  19. "fmt"
  20. "io"
  21. "io/ioutil"
  22. "net/http"
  23. "net/url"
  24. "os"
  25. "strings"
  26. "testing"
  27. )
  28. type customReader struct{}
  29. func (c *customReader) Read(p []byte) (n int, err error) {
  30. return 0, nil
  31. }
  32. func (c *customReader) Size() (n int64) {
  33. return 10
  34. }
  35. // Tests getReaderSize() for various Reader types.
  36. func TestGetReaderSize(t *testing.T) {
  37. var reader io.Reader
  38. size, err := getReaderSize(reader)
  39. if err != nil {
  40. t.Fatal("Error:", err)
  41. }
  42. if size != -1 {
  43. t.Fatal("Reader shouldn't have any length.")
  44. }
  45. bytesReader := bytes.NewReader([]byte("Hello World"))
  46. size, err = getReaderSize(bytesReader)
  47. if err != nil {
  48. t.Fatal("Error:", err)
  49. }
  50. if size != int64(len("Hello World")) {
  51. t.Fatalf("Reader length doesn't match got: %v, want: %v", size, len("Hello World"))
  52. }
  53. size, err = getReaderSize(new(customReader))
  54. if err != nil {
  55. t.Fatal("Error:", err)
  56. }
  57. if size != int64(10) {
  58. t.Fatalf("Reader length doesn't match got: %v, want: %v", size, 10)
  59. }
  60. stringsReader := strings.NewReader("Hello World")
  61. size, err = getReaderSize(stringsReader)
  62. if err != nil {
  63. t.Fatal("Error:", err)
  64. }
  65. if size != int64(len("Hello World")) {
  66. t.Fatalf("Reader length doesn't match got: %v, want: %v", size, len("Hello World"))
  67. }
  68. // Create request channel.
  69. reqCh := make(chan readRequest)
  70. // Create response channel.
  71. resCh := make(chan readResponse)
  72. // Create done channel.
  73. doneCh := make(chan struct{})
  74. // objectInfo.
  75. objectInfo := ObjectInfo{Size: 10}
  76. objectReader := newObject(reqCh, resCh, doneCh, objectInfo)
  77. defer objectReader.Close()
  78. size, err = getReaderSize(objectReader)
  79. if err != nil {
  80. t.Fatal("Error:", err)
  81. }
  82. if size != int64(10) {
  83. t.Fatalf("Reader length doesn't match got: %v, want: %v", size, 10)
  84. }
  85. fileReader, err := ioutil.TempFile(os.TempDir(), "prefix")
  86. if err != nil {
  87. t.Fatal("Error:", err)
  88. }
  89. defer fileReader.Close()
  90. defer os.RemoveAll(fileReader.Name())
  91. size, err = getReaderSize(fileReader)
  92. if err != nil {
  93. t.Fatal("Error:", err)
  94. }
  95. if size == -1 {
  96. t.Fatal("Reader length for file cannot be -1.")
  97. }
  98. // Verify for standard input, output and error file descriptors.
  99. size, err = getReaderSize(os.Stdin)
  100. if err != nil {
  101. t.Fatal("Error:", err)
  102. }
  103. if size != -1 {
  104. t.Fatal("Stdin should have length of -1.")
  105. }
  106. size, err = getReaderSize(os.Stdout)
  107. if err != nil {
  108. t.Fatal("Error:", err)
  109. }
  110. if size != -1 {
  111. t.Fatal("Stdout should have length of -1.")
  112. }
  113. size, err = getReaderSize(os.Stderr)
  114. if err != nil {
  115. t.Fatal("Error:", err)
  116. }
  117. if size != -1 {
  118. t.Fatal("Stderr should have length of -1.")
  119. }
  120. file, err := os.Open(os.TempDir())
  121. if err != nil {
  122. t.Fatal("Error:", err)
  123. }
  124. defer file.Close()
  125. _, err = getReaderSize(file)
  126. if err == nil {
  127. t.Fatal("Input file as directory should throw an error.")
  128. }
  129. }
  130. // Tests valid hosts for location.
  131. func TestValidBucketLocation(t *testing.T) {
  132. s3Hosts := []struct {
  133. bucketLocation string
  134. endpoint string
  135. }{
  136. {"us-east-1", "s3.amazonaws.com"},
  137. {"unknown", "s3.amazonaws.com"},
  138. {"ap-southeast-1", "s3-ap-southeast-1.amazonaws.com"},
  139. }
  140. for _, s3Host := range s3Hosts {
  141. endpoint := getS3Endpoint(s3Host.bucketLocation)
  142. if endpoint != s3Host.endpoint {
  143. t.Fatal("Error: invalid bucket location", endpoint)
  144. }
  145. }
  146. }
  147. // Tests valid bucket names.
  148. func TestBucketNames(t *testing.T) {
  149. buckets := []struct {
  150. name string
  151. valid error
  152. }{
  153. {".mybucket", ErrInvalidBucketName("Bucket name cannot start or end with a '.' dot.")},
  154. {"mybucket.", ErrInvalidBucketName("Bucket name cannot start or end with a '.' dot.")},
  155. {"mybucket-", ErrInvalidBucketName("Bucket name contains invalid characters.")},
  156. {"my", ErrInvalidBucketName("Bucket name cannot be smaller than 3 characters.")},
  157. {"", ErrInvalidBucketName("Bucket name cannot be empty.")},
  158. {"my..bucket", ErrInvalidBucketName("Bucket name cannot have successive periods.")},
  159. {"my.bucket.com", nil},
  160. {"my-bucket", nil},
  161. {"123my-bucket", nil},
  162. }
  163. for _, b := range buckets {
  164. err := isValidBucketName(b.name)
  165. if err != b.valid {
  166. t.Fatal("Error:", err)
  167. }
  168. }
  169. }
  170. // Tests temp file.
  171. func TestTempFile(t *testing.T) {
  172. tmpFile, err := newTempFile("testing")
  173. if err != nil {
  174. t.Fatal("Error:", err)
  175. }
  176. fileName := tmpFile.Name()
  177. // Closing temporary file purges the file.
  178. err = tmpFile.Close()
  179. if err != nil {
  180. t.Fatal("Error:", err)
  181. }
  182. st, err := os.Stat(fileName)
  183. if err != nil && !os.IsNotExist(err) {
  184. t.Fatal("Error:", err)
  185. }
  186. if err == nil && st != nil {
  187. t.Fatal("Error: file should be deleted and should not exist.")
  188. }
  189. }
  190. // Tests url encoding.
  191. func TestEncodeURL2Path(t *testing.T) {
  192. type urlStrings struct {
  193. objName string
  194. encodedObjName string
  195. }
  196. bucketName := "bucketName"
  197. want := []urlStrings{
  198. {
  199. objName: "本語",
  200. encodedObjName: "%E6%9C%AC%E8%AA%9E",
  201. },
  202. {
  203. objName: "本語.1",
  204. encodedObjName: "%E6%9C%AC%E8%AA%9E.1",
  205. },
  206. {
  207. objName: ">123>3123123",
  208. encodedObjName: "%3E123%3E3123123",
  209. },
  210. {
  211. objName: "test 1 2.txt",
  212. encodedObjName: "test%201%202.txt",
  213. },
  214. {
  215. objName: "test++ 1.txt",
  216. encodedObjName: "test%2B%2B%201.txt",
  217. },
  218. }
  219. for _, o := range want {
  220. u, err := url.Parse(fmt.Sprintf("https://%s.s3.amazonaws.com/%s", bucketName, o.objName))
  221. if err != nil {
  222. t.Fatal("Error:", err)
  223. }
  224. urlPath := "/" + bucketName + "/" + o.encodedObjName
  225. if urlPath != encodeURL2Path(u) {
  226. t.Fatal("Error")
  227. }
  228. }
  229. }
  230. // Tests error response structure.
  231. func TestErrorResponse(t *testing.T) {
  232. var err error
  233. err = ErrorResponse{
  234. Code: "Testing",
  235. }
  236. errResp := ToErrorResponse(err)
  237. if errResp.Code != "Testing" {
  238. t.Fatal("Type conversion failed, we have an empty struct.")
  239. }
  240. // Test http response decoding.
  241. var httpResponse *http.Response
  242. // Set empty variables
  243. httpResponse = nil
  244. var bucketName, objectName string
  245. // Should fail with invalid argument.
  246. err = httpRespToErrorResponse(httpResponse, bucketName, objectName)
  247. errResp = ToErrorResponse(err)
  248. if errResp.Code != "InvalidArgument" {
  249. t.Fatal("Empty response input should return invalid argument.")
  250. }
  251. }
  252. // Tests signature calculation.
  253. func TestSignatureCalculation(t *testing.T) {
  254. req, err := http.NewRequest("GET", "https://s3.amazonaws.com", nil)
  255. if err != nil {
  256. t.Fatal("Error:", err)
  257. }
  258. req = signV4(*req, "", "", "us-east-1")
  259. if req.Header.Get("Authorization") != "" {
  260. t.Fatal("Error: anonymous credentials should not have Authorization header.")
  261. }
  262. req = preSignV4(*req, "", "", "us-east-1", 0)
  263. if strings.Contains(req.URL.RawQuery, "X-Amz-Signature") {
  264. t.Fatal("Error: anonymous credentials should not have Signature query resource.")
  265. }
  266. req = signV2(*req, "", "")
  267. if req.Header.Get("Authorization") != "" {
  268. t.Fatal("Error: anonymous credentials should not have Authorization header.")
  269. }
  270. req = preSignV2(*req, "", "", 0)
  271. if strings.Contains(req.URL.RawQuery, "Signature") {
  272. t.Fatal("Error: anonymous credentials should not have Signature query resource.")
  273. }
  274. req = signV4(*req, "ACCESS-KEY", "SECRET-KEY", "us-east-1")
  275. if req.Header.Get("Authorization") == "" {
  276. t.Fatal("Error: normal credentials should have Authorization header.")
  277. }
  278. req = preSignV4(*req, "ACCESS-KEY", "SECRET-KEY", "us-east-1", 0)
  279. if !strings.Contains(req.URL.RawQuery, "X-Amz-Signature") {
  280. t.Fatal("Error: normal credentials should have Signature query resource.")
  281. }
  282. req = signV2(*req, "ACCESS-KEY", "SECRET-KEY")
  283. if req.Header.Get("Authorization") == "" {
  284. t.Fatal("Error: normal credentials should have Authorization header.")
  285. }
  286. req = preSignV2(*req, "ACCESS-KEY", "SECRET-KEY", 0)
  287. if !strings.Contains(req.URL.RawQuery, "Signature") {
  288. t.Fatal("Error: normal credentials should not have Signature query resource.")
  289. }
  290. }
  291. // Tests signature type.
  292. func TestSignatureType(t *testing.T) {
  293. clnt := Client{}
  294. if !clnt.signature.isV4() {
  295. t.Fatal("Error")
  296. }
  297. clnt.signature = SignatureV2
  298. if !clnt.signature.isV2() {
  299. t.Fatal("Error")
  300. }
  301. if clnt.signature.isV4() {
  302. t.Fatal("Error")
  303. }
  304. clnt.signature = SignatureV4
  305. if !clnt.signature.isV4() {
  306. t.Fatal("Error")
  307. }
  308. }
  309. // Tests bucket acl types.
  310. func TestBucketACLTypes(t *testing.T) {
  311. want := map[string]bool{
  312. "private": true,
  313. "public-read": true,
  314. "public-read-write": true,
  315. "authenticated-read": true,
  316. "invalid": false,
  317. }
  318. for acl, ok := range want {
  319. if BucketACL(acl).isValidBucketACL() != ok {
  320. t.Fatal("Error")
  321. }
  322. }
  323. }
  324. // Tests optimal part size.
  325. func TestPartSize(t *testing.T) {
  326. totalPartsCount, partSize, lastPartSize, err := optimalPartInfo(5000000000000000000)
  327. if err == nil {
  328. t.Fatal("Error: should fail")
  329. }
  330. totalPartsCount, partSize, lastPartSize, err = optimalPartInfo(5497558138880)
  331. if err != nil {
  332. t.Fatal("Error: ", err)
  333. }
  334. if totalPartsCount != 9987 {
  335. t.Fatalf("Error: expecting total parts count of 9987: got %v instead", totalPartsCount)
  336. }
  337. if partSize != 550502400 {
  338. t.Fatalf("Error: expecting part size of 550502400: got %v instead", partSize)
  339. }
  340. if lastPartSize != 241172480 {
  341. t.Fatalf("Error: expecting last part size of 241172480: got %v instead", lastPartSize)
  342. }
  343. totalPartsCount, partSize, lastPartSize, err = optimalPartInfo(5000000000)
  344. if err != nil {
  345. t.Fatal("Error:", err)
  346. }
  347. if partSize != minPartSize {
  348. t.Fatalf("Error: expecting part size of %v: got %v instead", minPartSize, partSize)
  349. }
  350. totalPartsCount, partSize, lastPartSize, err = optimalPartInfo(-1)
  351. if err != nil {
  352. t.Fatal("Error:", err)
  353. }
  354. if totalPartsCount != 9987 {
  355. t.Fatalf("Error: expecting total parts count of 9987: got %v instead", totalPartsCount)
  356. }
  357. if partSize != 550502400 {
  358. t.Fatalf("Error: expecting part size of 550502400: got %v instead", partSize)
  359. }
  360. if lastPartSize != 241172480 {
  361. t.Fatalf("Error: expecting last part size of 241172480: got %v instead", lastPartSize)
  362. }
  363. }
  364. // Tests url encoding.
  365. func TestURLEncoding(t *testing.T) {
  366. type urlStrings struct {
  367. name string
  368. encodedName string
  369. }
  370. want := []urlStrings{
  371. {
  372. name: "bigfile-1._%",
  373. encodedName: "bigfile-1._%25",
  374. },
  375. {
  376. name: "本語",
  377. encodedName: "%E6%9C%AC%E8%AA%9E",
  378. },
  379. {
  380. name: "本語.1",
  381. encodedName: "%E6%9C%AC%E8%AA%9E.1",
  382. },
  383. {
  384. name: ">123>3123123",
  385. encodedName: "%3E123%3E3123123",
  386. },
  387. {
  388. name: "test 1 2.txt",
  389. encodedName: "test%201%202.txt",
  390. },
  391. {
  392. name: "test++ 1.txt",
  393. encodedName: "test%2B%2B%201.txt",
  394. },
  395. }
  396. for _, u := range want {
  397. if u.encodedName != urlEncodePath(u.name) {
  398. t.Fatal("Error")
  399. }
  400. }
  401. }
  402. // Tests constructing valid endpoint url.
  403. func TestGetEndpointURL(t *testing.T) {
  404. if _, err := getEndpointURL("s3.amazonaws.com", false); err != nil {
  405. t.Fatal("Error:", err)
  406. }
  407. if _, err := getEndpointURL("192.168.1.1", false); err != nil {
  408. t.Fatal("Error:", err)
  409. }
  410. if _, err := getEndpointURL("13333.123123.-", false); err == nil {
  411. t.Fatal("Error")
  412. }
  413. if _, err := getEndpointURL("s3.aamzza.-", false); err == nil {
  414. t.Fatal("Error")
  415. }
  416. if _, err := getEndpointURL("s3.amazonaws.com:443", false); err == nil {
  417. t.Fatal("Error")
  418. }
  419. }
  420. // Tests valid ip address.
  421. func TestValidIPAddr(t *testing.T) {
  422. type validIP struct {
  423. ip string
  424. valid bool
  425. }
  426. want := []validIP{
  427. {
  428. ip: "192.168.1.1",
  429. valid: true,
  430. },
  431. {
  432. ip: "192.1.8",
  433. valid: false,
  434. },
  435. {
  436. ip: "..192.",
  437. valid: false,
  438. },
  439. {
  440. ip: "192.168.1.1.1",
  441. valid: false,
  442. },
  443. }
  444. for _, w := range want {
  445. valid := isValidIP(w.ip)
  446. if valid != w.valid {
  447. t.Fatal("Error")
  448. }
  449. }
  450. }
  451. // Tests valid endpoint domain.
  452. func TestValidEndpointDomain(t *testing.T) {
  453. type validEndpoint struct {
  454. endpointDomain string
  455. valid bool
  456. }
  457. want := []validEndpoint{
  458. {
  459. endpointDomain: "s3.amazonaws.com",
  460. valid: true,
  461. },
  462. {
  463. endpointDomain: "s3.amazonaws.com_",
  464. valid: false,
  465. },
  466. {
  467. endpointDomain: "%$$$",
  468. valid: false,
  469. },
  470. {
  471. endpointDomain: "s3.amz.test.com",
  472. valid: true,
  473. },
  474. {
  475. endpointDomain: "s3.%%",
  476. valid: false,
  477. },
  478. {
  479. endpointDomain: "localhost",
  480. valid: true,
  481. },
  482. {
  483. endpointDomain: "-localhost",
  484. valid: false,
  485. },
  486. {
  487. endpointDomain: "",
  488. valid: false,
  489. },
  490. {
  491. endpointDomain: "\n \t",
  492. valid: false,
  493. },
  494. {
  495. endpointDomain: " ",
  496. valid: false,
  497. },
  498. }
  499. for _, w := range want {
  500. valid := isValidDomain(w.endpointDomain)
  501. if valid != w.valid {
  502. t.Fatal("Error:", w.endpointDomain)
  503. }
  504. }
  505. }
  506. // Tests valid endpoint url.
  507. func TestValidEndpointURL(t *testing.T) {
  508. type validURL struct {
  509. url string
  510. valid bool
  511. }
  512. want := []validURL{
  513. {
  514. url: "https://s3.amazonaws.com",
  515. valid: true,
  516. },
  517. {
  518. url: "https://s3.amazonaws.com/bucket/object",
  519. valid: false,
  520. },
  521. {
  522. url: "192.168.1.1",
  523. valid: false,
  524. },
  525. }
  526. for _, w := range want {
  527. u, err := url.Parse(w.url)
  528. if err != nil {
  529. t.Fatal("Error:", err)
  530. }
  531. valid := false
  532. if err := isValidEndpointURL(u); err == nil {
  533. valid = true
  534. }
  535. if valid != w.valid {
  536. t.Fatal("Error")
  537. }
  538. }
  539. }