data_test.go (8744B)
1 // Copyright 2017 The Hugo Authors. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13
14 package data
15
16 import (
17 "bytes"
18 "html/template"
19 "net/http"
20 "net/http/httptest"
21 "path/filepath"
22 "strings"
23 "testing"
24
25 "github.com/gohugoio/hugo/common/maps"
26
27 qt "github.com/frankban/quicktest"
28 )
29
30 func TestGetCSV(t *testing.T) {
31 t.Parallel()
32 c := qt.New(t)
33
34 for i, test := range []struct {
35 sep string
36 url string
37 content string
38 expect any
39 }{
40 // Remotes
41 {
42 ",",
43 `http://success/`,
44 "gomeetup,city\nyes,Sydney\nyes,San Francisco\nyes,Stockholm\n",
45 [][]string{{"gomeetup", "city"}, {"yes", "Sydney"}, {"yes", "San Francisco"}, {"yes", "Stockholm"}},
46 },
47 {
48 ",",
49 `http://error.extra.field/`,
50 "gomeetup,city\nyes,Sydney\nyes,San Francisco\nyes,Stockholm,EXTRA\n",
51 false,
52 },
53 {
54 ",",
55 `http://nofound/404`,
56 ``,
57 false,
58 },
59
60 // Locals
61 {
62 ";",
63 "pass/semi",
64 "gomeetup;city\nyes;Sydney\nyes;San Francisco\nyes;Stockholm\n",
65 [][]string{{"gomeetup", "city"}, {"yes", "Sydney"}, {"yes", "San Francisco"}, {"yes", "Stockholm"}},
66 },
67 {
68 ";",
69 "fail/no-file",
70 "",
71 false,
72 },
73 } {
74
75 c.Run(test.url, func(c *qt.C) {
76 msg := qt.Commentf("Test %d", i)
77
78 ns := newTestNs()
79
80 // Setup HTTP test server
81 var srv *httptest.Server
82 srv, ns.client = getTestServer(func(w http.ResponseWriter, r *http.Request) {
83 if !hasHeaderValue(r.Header, "Accept", "text/csv") && !hasHeaderValue(r.Header, "Accept", "text/plain") {
84 http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
85 return
86 }
87
88 if r.URL.Path == "/404" {
89 http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
90 return
91 }
92
93 w.Header().Add("Content-type", "text/csv")
94
95 w.Write([]byte(test.content))
96 })
97 defer func() { srv.Close() }()
98
99 // Setup local test file for schema-less URLs
100 if !strings.Contains(test.url, ":") && !strings.HasPrefix(test.url, "fail/") {
101 f, err := ns.deps.Fs.Source.Create(filepath.Join(ns.deps.Cfg.GetString("workingDir"), test.url))
102 c.Assert(err, qt.IsNil, msg)
103 f.WriteString(test.content)
104 f.Close()
105 }
106
107 // Get on with it
108 got, err := ns.GetCSV(test.sep, test.url)
109
110 if _, ok := test.expect.(bool); ok {
111 c.Assert(int(ns.deps.Log.LogCounters().ErrorCounter.Count()), qt.Equals, 1)
112 c.Assert(got, qt.IsNil)
113 return
114 }
115
116 c.Assert(err, qt.IsNil, msg)
117 c.Assert(int(ns.deps.Log.LogCounters().ErrorCounter.Count()), qt.Equals, 0)
118 c.Assert(got, qt.Not(qt.IsNil), msg)
119 c.Assert(got, qt.DeepEquals, test.expect, msg)
120 })
121
122 }
123 }
124
125 func TestGetJSON(t *testing.T) {
126 t.Parallel()
127 c := qt.New(t)
128
129 for i, test := range []struct {
130 url string
131 content string
132 expect any
133 }{
134 {
135 `http://success/`,
136 `{"gomeetup":["Sydney","San Francisco","Stockholm"]}`,
137 map[string]any{"gomeetup": []any{"Sydney", "San Francisco", "Stockholm"}},
138 },
139 {
140 `http://malformed/`,
141 `{gomeetup:["Sydney","San Francisco","Stockholm"]}`,
142 false,
143 },
144 {
145 `http://nofound/404`,
146 ``,
147 false,
148 },
149 // Locals
150 {
151 "pass/semi",
152 `{"gomeetup":["Sydney","San Francisco","Stockholm"]}`,
153 map[string]any{"gomeetup": []any{"Sydney", "San Francisco", "Stockholm"}},
154 },
155 {
156 "fail/no-file",
157 "",
158 false,
159 },
160 {
161 `pass/üńīçøðê-url.json`,
162 `{"gomeetup":["Sydney","San Francisco","Stockholm"]}`,
163 map[string]any{"gomeetup": []any{"Sydney", "San Francisco", "Stockholm"}},
164 },
165 } {
166
167 c.Run(test.url, func(c *qt.C) {
168
169 msg := qt.Commentf("Test %d", i)
170 ns := newTestNs()
171
172 // Setup HTTP test server
173 var srv *httptest.Server
174 srv, ns.client = getTestServer(func(w http.ResponseWriter, r *http.Request) {
175 if !hasHeaderValue(r.Header, "Accept", "application/json") {
176 http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
177 return
178 }
179
180 if r.URL.Path == "/404" {
181 http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
182 return
183 }
184
185 w.Header().Add("Content-type", "application/json")
186
187 w.Write([]byte(test.content))
188 })
189 defer func() { srv.Close() }()
190
191 // Setup local test file for schema-less URLs
192 if !strings.Contains(test.url, ":") && !strings.HasPrefix(test.url, "fail/") {
193 f, err := ns.deps.Fs.Source.Create(filepath.Join(ns.deps.Cfg.GetString("workingDir"), test.url))
194 c.Assert(err, qt.IsNil, msg)
195 f.WriteString(test.content)
196 f.Close()
197 }
198
199 // Get on with it
200 got, _ := ns.GetJSON(test.url)
201
202 if _, ok := test.expect.(bool); ok {
203 c.Assert(int(ns.deps.Log.LogCounters().ErrorCounter.Count()), qt.Equals, 1)
204 return
205 }
206
207 c.Assert(int(ns.deps.Log.LogCounters().ErrorCounter.Count()), qt.Equals, 0, msg)
208 c.Assert(got, qt.Not(qt.IsNil), msg)
209 c.Assert(got, qt.DeepEquals, test.expect)
210
211 })
212 }
213 }
214
215 func TestHeaders(t *testing.T) {
216 t.Parallel()
217 c := qt.New(t)
218
219 for _, test := range []struct {
220 name string
221 headers any
222 assert func(c *qt.C, headers string)
223 }{
224 {
225 `Misc header variants`,
226 map[string]any{
227 "Accept-Charset": "utf-8",
228 "Max-forwards": "10",
229 "X-Int": 32,
230 "X-Templ": template.HTML("a"),
231 "X-Multiple": []string{"a", "b"},
232 "X-MultipleInt": []int{3, 4},
233 },
234 func(c *qt.C, headers string) {
235 c.Assert(headers, qt.Contains, "Accept-Charset: utf-8")
236 c.Assert(headers, qt.Contains, "Max-Forwards: 10")
237 c.Assert(headers, qt.Contains, "X-Int: 32")
238 c.Assert(headers, qt.Contains, "X-Templ: a")
239 c.Assert(headers, qt.Contains, "X-Multiple: a")
240 c.Assert(headers, qt.Contains, "X-Multiple: b")
241 c.Assert(headers, qt.Contains, "X-Multipleint: 3")
242 c.Assert(headers, qt.Contains, "X-Multipleint: 4")
243 c.Assert(headers, qt.Contains, "User-Agent: Hugo Static Site Generator")
244 },
245 },
246 {
247 `Params`,
248 maps.Params{
249 "Accept-Charset": "utf-8",
250 },
251 func(c *qt.C, headers string) {
252 c.Assert(headers, qt.Contains, "Accept-Charset: utf-8")
253 },
254 },
255 {
256 `Override User-Agent`,
257 map[string]any{
258 "User-Agent": "007",
259 },
260 func(c *qt.C, headers string) {
261 c.Assert(headers, qt.Contains, "User-Agent: 007")
262 },
263 },
264 } {
265
266 c.Run(test.name, func(c *qt.C) {
267
268 ns := newTestNs()
269
270 // Setup HTTP test server
271 var srv *httptest.Server
272 var headers bytes.Buffer
273 srv, ns.client = getTestServer(func(w http.ResponseWriter, r *http.Request) {
274 c.Assert(r.URL.String(), qt.Equals, "http://gohugo.io/api?foo")
275 w.Write([]byte("{}"))
276 r.Header.Write(&headers)
277
278 })
279 defer func() { srv.Close() }()
280
281 testFunc := func(fn func(args ...any) error) {
282 defer headers.Reset()
283 err := fn("http://example.org/api", "?foo", test.headers)
284
285 c.Assert(err, qt.IsNil)
286 c.Assert(int(ns.deps.Log.LogCounters().ErrorCounter.Count()), qt.Equals, 0)
287 test.assert(c, headers.String())
288 }
289
290 testFunc(func(args ...any) error {
291 _, err := ns.GetJSON(args...)
292 return err
293 })
294 testFunc(func(args ...any) error {
295 _, err := ns.GetCSV(",", args...)
296 return err
297 })
298
299 })
300
301 }
302 }
303
304 func TestToURLAndHeaders(t *testing.T) {
305 t.Parallel()
306 c := qt.New(t)
307 url, headers := toURLAndHeaders([]any{"https://foo?id=", 32})
308 c.Assert(url, qt.Equals, "https://foo?id=32")
309 c.Assert(headers, qt.IsNil)
310
311 url, headers = toURLAndHeaders([]any{"https://foo?id=", 32, map[string]any{"a": "b"}})
312 c.Assert(url, qt.Equals, "https://foo?id=32")
313 c.Assert(headers, qt.DeepEquals, map[string]any{"a": "b"})
314 }
315
316 func TestParseCSV(t *testing.T) {
317 t.Parallel()
318 c := qt.New(t)
319
320 for i, test := range []struct {
321 csv []byte
322 sep string
323 exp string
324 err bool
325 }{
326 {[]byte("a,b,c\nd,e,f\n"), "", "", true},
327 {[]byte("a,b,c\nd,e,f\n"), "~/", "", true},
328 {[]byte("a,b,c\nd,e,f"), "|", "a,b,cd,e,f", false},
329 {[]byte("q,w,e\nd,e,f"), ",", "qwedef", false},
330 {[]byte("a|b|c\nd|e|f|g"), "|", "abcdefg", true},
331 {[]byte("z|y|c\nd|e|f"), "|", "zycdef", false},
332 } {
333 msg := qt.Commentf("Test %d: %v", i, test)
334
335 csv, err := parseCSV(test.csv, test.sep)
336 if test.err {
337 c.Assert(err, qt.Not(qt.IsNil), msg)
338 continue
339 }
340 c.Assert(err, qt.IsNil, msg)
341
342 act := ""
343 for _, v := range csv {
344 act = act + strings.Join(v, "")
345 }
346
347 c.Assert(act, qt.Equals, test.exp, msg)
348 }
349 }