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