github.com/gohugoio/hugo@v0.88.1/commands/commands_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 commands
    15  
    16  import (
    17  	"fmt"
    18  	"io/ioutil"
    19  	"os"
    20  	"path/filepath"
    21  	"testing"
    22  
    23  	"github.com/gohugoio/hugo/config"
    24  
    25  	"github.com/gohugoio/hugo/htesting"
    26  
    27  	"github.com/spf13/afero"
    28  
    29  	"github.com/gohugoio/hugo/hugofs"
    30  
    31  	"github.com/gohugoio/hugo/common/types"
    32  
    33  	"github.com/spf13/cobra"
    34  
    35  	qt "github.com/frankban/quicktest"
    36  )
    37  
    38  func TestExecute(t *testing.T) {
    39  	c := qt.New(t)
    40  
    41  	createSite := func(c *qt.C) (string, func()) {
    42  		dir, clean, err := createSimpleTestSite(t, testSiteConfig{})
    43  		c.Assert(err, qt.IsNil)
    44  		return dir, clean
    45  	}
    46  
    47  	c.Run("hugo", func(c *qt.C) {
    48  		dir, clean := createSite(c)
    49  		defer clean()
    50  		resp := Execute([]string{"-s=" + dir})
    51  		c.Assert(resp.Err, qt.IsNil)
    52  		result := resp.Result
    53  		c.Assert(len(result.Sites) == 1, qt.Equals, true)
    54  		c.Assert(len(result.Sites[0].RegularPages()) == 1, qt.Equals, true)
    55  		c.Assert(result.Sites[0].Info.Params()["myparam"], qt.Equals, "paramproduction")
    56  	})
    57  
    58  	c.Run("hugo, set environment", func(c *qt.C) {
    59  		dir, clean := createSite(c)
    60  		defer clean()
    61  		resp := Execute([]string{"-s=" + dir, "-e=staging"})
    62  		c.Assert(resp.Err, qt.IsNil)
    63  		result := resp.Result
    64  		c.Assert(result.Sites[0].Info.Params()["myparam"], qt.Equals, "paramstaging")
    65  	})
    66  
    67  	c.Run("convert toJSON", func(c *qt.C) {
    68  		dir, clean := createSite(c)
    69  		output := filepath.Join(dir, "myjson")
    70  		defer clean()
    71  		resp := Execute([]string{"convert", "toJSON", "-s=" + dir, "-e=staging", "-o=" + output})
    72  		c.Assert(resp.Err, qt.IsNil)
    73  		converted := readFileFrom(c, filepath.Join(output, "content", "p1.md"))
    74  		c.Assert(converted, qt.Equals, "{\n   \"title\": \"P1\",\n   \"weight\": 1\n}\n\nContent\n\n", qt.Commentf(converted))
    75  	})
    76  
    77  	c.Run("config, set environment", func(c *qt.C) {
    78  		dir, clean := createSite(c)
    79  		defer clean()
    80  		out, err := captureStdout(func() error {
    81  			resp := Execute([]string{"config", "-s=" + dir, "-e=staging"})
    82  			return resp.Err
    83  		})
    84  		c.Assert(err, qt.IsNil)
    85  		c.Assert(out, qt.Contains, "params = map[myparam:paramstaging]", qt.Commentf(out))
    86  	})
    87  
    88  	c.Run("deploy, environment set", func(c *qt.C) {
    89  		dir, clean := createSite(c)
    90  		defer clean()
    91  		resp := Execute([]string{"deploy", "-s=" + dir, "-e=staging", "--target=mydeployment", "--dryRun"})
    92  		c.Assert(resp.Err, qt.Not(qt.IsNil))
    93  		c.Assert(resp.Err.Error(), qt.Contains, `no driver registered for "hugocloud"`)
    94  	})
    95  
    96  	c.Run("list", func(c *qt.C) {
    97  		dir, clean := createSite(c)
    98  		defer clean()
    99  		out, err := captureStdout(func() error {
   100  			resp := Execute([]string{"list", "all", "-s=" + dir, "-e=staging"})
   101  			return resp.Err
   102  		})
   103  		c.Assert(err, qt.IsNil)
   104  		c.Assert(out, qt.Contains, "p1.md")
   105  	})
   106  
   107  	c.Run("new theme", func(c *qt.C) {
   108  		dir, clean := createSite(c)
   109  		defer clean()
   110  		themesDir := filepath.Join(dir, "mythemes")
   111  		resp := Execute([]string{"new", "theme", "mytheme", "-s=" + dir, "-e=staging", "--themesDir=" + themesDir})
   112  		c.Assert(resp.Err, qt.IsNil)
   113  		themeTOML := readFileFrom(c, filepath.Join(themesDir, "mytheme", "theme.toml"))
   114  		c.Assert(themeTOML, qt.Contains, "name = \"Mytheme\"")
   115  	})
   116  
   117  	c.Run("new site", func(c *qt.C) {
   118  		dir, clean := createSite(c)
   119  		defer clean()
   120  		siteDir := filepath.Join(dir, "mysite")
   121  		resp := Execute([]string{"new", "site", siteDir, "-e=staging"})
   122  		c.Assert(resp.Err, qt.IsNil)
   123  		config := readFileFrom(c, filepath.Join(siteDir, "config.toml"))
   124  		c.Assert(config, qt.Contains, "baseURL = 'http://example.org/'")
   125  		checkNewSiteInited(c, siteDir)
   126  	})
   127  }
   128  
   129  func checkNewSiteInited(c *qt.C, basepath string) {
   130  	paths := []string{
   131  		filepath.Join(basepath, "layouts"),
   132  		filepath.Join(basepath, "content"),
   133  		filepath.Join(basepath, "archetypes"),
   134  		filepath.Join(basepath, "static"),
   135  		filepath.Join(basepath, "data"),
   136  		filepath.Join(basepath, "config.toml"),
   137  	}
   138  
   139  	for _, path := range paths {
   140  		_, err := os.Stat(path)
   141  		c.Assert(err, qt.IsNil)
   142  	}
   143  }
   144  
   145  func readFileFrom(c *qt.C, filename string) string {
   146  	c.Helper()
   147  	filename = filepath.Clean(filename)
   148  	b, err := afero.ReadFile(hugofs.Os, filename)
   149  	c.Assert(err, qt.IsNil)
   150  	return string(b)
   151  }
   152  
   153  func TestFlags(t *testing.T) {
   154  	c := qt.New(t)
   155  
   156  	noOpRunE := func(cmd *cobra.Command, args []string) error {
   157  		return nil
   158  	}
   159  
   160  	tests := []struct {
   161  		name  string
   162  		args  []string
   163  		check func(c *qt.C, cmd *serverCmd)
   164  	}{
   165  		{
   166  			// https://github.com/gohugoio/hugo/issues/7642
   167  			name: "ignoreVendor as bool",
   168  			args: []string{"server", "--ignoreVendor"},
   169  			check: func(c *qt.C, cmd *serverCmd) {
   170  				cfg := config.New()
   171  				cmd.flagsToConfig(cfg)
   172  				c.Assert(cfg.Get("ignoreVendor"), qt.Equals, true)
   173  			},
   174  		},
   175  		{
   176  			// https://github.com/gohugoio/hugo/issues/7642
   177  			name: "ignoreVendorPaths",
   178  			args: []string{"server", "--ignoreVendorPaths=github.com/**"},
   179  			check: func(c *qt.C, cmd *serverCmd) {
   180  				cfg := config.New()
   181  				cmd.flagsToConfig(cfg)
   182  				c.Assert(cfg.Get("ignoreVendorPaths"), qt.Equals, "github.com/**")
   183  			},
   184  		},
   185  		{
   186  			name: "Persistent flags",
   187  			args: []string{
   188  				"server",
   189  				"--config=myconfig.toml",
   190  				"--configDir=myconfigdir",
   191  				"--contentDir=mycontent",
   192  				"--disableKinds=page,home",
   193  				"--environment=testing",
   194  				"--configDir=myconfigdir",
   195  				"--layoutDir=mylayouts",
   196  				"--theme=mytheme",
   197  				"--gc",
   198  				"--themesDir=mythemes",
   199  				"--cleanDestinationDir",
   200  				"--navigateToChanged",
   201  				"--disableLiveReload",
   202  				"--noHTTPCache",
   203  				"--i18n-warnings",
   204  				"--destination=/tmp/mydestination",
   205  				"-b=https://example.com/b/",
   206  				"--port=1366",
   207  				"--renderToDisk",
   208  				"--source=mysource",
   209  				"--path-warnings",
   210  			},
   211  			check: func(c *qt.C, sc *serverCmd) {
   212  				c.Assert(sc, qt.Not(qt.IsNil))
   213  				c.Assert(sc.navigateToChanged, qt.Equals, true)
   214  				c.Assert(sc.disableLiveReload, qt.Equals, true)
   215  				c.Assert(sc.noHTTPCache, qt.Equals, true)
   216  				c.Assert(sc.renderToDisk, qt.Equals, true)
   217  				c.Assert(sc.serverPort, qt.Equals, 1366)
   218  				c.Assert(sc.environment, qt.Equals, "testing")
   219  
   220  				cfg := config.New()
   221  				sc.flagsToConfig(cfg)
   222  				c.Assert(cfg.GetString("publishDir"), qt.Equals, "/tmp/mydestination")
   223  				c.Assert(cfg.GetString("contentDir"), qt.Equals, "mycontent")
   224  				c.Assert(cfg.GetString("layoutDir"), qt.Equals, "mylayouts")
   225  				c.Assert(cfg.GetStringSlice("theme"), qt.DeepEquals, []string{"mytheme"})
   226  				c.Assert(cfg.GetString("themesDir"), qt.Equals, "mythemes")
   227  				c.Assert(cfg.GetString("baseURL"), qt.Equals, "https://example.com/b/")
   228  
   229  				c.Assert(cfg.Get("disableKinds"), qt.DeepEquals, []string{"page", "home"})
   230  
   231  				c.Assert(cfg.GetBool("gc"), qt.Equals, true)
   232  
   233  				// The flag is named path-warnings
   234  				c.Assert(cfg.GetBool("logPathWarnings"), qt.Equals, true)
   235  
   236  				// The flag is named i18n-warnings
   237  				c.Assert(cfg.GetBool("logI18nWarnings"), qt.Equals, true)
   238  			},
   239  		},
   240  	}
   241  
   242  	for _, test := range tests {
   243  		c.Run(test.name, func(c *qt.C) {
   244  			b := newCommandsBuilder()
   245  			root := b.addAll().build()
   246  
   247  			for _, cmd := range b.commands {
   248  				if cmd.getCommand() == nil {
   249  					continue
   250  				}
   251  				// We are only intereseted in the flag handling here.
   252  				cmd.getCommand().RunE = noOpRunE
   253  			}
   254  			rootCmd := root.getCommand()
   255  			rootCmd.SetArgs(test.args)
   256  			c.Assert(rootCmd.Execute(), qt.IsNil)
   257  			test.check(c, b.commands[0].(*serverCmd))
   258  		})
   259  	}
   260  }
   261  
   262  func TestCommandsExecute(t *testing.T) {
   263  	c := qt.New(t)
   264  
   265  	dir, clean, err := createSimpleTestSite(t, testSiteConfig{})
   266  	c.Assert(err, qt.IsNil)
   267  
   268  	dirOut, clean2, err := htesting.CreateTempDir(hugofs.Os, "hugo-cli-out")
   269  	c.Assert(err, qt.IsNil)
   270  
   271  	defer clean()
   272  	defer clean2()
   273  
   274  	sourceFlag := fmt.Sprintf("-s=%s", dir)
   275  
   276  	tests := []struct {
   277  		commands           []string
   278  		flags              []string
   279  		expectErrToContain string
   280  	}{
   281  		// TODO(bep) permission issue on my OSX? "operation not permitted" {[]string{"check", "ulimit"}, nil, false},
   282  		{[]string{"env"}, nil, ""},
   283  		{[]string{"version"}, nil, ""},
   284  		// no args = hugo build
   285  		{nil, []string{sourceFlag}, ""},
   286  		{nil, []string{sourceFlag, "--renderToMemory"}, ""},
   287  		{[]string{"config"}, []string{sourceFlag}, ""},
   288  		{[]string{"convert", "toTOML"}, []string{sourceFlag, "-o=" + filepath.Join(dirOut, "toml")}, ""},
   289  		{[]string{"convert", "toYAML"}, []string{sourceFlag, "-o=" + filepath.Join(dirOut, "yaml")}, ""},
   290  		{[]string{"convert", "toJSON"}, []string{sourceFlag, "-o=" + filepath.Join(dirOut, "json")}, ""},
   291  		{[]string{"gen", "autocomplete"}, []string{"--completionfile=" + filepath.Join(dirOut, "autocomplete.txt")}, ""},
   292  		{[]string{"gen", "chromastyles"}, []string{"--style=manni"}, ""},
   293  		{[]string{"gen", "doc"}, []string{"--dir=" + filepath.Join(dirOut, "doc")}, ""},
   294  		{[]string{"gen", "man"}, []string{"--dir=" + filepath.Join(dirOut, "man")}, ""},
   295  		{[]string{"list", "drafts"}, []string{sourceFlag}, ""},
   296  		{[]string{"list", "expired"}, []string{sourceFlag}, ""},
   297  		{[]string{"list", "future"}, []string{sourceFlag}, ""},
   298  		{[]string{"new", "new-page.md"}, []string{sourceFlag}, ""},
   299  		{[]string{"new", "site", filepath.Join(dirOut, "new-site")}, nil, ""},
   300  		{[]string{"unknowncommand"}, nil, "unknown command"},
   301  		// TODO(bep) cli refactor fix https://github.com/gohugoio/hugo/issues/4450
   302  		//{[]string{"new", "theme", filepath.Join(dirOut, "new-theme")}, nil,false},
   303  	}
   304  
   305  	for _, test := range tests {
   306  		b := newCommandsBuilder().addAll().build()
   307  		hugoCmd := b.getCommand()
   308  		test.flags = append(test.flags, "--quiet")
   309  		hugoCmd.SetArgs(append(test.commands, test.flags...))
   310  
   311  		// TODO(bep) capture output and add some simple asserts
   312  		// TODO(bep) misspelled subcommands does not return an error. We should investigate this
   313  		// but before that, check for "Error: unknown command".
   314  
   315  		_, err := hugoCmd.ExecuteC()
   316  		if test.expectErrToContain != "" {
   317  			c.Assert(err, qt.Not(qt.IsNil))
   318  			c.Assert(err.Error(), qt.Contains, test.expectErrToContain)
   319  		} else {
   320  			c.Assert(err, qt.IsNil)
   321  		}
   322  
   323  		// Assert that we have not left any development debug artifacts in
   324  		// the code.
   325  		if b.c != nil {
   326  			_, ok := b.c.destinationFs.(types.DevMarker)
   327  			c.Assert(ok, qt.Equals, false)
   328  		}
   329  
   330  	}
   331  }
   332  
   333  type testSiteConfig struct {
   334  	configTOML string
   335  	contentDir string
   336  }
   337  
   338  func createSimpleTestSite(t *testing.T, cfg testSiteConfig) (string, func(), error) {
   339  	d, clean, e := htesting.CreateTempDir(hugofs.Os, "hugo-cli")
   340  	if e != nil {
   341  		return "", nil, e
   342  	}
   343  
   344  	cfgStr := `
   345  
   346  baseURL = "https://example.org"
   347  title = "Hugo Commands"
   348  
   349  
   350  `
   351  
   352  	contentDir := "content"
   353  
   354  	if cfg.configTOML != "" {
   355  		cfgStr = cfg.configTOML
   356  	}
   357  	if cfg.contentDir != "" {
   358  		contentDir = cfg.contentDir
   359  	}
   360  
   361  	os.MkdirAll(filepath.Join(d, "public"), 0777)
   362  
   363  	// Just the basic. These are for CLI tests, not site testing.
   364  	writeFile(t, filepath.Join(d, "config.toml"), cfgStr)
   365  	writeFile(t, filepath.Join(d, "config", "staging", "params.toml"), `myparam="paramstaging"`)
   366  	writeFile(t, filepath.Join(d, "config", "staging", "deployment.toml"), `
   367  [[targets]]
   368  name = "mydeployment"
   369  URL = "hugocloud://hugotestbucket"
   370  `)
   371  
   372  	writeFile(t, filepath.Join(d, "config", "testing", "params.toml"), `myparam="paramtesting"`)
   373  	writeFile(t, filepath.Join(d, "config", "production", "params.toml"), `myparam="paramproduction"`)
   374  
   375  	writeFile(t, filepath.Join(d, contentDir, "p1.md"), `
   376  ---
   377  title: "P1"
   378  weight: 1
   379  ---
   380  
   381  Content
   382  
   383  `)
   384  
   385  	writeFile(t, filepath.Join(d, "layouts", "_default", "single.html"), `
   386  
   387  Single: {{ .Title }}
   388  
   389  `)
   390  
   391  	writeFile(t, filepath.Join(d, "layouts", "_default", "list.html"), `
   392  
   393  List: {{ .Title }}
   394  Environment: {{ hugo.Environment }}
   395  
   396  `)
   397  
   398  	return d, clean, nil
   399  }
   400  
   401  func writeFile(t *testing.T, filename, content string) {
   402  	must(t, os.MkdirAll(filepath.Dir(filename), os.FileMode(0755)))
   403  	must(t, ioutil.WriteFile(filename, []byte(content), os.FileMode(0755)))
   404  }
   405  
   406  func must(t *testing.T, err error) {
   407  	if err != nil {
   408  		t.Fatal(err)
   409  	}
   410  }