...

Source file src/edge-infra.dev/pkg/lib/ini/file_test.go

Documentation: edge-infra.dev/pkg/lib/ini

     1  package ini
     2  
     3  import (
     4  	"bytes"
     5  	"os"
     6  	"path/filepath"
     7  	"runtime"
     8  	"sort"
     9  	"testing"
    10  
    11  	"github.com/stretchr/testify/assert"
    12  	"github.com/stretchr/testify/require"
    13  
    14  	"edge-infra.dev/pkg/lib/build/bazel"
    15  )
    16  
    17  func TestEmpty(t *testing.T) {
    18  	f := Empty()
    19  	require.NotNil(t, f)
    20  
    21  	// Should only have the default section
    22  	assert.Len(t, f.Sections(), 1)
    23  
    24  	// Default section should not contain any key
    25  	assert.Len(t, f.Section("").Keys(), 0)
    26  }
    27  
    28  func TestFile_NewSection(t *testing.T) {
    29  	f := Empty()
    30  	require.NotNil(t, f)
    31  
    32  	sec, err := f.NewSection("author")
    33  	require.NoError(t, err)
    34  	require.NotNil(t, sec)
    35  	assert.Equal(t, "author", sec.Name())
    36  
    37  	assert.Equal(t, []string{DefaultSection, "author"}, f.SectionStrings())
    38  
    39  	t.Run("with duplicated name", func(t *testing.T) {
    40  		sec, err := f.NewSection("author")
    41  		require.NoError(t, err)
    42  		require.NotNil(t, sec)
    43  
    44  		// Does nothing if section already exists
    45  		assert.Equal(t, []string{DefaultSection, "author"}, f.SectionStrings())
    46  	})
    47  
    48  	t.Run("with empty string", func(t *testing.T) {
    49  		_, err := f.NewSection("")
    50  		require.Error(t, err)
    51  	})
    52  }
    53  
    54  func TestFile_NonUniqueSection(t *testing.T) {
    55  	t.Run("read and write non-unique sections", func(t *testing.T) {
    56  		f, err := LoadSources(LoadOptions{
    57  			AllowNonUniqueSections: true,
    58  		}, []byte(`[Interface]
    59  Address = 192.168.2.1
    60  PrivateKey = <server's privatekey>
    61  ListenPort = 51820
    62  
    63  [Peer]
    64  PublicKey = <client's publickey>
    65  AllowedIPs = 192.168.2.2/32
    66  
    67  [Peer]
    68  PublicKey = <client2's publickey>
    69  AllowedIPs = 192.168.2.3/32`))
    70  		require.NoError(t, err)
    71  		require.NotNil(t, f)
    72  
    73  		sec, err := f.NewSection("Peer")
    74  		require.NoError(t, err)
    75  		require.NotNil(t, f)
    76  
    77  		_, _ = sec.NewKey("PublicKey", "<client3's publickey>")
    78  		_, _ = sec.NewKey("AllowedIPs", "192.168.2.4/32")
    79  
    80  		var buf bytes.Buffer
    81  		_, err = f.WriteTo(&buf)
    82  		require.NoError(t, err)
    83  		str := buf.String()
    84  		assert.Equal(t, `[Interface]
    85  Address    = 192.168.2.1
    86  PrivateKey = <server's privatekey>
    87  ListenPort = 51820
    88  
    89  [Peer]
    90  PublicKey  = <client's publickey>
    91  AllowedIPs = 192.168.2.2/32
    92  
    93  [Peer]
    94  PublicKey  = <client2's publickey>
    95  AllowedIPs = 192.168.2.3/32
    96  
    97  [Peer]
    98  PublicKey  = <client3's publickey>
    99  AllowedIPs = 192.168.2.4/32
   100  `, str)
   101  	})
   102  
   103  	t.Run("delete non-unique section", func(t *testing.T) {
   104  		f, err := LoadSources(LoadOptions{
   105  			AllowNonUniqueSections: true,
   106  		}, []byte(`[Interface]
   107  Address    = 192.168.2.1
   108  PrivateKey = <server's privatekey>
   109  ListenPort = 51820
   110  
   111  [Peer]
   112  PublicKey  = <client's publickey>
   113  AllowedIPs = 192.168.2.2/32
   114  
   115  [Peer]
   116  PublicKey  = <client2's publickey>
   117  AllowedIPs = 192.168.2.3/32
   118  
   119  [Peer]
   120  PublicKey  = <client3's publickey>
   121  AllowedIPs = 192.168.2.4/32
   122  
   123  `))
   124  		require.NoError(t, err)
   125  		require.NotNil(t, f)
   126  
   127  		err = f.DeleteSectionWithIndex("Peer", 1)
   128  		require.NoError(t, err)
   129  
   130  		var buf bytes.Buffer
   131  		_, err = f.WriteTo(&buf)
   132  		require.NoError(t, err)
   133  		str := buf.String()
   134  		assert.Equal(t, `[Interface]
   135  Address    = 192.168.2.1
   136  PrivateKey = <server's privatekey>
   137  ListenPort = 51820
   138  
   139  [Peer]
   140  PublicKey  = <client's publickey>
   141  AllowedIPs = 192.168.2.2/32
   142  
   143  [Peer]
   144  PublicKey  = <client3's publickey>
   145  AllowedIPs = 192.168.2.4/32
   146  `, str)
   147  	})
   148  
   149  	t.Run("delete all sections", func(t *testing.T) {
   150  		f := Empty(LoadOptions{
   151  			AllowNonUniqueSections: true,
   152  		})
   153  		require.NotNil(t, f)
   154  
   155  		_ = f.NewSections("Interface", "Peer", "Peer")
   156  		assert.Equal(t, []string{DefaultSection, "Interface", "Peer", "Peer"}, f.SectionStrings())
   157  		f.DeleteSection("Peer")
   158  		assert.Equal(t, []string{DefaultSection, "Interface"}, f.SectionStrings())
   159  	})
   160  }
   161  
   162  func TestFile_NewRawSection(t *testing.T) {
   163  	f := Empty()
   164  	require.NotNil(t, f)
   165  
   166  	sec, err := f.NewRawSection("comments", `1111111111111111111000000000000000001110000
   167  111111111111111111100000000000111000000000`)
   168  	require.NoError(t, err)
   169  	require.NotNil(t, sec)
   170  	assert.Equal(t, "comments", sec.Name())
   171  
   172  	assert.Equal(t, []string{DefaultSection, "comments"}, f.SectionStrings())
   173  	assert.Equal(t, `1111111111111111111000000000000000001110000
   174  111111111111111111100000000000111000000000`, f.Section("comments").Body())
   175  
   176  	t.Run("with duplicated name", func(t *testing.T) {
   177  		sec, err := f.NewRawSection("comments", `1111111111111111111000000000000000001110000`)
   178  		require.NoError(t, err)
   179  		require.NotNil(t, sec)
   180  		assert.Equal(t, []string{DefaultSection, "comments"}, f.SectionStrings())
   181  
   182  		// Overwrite previous existed section
   183  		assert.Equal(t, `1111111111111111111000000000000000001110000`, f.Section("comments").Body())
   184  	})
   185  
   186  	t.Run("with empty string", func(t *testing.T) {
   187  		_, err := f.NewRawSection("", "")
   188  		require.Error(t, err)
   189  	})
   190  }
   191  
   192  func TestFile_NewSections(t *testing.T) {
   193  	f := Empty()
   194  	require.NotNil(t, f)
   195  
   196  	assert.NoError(t, f.NewSections("package", "author"))
   197  	assert.Equal(t, []string{DefaultSection, "package", "author"}, f.SectionStrings())
   198  
   199  	t.Run("with duplicated name", func(t *testing.T) {
   200  		assert.NoError(t, f.NewSections("author", "features"))
   201  
   202  		// Ignore section already exists
   203  		assert.Equal(t, []string{DefaultSection, "package", "author", "features"}, f.SectionStrings())
   204  	})
   205  
   206  	t.Run("with empty string", func(t *testing.T) {
   207  		assert.Error(t, f.NewSections("", ""))
   208  	})
   209  }
   210  
   211  func TestFile_GetSection(t *testing.T) {
   212  	f, err := Load(fullConf)
   213  	require.NoError(t, err)
   214  	require.NotNil(t, f)
   215  
   216  	sec, err := f.GetSection("author")
   217  	require.NoError(t, err)
   218  	require.NotNil(t, sec)
   219  	assert.Equal(t, "author", sec.Name())
   220  
   221  	t.Run("section not exists", func(t *testing.T) {
   222  		_, err := f.GetSection("404")
   223  		require.Error(t, err)
   224  	})
   225  }
   226  
   227  func TestFile_HasSection(t *testing.T) {
   228  	f, err := Load(fullConf)
   229  	require.NoError(t, err)
   230  	require.NotNil(t, f)
   231  
   232  	sec := f.HasSection("author")
   233  	assert.True(t, sec)
   234  
   235  	t.Run("section not exists", func(t *testing.T) {
   236  		nonexistent := f.HasSection("404")
   237  		assert.False(t, nonexistent)
   238  	})
   239  }
   240  
   241  func TestFile_Section(t *testing.T) {
   242  	t.Run("get a section", func(t *testing.T) {
   243  		f, err := Load(fullConf)
   244  		require.NoError(t, err)
   245  		require.NotNil(t, f)
   246  
   247  		sec := f.Section("author")
   248  		require.NotNil(t, sec)
   249  		assert.Equal(t, "author", sec.Name())
   250  
   251  		t.Run("section not exists", func(t *testing.T) {
   252  			sec := f.Section("404")
   253  			require.NotNil(t, sec)
   254  			assert.Equal(t, "404", sec.Name())
   255  		})
   256  	})
   257  
   258  	t.Run("get default section in lower case with insensitive load", func(t *testing.T) {
   259  		f, err := InsensitiveLoad([]byte(`
   260  [default]
   261  NAME = ini
   262  VERSION = v1`))
   263  		require.NoError(t, err)
   264  		require.NotNil(t, f)
   265  
   266  		assert.Equal(t, "ini", f.Section("").Key("name").String())
   267  		assert.Equal(t, "v1", f.Section("").Key("version").String())
   268  	})
   269  
   270  	t.Run("get sections after deletion", func(t *testing.T) {
   271  		f, err := Load([]byte(`
   272  [RANDOM]
   273  `))
   274  		require.NoError(t, err)
   275  		require.NotNil(t, f)
   276  
   277  		sectionNames := f.SectionStrings()
   278  		sort.Strings(sectionNames)
   279  		assert.Equal(t, []string{DefaultSection, "RANDOM"}, sectionNames)
   280  
   281  		for _, currentSection := range sectionNames {
   282  			f.DeleteSection(currentSection)
   283  		}
   284  
   285  		for sectionParam, expectedSectionName := range map[string]string{
   286  			"":       DefaultSection,
   287  			"RANDOM": "RANDOM",
   288  		} {
   289  			sec := f.Section(sectionParam)
   290  			require.NotNil(t, sec)
   291  			assert.Equal(t, expectedSectionName, sec.Name())
   292  		}
   293  	})
   294  
   295  }
   296  
   297  func TestFile_Sections(t *testing.T) {
   298  	f, err := Load(fullConf)
   299  	require.NoError(t, err)
   300  	require.NotNil(t, f)
   301  
   302  	secs := f.Sections()
   303  	names := []string{DefaultSection, "author", "package", "package.sub", "features", "types", "array", "note", "comments", "string escapes", "advance"}
   304  	assert.Len(t, secs, len(names))
   305  	for i, name := range names {
   306  		assert.Equal(t, name, secs[i].Name())
   307  	}
   308  }
   309  
   310  func TestFile_ChildSections(t *testing.T) {
   311  	f, err := Load([]byte(`
   312  [node]
   313  [node.biz1]
   314  [node.biz2]
   315  [node.biz3]
   316  [node.bizN]
   317  `))
   318  	require.NoError(t, err)
   319  	require.NotNil(t, f)
   320  
   321  	children := f.ChildSections("node")
   322  	names := []string{"node.biz1", "node.biz2", "node.biz3", "node.bizN"}
   323  	assert.Len(t, children, len(names))
   324  	for i, name := range names {
   325  		assert.Equal(t, name, children[i].Name())
   326  	}
   327  }
   328  
   329  func TestFile_SectionStrings(t *testing.T) {
   330  	f, err := Load(fullConf)
   331  	require.NoError(t, err)
   332  	require.NotNil(t, f)
   333  
   334  	assert.Equal(t, []string{DefaultSection, "author", "package", "package.sub", "features", "types", "array", "note", "comments", "string escapes", "advance"}, f.SectionStrings())
   335  }
   336  
   337  func TestFile_DeleteSection(t *testing.T) {
   338  	t.Run("delete a section", func(t *testing.T) {
   339  		f := Empty()
   340  		require.NotNil(t, f)
   341  
   342  		_ = f.NewSections("author", "package", "features")
   343  		f.DeleteSection("features")
   344  		f.DeleteSection("")
   345  		assert.Equal(t, []string{"author", "package"}, f.SectionStrings())
   346  	})
   347  
   348  	t.Run("delete default section", func(t *testing.T) {
   349  		f := Empty()
   350  		require.NotNil(t, f)
   351  
   352  		f.Section("").Key("foo").SetValue("bar")
   353  		f.Section("section1").Key("key1").SetValue("value1")
   354  		f.DeleteSection("")
   355  		assert.Equal(t, []string{"section1"}, f.SectionStrings())
   356  
   357  		var buf bytes.Buffer
   358  		_, err := f.WriteTo(&buf)
   359  		require.NoError(t, err)
   360  
   361  		assert.Equal(t, `[section1]
   362  key1 = value1
   363  `, buf.String())
   364  	})
   365  
   366  	t.Run("delete a section with InsensitiveSections", func(t *testing.T) {
   367  		f := Empty(LoadOptions{InsensitiveSections: true})
   368  		require.NotNil(t, f)
   369  
   370  		_ = f.NewSections("author", "package", "features")
   371  		f.DeleteSection("FEATURES")
   372  		f.DeleteSection("")
   373  		assert.Equal(t, []string{"author", "package"}, f.SectionStrings())
   374  	})
   375  }
   376  
   377  func TestFile_Append(t *testing.T) {
   378  	f := Empty()
   379  	require.NotNil(t, f)
   380  
   381  	assert.NoError(t, f.Append(minimalConf, []byte(`
   382  [author]
   383  NAME = Unknwon`)))
   384  
   385  	t.Run("with bad input", func(t *testing.T) {
   386  		assert.Error(t, f.Append(123))
   387  		assert.Error(t, f.Append(minimalConf, 123))
   388  	})
   389  }
   390  
   391  func TestFile_WriteTo(t *testing.T) {
   392  	if runtime.GOOS == "windows" {
   393  		t.Skip("Skipping testing on Windows")
   394  	}
   395  
   396  	t.Run("write content to somewhere", func(t *testing.T) {
   397  		f, err := Load(fullConf)
   398  		require.NoError(t, err)
   399  		require.NotNil(t, f)
   400  
   401  		f.Section("author").Comment = `Information about package author
   402  # Bio can be written in multiple lines.`
   403  		f.Section("author").Key("NAME").Comment = "This is author name"
   404  		_, _ = f.Section("note").NewBooleanKey("boolean_key")
   405  		_, _ = f.Section("note").NewKey("more", "notes")
   406  
   407  		var buf bytes.Buffer
   408  		_, err = f.WriteTo(&buf)
   409  		require.NoError(t, err)
   410  
   411  		golden := "testdata/TestFile_WriteTo.golden"
   412  		if *update {
   413  			require.NoError(t, os.WriteFile(golden, buf.Bytes(), 0644))
   414  		}
   415  
   416  		expected, err := os.ReadFile(golden)
   417  		require.NoError(t, err)
   418  		assert.Equal(t, string(expected), buf.String())
   419  	})
   420  
   421  	t.Run("support multiline comments", func(t *testing.T) {
   422  		f, err := Load([]byte(`
   423  #
   424  # general.domain
   425  #
   426  # Domain name of XX system.
   427  domain      = mydomain.com
   428  `))
   429  		require.NoError(t, err)
   430  
   431  		f.Section("").Key("test").Comment = "Multiline\nComment"
   432  
   433  		var buf bytes.Buffer
   434  		_, err = f.WriteTo(&buf)
   435  		require.NoError(t, err)
   436  
   437  		assert.Equal(t, `#
   438  # general.domain
   439  #
   440  # Domain name of XX system.
   441  domain = mydomain.com
   442  ; Multiline
   443  ; Comment
   444  test   =
   445  `, buf.String())
   446  	})
   447  
   448  	t.Run("keep leading and trailing spaces in value", func(t *testing.T) {
   449  		f, _ := Load([]byte(`[foo]
   450  bar1 = '  val ue1 '
   451  bar2 = """  val ue2 """
   452  bar3 = "  val ue3 "
   453  `))
   454  		require.NotNil(t, f)
   455  
   456  		var buf bytes.Buffer
   457  		_, err := f.WriteTo(&buf)
   458  		require.NoError(t, err)
   459  		assert.Equal(t, `[foo]
   460  bar1 = "  val ue1 "
   461  bar2 = "  val ue2 "
   462  bar3 = "  val ue3 "
   463  `, buf.String())
   464  	})
   465  }
   466  
   467  func TestFile_SaveTo(t *testing.T) {
   468  	f, err := Load(fullConf)
   469  	require.NoError(t, err)
   470  	require.NotNil(t, f)
   471  
   472  	tmpdir, err := bazel.NewTestTmpDir("edge-infra-ini-test-*")
   473  	assert.NoError(t, err)
   474  
   475  	p := filepath.Join(tmpdir, "conf_out.ini")
   476  	assert.NoError(t, f.SaveTo(p))
   477  	assert.NoError(t, f.SaveToIndent(p, "\t"))
   478  }
   479  
   480  func TestFile_WriteToWithOutputDelimiter(t *testing.T) {
   481  	f, err := LoadSources(LoadOptions{
   482  		KeyValueDelimiterOnWrite: "->",
   483  	}, []byte(`[Others]
   484  Cities = HangZhou|Boston
   485  Visits = 1993-10-07T20:17:05Z, 1993-10-07T20:17:05Z
   486  Years = 1993,1994
   487  Numbers = 10010,10086
   488  Ages = 18,19
   489  Populations = 12345678,98765432
   490  Coordinates = 192.168,10.11
   491  Flags       = true,false
   492  Note = Hello world!`))
   493  	require.NoError(t, err)
   494  	require.NotNil(t, f)
   495  
   496  	var actual bytes.Buffer
   497  	var expected = []byte(`[Others]
   498  Cities      -> HangZhou|Boston
   499  Visits      -> 1993-10-07T20:17:05Z, 1993-10-07T20:17:05Z
   500  Years       -> 1993,1994
   501  Numbers     -> 10010,10086
   502  Ages        -> 18,19
   503  Populations -> 12345678,98765432
   504  Coordinates -> 192.168,10.11
   505  Flags       -> true,false
   506  Note        -> Hello world!
   507  `)
   508  	_, err = f.WriteTo(&actual)
   509  	require.NoError(t, err)
   510  
   511  	assert.Equal(t, expected, actual.Bytes())
   512  }
   513  
   514  // Inspired by https://github.com/go-ini/ini/issues/207
   515  func TestReloadAfterShadowLoad(t *testing.T) {
   516  	f, err := ShadowLoad([]byte(`
   517  [slice]
   518  v = 1
   519  v = 2
   520  v = 3
   521  `))
   522  	require.NoError(t, err)
   523  	require.NotNil(t, f)
   524  
   525  	assert.Equal(t, []string{"1", "2", "3"}, f.Section("slice").Key("v").ValueWithShadows())
   526  
   527  	require.NoError(t, f.Reload())
   528  	assert.Equal(t, []string{"1", "2", "3"}, f.Section("slice").Key("v").ValueWithShadows())
   529  }
   530  

View as plain text