forked from coop-cloud-mirrors/godotenv
710 lines
18 KiB
Go
710 lines
18 KiB
Go
package godotenv
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"os"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
var noopPresets = make(map[string]string)
|
|
|
|
func parseAndCompare(t *testing.T, rawEnvLine string, expectedKey string, expectedValue string) {
|
|
result, _, err := Unmarshal(rawEnvLine)
|
|
if err != nil {
|
|
t.Errorf("Expected %q to parse as %q: %q, errored %q", rawEnvLine, expectedKey, expectedValue, err)
|
|
return
|
|
}
|
|
if result[expectedKey] != expectedValue {
|
|
t.Errorf("Expected '%v' to parse as '%v' => '%v', got %q instead", rawEnvLine, expectedKey, expectedValue, result)
|
|
}
|
|
}
|
|
|
|
func loadEnvAndCompareValues(t *testing.T, loader func(files ...string) error, envFileName string, expectedValues map[string]string, presets map[string]string) {
|
|
// first up, clear the env
|
|
os.Clearenv()
|
|
|
|
for k, v := range presets {
|
|
os.Setenv(k, v)
|
|
}
|
|
|
|
err := loader(envFileName)
|
|
if err != nil {
|
|
t.Fatalf("Error loading %v", envFileName)
|
|
}
|
|
|
|
for k := range expectedValues {
|
|
envValue := os.Getenv(k)
|
|
v := expectedValues[k]
|
|
if envValue != v {
|
|
t.Errorf("Mismatch for key '%v': expected '%#v' got '%#v'", k, v, envValue)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestLoadWithNoArgsLoadsDotEnv(t *testing.T) {
|
|
err := Load()
|
|
pathError := err.(*os.PathError)
|
|
if pathError == nil || pathError.Op != "open" || pathError.Path != ".env" {
|
|
t.Errorf("Didn't try and open .env by default")
|
|
}
|
|
}
|
|
|
|
func TestOverloadWithNoArgsOverloadsDotEnv(t *testing.T) {
|
|
err := Overload()
|
|
pathError := err.(*os.PathError)
|
|
if pathError == nil || pathError.Op != "open" || pathError.Path != ".env" {
|
|
t.Errorf("Didn't try and open .env by default")
|
|
}
|
|
}
|
|
|
|
func TestLoadFileNotFound(t *testing.T) {
|
|
err := Load("somefilethatwillneverexistever.env")
|
|
if err == nil {
|
|
t.Error("File wasn't found but Load didn't return an error")
|
|
}
|
|
}
|
|
|
|
func TestOverloadFileNotFound(t *testing.T) {
|
|
err := Overload("somefilethatwillneverexistever.env")
|
|
if err == nil {
|
|
t.Error("File wasn't found but Overload didn't return an error")
|
|
}
|
|
}
|
|
|
|
func TestReadPlainEnv(t *testing.T) {
|
|
envFileName := "fixtures/plain.env"
|
|
expectedValues := map[string]string{
|
|
"OPTION_A": "1",
|
|
"OPTION_B": "2",
|
|
"OPTION_C": "3",
|
|
"OPTION_D": "4",
|
|
"OPTION_E": "5",
|
|
"OPTION_F": "",
|
|
"OPTION_G": "",
|
|
"OPTION_H": "1 2",
|
|
}
|
|
|
|
envMap, _, err := Read(envFileName)
|
|
if err != nil {
|
|
t.Error("Error reading file")
|
|
}
|
|
|
|
if len(envMap) != len(expectedValues) {
|
|
t.Error("Didn't get the right size map back")
|
|
}
|
|
|
|
for key, value := range expectedValues {
|
|
if envMap[key] != value {
|
|
t.Error("Read got one of the keys wrong")
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestParse(t *testing.T) {
|
|
envMap, _, err := Parse(bytes.NewReader([]byte("ONE=1\nTWO='2'\nTHREE = \"3\"")))
|
|
expectedValues := map[string]string{
|
|
"ONE": "1",
|
|
"TWO": "2",
|
|
"THREE": "3",
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("error parsing env: %v", err)
|
|
}
|
|
for key, value := range expectedValues {
|
|
if envMap[key] != value {
|
|
t.Errorf("expected %s to be %s, got %s", key, value, envMap[key])
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestLoadDoesNotOverride(t *testing.T) {
|
|
envFileName := "fixtures/plain.env"
|
|
|
|
// ensure NO overload
|
|
presets := map[string]string{
|
|
"OPTION_A": "do_not_override",
|
|
"OPTION_B": "",
|
|
}
|
|
|
|
expectedValues := map[string]string{
|
|
"OPTION_A": "do_not_override",
|
|
"OPTION_B": "",
|
|
}
|
|
loadEnvAndCompareValues(t, Load, envFileName, expectedValues, presets)
|
|
}
|
|
|
|
func TestOverloadDoesOverride(t *testing.T) {
|
|
envFileName := "fixtures/plain.env"
|
|
|
|
// ensure NO overload
|
|
presets := map[string]string{
|
|
"OPTION_A": "do_not_override",
|
|
}
|
|
|
|
expectedValues := map[string]string{
|
|
"OPTION_A": "1",
|
|
}
|
|
loadEnvAndCompareValues(t, Overload, envFileName, expectedValues, presets)
|
|
}
|
|
|
|
func TestLoadPlainEnv(t *testing.T) {
|
|
envFileName := "fixtures/plain.env"
|
|
expectedValues := map[string]string{
|
|
"OPTION_A": "1",
|
|
"OPTION_B": "2",
|
|
"OPTION_C": "3",
|
|
"OPTION_D": "4",
|
|
"OPTION_E": "5",
|
|
"OPTION_H": "1 2",
|
|
}
|
|
|
|
loadEnvAndCompareValues(t, Load, envFileName, expectedValues, noopPresets)
|
|
}
|
|
|
|
func TestLoadExportedEnv(t *testing.T) {
|
|
envFileName := "fixtures/exported.env"
|
|
expectedValues := map[string]string{
|
|
"OPTION_A": "2",
|
|
"OPTION_B": "\\n",
|
|
}
|
|
|
|
loadEnvAndCompareValues(t, Load, envFileName, expectedValues, noopPresets)
|
|
}
|
|
|
|
func TestLoadEqualsEnv(t *testing.T) {
|
|
envFileName := "fixtures/equals.env"
|
|
expectedValues := map[string]string{
|
|
"OPTION_A": "postgres://localhost:5432/database?sslmode=disable",
|
|
}
|
|
|
|
loadEnvAndCompareValues(t, Load, envFileName, expectedValues, noopPresets)
|
|
}
|
|
|
|
func TestLoadQuotedEnv(t *testing.T) {
|
|
envFileName := "fixtures/quoted.env"
|
|
expectedValues := map[string]string{
|
|
"OPTION_A": "1",
|
|
"OPTION_B": "2",
|
|
"OPTION_C": "",
|
|
"OPTION_D": "\\n",
|
|
"OPTION_E": "1",
|
|
"OPTION_F": "2",
|
|
"OPTION_G": "",
|
|
"OPTION_H": "\n",
|
|
"OPTION_I": "echo 'asd'",
|
|
"OPTION_J": "line 1\nline 2",
|
|
"OPTION_K": "line one\nthis is \\'quoted\\'\none more line",
|
|
"OPTION_L": "line 1\nline 2",
|
|
"OPTION_M": "line one\nthis is \"quoted\"\none more line",
|
|
}
|
|
|
|
loadEnvAndCompareValues(t, Load, envFileName, expectedValues, noopPresets)
|
|
}
|
|
|
|
func TestSubstitutions(t *testing.T) {
|
|
envFileName := "fixtures/substitutions.env"
|
|
expectedValues := map[string]string{
|
|
"OPTION_A": "1",
|
|
"OPTION_B": "1",
|
|
"OPTION_C": "1",
|
|
"OPTION_D": "11",
|
|
"OPTION_E": "",
|
|
}
|
|
|
|
loadEnvAndCompareValues(t, Load, envFileName, expectedValues, noopPresets)
|
|
}
|
|
|
|
func TestExpanding(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
input string
|
|
expected map[string]string
|
|
}{
|
|
{
|
|
"expands variables found in values",
|
|
"FOO=test\nBAR=$FOO",
|
|
map[string]string{"FOO": "test", "BAR": "test"},
|
|
},
|
|
{
|
|
"parses variables wrapped in brackets",
|
|
"FOO=test\nBAR=${FOO}bar",
|
|
map[string]string{"FOO": "test", "BAR": "testbar"},
|
|
},
|
|
{
|
|
"expands undefined variables to an empty string",
|
|
"BAR=$FOO",
|
|
map[string]string{"BAR": ""},
|
|
},
|
|
{
|
|
"expands variables in double quoted strings",
|
|
"FOO=test\nBAR=\"quote $FOO\"",
|
|
map[string]string{"FOO": "test", "BAR": "quote test"},
|
|
},
|
|
{
|
|
"does not expand variables in single quoted strings",
|
|
"BAR='quote $FOO'",
|
|
map[string]string{"BAR": "quote $FOO"},
|
|
},
|
|
{
|
|
"does not expand escaped variables",
|
|
`FOO="foo\$BAR"`,
|
|
map[string]string{"FOO": "foo$BAR"},
|
|
},
|
|
{
|
|
"does not expand escaped variables",
|
|
`FOO="foo\${BAR}"`,
|
|
map[string]string{"FOO": "foo${BAR}"},
|
|
},
|
|
{
|
|
"does not expand escaped variables",
|
|
"FOO=test\nBAR=\"foo\\${FOO} ${FOO}\"",
|
|
map[string]string{"FOO": "test", "BAR": "foo${FOO} test"},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
env, _, err := Parse(strings.NewReader(tt.input))
|
|
if err != nil {
|
|
t.Errorf("Error: %s", err.Error())
|
|
}
|
|
for k, v := range tt.expected {
|
|
if strings.Compare(env[k], v) != 0 {
|
|
t.Errorf("Expected: %s, Actual: %s", v, env[k])
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestVariableStringValueSeparator(t *testing.T) {
|
|
input := "TEST_URLS=\"stratum+tcp://stratum.antpool.com:3333\nstratum+tcp://stratum.antpool.com:443\""
|
|
want := map[string]string{
|
|
"TEST_URLS": "stratum+tcp://stratum.antpool.com:3333\nstratum+tcp://stratum.antpool.com:443",
|
|
}
|
|
got, _, err := Parse(strings.NewReader(input))
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if len(got) != len(want) {
|
|
t.Fatalf(
|
|
"unexpected value:\nwant:\n\t%#v\n\ngot:\n\t%#v", want, got)
|
|
}
|
|
|
|
for k, wantVal := range want {
|
|
gotVal, ok := got[k]
|
|
if !ok {
|
|
t.Fatalf("key %q doesn't present in result", k)
|
|
}
|
|
if wantVal != gotVal {
|
|
t.Fatalf(
|
|
"mismatch in %q value:\nwant:\n\t%s\n\ngot:\n\t%s", k,
|
|
wantVal, gotVal)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestActualEnvVarsAreLeftAlone(t *testing.T) {
|
|
os.Clearenv()
|
|
os.Setenv("OPTION_A", "actualenv")
|
|
_ = Load("fixtures/plain.env")
|
|
|
|
if os.Getenv("OPTION_A") != "actualenv" {
|
|
t.Error("An ENV var set earlier was overwritten")
|
|
}
|
|
}
|
|
|
|
func TestParsing(t *testing.T) {
|
|
// unquoted values
|
|
parseAndCompare(t, "FOO=bar", "FOO", "bar")
|
|
|
|
// parses values with spaces around equal sign
|
|
parseAndCompare(t, "FOO =bar", "FOO", "bar")
|
|
parseAndCompare(t, "FOO= bar", "FOO", "bar")
|
|
|
|
// parses double quoted values
|
|
parseAndCompare(t, `FOO="bar"`, "FOO", "bar")
|
|
|
|
// parses single quoted values
|
|
parseAndCompare(t, "FOO='bar'", "FOO", "bar")
|
|
|
|
// parses escaped double quotes
|
|
parseAndCompare(t, `FOO="escaped\"bar"`, "FOO", `escaped"bar`)
|
|
|
|
// parses single quotes inside double quotes
|
|
parseAndCompare(t, `FOO="'d'"`, "FOO", `'d'`)
|
|
|
|
// parses yaml style options
|
|
parseAndCompare(t, "OPTION_A: 1", "OPTION_A", "1")
|
|
|
|
// parses yaml values with equal signs
|
|
parseAndCompare(t, "OPTION_A: Foo=bar", "OPTION_A", "Foo=bar")
|
|
|
|
// parses non-yaml options with colons
|
|
parseAndCompare(t, "OPTION_A=1:B", "OPTION_A", "1:B")
|
|
|
|
// parses export keyword
|
|
parseAndCompare(t, "export OPTION_A=2", "OPTION_A", "2")
|
|
parseAndCompare(t, `export OPTION_B='\n'`, "OPTION_B", "\\n")
|
|
parseAndCompare(t, "export exportFoo=2", "exportFoo", "2")
|
|
parseAndCompare(t, "exportFOO=2", "exportFOO", "2")
|
|
parseAndCompare(t, "export_FOO =2", "export_FOO", "2")
|
|
parseAndCompare(t, "export.FOO= 2", "export.FOO", "2")
|
|
parseAndCompare(t, "export\tOPTION_A=2", "OPTION_A", "2")
|
|
parseAndCompare(t, " export OPTION_A=2", "OPTION_A", "2")
|
|
parseAndCompare(t, "\texport OPTION_A=2", "OPTION_A", "2")
|
|
|
|
// it 'expands newlines in quoted strings' do
|
|
// expect(env('FOO="bar\nbaz"')).to eql('FOO' => "bar\nbaz")
|
|
parseAndCompare(t, `FOO="bar\nbaz"`, "FOO", "bar\nbaz")
|
|
|
|
// it 'parses variables with "." in the name' do
|
|
// expect(env('FOO.BAR=foobar')).to eql('FOO.BAR' => 'foobar')
|
|
parseAndCompare(t, "FOO.BAR=foobar", "FOO.BAR", "foobar")
|
|
|
|
// it 'parses variables with several "=" in the value' do
|
|
// expect(env('FOO=foobar=')).to eql('FOO' => 'foobar=')
|
|
parseAndCompare(t, "FOO=foobar=", "FOO", "foobar=")
|
|
|
|
// it 'strips unquoted values' do
|
|
// expect(env('foo=bar ')).to eql('foo' => 'bar') # not 'bar '
|
|
parseAndCompare(t, "FOO=bar ", "FOO", "bar")
|
|
|
|
// unquoted internal whitespace is preserved
|
|
parseAndCompare(t, `KEY=value value`, "KEY", "value value")
|
|
|
|
// it 'ignores inline comments' do
|
|
// expect(env("foo=bar # this is foo")).to eql('foo' => 'bar')
|
|
parseAndCompare(t, "FOO=bar # this is foo", "FOO", "bar")
|
|
|
|
// it 'allows # in quoted value' do
|
|
// expect(env('foo="bar#baz" # comment')).to eql('foo' => 'bar#baz')
|
|
parseAndCompare(t, `FOO="bar#baz" # comment`, "FOO", "bar#baz")
|
|
parseAndCompare(t, "FOO='bar#baz' # comment", "FOO", "bar#baz")
|
|
parseAndCompare(t, `FOO="bar#baz#bang" # comment`, "FOO", "bar#baz#bang")
|
|
|
|
// it 'parses # in quoted values' do
|
|
// expect(env('foo="ba#r"')).to eql('foo' => 'ba#r')
|
|
// expect(env("foo='ba#r'")).to eql('foo' => 'ba#r')
|
|
parseAndCompare(t, `FOO="ba#r"`, "FOO", "ba#r")
|
|
parseAndCompare(t, "FOO='ba#r'", "FOO", "ba#r")
|
|
|
|
// newlines and backslashes should be escaped
|
|
parseAndCompare(t, `FOO="bar\n\ b\az"`, "FOO", "bar\n baz")
|
|
parseAndCompare(t, `FOO="bar\\\n\ b\az"`, "FOO", "bar\\\n baz")
|
|
parseAndCompare(t, `FOO="bar\\r\ b\az"`, "FOO", "bar\\r baz")
|
|
|
|
parseAndCompare(t, `="value"`, "", "value")
|
|
|
|
// unquoted whitespace around keys should be ignored
|
|
parseAndCompare(t, " KEY =value", "KEY", "value")
|
|
parseAndCompare(t, " KEY=value", "KEY", "value")
|
|
parseAndCompare(t, "\tKEY=value", "KEY", "value")
|
|
|
|
// it 'throws an error if line format is incorrect' do
|
|
// expect{env('lol$wut')}.to raise_error(Dotenv::FormatError)
|
|
badlyFormattedLine := "lol$wut"
|
|
_, _, err := Unmarshal(badlyFormattedLine)
|
|
if err == nil {
|
|
t.Errorf("Expected \"%v\" to return error, but it didn't", badlyFormattedLine)
|
|
}
|
|
}
|
|
|
|
func TestLinesToIgnore(t *testing.T) {
|
|
cases := map[string]struct {
|
|
input string
|
|
want string
|
|
}{
|
|
"Line with nothing but line break": {
|
|
input: "\n",
|
|
},
|
|
"Line with nothing but windows-style line break": {
|
|
input: "\r\n",
|
|
},
|
|
"Line full of whitespace": {
|
|
input: "\t\t ",
|
|
},
|
|
"Comment": {
|
|
input: "# Comment",
|
|
},
|
|
"Indented comment": {
|
|
input: "\t # comment",
|
|
},
|
|
"non-ignored value": {
|
|
input: `export OPTION_B='\n'`,
|
|
want: `export OPTION_B='\n'`,
|
|
},
|
|
}
|
|
|
|
for n, c := range cases {
|
|
t.Run(n, func(t *testing.T) {
|
|
got := string(getStatementStart([]byte(c.input)))
|
|
if got != c.want {
|
|
t.Errorf("Expected:\t %q\nGot:\t %q", c.want, got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestErrorReadDirectory(t *testing.T) {
|
|
envFileName := "fixtures/"
|
|
envMap, _, err := Read(envFileName)
|
|
|
|
if err == nil {
|
|
t.Errorf("Expected error, got %v", envMap)
|
|
}
|
|
}
|
|
|
|
func TestErrorParsing(t *testing.T) {
|
|
envFileName := "fixtures/invalid1.env"
|
|
envMap, _, err := Read(envFileName)
|
|
if err == nil {
|
|
t.Errorf("Expected error, got %v", envMap)
|
|
}
|
|
}
|
|
|
|
func TestComments(t *testing.T) {
|
|
envFileName := "fixtures/comments.env"
|
|
expectedValues := map[string]string{
|
|
"foo": "bar",
|
|
"bar": "foo#baz",
|
|
"baz": "foo",
|
|
}
|
|
|
|
loadEnvAndCompareValues(t, Load, envFileName, expectedValues, noopPresets)
|
|
}
|
|
|
|
func TestWrite(t *testing.T) {
|
|
writeAndCompare := func(env string, expected string) {
|
|
envMap, _, _ := Unmarshal(env)
|
|
actual, _ := Marshal(envMap)
|
|
if expected != actual {
|
|
t.Errorf("Expected '%v' (%v) to write as '%v', got '%v' instead.", env, envMap, expected, actual)
|
|
}
|
|
}
|
|
// just test some single lines to show the general idea
|
|
// TestRoundtrip makes most of the good assertions
|
|
|
|
// values are always double-quoted
|
|
writeAndCompare(`key=value`, `key="value"`)
|
|
// double-quotes are escaped
|
|
writeAndCompare(`key=va"lu"e`, `key="va\"lu\"e"`)
|
|
// but single quotes are left alone
|
|
writeAndCompare(`key=va'lu'e`, `key="va'lu'e"`)
|
|
// newlines, backslashes, and some other special chars are escaped
|
|
writeAndCompare(`foo="\n\r\\r!"`, `foo="\n\r\\r\!"`)
|
|
// lines should be sorted
|
|
writeAndCompare("foo=bar\nbaz=buzz", "baz=\"buzz\"\nfoo=\"bar\"")
|
|
// integers should not be quoted
|
|
writeAndCompare(`key="10"`, `key=10`)
|
|
}
|
|
|
|
func TestRoundtrip(t *testing.T) {
|
|
fixtures := []string{"equals.env", "exported.env", "plain.env", "quoted.env"}
|
|
for _, fixture := range fixtures {
|
|
fixtureFilename := fmt.Sprintf("fixtures/%s", fixture)
|
|
env, _, err := readFile(fixtureFilename)
|
|
if err != nil {
|
|
t.Errorf("Expected '%s' to read without error (%v)", fixtureFilename, err)
|
|
}
|
|
rep, err := Marshal(env)
|
|
if err != nil {
|
|
t.Errorf("Expected '%s' to Marshal (%v)", fixtureFilename, err)
|
|
}
|
|
roundtripped, _, err := Unmarshal(rep)
|
|
if err != nil {
|
|
t.Errorf("Expected '%s' to Mashal and Unmarshal (%v)", fixtureFilename, err)
|
|
}
|
|
if !reflect.DeepEqual(env, roundtripped) {
|
|
t.Errorf("Expected '%s' to roundtrip as '%v', got '%v' instead", fixtureFilename, env, roundtripped)
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
func TestTrailingNewlines(t *testing.T) {
|
|
cases := map[string]struct {
|
|
input string
|
|
key string
|
|
value string
|
|
}{
|
|
"Simple value without trailing newline": {
|
|
input: "KEY=value",
|
|
key: "KEY",
|
|
value: "value",
|
|
},
|
|
"Value with internal whitespace without trailing newline": {
|
|
input: "KEY=value value",
|
|
key: "KEY",
|
|
value: "value value",
|
|
},
|
|
"Value with internal whitespace with trailing newline": {
|
|
input: "KEY=value value\n",
|
|
key: "KEY",
|
|
value: "value value",
|
|
},
|
|
"YAML style - value with internal whitespace without trailing newline": {
|
|
input: "KEY: value value",
|
|
key: "KEY",
|
|
value: "value value",
|
|
},
|
|
"YAML style - value with internal whitespace with trailing newline": {
|
|
input: "KEY: value value\n",
|
|
key: "KEY",
|
|
value: "value value",
|
|
},
|
|
}
|
|
|
|
for n, c := range cases {
|
|
t.Run(n, func(t *testing.T) {
|
|
result, _, err := Unmarshal(c.input)
|
|
if err != nil {
|
|
t.Errorf("Input: %q Unexpected error:\t%q", c.input, err)
|
|
}
|
|
if result[c.key] != c.value {
|
|
t.Errorf("Input %q Expected:\t %q/%q\nGot:\t %q", c.input, c.key, c.value, result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestWhitespace(t *testing.T) {
|
|
cases := map[string]struct {
|
|
input string
|
|
key string
|
|
value string
|
|
}{
|
|
"Leading whitespace": {
|
|
input: " A=a\n",
|
|
key: "A",
|
|
value: "a",
|
|
},
|
|
"Leading tab": {
|
|
input: "\tA=a\n",
|
|
key: "A",
|
|
value: "a",
|
|
},
|
|
"Leading mixed whitespace": {
|
|
input: " \t \t\n\t \t A=a\n",
|
|
key: "A",
|
|
value: "a",
|
|
},
|
|
"Leading whitespace before export": {
|
|
input: " \t\t export A=a\n",
|
|
key: "A",
|
|
value: "a",
|
|
},
|
|
"Trailing whitespace": {
|
|
input: "A=a \t \t\n",
|
|
key: "A",
|
|
value: "a",
|
|
},
|
|
"Trailing whitespace with export": {
|
|
input: "export A=a\t \t \n",
|
|
key: "A",
|
|
value: "a",
|
|
},
|
|
"No EOL": {
|
|
input: "A=a",
|
|
key: "A",
|
|
value: "a",
|
|
},
|
|
"Trailing whitespace with no EOL": {
|
|
input: "A=a ",
|
|
key: "A",
|
|
value: "a",
|
|
},
|
|
}
|
|
|
|
for n, c := range cases {
|
|
t.Run(n, func(t *testing.T) {
|
|
result, _, err := Unmarshal(c.input)
|
|
if err != nil {
|
|
t.Errorf("Input: %q Unexpected error:\t%q", c.input, err)
|
|
}
|
|
if result[c.key] != c.value {
|
|
t.Errorf("Input %q Expected:\t %q/%q\nGot:\t %q", c.input, c.key, c.value, result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestModfiers(t *testing.T) {
|
|
cases := map[string]struct {
|
|
input string
|
|
key string
|
|
value string
|
|
modifiers map[string]string
|
|
}{
|
|
"No Modifier": {
|
|
input: "A=a",
|
|
key: "A",
|
|
value: "a",
|
|
},
|
|
"With comment": {
|
|
input: "A=a # my comment",
|
|
key: "A",
|
|
value: "a",
|
|
},
|
|
"With single modifier": {
|
|
input: "A=a # foo=bar",
|
|
key: "A",
|
|
value: "a",
|
|
modifiers: map[string]string{
|
|
"foo": "bar",
|
|
},
|
|
},
|
|
"With multiple modifiers": {
|
|
input: "A=a # foo=bar length=10",
|
|
key: "A",
|
|
value: "a",
|
|
modifiers: map[string]string{
|
|
"foo": "bar",
|
|
"length": "10",
|
|
},
|
|
},
|
|
"With quoted var": {
|
|
input: "A='a' # foo=bar",
|
|
key: "A",
|
|
value: "a",
|
|
modifiers: map[string]string{
|
|
"foo": "bar",
|
|
},
|
|
},
|
|
"With quoted var 2": {
|
|
input: "A='a' # foo=bar\nB=b",
|
|
key: "A",
|
|
value: "a",
|
|
modifiers: map[string]string{
|
|
"foo": "bar",
|
|
},
|
|
},
|
|
}
|
|
|
|
for n, c := range cases {
|
|
t.Run(n, func(t *testing.T) {
|
|
values, modifiers, err := Unmarshal(c.input)
|
|
if err != nil {
|
|
t.Errorf("Input: %q Unexpected error:\t%q", c.input, err)
|
|
}
|
|
if values[c.key] != c.value {
|
|
t.Errorf("Input %q Expected:\t %q/%q\nGot:\t %q", c.input, c.key, c.value, values)
|
|
}
|
|
|
|
if modifiers[c.key] == nil && c.modifiers != nil {
|
|
t.Errorf("Input %q Expected modifiers\n Got: none", c.input)
|
|
} else {
|
|
for k, v := range c.modifiers {
|
|
if modifiers[c.key][k] != v {
|
|
t.Errorf("Input %q Expected modifier %s=%s\n Got: %s=%s", c.input, k, v, k, modifiers[c.key][k])
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|