5824: Assign MIME type by file extension. closes #6327
[arvados.git] / services / keep-web / server_test.go
1 package main
2
3 import (
4         "crypto/md5"
5         "fmt"
6         "os/exec"
7         "strings"
8         "testing"
9
10         "git.curoverse.com/arvados.git/sdk/go/arvadosclient"
11         "git.curoverse.com/arvados.git/sdk/go/arvadostest"
12         "git.curoverse.com/arvados.git/sdk/go/keepclient"
13         check "gopkg.in/check.v1"
14 )
15
16 var _ = check.Suite(&IntegrationSuite{})
17
18 const (
19         spectatorToken  = "zw2f4gwx8hw8cjre7yp6v1zylhrhn3m5gvjq73rtpwhmknrybu"
20         activeToken     = "3kg6k6lzmp9kj5cpkcoxie963cmvjahbt2fod9zru30k1jqdmi"
21         anonymousToken  = "4kg6k6lzmp9kj4cpkcoxie964cmvjahbt4fod9zru44k4jqdmi"
22         fooCollection   = "zzzzz-4zz18-fy296fx3hot09f7"
23         bogusCollection = "zzzzz-4zz18-totallynotexist"
24         hwCollection    = "zzzzz-4zz18-4en62shvi99lxd4"
25 )
26
27 // IntegrationSuite tests need an API server and an arv-git-httpd server
28 type IntegrationSuite struct {
29         testServer *server
30 }
31
32 func (s *IntegrationSuite) TestNoToken(c *check.C) {
33         for _, token := range []string{
34                 "",
35                 "bogustoken",
36         } {
37                 hdr, body := s.runCurl(c, token, "/collections/"+fooCollection+"/foo")
38                 c.Check(hdr, check.Matches, `(?s)HTTP/1.1 401 Unauthorized\r\n.*`)
39                 c.Check(body, check.Equals, "")
40
41                 if token != "" {
42                         hdr, body = s.runCurl(c, token, "/collections/download/"+fooCollection+"/"+token+"/foo")
43                         c.Check(hdr, check.Matches, `(?s)HTTP/1.1 404 Not Found\r\n.*`)
44                         c.Check(body, check.Equals, "")
45                 }
46
47                 hdr, body = s.runCurl(c, token, "/bad-route")
48                 c.Check(hdr, check.Matches, `(?s)HTTP/1.1 404 Not Found\r\n.*`)
49                 c.Check(body, check.Equals, "")
50         }
51 }
52
53 // TODO: Move most cases to functional tests -- at least use Go's own
54 // http client instead of forking curl. Just leave enough of an
55 // integration test to assure that the documented way of invoking curl
56 // really works against the server.
57 func (s *IntegrationSuite) Test404(c *check.C) {
58         for _, uri := range []string{
59                 // Routing errors
60                 "/",
61                 "/foo",
62                 "/download",
63                 "/collections",
64                 "/collections/",
65                 "/collections/" + fooCollection,
66                 "/collections/" + fooCollection + "/",
67                 // Non-existent file in collection
68                 "/collections/" + fooCollection + "/theperthcountyconspiracy",
69                 "/collections/download/" + fooCollection + "/" + activeToken + "/theperthcountyconspiracy",
70                 // Non-existent collection
71                 "/collections/" + bogusCollection,
72                 "/collections/" + bogusCollection + "/",
73                 "/collections/" + bogusCollection + "/theperthcountyconspiracy",
74                 "/collections/download/" + bogusCollection + "/" + activeToken + "/theperthcountyconspiracy",
75         } {
76                 hdr, body := s.runCurl(c, activeToken, uri)
77                 c.Check(hdr, check.Matches, "(?s)HTTP/1.1 404 Not Found\r\n.*")
78                 c.Check(body, check.Equals, "")
79         }
80 }
81
82 func (s *IntegrationSuite) Test200(c *check.C) {
83         anonymousTokens = []string{anonymousToken}
84         arv, err := arvadosclient.MakeArvadosClient()
85         c.Assert(err, check.Equals, nil)
86         arv.ApiToken = activeToken
87         kc, err := keepclient.MakeKeepClient(&arv)
88         c.Assert(err, check.Equals, nil)
89         kc.PutB([]byte("Hello world\n"))
90         kc.PutB([]byte("foo"))
91         for _, spec := range [][]string{
92                 // My collection
93                 {activeToken, "/collections/" + fooCollection + "/foo", "acbd18db4cc2f85cedef654fccc4a4d8"},
94                 {"", "/collections/download/" + fooCollection + "/" + activeToken + "/foo", "acbd18db4cc2f85cedef654fccc4a4d8"},
95                 {"tokensobogus", "/collections/download/" + fooCollection + "/" + activeToken + "/foo", "acbd18db4cc2f85cedef654fccc4a4d8"},
96                 {activeToken, "/collections/download/" + fooCollection + "/" + activeToken + "/foo", "acbd18db4cc2f85cedef654fccc4a4d8"},
97                 {anonymousToken, "/collections/download/" + fooCollection + "/" + activeToken + "/foo", "acbd18db4cc2f85cedef654fccc4a4d8"},
98                 // Anonymously accessible user agreement. These should
99                 // start working when CollectionFileReader provides
100                 // real data instead of fake/stub data.
101                 {"", "/collections/"+hwCollection+"/Hello%20world.txt", "f0ef7081e1539ac00ef5b761b4fb01b3"},
102                 {activeToken, "/collections/"+hwCollection+"/Hello%20world.txt", "f0ef7081e1539ac00ef5b761b4fb01b3"},
103                 {spectatorToken, "/collections/"+hwCollection+"/Hello%20world.txt", "f0ef7081e1539ac00ef5b761b4fb01b3"},
104                 {spectatorToken, "/collections/download/"+hwCollection+"/"+spectatorToken+"/Hello%20world.txt", "f0ef7081e1539ac00ef5b761b4fb01b3"},
105         } {
106                 hdr, body := s.runCurl(c, spec[0], spec[1])
107                 if strings.HasPrefix(hdr, "HTTP/1.1 501 Not Implemented\r\n") && body == "" {
108                         c.Log("Not implemented!")
109                         continue
110                 }
111                 c.Check(hdr, check.Matches, `(?s)HTTP/1.1 200 OK\r\n.*`)
112                 if strings.HasSuffix(spec[1], ".txt") {
113                         c.Check(hdr, check.Matches, `(?s).*\r\nContent-Type: text/plain.*`)
114                         // TODO: Check some types that aren't
115                         // automatically detected by Go's http server
116                         // by sniffing the content.
117                 }
118                 c.Check(fmt.Sprintf("%x", md5.Sum([]byte(body))), check.Equals, spec[2])
119         }
120 }
121
122 // Return header block and body.
123 func (s *IntegrationSuite) runCurl(c *check.C, token, uri string, args ...string) (hdr, body string) {
124         curlArgs := []string{"--silent", "--show-error", "--include"}
125         if token != "" {
126                 curlArgs = append(curlArgs, "-H", "Authorization: OAuth2 "+token)
127         }
128         curlArgs = append(curlArgs, args...)
129         curlArgs = append(curlArgs, "http://"+s.testServer.Addr+uri)
130         c.Log(fmt.Sprintf("curlArgs == %#v", curlArgs))
131         output, err := exec.Command("curl", curlArgs...).CombinedOutput()
132         // Without "-f", curl exits 0 as long as it gets a valid HTTP
133         // response from the server, even if the response status
134         // indicates that the request failed. In our test suite, we
135         // always expect a valid HTTP response, and we parse the
136         // headers ourselves. If curl exits non-zero, our testing
137         // environment is broken.
138         c.Assert(err, check.Equals, nil)
139         hdrsAndBody := strings.SplitN(string(output), "\r\n\r\n", 2)
140         c.Assert(len(hdrsAndBody), check.Equals, 2)
141         hdr = hdrsAndBody[0]
142         body = hdrsAndBody[1]
143         return
144 }
145
146 func (s *IntegrationSuite) SetUpSuite(c *check.C) {
147         arvadostest.StartAPI()
148         arvadostest.StartKeep()
149 }
150
151 func (s *IntegrationSuite) TearDownSuite(c *check.C) {
152         arvadostest.StopKeep()
153         arvadostest.StopAPI()
154 }
155
156 func (s *IntegrationSuite) SetUpTest(c *check.C) {
157         arvadostest.ResetEnv()
158         s.testServer = &server{}
159         var err error
160         address = "127.0.0.1:0"
161         err = s.testServer.Start()
162         c.Assert(err, check.Equals, nil)
163 }
164
165 func (s *IntegrationSuite) TearDownTest(c *check.C) {
166         var err error
167         if s.testServer != nil {
168                 err = s.testServer.Close()
169         }
170         c.Check(err, check.Equals, nil)
171 }
172
173 // Gocheck boilerplate
174 func Test(t *testing.T) {
175         check.TestingT(t)
176 }