github.com/neohugo/neohugo@v0.123.8/hugolib/filesystems/basefs_test.go (about)

     1  // Copyright 2024 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_test
    15  
    16  import (
    17  	"errors"
    18  	"fmt"
    19  	"os"
    20  	"path/filepath"
    21  	"runtime"
    22  	"strings"
    23  	"testing"
    24  
    25  	"github.com/neohugo/neohugo/config"
    26  	"github.com/neohugo/neohugo/config/testconfig"
    27  	"github.com/neohugo/neohugo/hugolib"
    28  
    29  	"github.com/spf13/afero"
    30  
    31  	qt "github.com/frankban/quicktest"
    32  	"github.com/neohugo/neohugo/hugofs"
    33  	"github.com/neohugo/neohugo/hugolib/filesystems"
    34  	"github.com/neohugo/neohugo/hugolib/paths"
    35  )
    36  
    37  func TestNewBaseFs(t *testing.T) {
    38  	c := qt.New(t)
    39  	v := config.New()
    40  
    41  	themes := []string{"btheme", "atheme"}
    42  
    43  	workingDir := filepath.FromSlash("/my/work")
    44  	v.Set("workingDir", workingDir)
    45  	v.Set("contentDir", "content")
    46  	v.Set("themesDir", "themes")
    47  	v.Set("defaultContentLanguage", "en")
    48  	v.Set("theme", themes[:1])
    49  	v.Set("publishDir", "public")
    50  
    51  	afs := afero.NewMemMapFs()
    52  
    53  	// Write some data to the themes
    54  	for _, theme := range themes {
    55  		for _, dir := range []string{"i18n", "data", "archetypes", "layouts"} {
    56  			base := filepath.Join(workingDir, "themes", theme, dir)
    57  			filenameTheme := filepath.Join(base, fmt.Sprintf("theme-file-%s.txt", theme))
    58  			filenameOverlap := filepath.Join(base, "f3.txt")
    59  			afs.Mkdir(base, 0o755) // nolint
    60  			content := []byte(fmt.Sprintf("content:%s:%s", theme, dir))
    61  			afero.WriteFile(afs, filenameTheme, content, 0o755)   // nolint
    62  			afero.WriteFile(afs, filenameOverlap, content, 0o755) // nolint
    63  		}
    64  		// Write some files to the root of the theme
    65  		base := filepath.Join(workingDir, "themes", theme)
    66  		// nolint
    67  		afero.WriteFile(afs, filepath.Join(base, fmt.Sprintf("theme-root-%s.txt", theme)), []byte(fmt.Sprintf("content:%s", theme)), 0o755)
    68  		// nolint
    69  		afero.WriteFile(afs, filepath.Join(base, "file-theme-root.txt"), []byte(fmt.Sprintf("content:%s", theme)), 0o755)
    70  	}
    71  	// nolint
    72  	afero.WriteFile(afs, filepath.Join(workingDir, "file-root.txt"), []byte("content-project"), 0o755)
    73  	// nolint
    74  	afero.WriteFile(afs, filepath.Join(workingDir, "themes", "btheme", "config.toml"), []byte(`
    75  theme = ["atheme"]
    76  `), 0o755)
    77  
    78  	setConfigAndWriteSomeFilesTo(afs, v, "contentDir", "mycontent", 3)      // nolint
    79  	setConfigAndWriteSomeFilesTo(afs, v, "i18nDir", "myi18n", 4)            // nolint
    80  	setConfigAndWriteSomeFilesTo(afs, v, "layoutDir", "mylayouts", 5)       // nolint
    81  	setConfigAndWriteSomeFilesTo(afs, v, "staticDir", "mystatic", 6)        // nolint
    82  	setConfigAndWriteSomeFilesTo(afs, v, "dataDir", "mydata", 7)            // nolint
    83  	setConfigAndWriteSomeFilesTo(afs, v, "archetypeDir", "myarchetypes", 8) // nolint
    84  	setConfigAndWriteSomeFilesTo(afs, v, "assetDir", "myassets", 9)         // nolint
    85  	setConfigAndWriteSomeFilesTo(afs, v, "resourceDir", "myrsesource", 10)  // nolint
    86  
    87  	conf := testconfig.GetTestConfig(afs, v)
    88  	fs := hugofs.NewFrom(afs, conf.BaseConfig())
    89  
    90  	p, err := paths.New(fs, conf)
    91  	c.Assert(err, qt.IsNil)
    92  
    93  	bfs, err := filesystems.NewBase(p, nil)
    94  	c.Assert(err, qt.IsNil)
    95  	c.Assert(bfs, qt.Not(qt.IsNil))
    96  
    97  	root, err := bfs.I18n.Fs.Open("")
    98  	c.Assert(err, qt.IsNil)
    99  	dirnames, err := root.Readdirnames(-1)
   100  	c.Assert(err, qt.IsNil)
   101  	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"})
   102  
   103  	root, err = bfs.Data.Fs.Open("")
   104  	c.Assert(err, qt.IsNil)
   105  	dirnames, err = root.Readdirnames(-1)
   106  	c.Assert(err, qt.IsNil)
   107  	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"})
   108  
   109  	checkFileCount(bfs.Layouts.Fs, "", c, 7)
   110  
   111  	checkFileCount(bfs.Content.Fs, "", c, 3)
   112  	checkFileCount(bfs.I18n.Fs, "", c, 8) // 4 + 4 themes
   113  
   114  	checkFileCount(bfs.Static[""].Fs, "", c, 6)
   115  	checkFileCount(bfs.Data.Fs, "", c, 11)       // 7 + 4 themes
   116  	checkFileCount(bfs.Archetypes.Fs, "", c, 10) // 8 + 2 themes
   117  	checkFileCount(bfs.Assets.Fs, "", c, 9)
   118  	checkFileCount(bfs.Work, "", c, 90)
   119  
   120  	c.Assert(bfs.IsStatic(filepath.Join(workingDir, "mystatic", "file1.txt")), qt.Equals, true)
   121  
   122  	contentFilename := filepath.Join(workingDir, "mycontent", "file1.txt")
   123  	c.Assert(bfs.IsContent(contentFilename), qt.Equals, true)
   124  	// Check Work fs vs theme
   125  	checkFileContent(bfs.Work, "file-root.txt", c, "content-project")
   126  	checkFileContent(bfs.Work, "theme-root-atheme.txt", c, "content:atheme")
   127  
   128  	// https://github.com/neohugo/neohugo/issues/5318
   129  	// Check both project and theme.
   130  	for _, fs := range []afero.Fs{bfs.Archetypes.Fs, bfs.Layouts.Fs} {
   131  		for _, filename := range []string{"/f1.txt", "/theme-file-atheme.txt"} {
   132  			filename = filepath.FromSlash(filename)
   133  			f, err := fs.Open(filename)
   134  			c.Assert(err, qt.IsNil)
   135  			f.Close()
   136  		}
   137  	}
   138  }
   139  
   140  func TestNewBaseFsEmpty(t *testing.T) {
   141  	c := qt.New(t)
   142  	afs := afero.NewMemMapFs()
   143  	conf := testconfig.GetTestConfig(afs, nil)
   144  	fs := hugofs.NewFrom(afs, conf.BaseConfig())
   145  	p, err := paths.New(fs, conf)
   146  	c.Assert(err, qt.IsNil)
   147  	bfs, err := filesystems.NewBase(p, nil)
   148  	c.Assert(err, qt.IsNil)
   149  	c.Assert(bfs, qt.Not(qt.IsNil))
   150  	c.Assert(bfs.Archetypes.Fs, qt.Not(qt.IsNil))
   151  	c.Assert(bfs.Layouts.Fs, qt.Not(qt.IsNil))
   152  	c.Assert(bfs.Data.Fs, qt.Not(qt.IsNil))
   153  	c.Assert(bfs.I18n.Fs, qt.Not(qt.IsNil))
   154  	c.Assert(bfs.Work, qt.Not(qt.IsNil))
   155  	c.Assert(bfs.Content.Fs, qt.Not(qt.IsNil))
   156  	c.Assert(bfs.Static, qt.Not(qt.IsNil))
   157  }
   158  
   159  func TestRealDirs(t *testing.T) {
   160  	c := qt.New(t)
   161  	v := config.New()
   162  	root, themesDir := t.TempDir(), t.TempDir()
   163  	v.Set("workingDir", root)
   164  	v.Set("themesDir", themesDir)
   165  	v.Set("assetDir", "myassets")
   166  	v.Set("theme", "mytheme")
   167  
   168  	afs := &hugofs.OpenFilesFs{Fs: hugofs.Os}
   169  
   170  	c.Assert(afs.MkdirAll(filepath.Join(root, "myassets", "scss", "sf1"), 0o755), qt.IsNil)
   171  	c.Assert(afs.MkdirAll(filepath.Join(root, "myassets", "scss", "sf2"), 0o755), qt.IsNil)
   172  	c.Assert(afs.MkdirAll(filepath.Join(themesDir, "mytheme", "assets", "scss", "sf2"), 0o755), qt.IsNil)
   173  	c.Assert(afs.MkdirAll(filepath.Join(themesDir, "mytheme", "assets", "scss", "sf3"), 0o755), qt.IsNil)
   174  	c.Assert(afs.MkdirAll(filepath.Join(root, "resources"), 0o755), qt.IsNil)
   175  	c.Assert(afs.MkdirAll(filepath.Join(themesDir, "mytheme", "resources"), 0o755), qt.IsNil)
   176  
   177  	c.Assert(afs.MkdirAll(filepath.Join(root, "myassets", "js", "f2"), 0o755), qt.IsNil)
   178  
   179  	afero.WriteFile(afs, filepath.Join(filepath.Join(root, "myassets", "scss", "sf1", "a1.scss")), []byte("content"), 0o755)               // nolint
   180  	afero.WriteFile(afs, filepath.Join(filepath.Join(root, "myassets", "scss", "sf2", "a3.scss")), []byte("content"), 0o755)               // nolint
   181  	afero.WriteFile(afs, filepath.Join(filepath.Join(root, "myassets", "scss", "a2.scss")), []byte("content"), 0o755)                      // nolint
   182  	afero.WriteFile(afs, filepath.Join(filepath.Join(themesDir, "mytheme", "assets", "scss", "sf2", "a3.scss")), []byte("content"), 0o755) // nolint
   183  	afero.WriteFile(afs, filepath.Join(filepath.Join(themesDir, "mytheme", "assets", "scss", "sf3", "a4.scss")), []byte("content"), 0o755) // nolint
   184  
   185  	afero.WriteFile(afs, filepath.Join(filepath.Join(themesDir, "mytheme", "resources", "t1.txt")), []byte("content"), 0o755) // nolint
   186  	afero.WriteFile(afs, filepath.Join(filepath.Join(root, "resources", "p1.txt")), []byte("content"), 0o755)                 // nolint
   187  	afero.WriteFile(afs, filepath.Join(filepath.Join(root, "resources", "p2.txt")), []byte("content"), 0o755)                 // nolint
   188  
   189  	afero.WriteFile(afs, filepath.Join(filepath.Join(root, "myassets", "js", "f2", "a1.js")), []byte("content"), 0o755) // nolint
   190  	afero.WriteFile(afs, filepath.Join(filepath.Join(root, "myassets", "js", "a2.js")), []byte("content"), 0o755)       // nolint
   191  
   192  	conf := testconfig.GetTestConfig(afs, v)
   193  	fs := hugofs.NewFrom(afs, conf.BaseConfig())
   194  	p, err := paths.New(fs, conf)
   195  	c.Assert(err, qt.IsNil)
   196  	bfs, err := filesystems.NewBase(p, nil)
   197  	c.Assert(err, qt.IsNil)
   198  	c.Assert(bfs, qt.Not(qt.IsNil))
   199  
   200  	checkFileCount(bfs.Assets.Fs, "", c, 6)
   201  
   202  	realDirs := bfs.Assets.RealDirs("scss")
   203  	c.Assert(len(realDirs), qt.Equals, 2)
   204  	c.Assert(realDirs[0], qt.Equals, filepath.Join(root, "myassets/scss"))
   205  	c.Assert(realDirs[len(realDirs)-1], qt.Equals, filepath.Join(themesDir, "mytheme/assets/scss"))
   206  
   207  	realDirs = bfs.Assets.RealDirs("foo")
   208  	c.Assert(len(realDirs), qt.Equals, 0)
   209  
   210  	c.Assert(afs.OpenFiles(), qt.HasLen, 0)
   211  }
   212  
   213  func TestWatchFilenames(t *testing.T) {
   214  	t.Parallel()
   215  	files := `
   216  -- hugo.toml --
   217  theme = "t1"
   218  [[module.mounts]]
   219  source = 'content'
   220  target = 'content'
   221  [[module.mounts]]
   222  source = 'content2'
   223  target = 'content/c2'
   224  [[module.mounts]]
   225  source = "hugo_stats.json"
   226  target = "assets/watching/hugo_stats.json"
   227  -- hugo_stats.json --
   228  Some stats.
   229  -- content/foo.md --
   230  foo
   231  -- content2/bar.md --
   232  -- themes/t1/layouts/_default/single.html --
   233  {{ .Content }}
   234  -- themes/t1/static/f1.txt --
   235  `
   236  	b := hugolib.Test(t, files)
   237  	bfs := b.H.BaseFs
   238  	watchFilenames := bfs.WatchFilenames()
   239  	//   []string{"/hugo_stats.json", "/content", "/content2", "/themes/t1/layouts", "/themes/t1/layouts/_default", "/themes/t1/static"}
   240  	b.Assert(watchFilenames, qt.HasLen, 6)
   241  }
   242  
   243  func TestNoSymlinks(t *testing.T) {
   244  	if runtime.GOOS == "windows" {
   245  		t.Skip("skip on Windows")
   246  	}
   247  	files := `
   248  -- hugo.toml --
   249  theme = "t1"
   250  -- content/a/foo.md --
   251  foo
   252  -- static/a/f1.txt --
   253  F1 text
   254  -- themes/t1/layouts/_default/single.html --
   255  {{ .Content }}
   256  -- themes/t1/static/a/f1.txt --
   257  `
   258  	tmpDir := t.TempDir()
   259  
   260  	wd, _ := os.Getwd()
   261  
   262  	for _, component := range []string{"content", "static"} {
   263  		aDir := filepath.Join(tmpDir, component, "a")
   264  		bDir := filepath.Join(tmpDir, component, "b")
   265  		os.MkdirAll(aDir, 0o755) // nolint
   266  		os.MkdirAll(bDir, 0o755) // nolint
   267  		os.Chdir(bDir)           // nolint
   268  		os.Symlink("../a", "c")  // nolint
   269  	}
   270  
   271  	os.Chdir(wd) // nolint
   272  
   273  	b := hugolib.NewIntegrationTestBuilder(
   274  		hugolib.IntegrationTestConfig{
   275  			T:           t,
   276  			TxtarString: files,
   277  			NeedsOsFS:   true,
   278  			WorkingDir:  tmpDir,
   279  		},
   280  	).Build()
   281  
   282  	bfs := b.H.BaseFs
   283  	watchFilenames := bfs.WatchFilenames()
   284  	b.Assert(watchFilenames, qt.HasLen, 10)
   285  }
   286  
   287  func TestStaticFs(t *testing.T) {
   288  	c := qt.New(t)
   289  	v := config.New()
   290  	workDir := "mywork"
   291  	v.Set("workingDir", workDir)
   292  	v.Set("themesDir", "themes")
   293  	v.Set("staticDir", "mystatic")
   294  	v.Set("theme", []string{"t1", "t2"})
   295  
   296  	afs := afero.NewMemMapFs()
   297  
   298  	themeStaticDir := filepath.Join(workDir, "themes", "t1", "static")
   299  	themeStaticDir2 := filepath.Join(workDir, "themes", "t2", "static")
   300  
   301  	afero.WriteFile(afs, filepath.Join(workDir, "mystatic", "f1.txt"), []byte("Hugo Rocks!"), 0o755)          // nolint
   302  	afero.WriteFile(afs, filepath.Join(themeStaticDir, "f1.txt"), []byte("Hugo Themes Rocks!"), 0o755)        // nolint
   303  	afero.WriteFile(afs, filepath.Join(themeStaticDir, "f2.txt"), []byte("Hugo Themes Still Rocks!"), 0o755)  // nolint
   304  	afero.WriteFile(afs, filepath.Join(themeStaticDir2, "f2.txt"), []byte("Hugo Themes Rocks in t2!"), 0o755) // nolint
   305  
   306  	conf := testconfig.GetTestConfig(afs, v)
   307  	fs := hugofs.NewFrom(afs, conf.BaseConfig())
   308  	p, err := paths.New(fs, conf)
   309  
   310  	c.Assert(err, qt.IsNil)
   311  	bfs, err := filesystems.NewBase(p, nil)
   312  	c.Assert(err, qt.IsNil)
   313  
   314  	sfs := bfs.StaticFs("en")
   315  
   316  	checkFileContent(sfs, "f1.txt", c, "Hugo Rocks!")
   317  	checkFileContent(sfs, "f2.txt", c, "Hugo Themes Still Rocks!")
   318  }
   319  
   320  func TestStaticFsMultiHost(t *testing.T) {
   321  	c := qt.New(t)
   322  	v := config.New()
   323  	workDir := "mywork"
   324  	v.Set("workingDir", workDir)
   325  	v.Set("themesDir", "themes")
   326  	v.Set("staticDir", "mystatic")
   327  	v.Set("theme", "t1")
   328  	v.Set("defaultContentLanguage", "en")
   329  
   330  	langConfig := map[string]any{
   331  		"no": map[string]any{
   332  			"staticDir": "static_no",
   333  			"baseURL":   "https://example.org/no/",
   334  		},
   335  		"en": map[string]any{
   336  			"baseURL": "https://example.org/en/",
   337  		},
   338  	}
   339  
   340  	v.Set("languages", langConfig)
   341  
   342  	afs := afero.NewMemMapFs()
   343  
   344  	themeStaticDir := filepath.Join(workDir, "themes", "t1", "static")
   345  
   346  	afero.WriteFile(afs, filepath.Join(workDir, "mystatic", "f1.txt"), []byte("Hugo Rocks!"), 0o755)            // nolint
   347  	afero.WriteFile(afs, filepath.Join(workDir, "static_no", "f1.txt"), []byte("Hugo Rocks in Norway!"), 0o755) // nolint
   348  
   349  	afero.WriteFile(afs, filepath.Join(themeStaticDir, "f1.txt"), []byte("Hugo Themes Rocks!"), 0o755)       // nolint
   350  	afero.WriteFile(afs, filepath.Join(themeStaticDir, "f2.txt"), []byte("Hugo Themes Still Rocks!"), 0o755) // nolint
   351  
   352  	conf := testconfig.GetTestConfig(afs, v)
   353  	fs := hugofs.NewFrom(afs, conf.BaseConfig())
   354  
   355  	p, err := paths.New(fs, conf)
   356  	c.Assert(err, qt.IsNil)
   357  	bfs, err := filesystems.NewBase(p, nil)
   358  	c.Assert(err, qt.IsNil)
   359  	enFs := bfs.StaticFs("en")
   360  	checkFileContent(enFs, "f1.txt", c, "Hugo Rocks!")
   361  	checkFileContent(enFs, "f2.txt", c, "Hugo Themes Still Rocks!")
   362  
   363  	noFs := bfs.StaticFs("no")
   364  	checkFileContent(noFs, "f1.txt", c, "Hugo Rocks in Norway!")
   365  	checkFileContent(noFs, "f2.txt", c, "Hugo Themes Still Rocks!")
   366  }
   367  
   368  func TestMakePathRelative(t *testing.T) {
   369  	files := `
   370  -- hugo.toml --
   371  [[module.mounts]]
   372  source = "bar.txt"
   373  target = "assets/foo/baz.txt"
   374  [[module.imports]]
   375  path = "t1"
   376  [[module.imports.mounts]]
   377  source = "src"
   378  target = "assets/foo/bar"
   379  -- bar.txt --
   380  Bar.
   381  -- themes/t1/src/main.js --
   382  Main.
   383  `
   384  	b := hugolib.Test(t, files)
   385  
   386  	rel, found := b.H.BaseFs.Assets.MakePathRelative(filepath.FromSlash("/themes/t1/src/main.js"), true)
   387  	b.Assert(found, qt.Equals, true)
   388  	b.Assert(rel, qt.Equals, filepath.FromSlash("foo/bar/main.js"))
   389  
   390  	rel, found = b.H.BaseFs.Assets.MakePathRelative(filepath.FromSlash("/bar.txt"), true)
   391  	b.Assert(found, qt.Equals, true)
   392  	b.Assert(rel, qt.Equals, filepath.FromSlash("foo/baz.txt"))
   393  }
   394  
   395  func TestAbsProjectContentDir(t *testing.T) {
   396  	tempDir := t.TempDir()
   397  
   398  	files := `
   399  -- hugo.toml --
   400  [[module.mounts]]
   401  source = "content"
   402  target = "content"
   403  -- content/foo.md --
   404  ---
   405  title: "Foo"
   406  ---
   407  `
   408  
   409  	b := hugolib.NewIntegrationTestBuilder(
   410  		hugolib.IntegrationTestConfig{
   411  			T:           t,
   412  			WorkingDir:  tempDir,
   413  			TxtarString: files,
   414  		},
   415  	).Build()
   416  
   417  	abs1 := filepath.Join(tempDir, "content", "foo.md")
   418  	rel, abs2, err := b.H.BaseFs.AbsProjectContentDir("foo.md")
   419  	b.Assert(err, qt.IsNil)
   420  	b.Assert(abs2, qt.Equals, abs1)
   421  	b.Assert(rel, qt.Equals, filepath.FromSlash("foo.md"))
   422  	rel2, abs3, err := b.H.BaseFs.AbsProjectContentDir(abs1)
   423  	b.Assert(err, qt.IsNil)
   424  	b.Assert(abs3, qt.Equals, abs1)
   425  	b.Assert(rel2, qt.Equals, rel)
   426  }
   427  
   428  func TestContentReverseLookup(t *testing.T) {
   429  	files := `
   430  -- README.md --
   431  ---
   432  title: README
   433  ---
   434  -- blog/b1.md --
   435  ---
   436  title: b1
   437  ---
   438  -- docs/d1.md --
   439  ---
   440  title: d1
   441  ---
   442  -- hugo.toml --
   443  baseURL = "https://example.com/"
   444  [module]
   445  [[module.mounts]]
   446  source = "layouts"
   447  target = "layouts"
   448  [[module.mounts]]
   449  source = "README.md"
   450  target = "content/_index.md"
   451  [[module.mounts]]
   452  source = "blog"
   453  target = "content/posts"
   454  [[module.mounts]]
   455  source = "docs"
   456  target = "content/mydocs"
   457  -- layouts/index.html --
   458  Home.
   459  
   460  `
   461  	b := hugolib.Test(t, files)
   462  
   463  	b.AssertFileContent("public/index.html", "Home.")
   464  
   465  	stat := func(path string) hugofs.FileMetaInfo {
   466  		ps, err := b.H.BaseFs.Content.ReverseLookup(filepath.FromSlash(path), true)
   467  		b.Assert(err, qt.IsNil)
   468  		b.Assert(ps, qt.HasLen, 1)
   469  		first := ps[0]
   470  		fi, err := b.H.BaseFs.Content.Fs.Stat(filepath.FromSlash(first.Path))
   471  		b.Assert(err, qt.IsNil)
   472  		b.Assert(fi, qt.Not(qt.IsNil))
   473  		return fi.(hugofs.FileMetaInfo)
   474  	}
   475  
   476  	sfs := b.H.Fs.Source
   477  
   478  	_, err := sfs.Stat("blog/b1.md")
   479  	b.Assert(err, qt.Not(qt.IsNil))
   480  
   481  	_ = stat("blog/b1.md")
   482  }
   483  
   484  func TestReverseLookupShouldOnlyConsiderFilesInCurrentComponent(t *testing.T) {
   485  	files := `
   486  -- hugo.toml --
   487  baseURL = "https://example.com/"
   488  [module]
   489  [[module.mounts]]
   490  source = "files/layouts"
   491  target = "layouts"
   492  [[module.mounts]]
   493  source = "files/layouts/assets"
   494  target = "assets"
   495  -- files/layouts/l1.txt --
   496  l1
   497  -- files/layouts/assets/l2.txt --
   498  l2
   499  `
   500  	b := hugolib.Test(t, files)
   501  
   502  	assetsFs := b.H.Assets
   503  
   504  	for _, checkExists := range []bool{false, true} {
   505  		cps, err := assetsFs.ReverseLookup(filepath.FromSlash("files/layouts/assets/l2.txt"), checkExists)
   506  		b.Assert(err, qt.IsNil)
   507  		b.Assert(cps, qt.HasLen, 1)
   508  		cps, err = assetsFs.ReverseLookup(filepath.FromSlash("files/layouts/l2.txt"), checkExists)
   509  		b.Assert(err, qt.IsNil)
   510  		b.Assert(cps, qt.HasLen, 0)
   511  	}
   512  }
   513  
   514  func TestAssetsIssue12175(t *testing.T) {
   515  	files := `
   516  -- hugo.toml --
   517  baseURL = "https://example.com/"
   518  [module]
   519  [[module.mounts]]
   520  source = "node_modules/@foo/core/assets"
   521  target = "assets"
   522  [[module.mounts]]
   523  source = "assets"
   524  target = "assets"
   525  -- node_modules/@foo/core/assets/js/app.js --
   526  JS.
   527  -- node_modules/@foo/core/assets/scss/app.scss --
   528  body { color: red; }
   529  -- assets/scss/app.scss --
   530  body { color: blue; }
   531  -- layouts/index.html --
   532  Home.
   533  SCSS: {{ with resources.Get "scss/app.scss" }}{{ .RelPermalink }}|{{ .Content }}{{ end }}|
   534  # Note that the pattern below will match 2 resources, which doesn't make much sense,
   535  # but is how the current (and also < v0.123.0) merge logic works, and for most practical purposes, it doesn't matter.
   536  SCSS Match: {{ with resources.Match "**.scss" }}{{ . | len }}|{{ range .}}{{ .RelPermalink }}|{{ end }}{{ end }}|
   537  
   538  `
   539  
   540  	b := hugolib.Test(t, files)
   541  
   542  	b.AssertFileContent("public/index.html", `
   543  SCSS: /scss/app.scss|body { color: blue; }|
   544  SCSS Match: 2|
   545  `)
   546  }
   547  
   548  func TestStaticComposite(t *testing.T) {
   549  	files := `
   550  -- hugo.toml --
   551  disableKinds = ["taxonomy", "term"]
   552  [module]
   553  [[module.mounts]]
   554  source = "myfiles/f1.txt"
   555  target = "static/files/f1.txt"
   556  [[module.mounts]]
   557  source = "f3.txt"
   558  target = "static/f3.txt"
   559  [[module.mounts]]
   560  source = "static"
   561  target = "static"
   562  -- static/files/f2.txt --
   563  f2
   564  -- myfiles/f1.txt --
   565  f1
   566  -- f3.txt --
   567  f3
   568  -- layouts/home.html --
   569  Home.
   570  
   571  `
   572  	b := hugolib.Test(t, files)
   573  
   574  	b.AssertFs(b.H.BaseFs.StaticFs(""), `
   575  . true
   576  f3.txt false
   577  files true
   578  files/f1.txt false
   579  files/f2.txt false
   580  `)
   581  }
   582  
   583  func TestMountIssue12141(t *testing.T) {
   584  	files := `
   585  -- hugo.toml --
   586  disableKinds = ["taxonomy", "term"]
   587  [module]
   588  [[module.mounts]]
   589  source = "myfiles"
   590  target = "static"
   591  [[module.mounts]]
   592  source = "myfiles/f1.txt"
   593  target = "static/f2.txt"
   594  -- myfiles/f1.txt --
   595  f1
   596  `
   597  	b := hugolib.Test(t, files)
   598  	fs := b.H.BaseFs.StaticFs("")
   599  
   600  	b.AssertFs(fs, `
   601  . true
   602  f1.txt false
   603  f2.txt false
   604  `)
   605  }
   606  
   607  func checkFileCount(fs afero.Fs, dirname string, c *qt.C, expected int) {
   608  	c.Helper()
   609  	count, names, err := countFilesAndGetFilenames(fs, dirname)
   610  	namesComment := qt.Commentf("filenames: %v", names)
   611  	c.Assert(err, qt.IsNil, namesComment)
   612  	c.Assert(count, qt.Equals, expected, namesComment)
   613  }
   614  
   615  func checkFileContent(fs afero.Fs, filename string, c *qt.C, expected ...string) {
   616  	b, err := afero.ReadFile(fs, filename)
   617  	c.Assert(err, qt.IsNil)
   618  
   619  	content := string(b)
   620  
   621  	for _, e := range expected {
   622  		c.Assert(content, qt.Contains, e)
   623  	}
   624  }
   625  
   626  func countFilesAndGetFilenames(fs afero.Fs, dirname string) (int, []string, error) {
   627  	if fs == nil {
   628  		return 0, nil, errors.New("no fs")
   629  	}
   630  
   631  	counter := 0
   632  	var filenames []string
   633  
   634  	wf := func(path string, info hugofs.FileMetaInfo) error {
   635  		if !info.IsDir() {
   636  			counter++
   637  		}
   638  
   639  		if info.Name() != "." {
   640  			name := info.Name()
   641  			name = strings.Replace(name, filepath.FromSlash("/my/work"), "WORK_DIR", 1)
   642  			filenames = append(filenames, name)
   643  		}
   644  
   645  		return nil
   646  	}
   647  
   648  	w := hugofs.NewWalkway(hugofs.WalkwayConfig{Fs: fs, Root: dirname, WalkFn: wf})
   649  
   650  	if err := w.Walk(); err != nil {
   651  		return -1, nil, err
   652  	}
   653  
   654  	return counter, filenames, nil
   655  }
   656  
   657  func setConfigAndWriteSomeFilesTo(fs afero.Fs, v config.Provider, key, val string, num int) error {
   658  	workingDir := v.GetString("workingDir")
   659  	v.Set(key, val)
   660  	fs.Mkdir(val, 0o755) // nolint
   661  	for i := 0; i < num; i++ {
   662  		filename := filepath.Join(workingDir, val, fmt.Sprintf("f%d.txt", i+1))
   663  		afero.WriteFile(fs, filename, []byte(fmt.Sprintf("content:%s:%d", key, i+1)), 0o755) // nolint
   664  	}
   665  
   666  	return nil
   667  }