You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
minio/pkg/quick/quick_test.go

537 lines
12 KiB

/*
* Quick - Quick key value store for config files and persistent state files
*
* Quick (C) 2015, 2016, 2017 MinIO, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package quick
import (
"bytes"
"encoding/json"
"io/ioutil"
"os"
"reflect"
"runtime"
"strings"
"testing"
"github.com/tidwall/gjson"
)
func TestReadVersion(t *testing.T) {
type myStruct struct {
Version string
}
saveMe := myStruct{"1"}
config, err := NewConfig(&saveMe, nil)
if err != nil {
t.Fatal(err)
}
err = config.Save("test.json")
if err != nil {
t.Fatal(err)
}
version, err := GetVersion("test.json", nil)
if err != nil {
t.Fatal(err)
}
if version != "1" {
t.Fatalf("Expected version '1', got '%v'", version)
}
}
func TestReadVersionErr(t *testing.T) {
type myStruct struct {
Version int
}
saveMe := myStruct{1}
_, err := NewConfig(&saveMe, nil)
if err == nil {
t.Fatal("Unexpected should fail in initialization for bad input")
}
err = ioutil.WriteFile("test.json", []byte("{ \"version\":2,"), 0644)
if err != nil {
t.Fatal(err)
}
_, err = GetVersion("test.json", nil)
if err == nil {
t.Fatal("Unexpected should fail to fetch version")
}
err = ioutil.WriteFile("test.json", []byte("{ \"version\":2 }"), 0644)
if err != nil {
t.Fatal(err)
}
_, err = GetVersion("test.json", nil)
if err == nil {
t.Fatal("Unexpected should fail to fetch version")
}
}
func TestSaveFailOnDir(t *testing.T) {
defer os.RemoveAll("test-1.json")
err := os.MkdirAll("test-1.json", 0644)
if err != nil {
t.Fatal(err)
}
type myStruct struct {
Version string
}
saveMe := myStruct{"1"}
config, err := NewConfig(&saveMe, nil)
if err != nil {
t.Fatal(err)
}
err = config.Save("test-1.json")
if err == nil {
t.Fatal("Unexpected should fail to save if test-1.json is a directory")
}
}
func TestCheckData(t *testing.T) {
err := CheckData(nil)
if err == nil {
t.Fatal("Unexpected should fail")
}
type myStructBadNoVersion struct {
User string
Password string
Directories []string
}
saveMeBadNoVersion := myStructBadNoVersion{"guest", "nopassword", []string{"Work", "Documents", "Music"}}
err = CheckData(&saveMeBadNoVersion)
if err == nil {
t.Fatal("Unexpected should fail if Version is not set")
}
type myStructBadVersionInt struct {
Version int
User string
Password string
}
saveMeBadVersionInt := myStructBadVersionInt{1, "guest", "nopassword"}
err = CheckData(&saveMeBadVersionInt)
if err == nil {
t.Fatal("Unexpected should fail if Version is integer")
}
type myStructGood struct {
Version string
User string
Password string
Directories []string
}
saveMeGood := myStructGood{"1", "guest", "nopassword", []string{"Work", "Documents", "Music"}}
err = CheckData(&saveMeGood)
if err != nil {
t.Fatal(err)
}
}
func TestLoadFile(t *testing.T) {
type myStruct struct {
Version string
User string
Password string
Directories []string
}
saveMe := myStruct{}
_, err := LoadConfig("test.json", nil, &saveMe)
if err == nil {
t.Fatal(err)
}
file, err := os.Create("test.json")
if err != nil {
t.Fatal(err)
}
if err = file.Close(); err != nil {
t.Fatal(err)
}
_, err = LoadConfig("test.json", nil, &saveMe)
if err == nil {
t.Fatal("Unexpected should fail to load empty JSON")
}
config, err := NewConfig(&saveMe, nil)
if err != nil {
t.Fatal(err)
}
err = config.Load("test-non-exist.json")
if err == nil {
t.Fatal("Unexpected should fail to Load non-existent config")
}
err = config.Load("test.json")
if err == nil {
t.Fatal("Unexpected should fail to load empty JSON")
}
saveMe = myStruct{"1", "guest", "nopassword", []string{"Work", "Documents", "Music"}}
config, err = NewConfig(&saveMe, nil)
if err != nil {
t.Fatal(err)
}
err = config.Save("test.json")
if err != nil {
t.Fatal(err)
}
saveMe1 := myStruct{}
_, err = LoadConfig("test.json", nil, &saveMe1)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(saveMe1, saveMe) {
t.Fatalf("Expected %v, got %v", saveMe1, saveMe)
}
saveMe2 := myStruct{}
err = json.Unmarshal([]byte(config.String()), &saveMe2)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(saveMe2, saveMe1) {
t.Fatalf("Expected %v, got %v", saveMe2, saveMe1)
}
}
func TestYAMLFormat(t *testing.T) {
testYAML := "test.yaml"
defer os.RemoveAll(testYAML)
type myStruct struct {
Version string
User string
Password string
Directories []string
}
plainYAML := `version: "1"
user: guest
password: nopassword
directories:
- Work
- Documents
- Music
`
if runtime.GOOS == "windows" {
plainYAML = strings.Replace(plainYAML, "\n", "\r\n", -1)
}
saveMe := myStruct{"1", "guest", "nopassword", []string{"Work", "Documents", "Music"}}
// Save format using
config, err := NewConfig(&saveMe, nil)
if err != nil {
t.Fatal(err)
}
err = config.Save(testYAML)
if err != nil {
t.Fatal(err)
}
// Check if the saved structure in actually an YAML format
b, err := ioutil.ReadFile(testYAML)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal([]byte(plainYAML), b) {
t.Fatalf("Expected %v, got %v", plainYAML, string(b))
}
// Check if the loaded data is the same as the saved one
loadMe := myStruct{}
config, err = NewConfig(&loadMe, nil)
if err != nil {
t.Fatal(err)
}
err = config.Load(testYAML)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(saveMe, loadMe) {
t.Fatalf("Expected %v, got %v", saveMe, loadMe)
}
}
func TestJSONFormat(t *testing.T) {
testJSON := "test.json"
defer os.RemoveAll(testJSON)
type myStruct struct {
Version string
User string
Password string
Directories []string
}
plainJSON := `{
"Version": "1",
"User": "guest",
"Password": "nopassword",
"Directories": [
"Work",
"Documents",
"Music"
]
}`
if runtime.GOOS == "windows" {
plainJSON = strings.Replace(plainJSON, "\n", "\r\n", -1)
}
saveMe := myStruct{"1", "guest", "nopassword", []string{"Work", "Documents", "Music"}}
// Save format using
config, err := NewConfig(&saveMe, nil)
if err != nil {
t.Fatal(err)
}
err = config.Save(testJSON)
if err != nil {
t.Fatal(err)
}
// Check if the saved structure in actually an JSON format
b, err := ioutil.ReadFile(testJSON)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal([]byte(plainJSON), b) {
t.Fatalf("Expected %v, got %v", plainJSON, string(b))
}
// Check if the loaded data is the same as the saved one
loadMe := myStruct{}
config, err = NewConfig(&loadMe, nil)
if err != nil {
t.Fatal(err)
}
err = config.Load(testJSON)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(saveMe, loadMe) {
t.Fatalf("Expected %v, got %v", saveMe, loadMe)
}
}
func TestSaveLoad(t *testing.T) {
defer os.RemoveAll("test.json")
type myStruct struct {
Version string
User string
Password string
Directories []string
}
saveMe := myStruct{"1", "guest", "nopassword", []string{"Work", "Documents", "Music"}}
config, err := NewConfig(&saveMe, nil)
if err != nil {
t.Fatal(err)
}
err = config.Save("test.json")
if err != nil {
t.Fatal(err)
}
loadMe := myStruct{Version: "1"}
newConfig, err := NewConfig(&loadMe, nil)
if err != nil {
t.Fatal(err)
}
err = newConfig.Load("test.json")
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(config.Data(), newConfig.Data()) {
t.Fatalf("Expected %v, got %v", config.Data(), newConfig.Data())
}
if !reflect.DeepEqual(config.Data(), &loadMe) {
t.Fatalf("Expected %v, got %v", config.Data(), &loadMe)
}
mismatch := myStruct{"1.1", "guest", "nopassword", []string{"Work", "Documents", "Music"}}
if reflect.DeepEqual(config.Data(), &mismatch) {
t.Fatal("Expected to mismatch but succeeded instead")
}
}
func TestSaveBackup(t *testing.T) {
defer os.RemoveAll("test.json")
defer os.RemoveAll("test.json.old")
type myStruct struct {
Version string
User string
Password string
Directories []string
}
saveMe := myStruct{"1", "guest", "nopassword", []string{"Work", "Documents", "Music"}}
config, err := NewConfig(&saveMe, nil)
if err != nil {
t.Fatal(err)
}
err = config.Save("test.json")
if err != nil {
t.Fatal(err)
}
loadMe := myStruct{Version: "1"}
newConfig, err := NewConfig(&loadMe, nil)
if err != nil {
t.Fatal(err)
}
err = newConfig.Load("test.json")
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(config.Data(), newConfig.Data()) {
t.Fatalf("Expected %v, got %v", config.Data(), newConfig.Data())
}
if !reflect.DeepEqual(config.Data(), &loadMe) {
t.Fatalf("Expected %v, got %v", config.Data(), &loadMe)
}
mismatch := myStruct{"1.1", "guest", "nopassword", []string{"Work", "Documents", "Music"}}
if reflect.DeepEqual(newConfig.Data(), &mismatch) {
t.Fatal("Expected to mismatch but succeeded instead")
}
config, err = NewConfig(&mismatch, nil)
if err != nil {
t.Fatal(err)
}
err = config.Save("test.json")
if err != nil {
t.Fatal(err)
}
}
func TestDiff(t *testing.T) {
type myStruct struct {
Version string
User string
Password string
Directories []string
}
saveMe := myStruct{"1", "guest", "nopassword", []string{"Work", "Documents", "Music"}}
config, err := NewConfig(&saveMe, nil)
if err != nil {
t.Fatal(err)
}
type myNewConfigStruct struct {
Version string
// User string
Password string
Directories []string
}
mismatch := myNewConfigStruct{"1", "nopassword", []string{"Work", "documents", "Music"}}
newConfig, err := NewConfig(&mismatch, nil)
if err != nil {
t.Fatal(err)
}
fields, err := config.Diff(newConfig)
if err != nil {
t.Fatal(err)
}
if len(fields) != 1 {
t.Fatalf("Expected len 1, got %v", len(fields))
}
// Uncomment for debugging
// for i, field := range fields {
// fmt.Printf("Diff[%d]: %s=%v\n", i, field.Name(), field.Value())
// }
}
func TestDeepDiff(t *testing.T) {
type myStruct struct {
Version string
User string
Password string
Directories []string
}
saveMe := myStruct{"1", "guest", "nopassword", []string{"Work", "Documents", "Music"}}
config, err := NewConfig(&saveMe, nil)
if err != nil {
t.Fatal(err)
}
mismatch := myStruct{"1", "Guest", "nopassword", []string{"Work", "documents", "Music"}}
newConfig, err := NewConfig(&mismatch, nil)
if err != nil {
t.Fatal(err)
}
fields, err := config.DeepDiff(newConfig)
if err != nil {
t.Fatal(err)
}
if len(fields) != 2 {
t.Fatalf("Expected len 2, got %v", len(fields))
}
// Uncomment for debugging
// for i, field := range fields {
// fmt.Printf("DeepDiff[%d]: %s=%v\n", i, field.Name(), field.Value())
// }
}
func TestCheckDupJSONKeys(t *testing.T) {
testCases := []struct {
json string
shouldPass bool
}{
{`{}`, true},
{`{"version" : "13"}`, true},
{`{"version" : "13", "version": "14"}`, false},
{`{"version" : "13", "credential": {"accessKey": "12345"}}`, true},
{`{"version" : "13", "credential": {"accessKey": "12345", "accessKey":"12345"}}`, false},
{`{"version" : "13", "notify": {"amqp": {"1"}, "webhook":{"3"}}}`, true},
{`{"version" : "13", "notify": {"amqp": {"1"}, "amqp":{"3"}}}`, false},
{`{"version" : "13", "notify": {"amqp": {"1":{}, "2":{}}}}`, true},
{`{"version" : "13", "notify": {"amqp": {"1":{}, "1":{}}}}`, false},
}
for i, testCase := range testCases {
err := doCheckDupJSONKeys(gjson.Result{}, gjson.Parse(testCase.json))
if testCase.shouldPass && err != nil {
t.Errorf("Test %d, should pass but it failed with err = %v", i+1, err)
}
if !testCase.shouldPass && err == nil {
t.Errorf("Test %d, should fail but it succeed.", i+1)
}
}
}