5824: Add keepdl.
[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                 c.Check(fmt.Sprintf("%x", md5.Sum([]byte(body))), check.Equals, spec[2])
113         }
114 }
115
116 // Return header block and body.
117 func (s *IntegrationSuite) runCurl(c *check.C, token, uri string, args ...string) (hdr, body string) {
118         curlArgs := []string{"--silent", "--show-error", "--include"}
119         if token != "" {
120                 curlArgs = append(curlArgs, "-H", "Authorization: OAuth2 "+token)
121         }
122         curlArgs = append(curlArgs, args...)
123         curlArgs = append(curlArgs, "http://"+s.testServer.Addr+uri)
124         c.Log(fmt.Sprintf("curlArgs == %#v", curlArgs))
125         output, err := exec.Command("curl", curlArgs...).CombinedOutput()
126         // Without "-f", curl exits 0 as long as it gets a valid HTTP
127         // response from the server, even if the response status
128         // indicates that the request failed. In our test suite, we
129         // always expect a valid HTTP response, and we parse the
130         // headers ourselves. If curl exits non-zero, our testing
131         // environment is broken.
132         c.Assert(err, check.Equals, nil)
133         hdrsAndBody := strings.SplitN(string(output), "\r\n\r\n", 2)
134         c.Assert(len(hdrsAndBody), check.Equals, 2)
135         hdr = hdrsAndBody[0]
136         body = hdrsAndBody[1]
137         return
138 }
139
140 func (s *IntegrationSuite) SetUpSuite(c *check.C) {
141         arvadostest.StartAPI()
142         arvadostest.StartKeep()
143 }
144
145 func (s *IntegrationSuite) TearDownSuite(c *check.C) {
146         arvadostest.StopKeep()
147         arvadostest.StopAPI()
148 }
149
150 func (s *IntegrationSuite) SetUpTest(c *check.C) {
151         arvadostest.ResetEnv()
152         s.testServer = &server{}
153         var err error
154         address = "127.0.0.1:0"
155         err = s.testServer.Start()
156         c.Assert(err, check.Equals, nil)
157 }
158
159 func (s *IntegrationSuite) TearDownTest(c *check.C) {
160         var err error
161         if s.testServer != nil {
162                 err = s.testServer.Close()
163         }
164         c.Check(err, check.Equals, nil)
165 }
166
167 // Gocheck boilerplate
168 func Test(t *testing.T) {
169         check.TestingT(t)
170 }