github.com/linchen2chris/hugo@v0.0.0-20230307053224-cec209389705/resources/page/permalinks_test.go (about) 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 page 15 16 import ( 17 "fmt" 18 "regexp" 19 "sync" 20 "testing" 21 "time" 22 23 qt "github.com/frankban/quicktest" 24 ) 25 26 // testdataPermalinks is used by a couple of tests; the expandsTo content is 27 // subject to the data in simplePageJSON. 28 var testdataPermalinks = []struct { 29 spec string 30 valid bool 31 expandsTo string 32 }{ 33 {":title", true, "spf13-vim-3.0-release-and-new-website"}, 34 {"/:year-:month-:title", true, "/2012-04-spf13-vim-3.0-release-and-new-website"}, 35 {"/:year/:yearday/:month/:monthname/:day/:weekday/:weekdayname/", true, "/2012/97/04/April/06/5/Friday/"}, // Dates 36 {"/:section/", true, "/blue/"}, // Section 37 {"/:title/", true, "/spf13-vim-3.0-release-and-new-website/"}, // Title 38 {"/:slug/", true, "/the-slug/"}, // Slug 39 {"/:slugorfilename/", true, "/the-slug/"}, // Slug or filename 40 {"/:filename/", true, "/test-page/"}, // Filename 41 {"/:06-:1-:2-:Monday", true, "/12-4-6-Friday"}, // Dates with Go formatting 42 {"/:2006_01_02_15_04_05.000", true, "/2012_04_06_03_01_59.000"}, // Complicated custom date format 43 {"/:sections/", true, "/a/b/c/"}, // Sections 44 {"/:sections[last]/", true, "/c/"}, // Sections 45 {"/:sections[0]/:sections[last]/", true, "/a/c/"}, // Sections 46 47 // Failures 48 {"/blog/:fred", false, ""}, 49 {"/:year//:title", false, ""}, 50 {"/:TITLE", false, ""}, // case is not normalized 51 {"/:2017", false, ""}, // invalid date format 52 {"/:2006-01-02", false, ""}, // valid date format but invalid attribute name 53 } 54 55 func TestPermalinkExpansion(t *testing.T) { 56 t.Parallel() 57 58 c := qt.New(t) 59 60 page := newTestPageWithFile("/test-page/index.md") 61 page.title = "Spf13 Vim 3.0 Release and new website" 62 d, _ := time.Parse("2006-01-02 15:04:05", "2012-04-06 03:01:59") 63 page.date = d 64 page.section = "blue" 65 page.slug = "The Slug" 66 67 for _, item := range testdataPermalinks { 68 if !item.valid { 69 continue 70 } 71 72 specNameCleaner := regexp.MustCompile(`[\:\/\[\]]`) 73 name := specNameCleaner.ReplaceAllString(item.spec, "") 74 75 c.Run(name, func(c *qt.C) { 76 77 permalinksConfig := map[string]string{ 78 "posts": item.spec, 79 } 80 81 ps := newTestPathSpec() 82 ps.Cfg.Set("permalinks", permalinksConfig) 83 84 expander, err := NewPermalinkExpander(ps) 85 c.Assert(err, qt.IsNil) 86 87 expanded, err := expander.Expand("posts", page) 88 c.Assert(err, qt.IsNil) 89 c.Assert(expanded, qt.Equals, item.expandsTo) 90 }) 91 92 } 93 } 94 95 func TestPermalinkExpansionMultiSection(t *testing.T) { 96 t.Parallel() 97 98 c := qt.New(t) 99 100 page := newTestPage() 101 page.title = "Page Title" 102 d, _ := time.Parse("2006-01-02", "2012-04-06") 103 page.date = d 104 page.section = "blue" 105 page.slug = "The Slug" 106 107 page_slug_fallback := newTestPageWithFile("/page-filename/index.md") 108 page_slug_fallback.title = "Page Title" 109 110 permalinksConfig := map[string]string{ 111 "posts": "/:slug", 112 "blog": "/:section/:year", 113 "recipes": "/:slugorfilename", 114 } 115 116 ps := newTestPathSpec() 117 ps.Cfg.Set("permalinks", permalinksConfig) 118 119 expander, err := NewPermalinkExpander(ps) 120 c.Assert(err, qt.IsNil) 121 122 expanded, err := expander.Expand("posts", page) 123 c.Assert(err, qt.IsNil) 124 c.Assert(expanded, qt.Equals, "/the-slug") 125 126 expanded, err = expander.Expand("blog", page) 127 c.Assert(err, qt.IsNil) 128 c.Assert(expanded, qt.Equals, "/blue/2012") 129 130 expanded, err = expander.Expand("posts", page_slug_fallback) 131 c.Assert(err, qt.IsNil) 132 c.Assert(expanded, qt.Equals, "/page-title") 133 134 expanded, err = expander.Expand("recipes", page_slug_fallback) 135 c.Assert(err, qt.IsNil) 136 c.Assert(expanded, qt.Equals, "/page-filename") 137 } 138 139 func TestPermalinkExpansionConcurrent(t *testing.T) { 140 t.Parallel() 141 142 c := qt.New(t) 143 144 permalinksConfig := map[string]string{ 145 "posts": "/:slug/", 146 } 147 148 ps := newTestPathSpec() 149 ps.Cfg.Set("permalinks", permalinksConfig) 150 151 expander, err := NewPermalinkExpander(ps) 152 c.Assert(err, qt.IsNil) 153 154 var wg sync.WaitGroup 155 156 for i := 1; i < 20; i++ { 157 wg.Add(1) 158 go func(i int) { 159 defer wg.Done() 160 page := newTestPage() 161 for j := 1; j < 20; j++ { 162 page.slug = fmt.Sprintf("slug%d", i+j) 163 expanded, err := expander.Expand("posts", page) 164 c.Assert(err, qt.IsNil) 165 c.Assert(expanded, qt.Equals, fmt.Sprintf("/%s/", page.slug)) 166 } 167 }(i) 168 } 169 170 wg.Wait() 171 } 172 173 func TestPermalinkExpansionSliceSyntax(t *testing.T) { 174 t.Parallel() 175 176 c := qt.New(t) 177 exp, _ := NewPermalinkExpander(newTestPathSpec()) 178 slice := []string{"a", "b", "c", "d"} 179 fn := func(s string) []string { 180 return exp.toSliceFunc(s)(slice) 181 } 182 183 c.Run("Basic", func(c *qt.C) { 184 c.Assert(fn("[1:3]"), qt.DeepEquals, []string{"b", "c"}) 185 c.Assert(fn("[1:]"), qt.DeepEquals, []string{"b", "c", "d"}) 186 c.Assert(fn("[:2]"), qt.DeepEquals, []string{"a", "b"}) 187 c.Assert(fn("[0:2]"), qt.DeepEquals, []string{"a", "b"}) 188 c.Assert(fn("[:]"), qt.DeepEquals, []string{"a", "b", "c", "d"}) 189 c.Assert(fn(""), qt.DeepEquals, []string{"a", "b", "c", "d"}) 190 c.Assert(fn("[last]"), qt.DeepEquals, []string{"d"}) 191 c.Assert(fn("[:last]"), qt.DeepEquals, []string{"a", "b", "c"}) 192 193 }) 194 195 c.Run("Out of bounds", func(c *qt.C) { 196 c.Assert(fn("[1:5]"), qt.DeepEquals, []string{"b", "c", "d"}) 197 c.Assert(fn("[-1:5]"), qt.DeepEquals, []string{"a", "b", "c", "d"}) 198 c.Assert(fn("[5:]"), qt.DeepEquals, []string{}) 199 c.Assert(fn("[5:]"), qt.DeepEquals, []string{}) 200 c.Assert(fn("[5:32]"), qt.DeepEquals, []string{}) 201 c.Assert(exp.toSliceFunc("[:1]")(nil), qt.DeepEquals, []string(nil)) 202 c.Assert(exp.toSliceFunc("[:1]")([]string{}), qt.DeepEquals, []string(nil)) 203 204 // These all return nil 205 c.Assert(fn("[]"), qt.IsNil) 206 c.Assert(fn("[1:}"), qt.IsNil) 207 c.Assert(fn("foo"), qt.IsNil) 208 209 }) 210 211 } 212 213 func BenchmarkPermalinkExpand(b *testing.B) { 214 page := newTestPage() 215 page.title = "Hugo Rocks" 216 d, _ := time.Parse("2006-01-02", "2019-02-28") 217 page.date = d 218 219 permalinksConfig := map[string]string{ 220 "posts": "/:year-:month-:title", 221 } 222 223 ps := newTestPathSpec() 224 ps.Cfg.Set("permalinks", permalinksConfig) 225 226 expander, err := NewPermalinkExpander(ps) 227 if err != nil { 228 b.Fatal(err) 229 } 230 231 b.ResetTimer() 232 for i := 0; i < b.N; i++ { 233 s, err := expander.Expand("posts", page) 234 if err != nil { 235 b.Fatal(err) 236 } 237 if s != "/2019-02-hugo-rocks" { 238 b.Fatal(s) 239 } 240 241 } 242 }