docker-cli/pkg/kvfile/kvfile_test.go
Nicolas De Loof 9ecfe4f5a7
move parsing key-value files to a separate package
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>
2024-10-04 12:27:10 +02:00

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))
}