server_test.go (7335B)
1 // Copyright 2015 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 commands
15
16 import (
17 "fmt"
18 "net/http"
19 "os"
20 "path/filepath"
21 "runtime"
22 "strings"
23 "testing"
24 "time"
25
26 "github.com/gohugoio/hugo/config"
27 "github.com/gohugoio/hugo/helpers"
28 "golang.org/x/net/context"
29 "golang.org/x/sync/errgroup"
30
31 qt "github.com/frankban/quicktest"
32 )
33
34 // Issue 9518
35 func TestServerPanicOnConfigError(t *testing.T) {
36 c := qt.New(t)
37
38 config := `
39 [markup]
40 [markup.highlight]
41 linenos='table'
42 `
43
44 r := runServerTest(c, 0, config)
45
46 c.Assert(r.err, qt.IsNotNil)
47 c.Assert(r.err.Error(), qt.Contains, "cannot parse 'Highlight.LineNos' as bool:")
48 }
49
50 func TestServerFlags(t *testing.T) {
51 c := qt.New(t)
52
53 assertPublic := func(c *qt.C, r serverTestResult, renderStaticToDisk bool) {
54 c.Assert(r.err, qt.IsNil)
55 c.Assert(r.homesContent[0], qt.Contains, "Environment: development")
56 c.Assert(r.publicDirnames["myfile.txt"], qt.Equals, renderStaticToDisk)
57
58 }
59
60 for _, test := range []struct {
61 flag string
62 assert func(c *qt.C, r serverTestResult)
63 }{
64 {"", func(c *qt.C, r serverTestResult) {
65 assertPublic(c, r, false)
66 }},
67 {"--renderToDisk", func(c *qt.C, r serverTestResult) {
68 assertPublic(c, r, true)
69 }},
70 {"--renderStaticToDisk", func(c *qt.C, r serverTestResult) {
71 assertPublic(c, r, true)
72 }},
73 } {
74 c.Run(test.flag, func(c *qt.C) {
75 config := `
76 baseURL="https://example.org"
77 `
78
79 var args []string
80 if test.flag != "" {
81 args = strings.Split(test.flag, "=")
82 }
83
84 r := runServerTest(c, 1, config, args...)
85
86 test.assert(c, r)
87
88 })
89
90 }
91
92 }
93
94 func TestServerBugs(t *testing.T) {
95 c := qt.New(t)
96
97 for _, test := range []struct {
98 name string
99 config string
100 flag string
101 numservers int
102 assert func(c *qt.C, r serverTestResult)
103 }{
104 // Issue 9788
105 {"PostProcess, memory", "", "", 1, func(c *qt.C, r serverTestResult) {
106 c.Assert(r.err, qt.IsNil)
107 c.Assert(r.homesContent[0], qt.Contains, "PostProcess: /foo.min.css")
108 }},
109 {"PostProcess, disk", "", "--renderToDisk", 1, func(c *qt.C, r serverTestResult) {
110 c.Assert(r.err, qt.IsNil)
111 c.Assert(r.homesContent[0], qt.Contains, "PostProcess: /foo.min.css")
112 }},
113 // Isue 9901
114 {"Multihost", `
115 defaultContentLanguage = 'en'
116 [languages]
117 [languages.en]
118 baseURL = 'https://example.com'
119 title = 'My blog'
120 weight = 1
121 [languages.fr]
122 baseURL = 'https://example.fr'
123 title = 'Mon blogue'
124 weight = 2
125 `, "", 2, func(c *qt.C, r serverTestResult) {
126 c.Assert(r.err, qt.IsNil)
127 for i, s := range []string{"My blog", "Mon blogue"} {
128 c.Assert(r.homesContent[i], qt.Contains, s)
129 }
130 }},
131 } {
132 c.Run(test.name, func(c *qt.C) {
133 if test.config == "" {
134 test.config = `
135 baseURL="https://example.org"
136 `
137 }
138
139 var args []string
140 if test.flag != "" {
141 args = strings.Split(test.flag, "=")
142 }
143 r := runServerTest(c, test.numservers, test.config, args...)
144 test.assert(c, r)
145
146 })
147
148 }
149
150 }
151
152 type serverTestResult struct {
153 err error
154 homesContent []string
155 publicDirnames map[string]bool
156 }
157
158 func runServerTest(c *qt.C, getNumHomes int, config string, args ...string) (result serverTestResult) {
159 dir := createSimpleTestSite(c, testSiteConfig{configTOML: config})
160
161 sp, err := helpers.FindAvailablePort()
162 c.Assert(err, qt.IsNil)
163 port := sp.Port
164
165 defer func() {
166 os.RemoveAll(dir)
167 }()
168
169 stop := make(chan bool)
170
171 b := newCommandsBuilder()
172 scmd := b.newServerCmdSignaled(stop)
173
174 cmd := scmd.getCommand()
175 args = append([]string{"-s=" + dir, fmt.Sprintf("-p=%d", port)}, args...)
176 cmd.SetArgs(args)
177
178 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
179 defer cancel()
180 wg, ctx := errgroup.WithContext(ctx)
181
182 wg.Go(func() error {
183 _, err := cmd.ExecuteC()
184 return err
185 })
186
187 if getNumHomes > 0 {
188 // Esp. on slow CI machines, we need to wait a little before the web
189 // server is ready.
190 time.Sleep(567 * time.Millisecond)
191 result.homesContent = make([]string, getNumHomes)
192 for i := 0; i < getNumHomes; i++ {
193 func() {
194 resp, err := http.Get(fmt.Sprintf("http://localhost:%d/", port+i))
195 c.Check(err, qt.IsNil)
196 c.Check(resp.StatusCode, qt.Equals, http.StatusOK)
197 if err == nil {
198 defer resp.Body.Close()
199 result.homesContent[i] = helpers.ReaderToString(resp.Body)
200 }
201 }()
202 }
203 }
204
205 time.Sleep(1 * time.Second)
206
207 select {
208 case <-stop:
209 case stop <- true:
210 }
211
212 pubFiles, err := os.ReadDir(filepath.Join(dir, "public"))
213 c.Check(err, qt.IsNil)
214 result.publicDirnames = make(map[string]bool)
215 for _, f := range pubFiles {
216 result.publicDirnames[f.Name()] = true
217 }
218
219 result.err = wg.Wait()
220
221 return
222
223 }
224
225 func TestFixURL(t *testing.T) {
226 type data struct {
227 TestName string
228 CLIBaseURL string
229 CfgBaseURL string
230 AppendPort bool
231 Port int
232 Result string
233 }
234 tests := []data{
235 {"Basic http localhost", "", "http://foo.com", true, 1313, "http://localhost:1313/"},
236 {"Basic https production, http localhost", "", "https://foo.com", true, 1313, "http://localhost:1313/"},
237 {"Basic subdir", "", "http://foo.com/bar", true, 1313, "http://localhost:1313/bar/"},
238 {"Basic production", "http://foo.com", "http://foo.com", false, 80, "http://foo.com/"},
239 {"Production subdir", "http://foo.com/bar", "http://foo.com/bar", false, 80, "http://foo.com/bar/"},
240 {"No http", "", "foo.com", true, 1313, "//localhost:1313/"},
241 {"Override configured port", "", "foo.com:2020", true, 1313, "//localhost:1313/"},
242 {"No http production", "foo.com", "foo.com", false, 80, "//foo.com/"},
243 {"No http production with port", "foo.com", "foo.com", true, 2020, "//foo.com:2020/"},
244 {"No config", "", "", true, 1313, "//localhost:1313/"},
245 }
246
247 for _, test := range tests {
248 t.Run(test.TestName, func(t *testing.T) {
249 b := newCommandsBuilder()
250 s := b.newServerCmd()
251 v := config.NewWithTestDefaults()
252 baseURL := test.CLIBaseURL
253 v.Set("baseURL", test.CfgBaseURL)
254 s.serverAppend = test.AppendPort
255 s.serverPort = test.Port
256 result, err := s.fixURL(v, baseURL, s.serverPort)
257 if err != nil {
258 t.Errorf("Unexpected error %s", err)
259 }
260 if result != test.Result {
261 t.Errorf("Expected %q, got %q", test.Result, result)
262 }
263 })
264 }
265 }
266
267 func TestRemoveErrorPrefixFromLog(t *testing.T) {
268 c := qt.New(t)
269 content := `ERROR 2018/10/07 13:11:12 Error while rendering "home": template: _default/baseof.html:4:3: executing "main" at <partial "logo" .>: error calling partial: template: partials/logo.html:5:84: executing "partials/logo.html" at <$resized.AHeight>: can't evaluate field AHeight in type *resource.Image
270 ERROR 2018/10/07 13:11:12 Rebuild failed: logged 1 error(s)
271 `
272
273 withoutError := removeErrorPrefixFromLog(content)
274
275 c.Assert(strings.Contains(withoutError, "ERROR"), qt.Equals, false)
276 }
277
278 func isWindowsCI() bool {
279 return runtime.GOOS == "windows" && os.Getenv("CI") != ""
280 }