hugo

Fork of github.com/gohugoio/hugo with reverse pagination support

git clone git://git.shimmy1996.com/hugo.git

basefs_test.go (15429B)

    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 filesystems
   15 
   16 import (
   17 	"errors"
   18 	"fmt"
   19 	"os"
   20 	"path/filepath"
   21 	"strings"
   22 	"testing"
   23 
   24 	"github.com/gobwas/glob"
   25 
   26 	"github.com/gohugoio/hugo/config"
   27 
   28 	"github.com/gohugoio/hugo/langs"
   29 
   30 	"github.com/spf13/afero"
   31 
   32 	qt "github.com/frankban/quicktest"
   33 	"github.com/gohugoio/hugo/hugofs"
   34 	"github.com/gohugoio/hugo/hugolib/paths"
   35 	"github.com/gohugoio/hugo/modules"
   36 )
   37 
   38 func initConfig(fs afero.Fs, cfg config.Provider) error {
   39 	if _, err := langs.LoadLanguageSettings(cfg, nil); err != nil {
   40 		return err
   41 	}
   42 
   43 	modConfig, err := modules.DecodeConfig(cfg)
   44 	if err != nil {
   45 		return err
   46 	}
   47 
   48 	workingDir := cfg.GetString("workingDir")
   49 	themesDir := cfg.GetString("themesDir")
   50 	if !filepath.IsAbs(themesDir) {
   51 		themesDir = filepath.Join(workingDir, themesDir)
   52 	}
   53 	globAll := glob.MustCompile("**", '/')
   54 	modulesClient := modules.NewClient(modules.ClientConfig{
   55 		Fs:           fs,
   56 		WorkingDir:   workingDir,
   57 		ThemesDir:    themesDir,
   58 		ModuleConfig: modConfig,
   59 		IgnoreVendor: globAll,
   60 	})
   61 
   62 	moduleConfig, err := modulesClient.Collect()
   63 	if err != nil {
   64 		return err
   65 	}
   66 
   67 	if err := modules.ApplyProjectConfigDefaults(cfg, moduleConfig.ActiveModules[0]); err != nil {
   68 		return err
   69 	}
   70 
   71 	cfg.Set("allModules", moduleConfig.ActiveModules)
   72 
   73 	return nil
   74 }
   75 
   76 func TestNewBaseFs(t *testing.T) {
   77 	c := qt.New(t)
   78 	v := config.NewWithTestDefaults()
   79 
   80 	fs := hugofs.NewMem(v)
   81 
   82 	themes := []string{"btheme", "atheme"}
   83 
   84 	workingDir := filepath.FromSlash("/my/work")
   85 	v.Set("workingDir", workingDir)
   86 	v.Set("contentDir", "content")
   87 	v.Set("themesDir", "themes")
   88 	v.Set("defaultContentLanguage", "en")
   89 	v.Set("theme", themes[:1])
   90 
   91 	// Write some data to the themes
   92 	for _, theme := range themes {
   93 		for _, dir := range []string{"i18n", "data", "archetypes", "layouts"} {
   94 			base := filepath.Join(workingDir, "themes", theme, dir)
   95 			filenameTheme := filepath.Join(base, fmt.Sprintf("theme-file-%s.txt", theme))
   96 			filenameOverlap := filepath.Join(base, "f3.txt")
   97 			fs.Source.Mkdir(base, 0755)
   98 			content := []byte(fmt.Sprintf("content:%s:%s", theme, dir))
   99 			afero.WriteFile(fs.Source, filenameTheme, content, 0755)
  100 			afero.WriteFile(fs.Source, filenameOverlap, content, 0755)
  101 		}
  102 		// Write some files to the root of the theme
  103 		base := filepath.Join(workingDir, "themes", theme)
  104 		afero.WriteFile(fs.Source, filepath.Join(base, fmt.Sprintf("theme-root-%s.txt", theme)), []byte(fmt.Sprintf("content:%s", theme)), 0755)
  105 		afero.WriteFile(fs.Source, filepath.Join(base, "file-theme-root.txt"), []byte(fmt.Sprintf("content:%s", theme)), 0755)
  106 	}
  107 
  108 	afero.WriteFile(fs.Source, filepath.Join(workingDir, "file-root.txt"), []byte("content-project"), 0755)
  109 
  110 	afero.WriteFile(fs.Source, filepath.Join(workingDir, "themes", "btheme", "config.toml"), []byte(`
  111 theme = ["atheme"]
  112 `), 0755)
  113 
  114 	setConfigAndWriteSomeFilesTo(fs.Source, v, "contentDir", "mycontent", 3)
  115 	setConfigAndWriteSomeFilesTo(fs.Source, v, "i18nDir", "myi18n", 4)
  116 	setConfigAndWriteSomeFilesTo(fs.Source, v, "layoutDir", "mylayouts", 5)
  117 	setConfigAndWriteSomeFilesTo(fs.Source, v, "staticDir", "mystatic", 6)
  118 	setConfigAndWriteSomeFilesTo(fs.Source, v, "dataDir", "mydata", 7)
  119 	setConfigAndWriteSomeFilesTo(fs.Source, v, "archetypeDir", "myarchetypes", 8)
  120 	setConfigAndWriteSomeFilesTo(fs.Source, v, "assetDir", "myassets", 9)
  121 	setConfigAndWriteSomeFilesTo(fs.Source, v, "resourceDir", "myrsesource", 10)
  122 
  123 	v.Set("publishDir", "public")
  124 	c.Assert(initConfig(fs.Source, v), qt.IsNil)
  125 
  126 	p, err := paths.New(fs, v)
  127 	c.Assert(err, qt.IsNil)
  128 
  129 	bfs, err := NewBase(p, nil)
  130 	c.Assert(err, qt.IsNil)
  131 	c.Assert(bfs, qt.Not(qt.IsNil))
  132 
  133 	root, err := bfs.I18n.Fs.Open("")
  134 	c.Assert(err, qt.IsNil)
  135 	dirnames, err := root.Readdirnames(-1)
  136 	c.Assert(err, qt.IsNil)
  137 	c.Assert(dirnames, qt.DeepEquals, []string{"f1.txt", "f2.txt", "f3.txt", "f4.txt", "f3.txt", "theme-file-btheme.txt", "f3.txt", "theme-file-atheme.txt"})
  138 
  139 	root, err = bfs.Data.Fs.Open("")
  140 	c.Assert(err, qt.IsNil)
  141 	dirnames, err = root.Readdirnames(-1)
  142 	c.Assert(err, qt.IsNil)
  143 	c.Assert(dirnames, qt.DeepEquals, []string{"f1.txt", "f2.txt", "f3.txt", "f4.txt", "f5.txt", "f6.txt", "f7.txt", "f3.txt", "theme-file-btheme.txt", "f3.txt", "theme-file-atheme.txt"})
  144 
  145 	checkFileCount(bfs.Layouts.Fs, "", c, 7)
  146 
  147 	checkFileCount(bfs.Content.Fs, "", c, 3)
  148 	checkFileCount(bfs.I18n.Fs, "", c, 8) // 4 + 4 themes
  149 
  150 	checkFileCount(bfs.Static[""].Fs, "", c, 6)
  151 	checkFileCount(bfs.Data.Fs, "", c, 11)       // 7 + 4 themes
  152 	checkFileCount(bfs.Archetypes.Fs, "", c, 10) // 8 + 2 themes
  153 	checkFileCount(bfs.Assets.Fs, "", c, 9)
  154 	checkFileCount(bfs.Work, "", c, 90)
  155 
  156 	c.Assert(bfs.IsData(filepath.Join(workingDir, "mydata", "file1.txt")), qt.Equals, true)
  157 	c.Assert(bfs.IsI18n(filepath.Join(workingDir, "myi18n", "file1.txt")), qt.Equals, true)
  158 	c.Assert(bfs.IsLayout(filepath.Join(workingDir, "mylayouts", "file1.txt")), qt.Equals, true)
  159 	c.Assert(bfs.IsStatic(filepath.Join(workingDir, "mystatic", "file1.txt")), qt.Equals, true)
  160 	c.Assert(bfs.IsAsset(filepath.Join(workingDir, "myassets", "file1.txt")), qt.Equals, true)
  161 
  162 	contentFilename := filepath.Join(workingDir, "mycontent", "file1.txt")
  163 	c.Assert(bfs.IsContent(contentFilename), qt.Equals, true)
  164 	rel := bfs.RelContentDir(contentFilename)
  165 	c.Assert(rel, qt.Equals, "file1.txt")
  166 
  167 	// Check Work fs vs theme
  168 	checkFileContent(bfs.Work, "file-root.txt", c, "content-project")
  169 	checkFileContent(bfs.Work, "theme-root-atheme.txt", c, "content:atheme")
  170 
  171 	// https://github.com/gohugoio/hugo/issues/5318
  172 	// Check both project and theme.
  173 	for _, fs := range []afero.Fs{bfs.Archetypes.Fs, bfs.Layouts.Fs} {
  174 		for _, filename := range []string{"/f1.txt", "/theme-file-atheme.txt"} {
  175 			filename = filepath.FromSlash(filename)
  176 			f, err := fs.Open(filename)
  177 			c.Assert(err, qt.IsNil)
  178 			f.Close()
  179 		}
  180 	}
  181 }
  182 
  183 func createConfig() config.Provider {
  184 	v := config.NewWithTestDefaults()
  185 	v.Set("contentDir", "mycontent")
  186 	v.Set("i18nDir", "myi18n")
  187 	v.Set("staticDir", "mystatic")
  188 	v.Set("dataDir", "mydata")
  189 	v.Set("layoutDir", "mylayouts")
  190 	v.Set("archetypeDir", "myarchetypes")
  191 	v.Set("assetDir", "myassets")
  192 	v.Set("resourceDir", "resources")
  193 	v.Set("publishDir", "public")
  194 	v.Set("defaultContentLanguage", "en")
  195 
  196 	return v
  197 }
  198 
  199 func TestNewBaseFsEmpty(t *testing.T) {
  200 	c := qt.New(t)
  201 	v := createConfig()
  202 	fs := hugofs.NewMem(v)
  203 	c.Assert(initConfig(fs.Source, v), qt.IsNil)
  204 
  205 	p, err := paths.New(fs, v)
  206 	c.Assert(err, qt.IsNil)
  207 	bfs, err := NewBase(p, nil)
  208 	c.Assert(err, qt.IsNil)
  209 	c.Assert(bfs, qt.Not(qt.IsNil))
  210 	c.Assert(bfs.Archetypes.Fs, qt.Not(qt.IsNil))
  211 	c.Assert(bfs.Layouts.Fs, qt.Not(qt.IsNil))
  212 	c.Assert(bfs.Data.Fs, qt.Not(qt.IsNil))
  213 	c.Assert(bfs.I18n.Fs, qt.Not(qt.IsNil))
  214 	c.Assert(bfs.Work, qt.Not(qt.IsNil))
  215 	c.Assert(bfs.Content.Fs, qt.Not(qt.IsNil))
  216 	c.Assert(bfs.Static, qt.Not(qt.IsNil))
  217 }
  218 
  219 func TestRealDirs(t *testing.T) {
  220 	c := qt.New(t)
  221 	v := createConfig()
  222 	root, themesDir := t.TempDir(), t.TempDir()
  223 	v.Set("workingDir", root)
  224 	v.Set("themesDir", themesDir)
  225 	v.Set("theme", "mytheme")
  226 
  227 	fs := hugofs.NewDefault(v)
  228 	sfs := fs.Source
  229 
  230 	defer func() {
  231 		os.RemoveAll(root)
  232 		os.RemoveAll(themesDir)
  233 	}()
  234 
  235 	c.Assert(sfs.MkdirAll(filepath.Join(root, "myassets", "scss", "sf1"), 0755), qt.IsNil)
  236 	c.Assert(sfs.MkdirAll(filepath.Join(root, "myassets", "scss", "sf2"), 0755), qt.IsNil)
  237 	c.Assert(sfs.MkdirAll(filepath.Join(themesDir, "mytheme", "assets", "scss", "sf2"), 0755), qt.IsNil)
  238 	c.Assert(sfs.MkdirAll(filepath.Join(themesDir, "mytheme", "assets", "scss", "sf3"), 0755), qt.IsNil)
  239 	c.Assert(sfs.MkdirAll(filepath.Join(root, "resources"), 0755), qt.IsNil)
  240 	c.Assert(sfs.MkdirAll(filepath.Join(themesDir, "mytheme", "resources"), 0755), qt.IsNil)
  241 
  242 	c.Assert(sfs.MkdirAll(filepath.Join(root, "myassets", "js", "f2"), 0755), qt.IsNil)
  243 
  244 	afero.WriteFile(sfs, filepath.Join(filepath.Join(root, "myassets", "scss", "sf1", "a1.scss")), []byte("content"), 0755)
  245 	afero.WriteFile(sfs, filepath.Join(filepath.Join(root, "myassets", "scss", "sf2", "a3.scss")), []byte("content"), 0755)
  246 	afero.WriteFile(sfs, filepath.Join(filepath.Join(root, "myassets", "scss", "a2.scss")), []byte("content"), 0755)
  247 	afero.WriteFile(sfs, filepath.Join(filepath.Join(themesDir, "mytheme", "assets", "scss", "sf2", "a3.scss")), []byte("content"), 0755)
  248 	afero.WriteFile(sfs, filepath.Join(filepath.Join(themesDir, "mytheme", "assets", "scss", "sf3", "a4.scss")), []byte("content"), 0755)
  249 
  250 	afero.WriteFile(sfs, filepath.Join(filepath.Join(themesDir, "mytheme", "resources", "t1.txt")), []byte("content"), 0755)
  251 	afero.WriteFile(sfs, filepath.Join(filepath.Join(root, "resources", "p1.txt")), []byte("content"), 0755)
  252 	afero.WriteFile(sfs, filepath.Join(filepath.Join(root, "resources", "p2.txt")), []byte("content"), 0755)
  253 
  254 	afero.WriteFile(sfs, filepath.Join(filepath.Join(root, "myassets", "js", "f2", "a1.js")), []byte("content"), 0755)
  255 	afero.WriteFile(sfs, filepath.Join(filepath.Join(root, "myassets", "js", "a2.js")), []byte("content"), 0755)
  256 
  257 	c.Assert(initConfig(fs.Source, v), qt.IsNil)
  258 
  259 	p, err := paths.New(fs, v)
  260 	c.Assert(err, qt.IsNil)
  261 	bfs, err := NewBase(p, nil)
  262 	c.Assert(err, qt.IsNil)
  263 	c.Assert(bfs, qt.Not(qt.IsNil))
  264 
  265 	checkFileCount(bfs.Assets.Fs, "", c, 6)
  266 
  267 	realDirs := bfs.Assets.RealDirs("scss")
  268 	c.Assert(len(realDirs), qt.Equals, 2)
  269 	c.Assert(realDirs[0], qt.Equals, filepath.Join(root, "myassets/scss"))
  270 	c.Assert(realDirs[len(realDirs)-1], qt.Equals, filepath.Join(themesDir, "mytheme/assets/scss"))
  271 
  272 	c.Assert(bfs.theBigFs, qt.Not(qt.IsNil))
  273 }
  274 
  275 func TestStaticFs(t *testing.T) {
  276 	c := qt.New(t)
  277 	v := createConfig()
  278 	workDir := "mywork"
  279 	v.Set("workingDir", workDir)
  280 	v.Set("themesDir", "themes")
  281 	v.Set("theme", []string{"t1", "t2"})
  282 
  283 	fs := hugofs.NewMem(v)
  284 
  285 	themeStaticDir := filepath.Join(workDir, "themes", "t1", "static")
  286 	themeStaticDir2 := filepath.Join(workDir, "themes", "t2", "static")
  287 
  288 	afero.WriteFile(fs.Source, filepath.Join(workDir, "mystatic", "f1.txt"), []byte("Hugo Rocks!"), 0755)
  289 	afero.WriteFile(fs.Source, filepath.Join(themeStaticDir, "f1.txt"), []byte("Hugo Themes Rocks!"), 0755)
  290 	afero.WriteFile(fs.Source, filepath.Join(themeStaticDir, "f2.txt"), []byte("Hugo Themes Still Rocks!"), 0755)
  291 	afero.WriteFile(fs.Source, filepath.Join(themeStaticDir2, "f2.txt"), []byte("Hugo Themes Rocks in t2!"), 0755)
  292 
  293 	c.Assert(initConfig(fs.Source, v), qt.IsNil)
  294 
  295 	p, err := paths.New(fs, v)
  296 	c.Assert(err, qt.IsNil)
  297 	bfs, err := NewBase(p, nil)
  298 	c.Assert(err, qt.IsNil)
  299 
  300 	sfs := bfs.StaticFs("en")
  301 	checkFileContent(sfs, "f1.txt", c, "Hugo Rocks!")
  302 	checkFileContent(sfs, "f2.txt", c, "Hugo Themes Still Rocks!")
  303 }
  304 
  305 func TestStaticFsMultiHost(t *testing.T) {
  306 	c := qt.New(t)
  307 	v := createConfig()
  308 	workDir := "mywork"
  309 	v.Set("workingDir", workDir)
  310 	v.Set("themesDir", "themes")
  311 	v.Set("theme", "t1")
  312 	v.Set("defaultContentLanguage", "en")
  313 
  314 	langConfig := map[string]any{
  315 		"no": map[string]any{
  316 			"staticDir": "static_no",
  317 			"baseURL":   "https://example.org/no/",
  318 		},
  319 		"en": map[string]any{
  320 			"baseURL": "https://example.org/en/",
  321 		},
  322 	}
  323 
  324 	v.Set("languages", langConfig)
  325 
  326 	fs := hugofs.NewMem(v)
  327 
  328 	themeStaticDir := filepath.Join(workDir, "themes", "t1", "static")
  329 
  330 	afero.WriteFile(fs.Source, filepath.Join(workDir, "mystatic", "f1.txt"), []byte("Hugo Rocks!"), 0755)
  331 	afero.WriteFile(fs.Source, filepath.Join(workDir, "static_no", "f1.txt"), []byte("Hugo Rocks in Norway!"), 0755)
  332 
  333 	afero.WriteFile(fs.Source, filepath.Join(themeStaticDir, "f1.txt"), []byte("Hugo Themes Rocks!"), 0755)
  334 	afero.WriteFile(fs.Source, filepath.Join(themeStaticDir, "f2.txt"), []byte("Hugo Themes Still Rocks!"), 0755)
  335 
  336 	c.Assert(initConfig(fs.Source, v), qt.IsNil)
  337 
  338 	p, err := paths.New(fs, v)
  339 	c.Assert(err, qt.IsNil)
  340 	bfs, err := NewBase(p, nil)
  341 	c.Assert(err, qt.IsNil)
  342 	enFs := bfs.StaticFs("en")
  343 	checkFileContent(enFs, "f1.txt", c, "Hugo Rocks!")
  344 	checkFileContent(enFs, "f2.txt", c, "Hugo Themes Still Rocks!")
  345 
  346 	noFs := bfs.StaticFs("no")
  347 	checkFileContent(noFs, "f1.txt", c, "Hugo Rocks in Norway!")
  348 	checkFileContent(noFs, "f2.txt", c, "Hugo Themes Still Rocks!")
  349 }
  350 
  351 func TestMakePathRelative(t *testing.T) {
  352 	c := qt.New(t)
  353 	v := createConfig()
  354 	fs := hugofs.NewMem(v)
  355 	workDir := "mywork"
  356 	v.Set("workingDir", workDir)
  357 
  358 	c.Assert(fs.Source.MkdirAll(filepath.Join(workDir, "dist", "d1"), 0777), qt.IsNil)
  359 	c.Assert(fs.Source.MkdirAll(filepath.Join(workDir, "static", "d2"), 0777), qt.IsNil)
  360 	c.Assert(fs.Source.MkdirAll(filepath.Join(workDir, "dust", "d2"), 0777), qt.IsNil)
  361 
  362 	moduleCfg := map[string]any{
  363 		"mounts": []any{
  364 			map[string]any{
  365 				"source": "dist",
  366 				"target": "static/mydist",
  367 			},
  368 			map[string]any{
  369 				"source": "dust",
  370 				"target": "static/foo/bar",
  371 			},
  372 			map[string]any{
  373 				"source": "static",
  374 				"target": "static",
  375 			},
  376 		},
  377 	}
  378 
  379 	v.Set("module", moduleCfg)
  380 
  381 	c.Assert(initConfig(fs.Source, v), qt.IsNil)
  382 
  383 	p, err := paths.New(fs, v)
  384 	c.Assert(err, qt.IsNil)
  385 	bfs, err := NewBase(p, nil)
  386 	c.Assert(err, qt.IsNil)
  387 
  388 	sfs := bfs.Static[""]
  389 	c.Assert(sfs, qt.Not(qt.IsNil))
  390 
  391 	makeRel := func(s string) string {
  392 		r, _ := sfs.MakePathRelative(s)
  393 		return r
  394 	}
  395 
  396 	c.Assert(makeRel(filepath.Join(workDir, "dist", "d1", "foo.txt")), qt.Equals, filepath.FromSlash("mydist/d1/foo.txt"))
  397 	c.Assert(makeRel(filepath.Join(workDir, "static", "d2", "foo.txt")), qt.Equals, filepath.FromSlash("d2/foo.txt"))
  398 	c.Assert(makeRel(filepath.Join(workDir, "dust", "d3", "foo.txt")), qt.Equals, filepath.FromSlash("foo/bar/d3/foo.txt"))
  399 }
  400 
  401 func checkFileCount(fs afero.Fs, dirname string, c *qt.C, expected int) {
  402 	count, _, err := countFilesAndGetFilenames(fs, dirname)
  403 	c.Assert(err, qt.IsNil)
  404 	c.Assert(count, qt.Equals, expected)
  405 }
  406 
  407 func checkFileContent(fs afero.Fs, filename string, c *qt.C, expected ...string) {
  408 	b, err := afero.ReadFile(fs, filename)
  409 	c.Assert(err, qt.IsNil)
  410 
  411 	content := string(b)
  412 
  413 	for _, e := range expected {
  414 		c.Assert(content, qt.Contains, e)
  415 	}
  416 }
  417 
  418 func countFilesAndGetFilenames(fs afero.Fs, dirname string) (int, []string, error) {
  419 	if fs == nil {
  420 		return 0, nil, errors.New("no fs")
  421 	}
  422 
  423 	counter := 0
  424 	var filenames []string
  425 
  426 	wf := func(path string, info hugofs.FileMetaInfo, err error) error {
  427 		if err != nil {
  428 			return err
  429 		}
  430 		if !info.IsDir() {
  431 			counter++
  432 		}
  433 
  434 		if info.Name() != "." {
  435 			name := info.Name()
  436 			name = strings.Replace(name, filepath.FromSlash("/my/work"), "WORK_DIR", 1)
  437 			filenames = append(filenames, name)
  438 		}
  439 
  440 		return nil
  441 	}
  442 
  443 	w := hugofs.NewWalkway(hugofs.WalkwayConfig{Fs: fs, Root: dirname, WalkFn: wf})
  444 
  445 	if err := w.Walk(); err != nil {
  446 		return -1, nil, err
  447 	}
  448 
  449 	return counter, filenames, nil
  450 }
  451 
  452 func setConfigAndWriteSomeFilesTo(fs afero.Fs, v config.Provider, key, val string, num int) {
  453 	workingDir := v.GetString("workingDir")
  454 	v.Set(key, val)
  455 	fs.Mkdir(val, 0755)
  456 	for i := 0; i < num; i++ {
  457 		filename := filepath.Join(workingDir, val, fmt.Sprintf("f%d.txt", i+1))
  458 		afero.WriteFile(fs, filename, []byte(fmt.Sprintf("content:%s:%d", key, i+1)), 0755)
  459 	}
  460 }