5538: using fake arvados server to generate errors, added tests with retries.
[arvados.git] / sdk / go / arvadosclient / arvadosclient_test.go
1 package arvadosclient
2
3 import (
4         "git.curoverse.com/arvados.git/sdk/go/arvadostest"
5         . "gopkg.in/check.v1"
6         "net"
7         "net/http"
8         "os"
9         "strings"
10         "testing"
11 )
12
13 // Gocheck boilerplate
14 func Test(t *testing.T) {
15         TestingT(t)
16 }
17
18 var _ = Suite(&ServerRequiredSuite{})
19 var _ = Suite(&UnitSuite{})
20 var _ = Suite(&MockArvadosServerSuite{})
21
22 // Tests that require the Keep server running
23 type ServerRequiredSuite struct{}
24
25 func (s *ServerRequiredSuite) SetUpSuite(c *C) {
26         arvadostest.StartAPI()
27         arvadostest.StartKeep(2, false)
28 }
29
30 func (s *ServerRequiredSuite) SetUpTest(c *C) {
31         arvadostest.ResetEnv()
32 }
33
34 func (s *ServerRequiredSuite) TestMakeArvadosClientSecure(c *C) {
35         os.Setenv("ARVADOS_API_HOST_INSECURE", "")
36         kc, err := MakeArvadosClient()
37         c.Assert(err, Equals, nil)
38         c.Check(kc.ApiServer, Equals, os.Getenv("ARVADOS_API_HOST"))
39         c.Check(kc.ApiToken, Equals, os.Getenv("ARVADOS_API_TOKEN"))
40         c.Check(kc.ApiInsecure, Equals, false)
41 }
42
43 func (s *ServerRequiredSuite) TestMakeArvadosClientInsecure(c *C) {
44         os.Setenv("ARVADOS_API_HOST_INSECURE", "true")
45         kc, err := MakeArvadosClient()
46         c.Assert(err, Equals, nil)
47         c.Check(kc.ApiInsecure, Equals, true)
48         c.Check(kc.ApiServer, Equals, os.Getenv("ARVADOS_API_HOST"))
49         c.Check(kc.ApiToken, Equals, os.Getenv("ARVADOS_API_TOKEN"))
50         c.Check(kc.Client.Transport.(*http.Transport).TLSClientConfig.InsecureSkipVerify, Equals, true)
51 }
52
53 func (s *ServerRequiredSuite) TestGetInvalidUUID(c *C) {
54         arv, err := MakeArvadosClient()
55
56         getback := make(Dict)
57         err = arv.Get("collections", "", nil, &getback)
58         c.Assert(err, Equals, ErrInvalidArgument)
59         c.Assert(len(getback), Equals, 0)
60
61         err = arv.Get("collections", "zebra-moose-unicorn", nil, &getback)
62         c.Assert(err, Equals, ErrInvalidArgument)
63         c.Assert(len(getback), Equals, 0)
64
65         err = arv.Get("collections", "acbd18db4cc2f85cedef654fccc4a4d8", nil, &getback)
66         c.Assert(err, Equals, ErrInvalidArgument)
67         c.Assert(len(getback), Equals, 0)
68 }
69
70 func (s *ServerRequiredSuite) TestGetValidUUID(c *C) {
71         arv, err := MakeArvadosClient()
72
73         getback := make(Dict)
74         err = arv.Get("collections", "zzzzz-4zz18-abcdeabcdeabcde", nil, &getback)
75         c.Assert(err, FitsTypeOf, APIServerError{})
76         c.Assert(err.(APIServerError).HttpStatusCode, Equals, http.StatusNotFound)
77         c.Assert(len(getback), Equals, 0)
78
79         err = arv.Get("collections", "acbd18db4cc2f85cedef654fccc4a4d8+3", nil, &getback)
80         c.Assert(err, FitsTypeOf, APIServerError{})
81         c.Assert(err.(APIServerError).HttpStatusCode, Equals, http.StatusNotFound)
82         c.Assert(len(getback), Equals, 0)
83 }
84
85 func (s *ServerRequiredSuite) TestInvalidResourceType(c *C) {
86         arv, err := MakeArvadosClient()
87
88         getback := make(Dict)
89         err = arv.Get("unicorns", "zzzzz-zebra-unicorn7unicorn", nil, &getback)
90         c.Assert(err, FitsTypeOf, APIServerError{})
91         c.Assert(err.(APIServerError).HttpStatusCode, Equals, http.StatusNotFound)
92         c.Assert(len(getback), Equals, 0)
93
94         err = arv.Update("unicorns", "zzzzz-zebra-unicorn7unicorn", nil, &getback)
95         c.Assert(err, FitsTypeOf, APIServerError{})
96         c.Assert(err.(APIServerError).HttpStatusCode, Equals, http.StatusNotFound)
97         c.Assert(len(getback), Equals, 0)
98
99         err = arv.List("unicorns", nil, &getback)
100         c.Assert(err, FitsTypeOf, APIServerError{})
101         c.Assert(err.(APIServerError).HttpStatusCode, Equals, http.StatusNotFound)
102         c.Assert(len(getback), Equals, 0)
103 }
104
105 func (s *ServerRequiredSuite) TestCreatePipelineTemplate(c *C) {
106         arv, err := MakeArvadosClient()
107
108         getback := make(Dict)
109         err = arv.Create("pipeline_templates",
110                 Dict{"pipeline_template": Dict{
111                         "name": "tmp",
112                         "components": Dict{
113                                 "c1": map[string]string{"script": "script1"},
114                                 "c2": map[string]string{"script": "script2"}}}},
115                 &getback)
116         c.Assert(err, Equals, nil)
117         c.Assert(getback["name"], Equals, "tmp")
118         c.Assert(getback["components"].(map[string]interface{})["c2"].(map[string]interface{})["script"], Equals, "script2")
119
120         uuid := getback["uuid"].(string)
121
122         getback = make(Dict)
123         err = arv.Get("pipeline_templates", uuid, nil, &getback)
124         c.Assert(err, Equals, nil)
125         c.Assert(getback["name"], Equals, "tmp")
126         c.Assert(getback["components"].(map[string]interface{})["c1"].(map[string]interface{})["script"], Equals, "script1")
127
128         getback = make(Dict)
129         err = arv.Update("pipeline_templates", uuid,
130                 Dict{
131                         "pipeline_template": Dict{"name": "tmp2"}},
132                 &getback)
133         c.Assert(err, Equals, nil)
134         c.Assert(getback["name"], Equals, "tmp2")
135
136         c.Assert(getback["uuid"].(string), Equals, uuid)
137         getback = make(Dict)
138         err = arv.Delete("pipeline_templates", uuid, nil, &getback)
139         c.Assert(err, Equals, nil)
140         c.Assert(getback["name"], Equals, "tmp2")
141 }
142
143 func (s *ServerRequiredSuite) TestErrorResponse(c *C) {
144         arv, _ := MakeArvadosClient()
145
146         getback := make(Dict)
147
148         {
149                 err := arv.Create("logs",
150                         Dict{"log": Dict{"bogus_attr": "foo"}},
151                         &getback)
152                 c.Assert(err, ErrorMatches, "arvados API server error: .*")
153                 c.Assert(err, ErrorMatches, ".*unknown attribute: bogus_attr.*")
154                 c.Assert(err, FitsTypeOf, APIServerError{})
155                 c.Assert(err.(APIServerError).HttpStatusCode, Equals, 422)
156         }
157
158         {
159                 err := arv.Create("bogus",
160                         Dict{"bogus": Dict{}},
161                         &getback)
162                 c.Assert(err, ErrorMatches, "arvados API server error: .*")
163                 c.Assert(err, ErrorMatches, ".*Path not found.*")
164                 c.Assert(err, FitsTypeOf, APIServerError{})
165                 c.Assert(err.(APIServerError).HttpStatusCode, Equals, 404)
166         }
167 }
168
169 func (s *ServerRequiredSuite) TestAPIDiscovery_Get_defaultCollectionReplication(c *C) {
170         arv, err := MakeArvadosClient()
171         value, err := arv.Discovery("defaultCollectionReplication")
172         c.Assert(err, IsNil)
173         c.Assert(value, NotNil)
174 }
175
176 func (s *ServerRequiredSuite) TestAPIDiscovery_Get_noSuchParameter(c *C) {
177         arv, err := MakeArvadosClient()
178         value, err := arv.Discovery("noSuchParameter")
179         c.Assert(err, NotNil)
180         c.Assert(value, IsNil)
181 }
182
183 type UnitSuite struct{}
184
185 func (s *UnitSuite) TestUUIDMatch(c *C) {
186         c.Assert(UUIDMatch("zzzzz-tpzed-000000000000000"), Equals, true)
187         c.Assert(UUIDMatch("zzzzz-zebra-000000000000000"), Equals, true)
188         c.Assert(UUIDMatch("00000-00000-zzzzzzzzzzzzzzz"), Equals, true)
189         c.Assert(UUIDMatch("ZEBRA-HORSE-AFRICANELEPHANT"), Equals, false)
190         c.Assert(UUIDMatch(" zzzzz-tpzed-000000000000000"), Equals, false)
191         c.Assert(UUIDMatch("d41d8cd98f00b204e9800998ecf8427e"), Equals, false)
192         c.Assert(UUIDMatch("d41d8cd98f00b204e9800998ecf8427e+0"), Equals, false)
193         c.Assert(UUIDMatch(""), Equals, false)
194 }
195
196 func (s *UnitSuite) TestPDHMatch(c *C) {
197         c.Assert(PDHMatch("zzzzz-tpzed-000000000000000"), Equals, false)
198         c.Assert(PDHMatch("d41d8cd98f00b204e9800998ecf8427e"), Equals, false)
199         c.Assert(PDHMatch("d41d8cd98f00b204e9800998ecf8427e+0"), Equals, true)
200         c.Assert(PDHMatch("d41d8cd98f00b204e9800998ecf8427e+12345"), Equals, true)
201         c.Assert(PDHMatch("d41d8cd98f00b204e9800998ecf8427e 12345"), Equals, false)
202         c.Assert(PDHMatch("D41D8CD98F00B204E9800998ECF8427E+12345"), Equals, false)
203         c.Assert(PDHMatch("d41d8cd98f00b204e9800998ecf8427e+12345 "), Equals, false)
204         c.Assert(PDHMatch("d41d8cd98f00b204e9800998ecf8427e+abcdef"), Equals, false)
205         c.Assert(PDHMatch("da39a3ee5e6b4b0d3255bfef95601890afd80709"), Equals, false)
206         c.Assert(PDHMatch("da39a3ee5e6b4b0d3255bfef95601890afd80709+0"), Equals, false)
207         c.Assert(PDHMatch("d41d8cd98f00b204e9800998ecf8427+12345"), Equals, false)
208         c.Assert(PDHMatch("d41d8cd98f00b204e9800998ecf8427e+12345\n"), Equals, false)
209         c.Assert(PDHMatch("+12345"), Equals, false)
210         c.Assert(PDHMatch(""), Equals, false)
211 }
212
213 // Tests that use mock arvados server
214 type MockArvadosServerSuite struct{}
215
216 func (s *MockArvadosServerSuite) SetUpSuite(c *C) {
217 }
218
219 func (s *MockArvadosServerSuite) SetUpTest(c *C) {
220         arvadostest.ResetEnv()
221 }
222
223 type APIServer struct {
224         listener net.Listener
225         url      string
226 }
227
228 func RunFakeArvadosServer(st http.Handler) (api APIServer, err error) {
229         api.listener, err = net.ListenTCP("tcp", &net.TCPAddr{Port: 0})
230         if err != nil {
231                 return
232         }
233         api.url = api.listener.Addr().String()
234         go http.Serve(api.listener, st)
235         return
236 }
237
238 type FailHandler struct {
239         status int
240 }
241
242 func (h FailHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
243         resp.WriteHeader(h.status)
244 }
245
246 func (s *MockArvadosServerSuite) TestFailWithRetries(c *C) {
247         for _, testCase := range []string{
248                 "get",
249                 "create",
250         } {
251                 stub := FailHandler{500}
252
253                 api, err := RunFakeArvadosServer(stub)
254                 c.Check(err, IsNil)
255
256                 defer api.listener.Close()
257
258                 arv := ArvadosClient{
259                         Scheme:      "http",
260                         ApiServer:   api.url,
261                         ApiToken:    "abc123",
262                         ApiInsecure: true,
263                         Client:      &http.Client{},
264                         Retries:     2}
265
266                 getback := make(Dict)
267                 switch testCase {
268                 case "get":
269                         err = arv.Get("collections", "zzzzz-4zz18-znfnqtbbv4spc3w", nil, &getback)
270                 case "create":
271                         err = arv.Create("collections",
272                                 Dict{"collection": Dict{"name": "testing"}},
273                                 &getback)
274                 }
275                 c.Check(err, NotNil)
276                 c.Check(strings.Contains(err.Error(), "arvados API server error: 500"), Equals, true)
277                 c.Assert(err.(APIServerError).HttpStatusCode, Equals, 500)
278         }
279 }
280
281 type FailThenSucceedHandler struct {
282         count      int
283         failStatus int
284 }
285
286 func (h *FailThenSucceedHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
287         if h.count == 0 {
288                 resp.WriteHeader(h.failStatus)
289                 h.count += 1
290         } else {
291                 resp.WriteHeader(http.StatusOK)
292                 respJSON := []byte(`{"name":"testing"}`)
293                 resp.Write(respJSON)
294         }
295 }
296
297 func (s *MockArvadosServerSuite) TestFailThenSucceed(c *C) {
298         for _, testCase := range []string{
299                 "get",
300                 "create",
301         } {
302                 stub := &FailThenSucceedHandler{0, 500}
303
304                 api, err := RunFakeArvadosServer(stub)
305                 c.Check(err, IsNil)
306
307                 defer api.listener.Close()
308
309                 arv := ArvadosClient{
310                         Scheme:      "http",
311                         ApiServer:   api.url,
312                         ApiToken:    "abc123",
313                         ApiInsecure: true,
314                         Client:      &http.Client{},
315                         Retries:     2}
316
317                 getback := make(Dict)
318                 switch testCase {
319                 case "get":
320                         err = arv.Get("collections", "zzzzz-4zz18-znfnqtbbv4spc3w", nil, &getback)
321                 case "create":
322                         err = arv.Create("collections",
323                                 Dict{"collection": Dict{"name": "testing"}},
324                                 &getback)
325                 }
326                 c.Check(err, IsNil)
327                 c.Assert(getback["name"], Equals, "testing")
328         }
329 }