Files
nats-server/conf/parse_test.go
Waldemar Quevedo ba596f7de1 config: make parsing configurations without usable values invalid
This makes configuration files that are empty, or read and processed
by the parser but with no detected values now return an error.

Signed-off-by: Waldemar Quevedo <wally@nats.io>
2023-08-01 21:55:09 -07:00

433 lines
9.4 KiB
Go
Raw Blame History

package conf
import (
"fmt"
"os"
"reflect"
"strings"
"testing"
"time"
)
// Test to make sure we get what we expect.
func test(t *testing.T, data string, ex map[string]interface{}) {
t.Helper()
m, err := Parse(data)
if err != nil {
t.Fatalf("Received err: %v\n", err)
}
if m == nil {
t.Fatal("Received nil map")
}
if !reflect.DeepEqual(m, ex) {
t.Fatalf("Not Equal:\nReceived: '%+v'\nExpected: '%+v'\n", m, ex)
}
}
func TestSimpleTopLevel(t *testing.T) {
ex := map[string]interface{}{
"foo": "1",
"bar": float64(2.2),
"baz": true,
"boo": int64(22),
}
test(t, "foo='1'; bar=2.2; baz=true; boo=22", ex)
}
func TestBools(t *testing.T) {
ex := map[string]interface{}{
"foo": true,
}
test(t, "foo=true", ex)
test(t, "foo=TRUE", ex)
test(t, "foo=true", ex)
test(t, "foo=yes", ex)
test(t, "foo=on", ex)
}
var varSample = `
index = 22
foo = $index
`
func TestSimpleVariable(t *testing.T) {
ex := map[string]interface{}{
"index": int64(22),
"foo": int64(22),
}
test(t, varSample, ex)
}
var varNestedSample = `
index = 22
nest {
index = 11
foo = $index
}
bar = $index
`
func TestNestedVariable(t *testing.T) {
ex := map[string]interface{}{
"index": int64(22),
"nest": map[string]interface{}{
"index": int64(11),
"foo": int64(11),
},
"bar": int64(22),
}
test(t, varNestedSample, ex)
}
func TestMissingVariable(t *testing.T) {
_, err := Parse("foo=$index")
if err == nil {
t.Fatalf("Expected an error for a missing variable, got none")
}
if !strings.HasPrefix(err.Error(), "variable reference") {
t.Fatalf("Wanted a variable reference err, got %q\n", err)
}
}
func TestEnvVariable(t *testing.T) {
ex := map[string]interface{}{
"foo": int64(22),
}
evar := "__UNIQ22__"
os.Setenv(evar, "22")
defer os.Unsetenv(evar)
test(t, fmt.Sprintf("foo = $%s", evar), ex)
}
func TestEnvVariableString(t *testing.T) {
ex := map[string]interface{}{
"foo": "xyz",
}
evar := "__UNIQ22__"
os.Setenv(evar, "xyz")
defer os.Unsetenv(evar)
test(t, fmt.Sprintf("foo = $%s", evar), ex)
}
func TestEnvVariableStringStartingWithNumber(t *testing.T) {
evar := "__UNIQ22__"
os.Setenv(evar, "3xyz")
defer os.Unsetenv(evar)
_, err := Parse("foo = $%s")
if err == nil {
t.Fatalf("Expected err not being able to process string: %v\n", err)
}
}
func TestEnvVariableStringStartingWithNumberAndSizeUnit(t *testing.T) {
ex := map[string]interface{}{
"foo": "3Gyz",
}
evar := "__UNIQ22__"
os.Setenv(evar, "3Gyz")
defer os.Unsetenv(evar)
test(t, fmt.Sprintf("foo = $%s", evar), ex)
}
func TestEnvVariableStringStartingWithNumberUsingQuotes(t *testing.T) {
ex := map[string]interface{}{
"foo": "3xyz",
}
evar := "__UNIQ22__"
os.Setenv(evar, "'3xyz'")
defer os.Unsetenv(evar)
test(t, fmt.Sprintf("foo = $%s", evar), ex)
}
func TestBcryptVariable(t *testing.T) {
ex := map[string]interface{}{
"password": "$2a$11$ooo",
}
test(t, "password: $2a$11$ooo", ex)
}
var easynum = `
k = 8k
kb = 4kb
ki = 3ki
kib = 4ki
m = 1m
mb = 2MB
mi = 2Mi
mib = 64MiB
g = 2g
gb = 22GB
gi = 22Gi
gib = 22GiB
tb = 22TB
ti = 22Ti
tib = 22TiB
pb = 22PB
pi = 22Pi
pib = 22PiB
`
func TestConvenientNumbers(t *testing.T) {
ex := map[string]interface{}{
"k": int64(8 * 1000),
"kb": int64(4 * 1024),
"ki": int64(3 * 1024),
"kib": int64(4 * 1024),
"m": int64(1000 * 1000),
"mb": int64(2 * 1024 * 1024),
"mi": int64(2 * 1024 * 1024),
"mib": int64(64 * 1024 * 1024),
"g": int64(2 * 1000 * 1000 * 1000),
"gb": int64(22 * 1024 * 1024 * 1024),
"gi": int64(22 * 1024 * 1024 * 1024),
"gib": int64(22 * 1024 * 1024 * 1024),
"tb": int64(22 * 1024 * 1024 * 1024 * 1024),
"ti": int64(22 * 1024 * 1024 * 1024 * 1024),
"tib": int64(22 * 1024 * 1024 * 1024 * 1024),
"pb": int64(22 * 1024 * 1024 * 1024 * 1024 * 1024),
"pi": int64(22 * 1024 * 1024 * 1024 * 1024 * 1024),
"pib": int64(22 * 1024 * 1024 * 1024 * 1024 * 1024),
}
test(t, easynum, ex)
}
var sample1 = `
foo {
host {
ip = '127.0.0.1'
port = 4242
}
servers = [ "a.com", "b.com", "c.com"]
}
`
func TestSample1(t *testing.T) {
ex := map[string]interface{}{
"foo": map[string]interface{}{
"host": map[string]interface{}{
"ip": "127.0.0.1",
"port": int64(4242),
},
"servers": []interface{}{"a.com", "b.com", "c.com"},
},
}
test(t, sample1, ex)
}
var cluster = `
cluster {
port: 4244
authorization {
user: route_user
password: top_secret
timeout: 1
}
# Routes are actively solicited and connected to from this server.
# Other servers can connect to us if they supply the correct credentials
# in their routes definitions from above.
// Test both styles of comments
routes = [
nats-route://foo:bar@apcera.me:4245
nats-route://foo:bar@apcera.me:4246
]
}
`
func TestSample2(t *testing.T) {
ex := map[string]interface{}{
"cluster": map[string]interface{}{
"port": int64(4244),
"authorization": map[string]interface{}{
"user": "route_user",
"password": "top_secret",
"timeout": int64(1),
},
"routes": []interface{}{
"nats-route://foo:bar@apcera.me:4245",
"nats-route://foo:bar@apcera.me:4246",
},
},
}
test(t, cluster, ex)
}
var sample3 = `
foo {
expr = '(true == "false")'
text = 'This is a multi-line
text block.'
}
`
func TestSample3(t *testing.T) {
ex := map[string]interface{}{
"foo": map[string]interface{}{
"expr": "(true == \"false\")",
"text": "This is a multi-line\ntext block.",
},
}
test(t, sample3, ex)
}
var sample4 = `
array [
{ abc: 123 }
{ xyz: "word" }
]
`
func TestSample4(t *testing.T) {
ex := map[string]interface{}{
"array": []interface{}{
map[string]interface{}{"abc": int64(123)},
map[string]interface{}{"xyz": "word"},
},
}
test(t, sample4, ex)
}
var sample5 = `
now = 2016-05-04T18:53:41Z
gmt = false
`
func TestSample5(t *testing.T) {
dt, _ := time.Parse("2006-01-02T15:04:05Z", "2016-05-04T18:53:41Z")
ex := map[string]interface{}{
"now": dt,
"gmt": false,
}
test(t, sample5, ex)
}
func TestIncludes(t *testing.T) {
ex := map[string]interface{}{
"listen": "127.0.0.1:4222",
"authorization": map[string]interface{}{
"ALICE_PASS": "$2a$10$UHR6GhotWhpLsKtVP0/i6.Nh9.fuY73cWjLoJjb2sKT8KISBcUW5q",
"BOB_PASS": "$2a$11$dZM98SpGeI7dCFFGSpt.JObQcix8YHml4TBUZoge9R1uxnMIln5ly",
"users": []interface{}{
map[string]interface{}{
"user": "alice",
"password": "$2a$10$UHR6GhotWhpLsKtVP0/i6.Nh9.fuY73cWjLoJjb2sKT8KISBcUW5q"},
map[string]interface{}{
"user": "bob",
"password": "$2a$11$dZM98SpGeI7dCFFGSpt.JObQcix8YHml4TBUZoge9R1uxnMIln5ly"},
},
"timeout": float64(0.5),
},
}
m, err := ParseFile("simple.conf")
if err != nil {
t.Fatalf("Received err: %v\n", err)
}
if m == nil {
t.Fatal("Received nil map")
}
if !reflect.DeepEqual(m, ex) {
t.Fatalf("Not Equal:\nReceived: '%+v'\nExpected: '%+v'\n", m, ex)
}
}
var varIncludedVariablesSample = `
authorization {
include "./includes/passwords.conf"
CAROL_PASS: foo
users = [
{user: alice, password: $ALICE_PASS}
{user: bob, password: $BOB_PASS}
{user: carol, password: $CAROL_PASS}
]
}
`
func TestIncludeVariablesWithChecks(t *testing.T) {
p, err := parse(varIncludedVariablesSample, "", true)
if err != nil {
t.Fatalf("Received err: %v\n", err)
}
key := "authorization"
m, ok := p.mapping[key]
if !ok {
t.Errorf("Expected %q to be in the config", key)
}
expectKeyVal := func(t *testing.T, m interface{}, expectedKey string, expectedVal string, expectedLine, expectedPos int) {
t.Helper()
tk := m.(*token)
v := tk.Value()
vv := v.(map[string]interface{})
value, ok := vv[expectedKey]
if !ok {
t.Errorf("Expected key %q", expectedKey)
}
tk, ok = value.(*token)
if !ok {
t.Fatalf("Expected token %v", value)
}
if tk.Line() != expectedLine {
t.Errorf("Expected token to be at line %d, got: %d", expectedLine, tk.Line())
}
if tk.Position() != expectedPos {
t.Errorf("Expected token to be at position %d, got: %d", expectedPos, tk.Position())
}
v = tk.Value()
if v != expectedVal {
t.Errorf("Expected %q, got: %s", expectedVal, v)
}
}
expectKeyVal(t, m, "ALICE_PASS", "$2a$10$UHR6GhotWhpLsKtVP0/i6.Nh9.fuY73cWjLoJjb2sKT8KISBcUW5q", 2, 1)
expectKeyVal(t, m, "BOB_PASS", "$2a$11$dZM98SpGeI7dCFFGSpt.JObQcix8YHml4TBUZoge9R1uxnMIln5ly", 3, 1)
expectKeyVal(t, m, "CAROL_PASS", "foo", 6, 3)
}
func TestParserNoInfiniteLoop(t *testing.T) {
for _, test := range []string{`A@@Føøøø?˛ø:{øøøø˙˙`, `include "9/<2F>`} {
if _, err := Parse(test); err == nil {
t.Fatal("expected an error")
} else if !strings.Contains(err.Error(), "Unexpected EOF") {
t.Fatal("expected unexpected eof error")
}
}
}
func TestParseWithNoValues(t *testing.T) {
for _, test := range []string{
``,
`aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa`,
` aaaaaaaaaaaaaaaaaaaaaaaaaaa`,
` aaaaaaaaaaaaaaaaaaaaaaaaaaa `,
`
# just comments with no values
# is also invalid.
`,
`
# with comments and no spaces to create key values
# is also an invalid config.
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
`,
`
a,a,a,a,a,a,a,a,a,a,a
`,
} {
if _, err := parse(test, "", true); err == nil {
t.Fatal("expected an error")
} else if !strings.Contains(err.Error(), "config has no values or is empty") {
t.Fatal("expected invalid conf error")
}
}
}