diff --git a/commands/server_test.go b/commands/server_test.go index 05d21a5165b..f9c6bf8f9bb 100644 --- a/commands/server_test.go +++ b/commands/server_test.go @@ -17,6 +17,8 @@ import ( "fmt" "net/http" "os" + "path" + "path/filepath" "runtime" "strings" "testing" @@ -24,6 +26,8 @@ import ( "github.com/gohugoio/hugo/config" "github.com/gohugoio/hugo/helpers" + "github.com/gohugoio/hugo/htesting" + "github.com/gohugoio/hugo/hugofs" qt "github.com/frankban/quicktest" ) @@ -131,3 +135,100 @@ ERROR 2018/10/07 13:11:12 Rebuild failed: logged 1 error(s) func isWindowsCI() bool { return runtime.GOOS == "windows" && os.Getenv("CI") != "" } + +// Issue 7043 +func TestServerReloadWithBadData(t *testing.T) { + c := qt.New(t) + dir, clean, err := htesting.CreateTempDir(hugofs.Os, "hugo-cli") + + cfgStr := ` + +baseURL = "https://example.org" +title = "Hugo Commands" + +` + os.MkdirAll(filepath.Join(dir, "public"), 0777) + os.MkdirAll(filepath.Join(dir, "data"), 0777) + os.MkdirAll(filepath.Join(dir, "layouts"), 0777) + + writeFile(t, filepath.Join(dir, "config.toml"), cfgStr) + writeFile(t, filepath.Join(dir, "content", "p1.md"), ` +--- +title: "P1" +weight: 1 +--- + +Content + +`) + defer clean() + c.Assert(err, qt.IsNil) + + // The first time we write to the data file, the JSON is valid. + writeFile(t, path.Join(dir, "data", "testdata.json"), `{ +"key1": "value1", +"key2": "value2" +}`) + + writeFile(t, path.Join(dir, "layouts", "index.html"), `{{- range $k, $v := .Site.Data.testdata -}} +

{{$k}} has the value {{$v}}

+{{ end }}`) + + port := 1331 + + b := newCommandsBuilder() + stop := make(chan bool) + scmd := b.newServerCmdSignaled(stop) + + defer func() { + // Stop the server. + stop <- true + }() + + cmd := scmd.getCommand() + cmd.SetArgs([]string{ + "-s=" + dir, + fmt.Sprintf("-p=%d", port), + "-d=" + path.Join(dir, "public"), + }) + + go func() { + _, err = cmd.ExecuteC() + c.Assert(err, qt.IsNil) + }() + + // There is no way to know exactly when the server is ready for connections. + // We could improve by something like https://golang.org/pkg/net/http/httptest/#Server + // But for now, let us sleep and pray! + time.Sleep(500 * time.Millisecond) + + // Break the JSON by removing the comma + writeFile(t, path.Join(dir, "data", "testdata.json"), `{ +"key1": "value1" +"key2": "value2" +}`) + + // Wait for the server to make the change + time.Sleep(500 * time.Millisecond) + + // Fix the JSON and add a line + writeFile(t, path.Join(dir, "data", "testdata.json"), `{ +"key1": "value1", +"key2": "value2", +"key3": "value3" +}`) + + // Wait for the server to make the change + time.Sleep(500 * time.Millisecond) + + exp := `

key1 has the value value1

+

key2 has the value value2

+

key3 has the value value3

+` + + af, err := os.ReadFile(path.Join(dir, "public", "index.html")) + c.Assert(err, qt.IsNil) + c.Assert(string(af), qt.Equals, exp) + + return +} diff --git a/lazy/init.go b/lazy/init.go index e2e70072ea4..58594b7f5a7 100644 --- a/lazy/init.go +++ b/lazy/init.go @@ -76,6 +76,10 @@ func (ini *Init) Do() (interface{}, error) { panic("init is nil") } + // Reset the error in case we have already called Do, e.g., when hugo + // server is running. + ini.err = nil + ini.init.Do(func() { prev := ini.prev if prev != nil { diff --git a/lazy/init_test.go b/lazy/init_test.go index 2051f6b1a5f..e136f4a2dc8 100644 --- a/lazy/init_test.go +++ b/lazy/init_test.go @@ -220,3 +220,24 @@ func TestInitBranchOrder(t *testing.T) { c.Assert(state.V2, qt.Equals, "ABAB") } + +// Sometimes need to call the same *Init's Do method multiple times, e.g., when +// hugo server is running. In these cases, we need to make sure that an error +// returned by an earlier call does not persist in memory for later calls. +// See issue 7043 +func TestAvoidRepeatDoError(t *testing.T) { + r := false + i := New().Add(func() (interface{}, error) { + if r { + return nil, nil + } + return nil, errors.New("r is false") + }) + i.Do() + r = true + i.Do() + if i.err != nil { + t.Errorf("expected a nil error but got: %v", i.err) + } + +}