Merge branch '20257-http-import' refs #20257
[arvados.git] / lib / controller / router / request_test.go
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 package router
6
7 import (
8         "bytes"
9         "encoding/json"
10         "io"
11         "mime/multipart"
12         "net/http"
13         "net/http/httptest"
14         "net/url"
15
16         "git.arvados.org/arvados.git/sdk/go/arvados"
17         "git.arvados.org/arvados.git/sdk/go/arvadostest"
18         check "gopkg.in/check.v1"
19 )
20
21 type testReq struct {
22         method   string
23         path     string
24         token    string // default is ActiveTokenV2; use noToken to omit
25         param    map[string]interface{}
26         attrs    map[string]interface{}
27         attrsKey string
28         header   http.Header
29
30         // variations on request formatting
31         json            bool
32         jsonAttrsTop    bool
33         jsonStringParam bool
34         tokenInBody     bool
35         tokenInQuery    bool
36         noContentType   bool
37
38         body *bytes.Buffer
39 }
40
41 const noToken = "(no token)"
42
43 func (tr *testReq) Request() *http.Request {
44         param := map[string]interface{}{}
45         for k, v := range tr.param {
46                 param[k] = v
47         }
48
49         if tr.body != nil {
50                 // caller provided a buffer
51         } else if tr.json {
52                 if tr.jsonAttrsTop {
53                         for k, v := range tr.attrs {
54                                 if tr.jsonStringParam {
55                                         j, err := json.Marshal(v)
56                                         if err != nil {
57                                                 panic(err)
58                                         }
59                                         param[k] = string(j)
60                                 } else {
61                                         param[k] = v
62                                 }
63                         }
64                 } else if tr.attrs != nil {
65                         if tr.jsonStringParam {
66                                 j, err := json.Marshal(tr.attrs)
67                                 if err != nil {
68                                         panic(err)
69                                 }
70                                 param[tr.attrsKey] = string(j)
71                         } else {
72                                 param[tr.attrsKey] = tr.attrs
73                         }
74                 }
75                 tr.body = bytes.NewBuffer(nil)
76                 err := json.NewEncoder(tr.body).Encode(param)
77                 if err != nil {
78                         panic(err)
79                 }
80         } else {
81                 values := make(url.Values)
82                 for k, v := range param {
83                         if vs, ok := v.(string); ok && !tr.jsonStringParam {
84                                 values.Set(k, vs)
85                         } else {
86                                 jv, err := json.Marshal(v)
87                                 if err != nil {
88                                         panic(err)
89                                 }
90                                 values.Set(k, string(jv))
91                         }
92                 }
93                 if tr.attrs != nil {
94                         jattrs, err := json.Marshal(tr.attrs)
95                         if err != nil {
96                                 panic(err)
97                         }
98                         values.Set(tr.attrsKey, string(jattrs))
99                 }
100                 tr.body = bytes.NewBuffer(nil)
101                 io.WriteString(tr.body, values.Encode())
102         }
103         method := tr.method
104         if method == "" {
105                 method = "GET"
106         }
107         path := tr.path
108         if path == "" {
109                 path = "example/test/path"
110         }
111         req := httptest.NewRequest(method, "https://an.example/"+path, tr.body)
112         token := tr.token
113         if token == "" {
114                 token = arvadostest.ActiveTokenV2
115         }
116         if token != noToken {
117                 req.Header.Set("Authorization", "Bearer "+token)
118         }
119         if tr.json {
120                 req.Header.Set("Content-Type", "application/json")
121         } else if tr.header.Get("Content-Type") == "" {
122                 req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
123         }
124         for k, v := range tr.header {
125                 req.Header[k] = append([]string(nil), v...)
126         }
127         return req
128 }
129
130 func (tr *testReq) bodyContent() string {
131         return string(tr.body.Bytes())
132 }
133
134 func (s *RouterSuite) TestAttrsInBody(c *check.C) {
135         attrs := map[string]interface{}{"foo": "bar"}
136
137         multipartBody := new(bytes.Buffer)
138         multipartWriter := multipart.NewWriter(multipartBody)
139         multipartWriter.WriteField("attrs", `{"foo":"bar"}`)
140         multipartWriter.Close()
141
142         for _, tr := range []testReq{
143                 {attrsKey: "model_name", json: true, attrs: attrs},
144                 {attrsKey: "model_name", json: true, attrs: attrs, jsonAttrsTop: true},
145                 {attrsKey: "model_name", json: true, attrs: attrs, jsonAttrsTop: true, jsonStringParam: true},
146                 {attrsKey: "model_name", json: true, attrs: attrs, jsonAttrsTop: false, jsonStringParam: true},
147                 {body: multipartBody, header: http.Header{"Content-Type": []string{multipartWriter.FormDataContentType()}}},
148         } {
149                 c.Logf("tr: %#v", tr)
150                 req := tr.Request()
151                 var opts struct{ Attrs struct{ Foo string } }
152                 params, err := s.rtr.loadRequestParams(req, tr.attrsKey, &opts)
153                 c.Logf("params: %#v", params)
154                 c.Assert(err, check.IsNil)
155                 c.Check(params, check.NotNil)
156                 c.Check(opts.Attrs.Foo, check.Equals, "bar")
157                 if c.Check(params["attrs"], check.FitsTypeOf, map[string]interface{}{}) {
158                         c.Check(params["attrs"].(map[string]interface{})["foo"], check.Equals, "bar")
159                 }
160         }
161 }
162
163 func (s *RouterSuite) TestBoolParam(c *check.C) {
164         testKey := "ensure_unique_name"
165
166         for i, tr := range []testReq{
167                 {method: "POST", param: map[string]interface{}{testKey: false}, json: true},
168                 {method: "POST", param: map[string]interface{}{testKey: false}},
169                 {method: "POST", param: map[string]interface{}{testKey: "false"}},
170                 {method: "POST", param: map[string]interface{}{testKey: "0"}},
171                 {method: "POST", param: map[string]interface{}{testKey: ""}},
172         } {
173                 c.Logf("#%d, tr: %#v", i, tr)
174                 req := tr.Request()
175                 c.Logf("tr.body: %s", tr.bodyContent())
176                 var opts struct{ EnsureUniqueName bool }
177                 params, err := s.rtr.loadRequestParams(req, tr.attrsKey, &opts)
178                 c.Logf("params: %#v", params)
179                 c.Assert(err, check.IsNil)
180                 c.Check(opts.EnsureUniqueName, check.Equals, false)
181                 if c.Check(params, check.NotNil) {
182                         c.Check(params[testKey], check.Equals, false)
183                 }
184         }
185
186         for i, tr := range []testReq{
187                 {method: "POST", param: map[string]interface{}{testKey: true}, json: true},
188                 {method: "POST", param: map[string]interface{}{testKey: true}},
189                 {method: "POST", param: map[string]interface{}{testKey: "true"}},
190                 {method: "POST", param: map[string]interface{}{testKey: "1"}},
191         } {
192                 c.Logf("#%d, tr: %#v", i, tr)
193                 req := tr.Request()
194                 c.Logf("tr.body: %s", tr.bodyContent())
195                 var opts struct {
196                         EnsureUniqueName bool `json:"ensure_unique_name"`
197                 }
198                 params, err := s.rtr.loadRequestParams(req, tr.attrsKey, &opts)
199                 c.Logf("params: %#v", params)
200                 c.Assert(err, check.IsNil)
201                 c.Check(opts.EnsureUniqueName, check.Equals, true)
202                 if c.Check(params, check.NotNil) {
203                         c.Check(params[testKey], check.Equals, true)
204                 }
205         }
206 }
207
208 func (s *RouterSuite) TestOrderParam(c *check.C) {
209         for i, tr := range []testReq{
210                 {method: "POST", param: map[string]interface{}{"order": ""}, json: true},
211                 {method: "POST", param: map[string]interface{}{"order": ""}, json: false},
212                 {method: "POST", param: map[string]interface{}{"order": []string{}}, json: true},
213                 {method: "POST", param: map[string]interface{}{"order": []string{}}, json: false},
214                 {method: "POST", param: map[string]interface{}{}, json: true},
215                 {method: "POST", param: map[string]interface{}{}, json: false},
216         } {
217                 c.Logf("#%d, tr: %#v", i, tr)
218                 req := tr.Request()
219                 params, err := s.rtr.loadRequestParams(req, tr.attrsKey, nil)
220                 c.Assert(err, check.IsNil)
221                 c.Assert(params, check.NotNil)
222                 if order, ok := params["order"]; ok && order != nil {
223                         c.Check(order, check.DeepEquals, []interface{}{})
224                 }
225         }
226
227         for i, tr := range []testReq{
228                 {method: "POST", param: map[string]interface{}{"order": "foo,bar desc"}, json: true},
229                 {method: "POST", param: map[string]interface{}{"order": "foo,bar desc"}, json: false},
230                 {method: "POST", param: map[string]interface{}{"order": "[\"foo\", \"bar desc\"]"}, json: false},
231                 {method: "POST", param: map[string]interface{}{"order": []string{"foo", "bar desc"}}, json: true},
232                 {method: "POST", param: map[string]interface{}{"order": []string{"foo", "bar desc"}}, json: false},
233         } {
234                 c.Logf("#%d, tr: %#v", i, tr)
235                 req := tr.Request()
236                 var opts arvados.ListOptions
237                 params, err := s.rtr.loadRequestParams(req, tr.attrsKey, &opts)
238                 c.Assert(err, check.IsNil)
239                 c.Check(opts.Order, check.DeepEquals, []string{"foo", "bar desc"})
240                 if _, ok := params["order"].([]string); ok {
241                         c.Check(params["order"], check.DeepEquals, []string{"foo", "bar desc"})
242                 } else {
243                         c.Check(params["order"], check.DeepEquals, []interface{}{"foo", "bar desc"})
244                 }
245         }
246 }