fix: cnf make cnf/* configuration formats more strict and not to include themselves (to really find out the good format) | add tests | TODO: json is a subset of yaml, create my own YAML parser that is strict to the 'well-known' YAML syntax
This commit is contained in:
parent
b26dec8576
commit
bf391fd5a0
|
@ -16,8 +16,15 @@ type ini struct {
|
|||
// ReadFrom implements io.ReaderFrom
|
||||
func (d *ini) ReadFrom(_reader io.Reader) (int64, error) {
|
||||
|
||||
// disallow "key: value"
|
||||
// when trying to find out the format from content
|
||||
// and avoids conflicts with YAML
|
||||
opts := lib.LoadOptions{
|
||||
KeyValueDelimiters: "=",
|
||||
}
|
||||
|
||||
file, err := lib.LoadSources(opts, ioutil.NopCloser(_reader))
|
||||
// 1. get json decoder
|
||||
file, err := lib.Load(ioutil.NopCloser(_reader))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package cnf
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
@ -13,39 +12,37 @@ var ErrUnknownExtension = errors.New("unknown extension format")
|
|||
// ErrUnknownFormat is raised when the format cannot be guessed from the content of the file
|
||||
var ErrUnknownFormat = errors.New("cannot infer format from content")
|
||||
|
||||
// ErrUnknownFormat is raised when required file does not exist
|
||||
var ErrFileNotExist = errors.New("cannot find file")
|
||||
|
||||
// Load the current file and create the configuration format accordingly
|
||||
func Load(path string) (ConfigurationFormat, error) {
|
||||
|
||||
var confFormat ConfigurationFormat
|
||||
|
||||
// 1. check file
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
return nil, fmt.Errorf("cannot find file '%s'", path)
|
||||
return nil, ErrFileNotExist
|
||||
}
|
||||
|
||||
// 2. Try to load from extension
|
||||
extension := filepath.Ext(path)
|
||||
if len(extension) > 0 {
|
||||
|
||||
confFormat = loadFromExtension(extension)
|
||||
if confFormat == nil {
|
||||
return nil, ErrUnknownExtension
|
||||
}
|
||||
if confFormat := loadFromExtension(extension); confFormat != nil {
|
||||
|
||||
// open file
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
// open file
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// parse
|
||||
_, err = confFormat.ReadFrom(file)
|
||||
if err == nil {
|
||||
return confFormat, nil
|
||||
}
|
||||
// parse
|
||||
_, err = confFormat.ReadFrom(file)
|
||||
file.Close()
|
||||
if err == nil {
|
||||
return confFormat, nil
|
||||
}
|
||||
|
||||
// return nil, fmt.Errorf("cannot parse file as '%s' | %s", extension, err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -89,7 +86,7 @@ func loadFromContent(path string) (ConfigurationFormat, error) {
|
|||
defer file.Close()
|
||||
|
||||
// extensions ordered by strictness of the language's syntax
|
||||
extensions := []string{".json", ".nginx", ".yaml", ".ini"}
|
||||
extensions := []string{".json", ".nginx", ".ini", ".yaml"}
|
||||
|
||||
// try to load each available extension
|
||||
for _, ext := range extensions {
|
||||
|
|
|
@ -0,0 +1,250 @@
|
|||
package cnf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLoadFileNotExist(t *testing.T) {
|
||||
|
||||
_, err := Load("/tmp/not-existing/file")
|
||||
if err == nil {
|
||||
t.Fatalf("expected error")
|
||||
}
|
||||
if err != ErrFileNotExist {
|
||||
t.Fatalf("expected error <%s>, got <%s>", ErrFileNotExist, err)
|
||||
}
|
||||
|
||||
}
|
||||
func TestLoadFromExtensionWithContentMatch(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
File string
|
||||
Format ConfigurationFormat
|
||||
Content string
|
||||
Field string
|
||||
}{
|
||||
|
||||
// key = value
|
||||
{"/tmp/load-test.json", new(json), `{ "key": "value" }`, "key"},
|
||||
{"/tmp/load-test.nginx", new(nginx), "key value;", "key"},
|
||||
{"/tmp/load-test.ini", new(ini), `key = value`, "key"},
|
||||
{"/tmp/load-test.yaml", new(yaml), "key: value", "key"},
|
||||
|
||||
// parent.key = value
|
||||
{"/tmp/load-test.json", new(json), `{ "parent": { "key": "value" } }`, "parent.key"},
|
||||
{"/tmp/load-test.nginx", new(nginx), "parent {\nkey value;\n}", "parent.key"},
|
||||
{"/tmp/load-test.ini", new(ini), "[parent]\nkey = value", "parent.key"},
|
||||
{"/tmp/load-test.yaml", new(yaml), "parent:\n key: value", "parent.key"},
|
||||
|
||||
// comments
|
||||
// {"/tmp/load-test.json", new(json), "{ \"parent\": { \"key\": \"value\" } }", "parent.key"},
|
||||
{"/tmp/load-test.nginx", new(nginx), ";comment\nparent {\nkey value;\n}", "parent.key"},
|
||||
{"/tmp/load-test.nginx", new(nginx), "#comment\nparent {\nkey value;\n}", "parent.key"},
|
||||
{"/tmp/load-test.ini", new(ini), ";comment\n[parent]\nkey = value", "parent.key"},
|
||||
{"/tmp/load-test.ini", new(ini), "#comment\n[parent]\nkey = value", "parent.key"},
|
||||
{"/tmp/load-test.yaml", new(yaml), "#comment\nparent:\n key: value", "parent.key"},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
|
||||
f, err := os.Create(test.File)
|
||||
if err != nil {
|
||||
t.Errorf("[%d] cannot create file '%s'", i, test.File)
|
||||
continue
|
||||
}
|
||||
f.Write([]byte(test.Content))
|
||||
f.Close()
|
||||
|
||||
format, err := Load(test.File)
|
||||
if err != nil {
|
||||
t.Errorf("[%d] unexpected error <%s>", i, err)
|
||||
continue
|
||||
}
|
||||
if format == nil {
|
||||
t.Errorf("[%d] expected format not to be null", i)
|
||||
continue
|
||||
}
|
||||
|
||||
// check type
|
||||
have, want := fmt.Sprintf("%T", format), fmt.Sprintf("%T", test.Format)
|
||||
if have != want {
|
||||
t.Errorf("[%d] expected format <%s>, got <%s>", i, want, have)
|
||||
continue
|
||||
}
|
||||
|
||||
// try to get value
|
||||
val, found := format.Get(test.Field)
|
||||
if !found {
|
||||
t.Errorf("[%d] expected to find '%s'", i, test.Field)
|
||||
continue
|
||||
}
|
||||
|
||||
if val != "value" {
|
||||
t.Errorf("[%d] expected value '%s', got '%s'", i, "value", val)
|
||||
continue
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
os.RemoveAll(test.File)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestLoadFromExtensionWithContentNotMatch(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
File string
|
||||
Format ConfigurationFormat
|
||||
Content string
|
||||
Field string
|
||||
}{
|
||||
|
||||
// [JSON] key = value
|
||||
{"/tmp/load-test.json", new(json), `{ "key": "value" }`, "key"},
|
||||
{"/tmp/load-test.nginx", new(json), `{ "key": "value" }`, "key"},
|
||||
{"/tmp/load-test.ini", new(json), `{ "key": "value" }`, "key"},
|
||||
// {"/tmp/load-test.yaml", new(json), `{ "key": "value" }`, "key"},
|
||||
|
||||
// [NGINX] key = value
|
||||
{"/tmp/load-test.json", new(nginx), "key value;", "key"},
|
||||
{"/tmp/load-test.nginx", new(nginx), "key value;", "key"},
|
||||
{"/tmp/load-test.ini", new(nginx), "key value;", "key"},
|
||||
// {"/tmp/load-test.yaml", new(nginx), "key value;", "key"},
|
||||
|
||||
// [INI] key = value
|
||||
{"/tmp/load-test.json", new(ini), `key = value`, "key"},
|
||||
{"/tmp/load-test.nginx", new(ini), `key = value`, "key"},
|
||||
{"/tmp/load-test.ini", new(ini), `key = value`, "key"},
|
||||
// {"/tmp/load-test.yaml", new(ini), `key = value`, "key"},
|
||||
|
||||
// [YAML] key = value
|
||||
{"/tmp/load-test.json", new(yaml), "key: value", "key"},
|
||||
{"/tmp/load-test.nginx", new(yaml), "key: value", "key"},
|
||||
{"/tmp/load-test.yaml", new(yaml), "key: value", "key"},
|
||||
{"/tmp/load-test.yaml", new(yaml), "key: value", "key"},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
|
||||
f, err := os.Create(test.File)
|
||||
if err != nil {
|
||||
t.Errorf("[%d] cannot create file '%s'", i, test.File)
|
||||
continue
|
||||
}
|
||||
f.Write([]byte(test.Content))
|
||||
f.Close()
|
||||
|
||||
format, err := Load(test.File)
|
||||
if err != nil {
|
||||
t.Errorf("[%d] unexpected error <%s>", i, err)
|
||||
continue
|
||||
}
|
||||
if format == nil {
|
||||
t.Errorf("[%d] expected format not to be null", i)
|
||||
continue
|
||||
}
|
||||
|
||||
// check type
|
||||
have, want := fmt.Sprintf("%T", format), fmt.Sprintf("%T", test.Format)
|
||||
if have != want {
|
||||
t.Errorf("[%d] expected format <%s>, got <%s>", i, want, have)
|
||||
continue
|
||||
}
|
||||
|
||||
// try to get value
|
||||
val, found := format.Get(test.Field)
|
||||
if !found {
|
||||
t.Errorf("[%d] expected to find '%s'", i, test.Field)
|
||||
continue
|
||||
}
|
||||
|
||||
if val != "value" {
|
||||
t.Errorf("[%d] expected value '%s', got '%s'", i, "value", val)
|
||||
continue
|
||||
}
|
||||
|
||||
}
|
||||
for _, test := range tests {
|
||||
os.RemoveAll(test.File)
|
||||
}
|
||||
|
||||
}
|
||||
func TestLoadContent(t *testing.T) {
|
||||
|
||||
file := "/tmp/no-extension-file"
|
||||
defer os.RemoveAll(file)
|
||||
|
||||
tests := []struct {
|
||||
Format ConfigurationFormat
|
||||
Content string
|
||||
Field string
|
||||
}{
|
||||
|
||||
// key = value
|
||||
{new(json), `{ "key": "value" }`, "key"},
|
||||
{new(nginx), "key value;", "key"},
|
||||
{new(ini), `key = value`, "key"},
|
||||
{new(yaml), "key: value", "key"},
|
||||
|
||||
// parent.key = value
|
||||
{new(json), `{ "parent": { "key": "value" } }`, "parent.key"},
|
||||
{new(nginx), "parent {\nkey value;\n}", "parent.key"},
|
||||
{new(ini), "[parent]\nkey = value", "parent.key"},
|
||||
{new(yaml), "parent:\n key: value", "parent.key"},
|
||||
|
||||
// comments
|
||||
{new(json), "{ \"parent\": { \"key\": \"value\" } }", "parent.key"},
|
||||
{new(nginx), ";comment\nparent {\nkey value;\n}", "parent.key"},
|
||||
{new(nginx), "#comment\nparent {\nkey value;\n}", "parent.key"},
|
||||
{new(ini), ";comment\n[parent]\nkey = value", "parent.key"},
|
||||
{new(ini), "#comment\n[parent]\nkey = value", "parent.key"},
|
||||
{new(yaml), "#comment\nparent:\n key: value", "parent.key"},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
|
||||
os.RemoveAll(file)
|
||||
f, err := os.Create(file)
|
||||
if err != nil {
|
||||
t.Errorf("[%d] cannot create file '%s'", i, file)
|
||||
continue
|
||||
}
|
||||
f.Write([]byte(test.Content))
|
||||
f.Close()
|
||||
|
||||
format, err := Load(file)
|
||||
if err != nil {
|
||||
t.Errorf("[%d] unexpected error <%s>", i, err)
|
||||
continue
|
||||
}
|
||||
if format == nil {
|
||||
t.Errorf("[%d] expected format not to be null", i)
|
||||
continue
|
||||
}
|
||||
|
||||
// check type
|
||||
have, want := fmt.Sprintf("%T", format), fmt.Sprintf("%T", test.Format)
|
||||
if have != want {
|
||||
t.Errorf("[%d] expected format <%s>, got <%s>", i, want, have)
|
||||
continue
|
||||
}
|
||||
|
||||
// try to get value
|
||||
val, found := format.Get(test.Field)
|
||||
if !found {
|
||||
t.Errorf("[%d] expected to find '%s'", i, test.Field)
|
||||
continue
|
||||
}
|
||||
|
||||
if val != "value" {
|
||||
t.Errorf("[%d] expected value '%s', got '%s'", i, "value", val)
|
||||
continue
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue