github.com/linchen2chris/hugo@v0.0.0-20230307053224-cec209389705/hugolib/config_test.go (about)

     1  // Copyright 2016-present 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 hugolib
    15  
    16  import (
    17  	"bytes"
    18  	"fmt"
    19  	"path/filepath"
    20  	"strings"
    21  	"testing"
    22  
    23  	"github.com/gohugoio/hugo/config"
    24  
    25  	"github.com/gohugoio/hugo/media"
    26  	"github.com/google/go-cmp/cmp"
    27  
    28  	qt "github.com/frankban/quicktest"
    29  	"github.com/gohugoio/hugo/common/maps"
    30  	"github.com/spf13/afero"
    31  )
    32  
    33  func TestLoadConfig(t *testing.T) {
    34  
    35  	c := qt.New(t)
    36  
    37  	loadConfig := func(c *qt.C, configContent string, fromDir bool) config.Provider {
    38  		mm := afero.NewMemMapFs()
    39  		filename := "config.toml"
    40  		descriptor := ConfigSourceDescriptor{Fs: mm}
    41  		if fromDir {
    42  			filename = filepath.Join("config", "_default", filename)
    43  			descriptor.AbsConfigDir = "config"
    44  		}
    45  		writeToFs(t, mm, filename, configContent)
    46  		cfg, _, err := LoadConfig(descriptor)
    47  		c.Assert(err, qt.IsNil)
    48  		return cfg
    49  	}
    50  
    51  	c.Run("Basic", func(c *qt.C) {
    52  		c.Parallel()
    53  		// Add a random config variable for testing.
    54  		// side = page in Norwegian.
    55  		cfg := loadConfig(c, `PaginatePath = "side"`, false)
    56  		c.Assert(cfg.GetString("paginatePath"), qt.Equals, "side")
    57  	})
    58  
    59  	// Issue #8763
    60  	for _, fromDir := range []bool{false, true} {
    61  		testName := "Taxonomy overrides"
    62  		if fromDir {
    63  			testName += " from dir"
    64  		}
    65  		c.Run(testName, func(c *qt.C) {
    66  			c.Parallel()
    67  			cfg := loadConfig(c, `[taxonomies]
    68  appellation = "appellations"
    69  vigneron = "vignerons"`, fromDir)
    70  
    71  			c.Assert(cfg.Get("taxonomies"), qt.DeepEquals, maps.Params{
    72  				"appellation": "appellations",
    73  				"vigneron":    "vignerons",
    74  			})
    75  		})
    76  	}
    77  }
    78  
    79  func TestLoadMultiConfig(t *testing.T) {
    80  	t.Parallel()
    81  
    82  	c := qt.New(t)
    83  
    84  	// Add a random config variable for testing.
    85  	// side = page in Norwegian.
    86  	configContentBase := `
    87  	DontChange = "same"
    88  	PaginatePath = "side"
    89  	`
    90  	configContentSub := `
    91  	PaginatePath = "top"
    92  	`
    93  	mm := afero.NewMemMapFs()
    94  
    95  	writeToFs(t, mm, "base.toml", configContentBase)
    96  
    97  	writeToFs(t, mm, "override.toml", configContentSub)
    98  
    99  	cfg, _, err := LoadConfig(ConfigSourceDescriptor{Fs: mm, Filename: "base.toml,override.toml"})
   100  	c.Assert(err, qt.IsNil)
   101  
   102  	c.Assert(cfg.GetString("paginatePath"), qt.Equals, "top")
   103  	c.Assert(cfg.GetString("DontChange"), qt.Equals, "same")
   104  }
   105  
   106  func TestLoadConfigFromThemes(t *testing.T) {
   107  	t.Parallel()
   108  
   109  	c := qt.New(t)
   110  
   111  	mainConfigTemplate := `
   112  theme = "test-theme"
   113  baseURL = "https://example.com/"
   114  
   115  [frontmatter]
   116  date = ["date","publishDate"]
   117  
   118  [params]
   119  MERGE_PARAMS
   120  p1 = "p1 main"
   121  [params.b]
   122  b1 = "b1 main"
   123  [params.b.c]
   124  bc1 = "bc1 main"
   125  
   126  [mediaTypes]
   127  [mediaTypes."text/m1"]
   128  suffixes = ["m1main"]
   129  
   130  [outputFormats.o1]
   131  mediaType = "text/m1"
   132  baseName = "o1main"
   133  
   134  [languages]
   135  [languages.en]
   136  languageName = "English"
   137  [languages.en.params]
   138  pl1 = "p1-en-main"
   139  [languages.nb]
   140  languageName = "Norsk"
   141  [languages.nb.params]
   142  pl1 = "p1-nb-main"
   143  
   144  [[menu.main]]
   145  name = "menu-main-main"
   146  
   147  [[menu.top]]
   148  name = "menu-top-main"
   149  
   150  `
   151  
   152  	themeConfig := `
   153  baseURL = "http://bep.is/"
   154  
   155  # Can not be set in theme.
   156  disableKinds = ["taxonomy", "term"]
   157  
   158  # Can not be set in theme.
   159  [frontmatter]
   160  expiryDate = ["date"]
   161  
   162  [params]
   163  p1 = "p1 theme"
   164  p2 = "p2 theme"
   165  [params.b]
   166  b1 = "b1 theme"
   167  b2 = "b2 theme"
   168  [params.b.c]
   169  bc1 = "bc1 theme"
   170  bc2 = "bc2 theme"
   171  [params.b.c.d]
   172  bcd1 = "bcd1 theme"
   173  
   174  [mediaTypes]
   175  [mediaTypes."text/m1"]
   176  suffixes = ["m1theme"]
   177  [mediaTypes."text/m2"]
   178  suffixes = ["m2theme"]
   179  
   180  [outputFormats.o1]
   181  mediaType = "text/m1"
   182  baseName = "o1theme"
   183  [outputFormats.o2]
   184  mediaType = "text/m2"
   185  baseName = "o2theme"
   186  
   187  [languages]
   188  [languages.en]
   189  languageName = "English2"
   190  [languages.en.params]
   191  pl1 = "p1-en-theme"
   192  pl2 = "p2-en-theme"
   193  [[languages.en.menu.main]]
   194  name   = "menu-lang-en-main"
   195  [[languages.en.menu.theme]]
   196  name   = "menu-lang-en-theme"
   197  [languages.nb]
   198  languageName = "Norsk2"
   199  [languages.nb.params]
   200  pl1 = "p1-nb-theme"
   201  pl2 = "p2-nb-theme"
   202  top = "top-nb-theme"
   203  [[languages.nb.menu.main]]
   204  name   = "menu-lang-nb-main"
   205  [[languages.nb.menu.theme]]
   206  name   = "menu-lang-nb-theme"
   207  [[languages.nb.menu.top]]
   208  name   = "menu-lang-nb-top"
   209  
   210  [[menu.main]]
   211  name = "menu-main-theme"
   212  
   213  [[menu.thememenu]]
   214  name = "menu-theme"
   215  
   216  `
   217  
   218  	buildForConfig := func(t testing.TB, mainConfig, themeConfig string) *sitesBuilder {
   219  		b := newTestSitesBuilder(t)
   220  		b.WithConfigFile("toml", mainConfig).WithThemeConfigFile("toml", themeConfig)
   221  		return b.Build(BuildCfg{})
   222  	}
   223  
   224  	buildForStrategy := func(t testing.TB, s string) *sitesBuilder {
   225  		mainConfig := strings.ReplaceAll(mainConfigTemplate, "MERGE_PARAMS", s)
   226  		return buildForConfig(t, mainConfig, themeConfig)
   227  	}
   228  
   229  	c.Run("Merge default", func(c *qt.C) {
   230  		b := buildForStrategy(c, "")
   231  
   232  		got := b.Cfg.Get("").(maps.Params)
   233  
   234  		// Issue #8866
   235  		b.Assert(b.Cfg.Get("disableKinds"), qt.IsNil)
   236  
   237  		b.Assert(got["params"], qt.DeepEquals, maps.Params{
   238  			"b": maps.Params{
   239  				"b1": "b1 main",
   240  				"c": maps.Params{
   241  					"bc1": "bc1 main",
   242  					"bc2": "bc2 theme",
   243  					"d":   maps.Params{"bcd1": string("bcd1 theme")},
   244  				},
   245  				"b2": "b2 theme",
   246  			},
   247  			"p2": "p2 theme",
   248  			"p1": "p1 main",
   249  		})
   250  
   251  		b.Assert(got["mediatypes"], qt.DeepEquals, maps.Params{
   252  			"text/m2": maps.Params{
   253  				"suffixes": []any{
   254  					"m2theme",
   255  				},
   256  			},
   257  			"text/m1": maps.Params{
   258  				"suffixes": []any{
   259  					"m1main",
   260  				},
   261  			},
   262  		})
   263  
   264  		var eq = qt.CmpEquals(
   265  			cmp.Comparer(func(m1, m2 media.Type) bool {
   266  				if m1.SubType != m2.SubType {
   267  					return false
   268  				}
   269  				return m1.FirstSuffix == m2.FirstSuffix
   270  			}),
   271  		)
   272  
   273  		mediaTypes := b.H.Sites[0].mediaTypesConfig
   274  		m1, _ := mediaTypes.GetByType("text/m1")
   275  		m2, _ := mediaTypes.GetByType("text/m2")
   276  
   277  		b.Assert(got["outputformats"], eq, maps.Params{
   278  			"o1": maps.Params{
   279  				"mediatype": m1,
   280  				"basename":  "o1main",
   281  			},
   282  			"o2": maps.Params{
   283  				"basename":  "o2theme",
   284  				"mediatype": m2,
   285  			},
   286  		})
   287  
   288  		b.Assert(got["languages"], qt.DeepEquals, maps.Params{
   289  			"en": maps.Params{
   290  				"languagename": "English",
   291  				"params": maps.Params{
   292  					"pl2": "p2-en-theme",
   293  					"pl1": "p1-en-main",
   294  				},
   295  				"menus": maps.Params{
   296  					"main": []any{
   297  						map[string]any{
   298  							"name": "menu-lang-en-main",
   299  						},
   300  					},
   301  					"theme": []any{
   302  						map[string]any{
   303  							"name": "menu-lang-en-theme",
   304  						},
   305  					},
   306  				},
   307  			},
   308  			"nb": maps.Params{
   309  				"languagename": "Norsk",
   310  				"params": maps.Params{
   311  					"top": "top-nb-theme",
   312  					"pl1": "p1-nb-main",
   313  					"pl2": "p2-nb-theme",
   314  				},
   315  				"menus": maps.Params{
   316  					"main": []any{
   317  						map[string]any{
   318  							"name": "menu-lang-nb-main",
   319  						},
   320  					},
   321  					"theme": []any{
   322  						map[string]any{
   323  							"name": "menu-lang-nb-theme",
   324  						},
   325  					},
   326  					"top": []any{
   327  						map[string]any{
   328  							"name": "menu-lang-nb-top",
   329  						},
   330  					},
   331  				},
   332  			},
   333  		})
   334  
   335  		c.Assert(got["baseurl"], qt.Equals, "https://example.com/")
   336  	})
   337  
   338  	c.Run("Merge shallow", func(c *qt.C) {
   339  		b := buildForStrategy(c, fmt.Sprintf("_merge=%q", "shallow"))
   340  
   341  		got := b.Cfg.Get("").(maps.Params)
   342  
   343  		// Shallow merge, only add new keys to params.
   344  		b.Assert(got["params"], qt.DeepEquals, maps.Params{
   345  			"p1": "p1 main",
   346  			"b": maps.Params{
   347  				"b1": "b1 main",
   348  				"c": maps.Params{
   349  					"bc1": "bc1 main",
   350  				},
   351  			},
   352  			"p2": "p2 theme",
   353  		})
   354  	})
   355  
   356  	c.Run("Merge no params in project", func(c *qt.C) {
   357  		b := buildForConfig(
   358  			c,
   359  			"baseURL=\"https://example.org\"\ntheme = \"test-theme\"\n",
   360  			"[params]\np1 = \"p1 theme\"\n",
   361  		)
   362  
   363  		got := b.Cfg.Get("").(maps.Params)
   364  
   365  		b.Assert(got["params"], qt.DeepEquals, maps.Params{
   366  			"p1": "p1 theme",
   367  		})
   368  	})
   369  
   370  	c.Run("Merge language no menus or params in project", func(c *qt.C) {
   371  		b := buildForConfig(
   372  			c,
   373  			`
   374  theme = "test-theme"
   375  baseURL = "https://example.com/"
   376  
   377  [languages]
   378  [languages.en]
   379  languageName = "English"
   380  
   381  `,
   382  			`
   383  [languages]
   384  [languages.en]
   385  languageName = "EnglishTheme"
   386  
   387  [languages.en.params]
   388  p1="themep1"
   389  
   390  [[languages.en.menus.main]]
   391  name   = "menu-theme"
   392  `,
   393  		)
   394  
   395  		got := b.Cfg.Get("").(maps.Params)
   396  
   397  		b.Assert(got["languages"], qt.DeepEquals,
   398  			maps.Params{
   399  				"en": maps.Params{
   400  					"languagename": "English",
   401  					"menus": maps.Params{
   402  						"main": []any{
   403  							map[string]any{
   404  								"name": "menu-theme",
   405  							},
   406  						},
   407  					},
   408  					"params": maps.Params{
   409  						"p1": "themep1",
   410  					},
   411  				},
   412  			},
   413  		)
   414  	})
   415  
   416  	// Issue #8724
   417  	for _, mergeStrategy := range []string{"none", "shallow"} {
   418  		c.Run(fmt.Sprintf("Merge with sitemap config in theme, mergestrategy %s", mergeStrategy), func(c *qt.C) {
   419  
   420  			smapConfigTempl := `[sitemap]
   421    changefreq = %q
   422    filename = "sitemap.xml"
   423    priority = 0.5`
   424  
   425  			b := buildForConfig(
   426  				c,
   427  				fmt.Sprintf("_merge=%q\nbaseURL=\"https://example.org\"\ntheme = \"test-theme\"\n", mergeStrategy),
   428  				"baseURL=\"http://example.com\"\n"+fmt.Sprintf(smapConfigTempl, "monthly"),
   429  			)
   430  
   431  			got := b.Cfg.Get("").(maps.Params)
   432  
   433  			if mergeStrategy == "none" {
   434  				b.Assert(got["sitemap"], qt.DeepEquals, maps.Params{
   435  					"priority": int(-1),
   436  					"filename": "sitemap.xml",
   437  				})
   438  
   439  				b.AssertFileContent("public/sitemap.xml", "schemas/sitemap")
   440  			} else {
   441  				b.Assert(got["sitemap"], qt.DeepEquals, maps.Params{
   442  					"priority":   int(-1),
   443  					"filename":   "sitemap.xml",
   444  					"changefreq": "monthly",
   445  				})
   446  
   447  				b.AssertFileContent("public/sitemap.xml", "<changefreq>monthly</changefreq>")
   448  			}
   449  
   450  		})
   451  	}
   452  
   453  }
   454  
   455  func TestLoadConfigFromThemeDir(t *testing.T) {
   456  	t.Parallel()
   457  
   458  	mainConfig := `
   459  theme = "test-theme"
   460  
   461  [params]
   462  m1 = "mv1"	
   463  `
   464  
   465  	themeConfig := `
   466  [params]
   467  t1 = "tv1"	
   468  t2 = "tv2"
   469  `
   470  
   471  	themeConfigDir := filepath.Join("themes", "test-theme", "config")
   472  	themeConfigDirDefault := filepath.Join(themeConfigDir, "_default")
   473  	themeConfigDirProduction := filepath.Join(themeConfigDir, "production")
   474  
   475  	projectConfigDir := "config"
   476  
   477  	b := newTestSitesBuilder(t)
   478  	b.WithConfigFile("toml", mainConfig).WithThemeConfigFile("toml", themeConfig)
   479  	b.Assert(b.Fs.Source.MkdirAll(themeConfigDirDefault, 0777), qt.IsNil)
   480  	b.Assert(b.Fs.Source.MkdirAll(themeConfigDirProduction, 0777), qt.IsNil)
   481  	b.Assert(b.Fs.Source.MkdirAll(projectConfigDir, 0777), qt.IsNil)
   482  
   483  	b.WithSourceFile(filepath.Join(projectConfigDir, "config.toml"), `[params]
   484  m2 = "mv2"
   485  `)
   486  	b.WithSourceFile(filepath.Join(themeConfigDirDefault, "config.toml"), `[params]
   487  t2 = "tv2d"
   488  t3 = "tv3d"
   489  `)
   490  
   491  	b.WithSourceFile(filepath.Join(themeConfigDirProduction, "config.toml"), `[params]
   492  t3 = "tv3p"
   493  `)
   494  
   495  	b.Build(BuildCfg{})
   496  
   497  	got := b.Cfg.Get("params").(maps.Params)
   498  
   499  	b.Assert(got, qt.DeepEquals, maps.Params{
   500  		"t3": "tv3p",
   501  		"m1": "mv1",
   502  		"t1": "tv1",
   503  		"t2": "tv2d",
   504  	})
   505  
   506  }
   507  
   508  func TestPrivacyConfig(t *testing.T) {
   509  	t.Parallel()
   510  
   511  	c := qt.New(t)
   512  
   513  	tomlConfig := `
   514  
   515  someOtherValue = "foo"
   516  
   517  [privacy]
   518  [privacy.youtube]
   519  privacyEnhanced = true
   520  `
   521  
   522  	b := newTestSitesBuilder(t)
   523  	b.WithConfigFile("toml", tomlConfig)
   524  	b.Build(BuildCfg{SkipRender: true})
   525  
   526  	c.Assert(b.H.Sites[0].Info.Config().Privacy.YouTube.PrivacyEnhanced, qt.Equals, true)
   527  }
   528  
   529  func TestLoadConfigModules(t *testing.T) {
   530  	t.Parallel()
   531  
   532  	c := qt.New(t)
   533  
   534  	// https://github.com/gohugoio/hugoThemes#themetoml
   535  
   536  	const (
   537  		// Before Hugo 0.56 each theme/component could have its own theme.toml
   538  		// with some settings, mostly used on the Hugo themes site.
   539  		// To preserve combability we read these files into the new "modules"
   540  		// section in config.toml.
   541  		o1t = `
   542  name = "Component o1"
   543  license = "MIT"
   544  min_version = 0.38
   545  `
   546  		// This is the component's config.toml, using the old theme syntax.
   547  		o1c = `
   548  theme = ["n2"]
   549  `
   550  
   551  		n1 = `
   552  title = "Component n1"
   553  
   554  [module]
   555  description = "Component n1 description"
   556  [module.hugoVersion]
   557  min = "0.40.0"
   558  max = "0.50.0"
   559  extended = true
   560  [[module.imports]]
   561  path="o1"
   562  [[module.imports]]
   563  path="n3"
   564  
   565  
   566  `
   567  
   568  		n2 = `
   569  title = "Component n2"
   570  `
   571  
   572  		n3 = `
   573  title = "Component n3"
   574  `
   575  
   576  		n4 = `
   577  title = "Component n4"
   578  `
   579  	)
   580  
   581  	b := newTestSitesBuilder(t)
   582  
   583  	writeThemeFiles := func(name, configTOML, themeTOML string) {
   584  		b.WithSourceFile(filepath.Join("themes", name, "data", "module.toml"), fmt.Sprintf("name=%q", name))
   585  		if configTOML != "" {
   586  			b.WithSourceFile(filepath.Join("themes", name, "config.toml"), configTOML)
   587  		}
   588  		if themeTOML != "" {
   589  			b.WithSourceFile(filepath.Join("themes", name, "theme.toml"), themeTOML)
   590  		}
   591  	}
   592  
   593  	writeThemeFiles("n1", n1, "")
   594  	writeThemeFiles("n2", n2, "")
   595  	writeThemeFiles("n3", n3, "")
   596  	writeThemeFiles("n4", n4, "")
   597  	writeThemeFiles("o1", o1c, o1t)
   598  
   599  	b.WithConfigFile("toml", `
   600  [module]
   601  [[module.imports]]
   602  path="n1"
   603  [[module.imports]]
   604  path="n4"
   605  
   606  `)
   607  
   608  	b.Build(BuildCfg{})
   609  
   610  	modulesClient := b.H.Paths.ModulesClient
   611  	var graphb bytes.Buffer
   612  	modulesClient.Graph(&graphb)
   613  
   614  	expected := `project n1
   615  n1 o1
   616  o1 n2
   617  n1 n3
   618  project n4
   619  `
   620  
   621  	c.Assert(graphb.String(), qt.Equals, expected)
   622  }
   623  
   624  func TestLoadConfigWithOsEnvOverrides(t *testing.T) {
   625  	c := qt.New(t)
   626  
   627  	baseConfig := `
   628  
   629  theme = "mytheme"
   630  environment = "production"
   631  enableGitInfo = true
   632  intSlice = [5,7,9]
   633  floatSlice = [3.14, 5.19]
   634  stringSlice = ["a", "b"]
   635  
   636  [outputFormats]
   637  [outputFormats.ofbase]
   638  mediaType = "text/plain"
   639  
   640  [params]
   641  paramWithNoEnvOverride="nooverride"
   642  [params.api_config]
   643  api_key="default_key"
   644  another_key="default another_key"
   645  
   646  [imaging]
   647  anchor = "smart"
   648  quality = 75 
   649  `
   650  
   651  	newB := func(t testing.TB) *sitesBuilder {
   652  		b := newTestSitesBuilder(t).WithConfigFile("toml", baseConfig)
   653  
   654  		b.WithSourceFile("themes/mytheme/config.toml", `
   655  
   656  [outputFormats]
   657  [outputFormats.oftheme]
   658  mediaType = "text/plain"
   659  [outputFormats.ofbase]
   660  mediaType = "application/xml"
   661  
   662  [params]
   663  [params.mytheme_section]
   664  theme_param="themevalue"
   665  theme_param_nooverride="nooverride"
   666  [params.mytheme_section2]
   667  theme_param="themevalue2"
   668  
   669  `)
   670  
   671  		return b
   672  	}
   673  
   674  	c.Run("Variations", func(c *qt.C) {
   675  
   676  		b := newB(c)
   677  
   678  		b.WithEnviron(
   679  			"HUGO_ENVIRONMENT", "test",
   680  			"HUGO_NEW", "new", // key not in config.toml
   681  			"HUGO_ENABLEGITINFO", "false",
   682  			"HUGO_IMAGING_ANCHOR", "top",
   683  			"HUGO_IMAGING_RESAMPLEFILTER", "CatmullRom",
   684  			"HUGO_STRINGSLICE", `["c", "d"]`,
   685  			"HUGO_INTSLICE", `[5, 8, 9]`,
   686  			"HUGO_FLOATSLICE", `[5.32]`,
   687  			// Issue #7829
   688  			"HUGOxPARAMSxAPI_CONFIGxAPI_KEY", "new_key",
   689  			// Delimiters are case sensitive.
   690  			"HUGOxPARAMSxAPI_CONFIGXANOTHER_KEY", "another_key",
   691  			// Issue #8346
   692  			"HUGOxPARAMSxMYTHEME_SECTIONxTHEME_PARAM", "themevalue_changed",
   693  			"HUGOxPARAMSxMYTHEME_SECTION2xTHEME_PARAM", "themevalue2_changed",
   694  			"HUGO_PARAMS_EMPTY", ``,
   695  			"HUGO_PARAMS_HTML", `<a target="_blank" />`,
   696  			// Issue #8618
   697  			"HUGO_SERVICES_GOOGLEANALYTICS_ID", `gaid`,
   698  			"HUGO_PARAMS_A_B_C", "abc",
   699  		)
   700  
   701  		b.Build(BuildCfg{})
   702  
   703  		cfg := b.H.Cfg
   704  		s := b.H.Sites[0]
   705  		scfg := s.siteConfigConfig.Services
   706  
   707  		c.Assert(cfg.Get("environment"), qt.Equals, "test")
   708  		c.Assert(cfg.GetBool("enablegitinfo"), qt.Equals, false)
   709  		c.Assert(cfg.Get("new"), qt.Equals, "new")
   710  		c.Assert(cfg.Get("imaging.anchor"), qt.Equals, "top")
   711  		c.Assert(cfg.Get("imaging.quality"), qt.Equals, int64(75))
   712  		c.Assert(cfg.Get("imaging.resamplefilter"), qt.Equals, "CatmullRom")
   713  		c.Assert(cfg.Get("stringSlice"), qt.DeepEquals, []any{"c", "d"})
   714  		c.Assert(cfg.Get("floatSlice"), qt.DeepEquals, []any{5.32})
   715  		c.Assert(cfg.Get("intSlice"), qt.DeepEquals, []any{5, 8, 9})
   716  		c.Assert(cfg.Get("params.api_config.api_key"), qt.Equals, "new_key")
   717  		c.Assert(cfg.Get("params.api_config.another_key"), qt.Equals, "default another_key")
   718  		c.Assert(cfg.Get("params.mytheme_section.theme_param"), qt.Equals, "themevalue_changed")
   719  		c.Assert(cfg.Get("params.mytheme_section.theme_param_nooverride"), qt.Equals, "nooverride")
   720  		c.Assert(cfg.Get("params.mytheme_section2.theme_param"), qt.Equals, "themevalue2_changed")
   721  		c.Assert(cfg.Get("params.empty"), qt.Equals, ``)
   722  		c.Assert(cfg.Get("params.html"), qt.Equals, `<a target="_blank" />`)
   723  
   724  		params := cfg.Get("params").(maps.Params)
   725  		c.Assert(params["paramwithnoenvoverride"], qt.Equals, "nooverride")
   726  		c.Assert(cfg.Get("params.paramwithnoenvoverride"), qt.Equals, "nooverride")
   727  		c.Assert(scfg.GoogleAnalytics.ID, qt.Equals, "gaid")
   728  		c.Assert(cfg.Get("params.a.b"), qt.DeepEquals, maps.Params{
   729  			"c": "abc",
   730  		})
   731  
   732  		ofBase, _ := s.outputFormatsConfig.GetByName("ofbase")
   733  		ofTheme, _ := s.outputFormatsConfig.GetByName("oftheme")
   734  
   735  		c.Assert(ofBase.MediaType, qt.Equals, media.TextType)
   736  		c.Assert(ofTheme.MediaType, qt.Equals, media.TextType)
   737  
   738  	})
   739  
   740  	// Issue #8709
   741  	c.Run("Set in string", func(c *qt.C) {
   742  		b := newB(c)
   743  
   744  		b.WithEnviron(
   745  			"HUGO_ENABLEGITINFO", "false",
   746  			// imaging.anchor is a string, and it's not possible
   747  			// to set a child attribute.
   748  			"HUGO_IMAGING_ANCHOR_FOO", "top",
   749  		)
   750  
   751  		b.Build(BuildCfg{})
   752  
   753  		cfg := b.H.Cfg
   754  		c.Assert(cfg.Get("imaging.anchor"), qt.Equals, "smart")
   755  
   756  	})
   757  
   758  }
   759  
   760  func TestInvalidDefaultMarkdownHandler(t *testing.T) {
   761  	t.Parallel()
   762  
   763  	files := `
   764  -- config.toml --
   765  [markup]
   766  defaultMarkdownHandler = 'blackfriday'
   767  -- content/_index.md --
   768  ## Foo
   769  -- layouts/index.html --
   770  {{ .Content }}
   771  
   772  `
   773  
   774  	b, err := NewIntegrationTestBuilder(
   775  		IntegrationTestConfig{
   776  			T:           t,
   777  			TxtarString: files,
   778  		},
   779  	).BuildE()
   780  
   781  	b.Assert(err, qt.IsNotNil)
   782  	b.Assert(err.Error(), qt.Contains, "Configured defaultMarkdownHandler \"blackfriday\" not found. Did you mean to use goldmark? Blackfriday was removed in Hugo v0.100.0.")
   783  
   784  }
   785  
   786  // Issue 8979
   787  func TestHugoConfig(t *testing.T) {
   788  	filesTemplate := `
   789  -- hugo.toml --
   790  theme = "mytheme"
   791  [params]
   792  rootparam = "rootvalue"
   793  -- config/_default/hugo.toml --
   794  [params]
   795  rootconfigparam = "rootconfigvalue"
   796  -- themes/mytheme/config/_default/hugo.toml --
   797  [params]
   798  themeconfigdirparam = "themeconfigdirvalue"
   799  -- themes/mytheme/hugo.toml --
   800  [params]
   801  themeparam = "themevalue"
   802  -- layouts/index.html --
   803  rootparam: {{ site.Params.rootparam }}
   804  rootconfigparam: {{ site.Params.rootconfigparam }}
   805  themeparam: {{ site.Params.themeparam }}
   806  themeconfigdirparam: {{ site.Params.themeconfigdirparam }}
   807  
   808  
   809  `
   810  
   811  	for _, configName := range []string{"hugo.toml", "config.toml"} {
   812  		configName := configName
   813  		t.Run(configName, func(t *testing.T) {
   814  			t.Parallel()
   815  
   816  			files := strings.ReplaceAll(filesTemplate, "hugo.toml", configName)
   817  
   818  			b, err := NewIntegrationTestBuilder(
   819  				IntegrationTestConfig{
   820  					T:           t,
   821  					TxtarString: files,
   822  				},
   823  			).BuildE()
   824  
   825  			b.Assert(err, qt.IsNil)
   826  			b.AssertFileContent("public/index.html",
   827  				"rootparam: rootvalue",
   828  				"rootconfigparam: rootconfigvalue",
   829  				"themeparam: themevalue",
   830  				"themeconfigdirparam: themeconfigdirvalue",
   831  			)
   832  
   833  		})
   834  	}
   835  
   836  }