Merge branch '2826-simple-go-sdk' closes #2826
[arvados.git] / sdk / go / src / arvados.org / keepclient / keepclient_test.go
1 package keepclient
2
3 import (
4         "arvados.org/sdk"
5         "arvados.org/streamer"
6         "crypto/md5"
7         "flag"
8         "fmt"
9         . "gopkg.in/check.v1"
10         "io"
11         "io/ioutil"
12         "log"
13         "net"
14         "net/http"
15         "os"
16         "os/exec"
17         "strings"
18         "testing"
19 )
20
21 // Gocheck boilerplate
22 func Test(t *testing.T) {
23         TestingT(t)
24 }
25
26 // Gocheck boilerplate
27 var _ = Suite(&ServerRequiredSuite{})
28 var _ = Suite(&StandaloneSuite{})
29
30 var no_server = flag.Bool("no-server", false, "Skip 'ServerRequireSuite'")
31
32 // Tests that require the Keep server running
33 type ServerRequiredSuite struct{}
34
35 // Standalone tests
36 type StandaloneSuite struct{}
37
38 func pythonDir() string {
39         gopath := os.Getenv("GOPATH")
40         return fmt.Sprintf("%s/../python/tests", strings.Split(gopath, ":")[0])
41 }
42
43 func (s *ServerRequiredSuite) SetUpSuite(c *C) {
44         if *no_server {
45                 c.Skip("Skipping tests that require server")
46         } else {
47                 os.Chdir(pythonDir())
48                 if err := exec.Command("python", "run_test_server.py", "start").Run(); err != nil {
49                         panic("'python run_test_server.py start' returned error")
50                 }
51                 if err := exec.Command("python", "run_test_server.py", "start_keep").Run(); err != nil {
52                         panic("'python run_test_server.py start_keep' returned error")
53                 }
54         }
55 }
56
57 func (s *ServerRequiredSuite) TearDownSuite(c *C) {
58         os.Chdir(pythonDir())
59         exec.Command("python", "run_test_server.py", "stop_keep").Run()
60         exec.Command("python", "run_test_server.py", "stop").Run()
61 }
62
63 func (s *ServerRequiredSuite) TestMakeKeepClient(c *C) {
64         os.Setenv("ARVADOS_API_HOST", "localhost:3001")
65         os.Setenv("ARVADOS_API_TOKEN", "4axaw8zxe0qm22wa6urpp5nskcne8z88cvbupv653y1njyi05h")
66         os.Setenv("ARVADOS_API_HOST_INSECURE", "true")
67
68         arv, err := sdk.MakeArvadosClient()
69         c.Assert(err, Equals, nil)
70
71         kc, err := MakeKeepClient(&arv)
72
73         c.Assert(err, Equals, nil)
74         c.Check(len(kc.ServiceRoots()), Equals, 2)
75         c.Check(kc.ServiceRoots()[0], Equals, "http://localhost:25107")
76         c.Check(kc.ServiceRoots()[1], Equals, "http://localhost:25108")
77 }
78
79 func (s *StandaloneSuite) TestShuffleServiceRoots(c *C) {
80         kc := KeepClient{}
81         kc.SetServiceRoots([]string{"http://localhost:25107", "http://localhost:25108", "http://localhost:25109", "http://localhost:25110", "http://localhost:25111", "http://localhost:25112", "http://localhost:25113", "http://localhost:25114", "http://localhost:25115", "http://localhost:25116", "http://localhost:25117", "http://localhost:25118", "http://localhost:25119", "http://localhost:25120", "http://localhost:25121", "http://localhost:25122", "http://localhost:25123"})
82
83         // "foo" acbd18db4cc2f85cedef654fccc4a4d8
84         foo_shuffle := []string{"http://localhost:25116", "http://localhost:25120", "http://localhost:25119", "http://localhost:25122", "http://localhost:25108", "http://localhost:25114", "http://localhost:25112", "http://localhost:25107", "http://localhost:25118", "http://localhost:25111", "http://localhost:25113", "http://localhost:25121", "http://localhost:25110", "http://localhost:25117", "http://localhost:25109", "http://localhost:25115", "http://localhost:25123"}
85         c.Check(kc.shuffledServiceRoots("acbd18db4cc2f85cedef654fccc4a4d8"), DeepEquals, foo_shuffle)
86
87         // "bar" 37b51d194a7513e45b56f6524f2d51f2
88         bar_shuffle := []string{"http://localhost:25108", "http://localhost:25112", "http://localhost:25119", "http://localhost:25107", "http://localhost:25110", "http://localhost:25116", "http://localhost:25122", "http://localhost:25120", "http://localhost:25121", "http://localhost:25117", "http://localhost:25111", "http://localhost:25123", "http://localhost:25118", "http://localhost:25113", "http://localhost:25114", "http://localhost:25115", "http://localhost:25109"}
89         c.Check(kc.shuffledServiceRoots("37b51d194a7513e45b56f6524f2d51f2"), DeepEquals, bar_shuffle)
90 }
91
92 type StubPutHandler struct {
93         c              *C
94         expectPath     string
95         expectApiToken string
96         expectBody     string
97         handled        chan string
98 }
99
100 func (this StubPutHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
101         this.c.Check(req.URL.Path, Equals, "/"+this.expectPath)
102         this.c.Check(req.Header.Get("Authorization"), Equals, fmt.Sprintf("OAuth2 %s", this.expectApiToken))
103         body, err := ioutil.ReadAll(req.Body)
104         this.c.Check(err, Equals, nil)
105         this.c.Check(body, DeepEquals, []byte(this.expectBody))
106         resp.WriteHeader(200)
107         this.handled <- fmt.Sprintf("http://%s", req.Host)
108 }
109
110 func RunBogusKeepServer(st http.Handler, port int) (listener net.Listener, url string) {
111         var err error
112         listener, err = net.ListenTCP("tcp", &net.TCPAddr{Port: port})
113         if err != nil {
114                 panic(fmt.Sprintf("Could not listen on tcp port %v", port))
115         }
116
117         url = fmt.Sprintf("http://localhost:%d", port)
118
119         go http.Serve(listener, st)
120         return listener, url
121 }
122
123 func UploadToStubHelper(c *C, st http.Handler, f func(KeepClient, string,
124         io.ReadCloser, io.WriteCloser, chan uploadStatus)) {
125
126         listener, url := RunBogusKeepServer(st, 2990)
127         defer listener.Close()
128
129         arv, _ := sdk.MakeArvadosClient()
130         arv.ApiToken = "abc123"
131
132         kc, _ := MakeKeepClient(&arv)
133
134         reader, writer := io.Pipe()
135         upload_status := make(chan uploadStatus)
136
137         f(kc, url, reader, writer, upload_status)
138 }
139
140 func (s *StandaloneSuite) TestUploadToStubKeepServer(c *C) {
141         log.Printf("TestUploadToStubKeepServer")
142
143         st := StubPutHandler{
144                 c,
145                 "acbd18db4cc2f85cedef654fccc4a4d8",
146                 "abc123",
147                 "foo",
148                 make(chan string)}
149
150         UploadToStubHelper(c, st,
151                 func(kc KeepClient, url string, reader io.ReadCloser,
152                         writer io.WriteCloser, upload_status chan uploadStatus) {
153
154                         go kc.uploadToKeepServer(url, st.expectPath, reader, upload_status, int64(len("foo")))
155
156                         writer.Write([]byte("foo"))
157                         writer.Close()
158
159                         <-st.handled
160                         status := <-upload_status
161                         c.Check(status, DeepEquals, uploadStatus{nil, fmt.Sprintf("%s/%s", url, st.expectPath), 200, 1, ""})
162                 })
163
164         log.Printf("TestUploadToStubKeepServer done")
165 }
166
167 func (s *StandaloneSuite) TestUploadToStubKeepServerBufferReader(c *C) {
168         log.Printf("TestUploadToStubKeepServerBufferReader")
169
170         st := StubPutHandler{
171                 c,
172                 "acbd18db4cc2f85cedef654fccc4a4d8",
173                 "abc123",
174                 "foo",
175                 make(chan string)}
176
177         UploadToStubHelper(c, st,
178                 func(kc KeepClient, url string, reader io.ReadCloser,
179                         writer io.WriteCloser, upload_status chan uploadStatus) {
180
181                         tr := streamer.AsyncStreamFromReader(512, reader)
182                         defer tr.Close()
183
184                         br1 := tr.MakeStreamReader()
185
186                         go kc.uploadToKeepServer(url, st.expectPath, br1, upload_status, 3)
187
188                         writer.Write([]byte("foo"))
189                         writer.Close()
190
191                         <-st.handled
192
193                         status := <-upload_status
194                         c.Check(status, DeepEquals, uploadStatus{nil, fmt.Sprintf("%s/%s", url, st.expectPath), 200, 1, ""})
195                 })
196
197         log.Printf("TestUploadToStubKeepServerBufferReader done")
198 }
199
200 type FailHandler struct {
201         handled chan string
202 }
203
204 func (this FailHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
205         resp.WriteHeader(500)
206         this.handled <- fmt.Sprintf("http://%s", req.Host)
207 }
208
209 func (s *StandaloneSuite) TestFailedUploadToStubKeepServer(c *C) {
210         log.Printf("TestFailedUploadToStubKeepServer")
211
212         st := FailHandler{
213                 make(chan string)}
214
215         hash := "acbd18db4cc2f85cedef654fccc4a4d8"
216
217         UploadToStubHelper(c, st,
218                 func(kc KeepClient, url string, reader io.ReadCloser,
219                         writer io.WriteCloser, upload_status chan uploadStatus) {
220
221                         go kc.uploadToKeepServer(url, hash, reader, upload_status, 3)
222
223                         writer.Write([]byte("foo"))
224                         writer.Close()
225
226                         <-st.handled
227
228                         status := <-upload_status
229                         c.Check(status.url, Equals, fmt.Sprintf("%s/%s", url, hash))
230                         c.Check(status.statusCode, Equals, 500)
231                 })
232         log.Printf("TestFailedUploadToStubKeepServer done")
233 }
234
235 type KeepServer struct {
236         listener net.Listener
237         url      string
238 }
239
240 func RunSomeFakeKeepServers(st http.Handler, n int, port int) (ks []KeepServer) {
241         ks = make([]KeepServer, n)
242
243         for i := 0; i < n; i += 1 {
244                 boguslistener, bogusurl := RunBogusKeepServer(st, port+i)
245                 ks[i] = KeepServer{boguslistener, bogusurl}
246         }
247
248         return ks
249 }
250
251 func (s *StandaloneSuite) TestPutB(c *C) {
252         log.Printf("TestPutB")
253
254         hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
255
256         st := StubPutHandler{
257                 c,
258                 hash,
259                 "abc123",
260                 "foo",
261                 make(chan string, 2)}
262
263         arv, _ := sdk.MakeArvadosClient()
264         kc, _ := MakeKeepClient(&arv)
265
266         kc.Want_replicas = 2
267         arv.ApiToken = "abc123"
268         service_roots := make([]string, 5)
269
270         ks := RunSomeFakeKeepServers(st, 5, 2990)
271
272         for i := 0; i < len(ks); i += 1 {
273                 service_roots[i] = ks[i].url
274                 defer ks[i].listener.Close()
275         }
276
277         kc.SetServiceRoots(service_roots)
278
279         kc.PutB([]byte("foo"))
280
281         shuff := kc.shuffledServiceRoots(fmt.Sprintf("%x", md5.Sum([]byte("foo"))))
282
283         s1 := <-st.handled
284         s2 := <-st.handled
285         c.Check((s1 == shuff[0] && s2 == shuff[1]) ||
286                 (s1 == shuff[1] && s2 == shuff[0]),
287                 Equals,
288                 true)
289
290         log.Printf("TestPutB done")
291 }
292
293 func (s *StandaloneSuite) TestPutHR(c *C) {
294         log.Printf("TestPutHR")
295
296         hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
297
298         st := StubPutHandler{
299                 c,
300                 hash,
301                 "abc123",
302                 "foo",
303                 make(chan string, 2)}
304
305         arv, _ := sdk.MakeArvadosClient()
306         kc, _ := MakeKeepClient(&arv)
307
308         kc.Want_replicas = 2
309         arv.ApiToken = "abc123"
310         service_roots := make([]string, 5)
311
312         ks := RunSomeFakeKeepServers(st, 5, 2990)
313
314         for i := 0; i < len(ks); i += 1 {
315                 service_roots[i] = ks[i].url
316                 defer ks[i].listener.Close()
317         }
318
319         kc.SetServiceRoots(service_roots)
320
321         reader, writer := io.Pipe()
322
323         go func() {
324                 writer.Write([]byte("foo"))
325                 writer.Close()
326         }()
327
328         kc.PutHR(hash, reader, 3)
329
330         shuff := kc.shuffledServiceRoots(hash)
331         log.Print(shuff)
332
333         s1 := <-st.handled
334         s2 := <-st.handled
335
336         c.Check((s1 == shuff[0] && s2 == shuff[1]) ||
337                 (s1 == shuff[1] && s2 == shuff[0]),
338                 Equals,
339                 true)
340
341         log.Printf("TestPutHR done")
342 }
343
344 func (s *StandaloneSuite) TestPutWithFail(c *C) {
345         log.Printf("TestPutWithFail")
346
347         hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
348
349         st := StubPutHandler{
350                 c,
351                 hash,
352                 "abc123",
353                 "foo",
354                 make(chan string, 2)}
355
356         fh := FailHandler{
357                 make(chan string, 1)}
358
359         arv, err := sdk.MakeArvadosClient()
360         kc, _ := MakeKeepClient(&arv)
361
362         kc.Want_replicas = 2
363         arv.ApiToken = "abc123"
364         service_roots := make([]string, 5)
365
366         ks1 := RunSomeFakeKeepServers(st, 4, 2990)
367         ks2 := RunSomeFakeKeepServers(fh, 1, 2995)
368
369         for i, k := range ks1 {
370                 service_roots[i] = k.url
371                 defer k.listener.Close()
372         }
373         for i, k := range ks2 {
374                 service_roots[len(ks1)+i] = k.url
375                 defer k.listener.Close()
376         }
377
378         kc.SetServiceRoots(service_roots)
379
380         shuff := kc.shuffledServiceRoots(fmt.Sprintf("%x", md5.Sum([]byte("foo"))))
381
382         phash, replicas, err := kc.PutB([]byte("foo"))
383
384         <-fh.handled
385
386         c.Check(err, Equals, nil)
387         c.Check(phash, Equals, "")
388         c.Check(replicas, Equals, 2)
389         c.Check(<-st.handled, Equals, shuff[1])
390         c.Check(<-st.handled, Equals, shuff[2])
391 }
392
393 func (s *StandaloneSuite) TestPutWithTooManyFail(c *C) {
394         log.Printf("TestPutWithTooManyFail")
395
396         hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
397
398         st := StubPutHandler{
399                 c,
400                 hash,
401                 "abc123",
402                 "foo",
403                 make(chan string, 1)}
404
405         fh := FailHandler{
406                 make(chan string, 4)}
407
408         arv, err := sdk.MakeArvadosClient()
409         kc, _ := MakeKeepClient(&arv)
410
411         kc.Want_replicas = 2
412         arv.ApiToken = "abc123"
413         service_roots := make([]string, 5)
414
415         ks1 := RunSomeFakeKeepServers(st, 1, 2990)
416         ks2 := RunSomeFakeKeepServers(fh, 4, 2991)
417
418         for i, k := range ks1 {
419                 service_roots[i] = k.url
420                 defer k.listener.Close()
421         }
422         for i, k := range ks2 {
423                 service_roots[len(ks1)+i] = k.url
424                 defer k.listener.Close()
425         }
426
427         kc.SetServiceRoots(service_roots)
428
429         shuff := kc.shuffledServiceRoots(fmt.Sprintf("%x", md5.Sum([]byte("foo"))))
430
431         _, replicas, err := kc.PutB([]byte("foo"))
432
433         c.Check(err, Equals, InsufficientReplicasError)
434         c.Check(replicas, Equals, 1)
435         c.Check(<-st.handled, Equals, shuff[1])
436
437         log.Printf("TestPutWithTooManyFail done")
438 }
439
440 type StubGetHandler struct {
441         c              *C
442         expectPath     string
443         expectApiToken string
444         returnBody     []byte
445 }
446
447 func (this StubGetHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
448         this.c.Check(req.URL.Path, Equals, "/"+this.expectPath)
449         this.c.Check(req.Header.Get("Authorization"), Equals, fmt.Sprintf("OAuth2 %s", this.expectApiToken))
450         resp.Header().Set("Content-Length", fmt.Sprintf("%d", len(this.returnBody)))
451         resp.Write(this.returnBody)
452 }
453
454 func (s *StandaloneSuite) TestGet(c *C) {
455         log.Printf("TestGet")
456
457         hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
458
459         st := StubGetHandler{
460                 c,
461                 hash,
462                 "abc123",
463                 []byte("foo")}
464
465         listener, url := RunBogusKeepServer(st, 2990)
466         defer listener.Close()
467
468         arv, err := sdk.MakeArvadosClient()
469         kc, _ := MakeKeepClient(&arv)
470         arv.ApiToken = "abc123"
471         kc.SetServiceRoots([]string{url})
472
473         r, n, url2, err := kc.Get(hash)
474         defer r.Close()
475         c.Check(err, Equals, nil)
476         c.Check(n, Equals, int64(3))
477         c.Check(url2, Equals, fmt.Sprintf("%s/%s", url, hash))
478
479         content, err2 := ioutil.ReadAll(r)
480         c.Check(err2, Equals, nil)
481         c.Check(content, DeepEquals, []byte("foo"))
482
483         log.Printf("TestGet done")
484 }
485
486 func (s *StandaloneSuite) TestGetFail(c *C) {
487         hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
488
489         st := FailHandler{make(chan string, 1)}
490
491         listener, url := RunBogusKeepServer(st, 2990)
492         defer listener.Close()
493
494         arv, err := sdk.MakeArvadosClient()
495         kc, _ := MakeKeepClient(&arv)
496         arv.ApiToken = "abc123"
497         kc.SetServiceRoots([]string{url})
498
499         r, n, url2, err := kc.Get(hash)
500         c.Check(err, Equals, BlockNotFound)
501         c.Check(n, Equals, int64(0))
502         c.Check(url2, Equals, "")
503         c.Check(r, Equals, nil)
504 }
505
506 type BarHandler struct {
507         handled chan string
508 }
509
510 func (this BarHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
511         resp.Write([]byte("bar"))
512         this.handled <- fmt.Sprintf("http://%s", req.Host)
513 }
514
515 func (s *StandaloneSuite) TestChecksum(c *C) {
516         foohash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
517         barhash := fmt.Sprintf("%x", md5.Sum([]byte("bar")))
518
519         st := BarHandler{make(chan string, 1)}
520
521         listener, url := RunBogusKeepServer(st, 2990)
522         defer listener.Close()
523
524         arv, err := sdk.MakeArvadosClient()
525         kc, _ := MakeKeepClient(&arv)
526         arv.ApiToken = "abc123"
527         kc.SetServiceRoots([]string{url})
528
529         r, n, _, err := kc.Get(barhash)
530         _, err = ioutil.ReadAll(r)
531         c.Check(n, Equals, int64(3))
532         c.Check(err, Equals, nil)
533
534         <-st.handled
535
536         r, n, _, err = kc.Get(foohash)
537         _, err = ioutil.ReadAll(r)
538         c.Check(n, Equals, int64(3))
539         c.Check(err, Equals, BadChecksum)
540
541         <-st.handled
542 }
543
544 func (s *StandaloneSuite) TestGetWithFailures(c *C) {
545
546         hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
547
548         fh := FailHandler{
549                 make(chan string, 1)}
550
551         st := StubGetHandler{
552                 c,
553                 hash,
554                 "abc123",
555                 []byte("foo")}
556
557         arv, err := sdk.MakeArvadosClient()
558         kc, _ := MakeKeepClient(&arv)
559         arv.ApiToken = "abc123"
560         service_roots := make([]string, 5)
561
562         ks1 := RunSomeFakeKeepServers(st, 1, 2990)
563         ks2 := RunSomeFakeKeepServers(fh, 4, 2991)
564
565         for i, k := range ks1 {
566                 service_roots[i] = k.url
567                 defer k.listener.Close()
568         }
569         for i, k := range ks2 {
570                 service_roots[len(ks1)+i] = k.url
571                 defer k.listener.Close()
572         }
573
574         kc.SetServiceRoots(service_roots)
575
576         r, n, url2, err := kc.Get(hash)
577         <-fh.handled
578         c.Check(err, Equals, nil)
579         c.Check(n, Equals, int64(3))
580         c.Check(url2, Equals, fmt.Sprintf("%s/%s", ks1[0].url, hash))
581
582         content, err2 := ioutil.ReadAll(r)
583         c.Check(err2, Equals, nil)
584         c.Check(content, DeepEquals, []byte("foo"))
585 }
586
587 func (s *ServerRequiredSuite) TestPutGetHead(c *C) {
588         os.Setenv("ARVADOS_API_HOST", "localhost:3001")
589         os.Setenv("ARVADOS_API_TOKEN", "4axaw8zxe0qm22wa6urpp5nskcne8z88cvbupv653y1njyi05h")
590         os.Setenv("ARVADOS_API_HOST_INSECURE", "true")
591
592         arv, err := sdk.MakeArvadosClient()
593         kc, err := MakeKeepClient(&arv)
594         c.Assert(err, Equals, nil)
595
596         hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
597
598         {
599                 n, _, err := kc.Ask(hash)
600                 c.Check(err, Equals, BlockNotFound)
601                 c.Check(n, Equals, int64(0))
602         }
603         {
604                 hash2, replicas, err := kc.PutB([]byte("foo"))
605                 c.Check(hash2, Equals, fmt.Sprintf("%s+%v", hash, 3))
606                 c.Check(replicas, Equals, 2)
607                 c.Check(err, Equals, nil)
608         }
609         {
610                 r, n, url2, err := kc.Get(hash)
611                 c.Check(err, Equals, nil)
612                 c.Check(n, Equals, int64(3))
613                 c.Check(url2, Equals, fmt.Sprintf("http://localhost:25108/%s", hash))
614
615                 content, err2 := ioutil.ReadAll(r)
616                 c.Check(err2, Equals, nil)
617                 c.Check(content, DeepEquals, []byte("foo"))
618         }
619         {
620                 n, url2, err := kc.Ask(hash)
621                 c.Check(err, Equals, nil)
622                 c.Check(n, Equals, int64(3))
623                 c.Check(url2, Equals, fmt.Sprintf("http://localhost:25108/%s", hash))
624         }
625 }
626
627 type StubProxyHandler struct {
628         handled chan string
629 }
630
631 func (this StubProxyHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
632         resp.Header().Set("X-Keep-Replicas-Stored", "2")
633         this.handled <- fmt.Sprintf("http://%s", req.Host)
634 }
635
636 func (s *StandaloneSuite) TestPutProxy(c *C) {
637         log.Printf("TestPutProxy")
638
639         st := StubProxyHandler{make(chan string, 1)}
640
641         arv, err := sdk.MakeArvadosClient()
642         kc, _ := MakeKeepClient(&arv)
643
644         kc.Want_replicas = 2
645         kc.Using_proxy = true
646         arv.ApiToken = "abc123"
647         service_roots := make([]string, 1)
648
649         ks1 := RunSomeFakeKeepServers(st, 1, 2990)
650
651         for i, k := range ks1 {
652                 service_roots[i] = k.url
653                 defer k.listener.Close()
654         }
655
656         kc.SetServiceRoots(service_roots)
657
658         _, replicas, err := kc.PutB([]byte("foo"))
659         <-st.handled
660
661         c.Check(err, Equals, nil)
662         c.Check(replicas, Equals, 2)
663
664         log.Printf("TestPutProxy done")
665 }
666
667 func (s *StandaloneSuite) TestPutProxyInsufficientReplicas(c *C) {
668         log.Printf("TestPutProxy")
669
670         st := StubProxyHandler{make(chan string, 1)}
671
672         arv, err := sdk.MakeArvadosClient()
673         kc, _ := MakeKeepClient(&arv)
674
675         kc.Want_replicas = 3
676         kc.Using_proxy = true
677         arv.ApiToken = "abc123"
678         service_roots := make([]string, 1)
679
680         ks1 := RunSomeFakeKeepServers(st, 1, 2990)
681
682         for i, k := range ks1 {
683                 service_roots[i] = k.url
684                 defer k.listener.Close()
685         }
686         kc.SetServiceRoots(service_roots)
687
688         _, replicas, err := kc.PutB([]byte("foo"))
689         <-st.handled
690
691         c.Check(err, Equals, InsufficientReplicasError)
692         c.Check(replicas, Equals, 2)
693
694         log.Printf("TestPutProxy done")
695 }
696
697 func (s *StandaloneSuite) TestMakeLocator(c *C) {
698         l := MakeLocator("91f372a266fe2bf2823cb8ec7fda31ce+3+Aabcde@12345678")
699
700         c.Check(l.Hash, Equals, "91f372a266fe2bf2823cb8ec7fda31ce")
701         c.Check(l.Size, Equals, 3)
702         c.Check(l.Signature, Equals, "abcde")
703         c.Check(l.Timestamp, Equals, "12345678")
704 }