mediaType_test.go (10354B)
1 // Copyright 2019 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 media
15
16 import (
17 "encoding/json"
18 "io/ioutil"
19 "path/filepath"
20 "sort"
21 "strings"
22 "testing"
23
24 qt "github.com/frankban/quicktest"
25 "github.com/gohugoio/hugo/common/paths"
26 )
27
28 func TestDefaultTypes(t *testing.T) {
29 c := qt.New(t)
30 for _, test := range []struct {
31 tp Type
32 expectedMainType string
33 expectedSubType string
34 expectedSuffix string
35 expectedType string
36 expectedString string
37 }{
38 {CalendarType, "text", "calendar", "ics", "text/calendar", "text/calendar"},
39 {CSSType, "text", "css", "css", "text/css", "text/css"},
40 {SCSSType, "text", "x-scss", "scss", "text/x-scss", "text/x-scss"},
41 {CSVType, "text", "csv", "csv", "text/csv", "text/csv"},
42 {HTMLType, "text", "html", "html", "text/html", "text/html"},
43 {JavascriptType, "application", "javascript", "js", "application/javascript", "application/javascript"},
44 {TypeScriptType, "application", "typescript", "ts", "application/typescript", "application/typescript"},
45 {TSXType, "text", "tsx", "tsx", "text/tsx", "text/tsx"},
46 {JSXType, "text", "jsx", "jsx", "text/jsx", "text/jsx"},
47 {JSONType, "application", "json", "json", "application/json", "application/json"},
48 {RSSType, "application", "rss", "xml", "application/rss+xml", "application/rss+xml"},
49 {SVGType, "image", "svg", "svg", "image/svg+xml", "image/svg+xml"},
50 {TextType, "text", "plain", "txt", "text/plain", "text/plain"},
51 {XMLType, "application", "xml", "xml", "application/xml", "application/xml"},
52 {TOMLType, "application", "toml", "toml", "application/toml", "application/toml"},
53 {YAMLType, "application", "yaml", "yaml", "application/yaml", "application/yaml"},
54 {PDFType, "application", "pdf", "pdf", "application/pdf", "application/pdf"},
55 {TrueTypeFontType, "font", "ttf", "ttf", "font/ttf", "font/ttf"},
56 {OpenTypeFontType, "font", "otf", "otf", "font/otf", "font/otf"},
57 } {
58 c.Assert(test.tp.MainType, qt.Equals, test.expectedMainType)
59 c.Assert(test.tp.SubType, qt.Equals, test.expectedSubType)
60
61 c.Assert(test.tp.Type(), qt.Equals, test.expectedType)
62 c.Assert(test.tp.String(), qt.Equals, test.expectedString)
63
64 }
65
66 c.Assert(len(DefaultTypes), qt.Equals, 34)
67 }
68
69 func TestGetByType(t *testing.T) {
70 c := qt.New(t)
71
72 types := Types{HTMLType, RSSType}
73
74 mt, found := types.GetByType("text/HTML")
75 c.Assert(found, qt.Equals, true)
76 c.Assert(HTMLType, qt.Equals, mt)
77
78 _, found = types.GetByType("text/nono")
79 c.Assert(found, qt.Equals, false)
80
81 mt, found = types.GetByType("application/rss+xml")
82 c.Assert(found, qt.Equals, true)
83 c.Assert(RSSType, qt.Equals, mt)
84
85 mt, found = types.GetByType("application/rss")
86 c.Assert(found, qt.Equals, true)
87 c.Assert(RSSType, qt.Equals, mt)
88 }
89
90 func TestGetByMainSubType(t *testing.T) {
91 c := qt.New(t)
92 f, found := DefaultTypes.GetByMainSubType("text", "plain")
93 c.Assert(found, qt.Equals, true)
94 c.Assert(f, qt.Equals, TextType)
95 _, found = DefaultTypes.GetByMainSubType("foo", "plain")
96 c.Assert(found, qt.Equals, false)
97 }
98
99 func TestBySuffix(t *testing.T) {
100 c := qt.New(t)
101 formats := DefaultTypes.BySuffix("xml")
102 c.Assert(len(formats), qt.Equals, 2)
103 c.Assert(formats[0].SubType, qt.Equals, "rss")
104 c.Assert(formats[1].SubType, qt.Equals, "xml")
105 }
106
107 func TestGetFirstBySuffix(t *testing.T) {
108 c := qt.New(t)
109
110 types := DefaultTypes
111
112 // Issue #8406
113 geoJSON := newMediaTypeWithMimeSuffix("application", "geo", "json", []string{"geojson", "gjson"})
114 types = append(types, geoJSON)
115 sort.Sort(types)
116
117 check := func(suffix string, expectedType Type) {
118 t, f, found := types.GetFirstBySuffix(suffix)
119 c.Assert(found, qt.Equals, true)
120 c.Assert(f, qt.Equals, SuffixInfo{
121 Suffix: suffix,
122 FullSuffix: "." + suffix,
123 })
124 c.Assert(t, qt.Equals, expectedType)
125 }
126
127 check("js", JavascriptType)
128 check("json", JSONType)
129 check("geojson", geoJSON)
130 check("gjson", geoJSON)
131 }
132
133 func TestFromTypeString(t *testing.T) {
134 c := qt.New(t)
135 f, err := fromString("text/html")
136 c.Assert(err, qt.IsNil)
137 c.Assert(f.Type(), qt.Equals, HTMLType.Type())
138
139 f, err = fromString("application/custom")
140 c.Assert(err, qt.IsNil)
141 c.Assert(f, qt.Equals, Type{MainType: "application", SubType: "custom", mimeSuffix: ""})
142
143 f, err = fromString("application/custom+sfx")
144 c.Assert(err, qt.IsNil)
145 c.Assert(f, qt.Equals, Type{MainType: "application", SubType: "custom", mimeSuffix: "sfx"})
146
147 _, err = fromString("noslash")
148 c.Assert(err, qt.Not(qt.IsNil))
149
150 f, err = fromString("text/xml; charset=utf-8")
151 c.Assert(err, qt.IsNil)
152
153 c.Assert(f, qt.Equals, Type{MainType: "text", SubType: "xml", mimeSuffix: ""})
154 }
155
156 func TestFromStringAndExt(t *testing.T) {
157 c := qt.New(t)
158 f, err := FromStringAndExt("text/html", "html")
159 c.Assert(err, qt.IsNil)
160 c.Assert(f, qt.Equals, HTMLType)
161 f, err = FromStringAndExt("text/html", ".html")
162 c.Assert(err, qt.IsNil)
163 c.Assert(f, qt.Equals, HTMLType)
164 }
165
166 // Add a test for the SVG case
167 // https://github.com/gohugoio/hugo/issues/4920
168 func TestFromExtensionMultipleSuffixes(t *testing.T) {
169 c := qt.New(t)
170 tp, si, found := DefaultTypes.GetBySuffix("svg")
171 c.Assert(found, qt.Equals, true)
172 c.Assert(tp.String(), qt.Equals, "image/svg+xml")
173 c.Assert(si.Suffix, qt.Equals, "svg")
174 c.Assert(si.FullSuffix, qt.Equals, ".svg")
175 c.Assert(tp.FirstSuffix.Suffix, qt.Equals, si.Suffix)
176 c.Assert(tp.FirstSuffix.FullSuffix, qt.Equals, si.FullSuffix)
177 ftp, found := DefaultTypes.GetByType("image/svg+xml")
178 c.Assert(found, qt.Equals, true)
179 c.Assert(ftp.String(), qt.Equals, "image/svg+xml")
180 c.Assert(found, qt.Equals, true)
181 }
182
183 func TestFromContent(t *testing.T) {
184 c := qt.New(t)
185
186 files, err := filepath.Glob("./testdata/resource.*")
187 c.Assert(err, qt.IsNil)
188 mtypes := DefaultTypes
189
190 for _, filename := range files {
191 name := filepath.Base(filename)
192 c.Run(name, func(c *qt.C) {
193 content, err := ioutil.ReadFile(filename)
194 c.Assert(err, qt.IsNil)
195 ext := strings.TrimPrefix(paths.Ext(filename), ".")
196 var exts []string
197 if ext == "jpg" {
198 exts = append(exts, "foo", "bar", "jpg")
199 } else {
200 exts = []string{ext}
201 }
202 expected, _, found := mtypes.GetFirstBySuffix(ext)
203 c.Assert(found, qt.IsTrue)
204 got := FromContent(mtypes, exts, content)
205 c.Assert(got, qt.Equals, expected)
206 })
207 }
208 }
209
210 func TestFromContentFakes(t *testing.T) {
211 c := qt.New(t)
212
213 files, err := filepath.Glob("./testdata/fake.*")
214 c.Assert(err, qt.IsNil)
215 mtypes := DefaultTypes
216
217 for _, filename := range files {
218 name := filepath.Base(filename)
219 c.Run(name, func(c *qt.C) {
220 content, err := ioutil.ReadFile(filename)
221 c.Assert(err, qt.IsNil)
222 ext := strings.TrimPrefix(paths.Ext(filename), ".")
223 got := FromContent(mtypes, []string{ext}, content)
224 c.Assert(got, qt.Equals, zero)
225 })
226 }
227 }
228
229 func TestDecodeTypes(t *testing.T) {
230 c := qt.New(t)
231
232 tests := []struct {
233 name string
234 maps []map[string]any
235 shouldError bool
236 assert func(t *testing.T, name string, tt Types)
237 }{
238 {
239 "Redefine JSON",
240 []map[string]any{
241 {
242 "application/json": map[string]any{
243 "suffixes": []string{"jasn"},
244 },
245 },
246 },
247 false,
248 func(t *testing.T, name string, tt Types) {
249 c.Assert(len(tt), qt.Equals, len(DefaultTypes))
250 json, si, found := tt.GetBySuffix("jasn")
251 c.Assert(found, qt.Equals, true)
252 c.Assert(json.String(), qt.Equals, "application/json")
253 c.Assert(si.FullSuffix, qt.Equals, ".jasn")
254 },
255 },
256 {
257 "MIME suffix in key, multiple file suffixes, custom delimiter",
258 []map[string]any{
259 {
260 "application/hugo+hg": map[string]any{
261 "suffixes": []string{"hg1", "hG2"},
262 "Delimiter": "_",
263 },
264 },
265 },
266 false,
267 func(t *testing.T, name string, tt Types) {
268 c.Assert(len(tt), qt.Equals, len(DefaultTypes)+1)
269 hg, si, found := tt.GetBySuffix("hg2")
270 c.Assert(found, qt.Equals, true)
271 c.Assert(hg.mimeSuffix, qt.Equals, "hg")
272 c.Assert(hg.FirstSuffix.Suffix, qt.Equals, "hg1")
273 c.Assert(hg.FirstSuffix.FullSuffix, qt.Equals, "_hg1")
274 c.Assert(si.Suffix, qt.Equals, "hg2")
275 c.Assert(si.FullSuffix, qt.Equals, "_hg2")
276 c.Assert(hg.String(), qt.Equals, "application/hugo+hg")
277
278 _, found = tt.GetByType("application/hugo+hg")
279 c.Assert(found, qt.Equals, true)
280 },
281 },
282 {
283 "Add custom media type",
284 []map[string]any{
285 {
286 "text/hugo+hgo": map[string]any{
287 "Suffixes": []string{"hgo2"},
288 },
289 },
290 },
291 false,
292 func(t *testing.T, name string, tp Types) {
293 c.Assert(len(tp), qt.Equals, len(DefaultTypes)+1)
294 // Make sure we have not broken the default config.
295
296 _, _, found := tp.GetBySuffix("json")
297 c.Assert(found, qt.Equals, true)
298
299 hugo, _, found := tp.GetBySuffix("hgo2")
300 c.Assert(found, qt.Equals, true)
301 c.Assert(hugo.String(), qt.Equals, "text/hugo+hgo")
302 },
303 },
304 }
305
306 for _, test := range tests {
307 result, err := DecodeTypes(test.maps...)
308 if test.shouldError {
309 c.Assert(err, qt.Not(qt.IsNil))
310 } else {
311 c.Assert(err, qt.IsNil)
312 test.assert(t, test.name, result)
313 }
314 }
315 }
316
317 func TestToJSON(t *testing.T) {
318 c := qt.New(t)
319 b, err := json.Marshal(MPEGType)
320 c.Assert(err, qt.IsNil)
321 c.Assert(string(b), qt.Equals, `{"mainType":"video","subType":"mpeg","delimiter":".","firstSuffix":{"suffix":"mpg","fullSuffix":".mpg"},"type":"video/mpeg","string":"video/mpeg","suffixes":["mpg","mpeg"]}`)
322 }
323
324 func BenchmarkTypeOps(b *testing.B) {
325 mt := MPEGType
326 mts := DefaultTypes
327 for i := 0; i < b.N; i++ {
328 ff := mt.FirstSuffix
329 _ = ff.FullSuffix
330 _ = mt.IsZero()
331 c, err := mt.MarshalJSON()
332 if c == nil || err != nil {
333 b.Fatal("failed")
334 }
335 _ = mt.String()
336 _ = ff.Suffix
337 _ = mt.Suffixes
338 _ = mt.Type()
339 _ = mts.BySuffix("xml")
340 _, _ = mts.GetByMainSubType("application", "xml")
341 _, _, _ = mts.GetBySuffix("xml")
342 _, _ = mts.GetByType("application")
343 _, _, _ = mts.GetFirstBySuffix("xml")
344
345 }
346 }