Move the code for parsing key-value files, such as used for env-files and label-files to a separate package. This allows other projects (such as compose) to use the same parsing logic, but provide custom lookup functions for their situation (which is slightly different). The new package provides utilities for parsing key-value files for either a file or an io.Reader. Most tests for EnvFile were now testing functionality that's already tested in the new package, so were (re)moved. Co-authored-by: Nicolas De Loof <nicolas.deloof@gmail.com> Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com> Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
146 lines
3.9 KiB
Go
146 lines
3.9 KiB
Go
package kvfile
|
|
|
|
import (
|
|
"bufio"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"gotest.tools/v3/assert"
|
|
is "gotest.tools/v3/assert/cmp"
|
|
)
|
|
|
|
// Test Parse for a non existent file.
|
|
func TestParseNonExistentFile(t *testing.T) {
|
|
_, err := Parse("no_such_file", nil)
|
|
assert.Check(t, is.ErrorType(err, os.IsNotExist))
|
|
}
|
|
|
|
// Test Parse from a file with a lookup function.
|
|
func TestParseWithLookup(t *testing.T) {
|
|
content := `# comment=
|
|
VAR=VAR_VALUE
|
|
EMPTY_VAR=
|
|
UNDEFINED_VAR
|
|
DEFINED_VAR
|
|
`
|
|
vars := map[string]string{
|
|
"DEFINED_VAR": "defined-value",
|
|
}
|
|
lookupFn := func(name string) (string, bool) {
|
|
v, ok := vars[name]
|
|
return v, ok
|
|
}
|
|
|
|
fileName := filepath.Join(t.TempDir(), "envfile")
|
|
err := os.WriteFile(fileName, []byte(content), 0o644)
|
|
assert.NilError(t, err)
|
|
|
|
variables, err := Parse(fileName, lookupFn)
|
|
assert.NilError(t, err)
|
|
|
|
expectedLines := []string{"VAR=VAR_VALUE", "EMPTY_VAR=", "DEFINED_VAR=defined-value"}
|
|
assert.Check(t, is.DeepEqual(variables, expectedLines))
|
|
}
|
|
|
|
// Test ParseEnvFile for a file with a few well formatted lines
|
|
func TestParseFromReaderGoodFile(t *testing.T) {
|
|
content := `foo=bar
|
|
baz=quux
|
|
# comment
|
|
|
|
_foobar=foobaz
|
|
with.dots=working
|
|
and_underscore=working too
|
|
`
|
|
// Adding a newline + a line with pure whitespace.
|
|
// This is being done like this instead of the block above
|
|
// because it's common for editors to trim trailing whitespace
|
|
// from lines, which becomes annoying since that's the
|
|
// exact thing we need to test.
|
|
content += "\n \t "
|
|
|
|
lines, err := ParseFromReader(strings.NewReader(content), nil)
|
|
assert.NilError(t, err)
|
|
|
|
expectedLines := []string{
|
|
"foo=bar",
|
|
"baz=quux",
|
|
"_foobar=foobaz",
|
|
"with.dots=working",
|
|
"and_underscore=working too",
|
|
}
|
|
|
|
assert.Check(t, is.DeepEqual(lines, expectedLines))
|
|
}
|
|
|
|
// Test ParseFromReader for an empty file
|
|
func TestParseFromReaderEmptyFile(t *testing.T) {
|
|
lines, err := ParseFromReader(strings.NewReader(""), nil)
|
|
assert.NilError(t, err)
|
|
assert.Check(t, is.Len(lines, 0))
|
|
}
|
|
|
|
// Test ParseFromReader for a badly formatted file
|
|
func TestParseFromReaderBadlyFormattedFile(t *testing.T) {
|
|
content := `foo=bar
|
|
f =quux
|
|
`
|
|
_, err := ParseFromReader(strings.NewReader(content), nil)
|
|
const expectedMessage = "variable 'f ' contains whitespaces"
|
|
assert.Check(t, is.ErrorContains(err, expectedMessage))
|
|
}
|
|
|
|
// Test ParseFromReader for a file with a line exceeding bufio.MaxScanTokenSize
|
|
func TestParseFromReaderLineTooLongFile(t *testing.T) {
|
|
content := "foo=" + strings.Repeat("a", bufio.MaxScanTokenSize+42)
|
|
|
|
_, err := ParseFromReader(strings.NewReader(content), nil)
|
|
const expectedMessage = "bufio.Scanner: token too long"
|
|
assert.Check(t, is.ErrorContains(err, expectedMessage))
|
|
}
|
|
|
|
// ParseEnvFile with a random file, pass through
|
|
func TestParseFromReaderRandomFile(t *testing.T) {
|
|
content := `first line
|
|
another invalid line`
|
|
|
|
_, err := ParseFromReader(strings.NewReader(content), nil)
|
|
const expectedMessage = "variable 'first line' contains whitespaces"
|
|
assert.Check(t, is.ErrorContains(err, expectedMessage))
|
|
}
|
|
|
|
// Test ParseFromReader with a lookup function.
|
|
func TestParseFromReaderWithLookup(t *testing.T) {
|
|
content := `# comment=
|
|
VAR=VAR_VALUE
|
|
EMPTY_VAR=
|
|
UNDEFINED_VAR
|
|
DEFINED_VAR
|
|
`
|
|
vars := map[string]string{
|
|
"DEFINED_VAR": "defined-value",
|
|
}
|
|
lookupFn := func(name string) (string, bool) {
|
|
v, ok := vars[name]
|
|
return v, ok
|
|
}
|
|
|
|
variables, err := ParseFromReader(strings.NewReader(content), lookupFn)
|
|
assert.NilError(t, err)
|
|
|
|
expectedLines := []string{"VAR=VAR_VALUE", "EMPTY_VAR=", "DEFINED_VAR=defined-value"}
|
|
assert.Check(t, is.DeepEqual(variables, expectedLines))
|
|
}
|
|
|
|
// Test ParseFromReader with empty variable name
|
|
func TestParseFromReaderWithNoName(t *testing.T) {
|
|
content := `# comment=
|
|
=blank variable names are an error case
|
|
`
|
|
_, err := ParseFromReader(strings.NewReader(content), nil)
|
|
const expectedMessage = "no variable name on line '=blank variable names are an error case'"
|
|
assert.Check(t, is.ErrorContains(err, expectedMessage))
|
|
}
|