Merge branch '19464-capture-git-info' refs #19464
[arvados.git] / sdk / go / arvados / client_test.go
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: Apache-2.0
4
5 package arvados
6
7 import (
8         "bytes"
9         "fmt"
10         "io/ioutil"
11         "net/http"
12         "net/url"
13         "os"
14         "strings"
15         "sync"
16         "testing/iotest"
17
18         check "gopkg.in/check.v1"
19 )
20
21 type stubTransport struct {
22         Responses map[string]string
23         Requests  []http.Request
24         sync.Mutex
25 }
26
27 func (stub *stubTransport) RoundTrip(req *http.Request) (*http.Response, error) {
28         stub.Lock()
29         stub.Requests = append(stub.Requests, *req)
30         stub.Unlock()
31
32         resp := &http.Response{
33                 Status:     "200 OK",
34                 StatusCode: 200,
35                 Proto:      "HTTP/1.1",
36                 ProtoMajor: 1,
37                 ProtoMinor: 1,
38                 Request:    req,
39         }
40         str := stub.Responses[req.URL.Path]
41         if str == "" {
42                 resp.Status = "404 Not Found"
43                 resp.StatusCode = 404
44                 str = "{}"
45         }
46         buf := bytes.NewBufferString(str)
47         resp.Body = ioutil.NopCloser(buf)
48         resp.ContentLength = int64(buf.Len())
49         return resp, nil
50 }
51
52 type errorTransport struct{}
53
54 func (stub *errorTransport) RoundTrip(req *http.Request) (*http.Response, error) {
55         return nil, fmt.Errorf("something awful happened")
56 }
57
58 type timeoutTransport struct {
59         response []byte
60 }
61
62 func (stub *timeoutTransport) RoundTrip(req *http.Request) (*http.Response, error) {
63         return &http.Response{
64                 Status:     "200 OK",
65                 StatusCode: 200,
66                 Proto:      "HTTP/1.1",
67                 ProtoMajor: 1,
68                 ProtoMinor: 1,
69                 Request:    req,
70                 Body:       ioutil.NopCloser(iotest.TimeoutReader(bytes.NewReader(stub.response))),
71         }, nil
72 }
73
74 var _ = check.Suite(&clientSuite{})
75
76 type clientSuite struct{}
77
78 func (*clientSuite) TestCurrentUser(c *check.C) {
79         stub := &stubTransport{
80                 Responses: map[string]string{
81                         "/arvados/v1/users/current": `{"uuid":"zzzzz-abcde-012340123401234"}`,
82                 },
83         }
84         client := &Client{
85                 Client: &http.Client{
86                         Transport: stub,
87                 },
88                 APIHost:   "zzzzz.arvadosapi.com",
89                 AuthToken: "xyzzy",
90         }
91         u, err := client.CurrentUser()
92         c.Check(err, check.IsNil)
93         c.Check(u.UUID, check.Equals, "zzzzz-abcde-012340123401234")
94         c.Check(stub.Requests, check.Not(check.HasLen), 0)
95         hdr := stub.Requests[len(stub.Requests)-1].Header
96         c.Check(hdr.Get("Authorization"), check.Equals, "OAuth2 xyzzy")
97
98         client.Client.Transport = &errorTransport{}
99         u, err = client.CurrentUser()
100         c.Check(err, check.NotNil)
101 }
102
103 func (*clientSuite) TestAnythingToValues(c *check.C) {
104         type testCase struct {
105                 in interface{}
106                 // ok==nil means anythingToValues should return an
107                 // error, otherwise it's a func that returns true if
108                 // out is correct
109                 ok func(out url.Values) bool
110         }
111         for _, tc := range []testCase{
112                 {
113                         in: map[string]interface{}{"foo": "bar"},
114                         ok: func(out url.Values) bool {
115                                 return out.Get("foo") == "bar"
116                         },
117                 },
118                 {
119                         in: map[string]interface{}{"foo": 2147483647},
120                         ok: func(out url.Values) bool {
121                                 return out.Get("foo") == "2147483647"
122                         },
123                 },
124                 {
125                         in: map[string]interface{}{"foo": 1.234},
126                         ok: func(out url.Values) bool {
127                                 return out.Get("foo") == "1.234"
128                         },
129                 },
130                 {
131                         in: map[string]interface{}{"foo": "1.234"},
132                         ok: func(out url.Values) bool {
133                                 return out.Get("foo") == "1.234"
134                         },
135                 },
136                 {
137                         in: map[string]interface{}{"foo": map[string]interface{}{"bar": 1.234}},
138                         ok: func(out url.Values) bool {
139                                 return out.Get("foo") == `{"bar":1.234}`
140                         },
141                 },
142                 {
143                         in: url.Values{"foo": {"bar"}},
144                         ok: func(out url.Values) bool {
145                                 return out.Get("foo") == "bar"
146                         },
147                 },
148                 {
149                         in: 1234,
150                         ok: nil,
151                 },
152                 {
153                         in: []string{"foo"},
154                         ok: nil,
155                 },
156         } {
157                 c.Logf("%#v", tc.in)
158                 out, err := anythingToValues(tc.in)
159                 if tc.ok == nil {
160                         c.Check(err, check.NotNil)
161                         continue
162                 }
163                 c.Check(err, check.IsNil)
164                 c.Check(tc.ok(out), check.Equals, true)
165         }
166 }
167
168 func (*clientSuite) TestLoadConfig(c *check.C) {
169         oldenv := os.Environ()
170         defer func() {
171                 os.Clearenv()
172                 for _, s := range oldenv {
173                         i := strings.IndexRune(s, '=')
174                         os.Setenv(s[:i], s[i+1:])
175                 }
176         }()
177
178         tmp := c.MkDir()
179         os.Setenv("HOME", tmp)
180         for _, s := range os.Environ() {
181                 if strings.HasPrefix(s, "ARVADOS_") {
182                         i := strings.IndexRune(s, '=')
183                         os.Unsetenv(s[:i])
184                 }
185         }
186         os.Mkdir(tmp+"/.config", 0777)
187         os.Mkdir(tmp+"/.config/arvados", 0777)
188
189         // Use $HOME/.config/arvados/settings.conf if no env vars are
190         // set
191         os.WriteFile(tmp+"/.config/arvados/settings.conf", []byte(`
192                 ARVADOS_API_HOST = localhost:1
193                 ARVADOS_API_TOKEN = token_from_settings_file1
194         `), 0777)
195         client := NewClientFromEnv()
196         c.Check(client.AuthToken, check.Equals, "token_from_settings_file1")
197         c.Check(client.APIHost, check.Equals, "localhost:1")
198         c.Check(client.Insecure, check.Equals, false)
199
200         // ..._INSECURE=true, comments, ignored lines in settings.conf
201         os.WriteFile(tmp+"/.config/arvados/settings.conf", []byte(`
202                 (ignored) = (ignored)
203                 #ARVADOS_API_HOST = localhost:2
204                 ARVADOS_API_TOKEN = token_from_settings_file2
205                 ARVADOS_API_HOST_INSECURE = true
206         `), 0777)
207         client = NewClientFromEnv()
208         c.Check(client.AuthToken, check.Equals, "token_from_settings_file2")
209         c.Check(client.APIHost, check.Equals, "")
210         c.Check(client.Insecure, check.Equals, true)
211
212         // Environment variables override settings.conf
213         os.Setenv("ARVADOS_API_HOST", "[::]:3")
214         os.Setenv("ARVADOS_API_HOST_INSECURE", "0")
215         client = NewClientFromEnv()
216         c.Check(client.AuthToken, check.Equals, "token_from_settings_file2")
217         c.Check(client.APIHost, check.Equals, "[::]:3")
218         c.Check(client.Insecure, check.Equals, false)
219 }