1// Copyright (c) 2015-2021 MinIO, Inc.
2//
3// This file is part of MinIO Object Storage stack
4//
5// This program is free software: you can redistribute it and/or modify
6// it under the terms of the GNU Affero General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// This program is distributed in the hope that it will be useful
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13// GNU Affero General Public License for more details.
14//
15// You should have received a copy of the GNU Affero General Public License
16// along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18package quick
19
20import (
21	"bytes"
22	"encoding/json"
23	"io/ioutil"
24	"os"
25	"reflect"
26	"runtime"
27	"strings"
28	"testing"
29)
30
31func TestReadVersion(t *testing.T) {
32	type myStruct struct {
33		Version string
34	}
35	saveMe := myStruct{"1"}
36	config, err := NewConfig(&saveMe, nil)
37	if err != nil {
38		t.Fatal(err)
39	}
40	err = config.Save("test.json")
41	if err != nil {
42		t.Fatal(err)
43	}
44
45	version, err := GetVersion("test.json", nil)
46	if err != nil {
47		t.Fatal(err)
48	}
49	if version != "1" {
50		t.Fatalf("Expected version '1', got '%v'", version)
51	}
52}
53
54func TestReadVersionErr(t *testing.T) {
55	type myStruct struct {
56		Version int
57	}
58	saveMe := myStruct{1}
59	_, err := NewConfig(&saveMe, nil)
60	if err == nil {
61		t.Fatal("Unexpected should fail in initialization for bad input")
62	}
63
64	err = ioutil.WriteFile("test.json", []byte("{ \"version\":2,"), 0644)
65	if err != nil {
66		t.Fatal(err)
67	}
68
69	_, err = GetVersion("test.json", nil)
70	if err == nil {
71		t.Fatal("Unexpected should fail to fetch version")
72	}
73
74	err = ioutil.WriteFile("test.json", []byte("{ \"version\":2 }"), 0644)
75	if err != nil {
76		t.Fatal(err)
77	}
78
79	_, err = GetVersion("test.json", nil)
80	if err == nil {
81		t.Fatal("Unexpected should fail to fetch version")
82	}
83}
84
85func TestSaveFailOnDir(t *testing.T) {
86	defer os.RemoveAll("test-1.json")
87	err := os.MkdirAll("test-1.json", 0644)
88	if err != nil {
89		t.Fatal(err)
90	}
91	type myStruct struct {
92		Version string
93	}
94	saveMe := myStruct{"1"}
95	config, err := NewConfig(&saveMe, nil)
96	if err != nil {
97		t.Fatal(err)
98	}
99	err = config.Save("test-1.json")
100	if err == nil {
101		t.Fatal("Unexpected should fail to save if test-1.json is a directory")
102	}
103}
104
105func TestCheckData(t *testing.T) {
106	err := CheckData(nil)
107	if err == nil {
108		t.Fatal("Unexpected should fail")
109	}
110
111	type myStructBadNoVersion struct {
112		User        string
113		Password    string
114		Directories []string
115	}
116	saveMeBadNoVersion := myStructBadNoVersion{"guest", "nopassword", []string{"Work", "Documents", "Music"}}
117	err = CheckData(&saveMeBadNoVersion)
118	if err == nil {
119		t.Fatal("Unexpected should fail if Version is not set")
120	}
121
122	type myStructBadVersionInt struct {
123		Version  int
124		User     string
125		Password string
126	}
127	saveMeBadVersionInt := myStructBadVersionInt{1, "guest", "nopassword"}
128	err = CheckData(&saveMeBadVersionInt)
129	if err == nil {
130		t.Fatal("Unexpected should fail if Version is integer")
131	}
132
133	type myStructGood struct {
134		Version     string
135		User        string
136		Password    string
137		Directories []string
138	}
139
140	saveMeGood := myStructGood{"1", "guest", "nopassword", []string{"Work", "Documents", "Music"}}
141	err = CheckData(&saveMeGood)
142	if err != nil {
143		t.Fatal(err)
144	}
145}
146
147func TestLoadFile(t *testing.T) {
148	type myStruct struct {
149		Version     string
150		User        string
151		Password    string
152		Directories []string
153	}
154	saveMe := myStruct{}
155	_, err := LoadConfig("test.json", nil, &saveMe)
156	if err == nil {
157		t.Fatal(err)
158	}
159
160	file, err := os.Create("test.json")
161	if err != nil {
162		t.Fatal(err)
163	}
164	if err = file.Close(); err != nil {
165		t.Fatal(err)
166	}
167	_, err = LoadConfig("test.json", nil, &saveMe)
168	if err == nil {
169		t.Fatal("Unexpected should fail to load empty JSON")
170	}
171	config, err := NewConfig(&saveMe, nil)
172	if err != nil {
173		t.Fatal(err)
174	}
175	err = config.Load("test-non-exist.json")
176	if err == nil {
177		t.Fatal("Unexpected should fail to Load non-existent config")
178	}
179
180	err = config.Load("test.json")
181	if err == nil {
182		t.Fatal("Unexpected should fail to load empty JSON")
183	}
184
185	saveMe = myStruct{"1", "guest", "nopassword", []string{"Work", "Documents", "Music"}}
186	config, err = NewConfig(&saveMe, nil)
187	if err != nil {
188		t.Fatal(err)
189	}
190	err = config.Save("test.json")
191	if err != nil {
192		t.Fatal(err)
193	}
194	saveMe1 := myStruct{}
195	_, err = LoadConfig("test.json", nil, &saveMe1)
196	if err != nil {
197		t.Fatal(err)
198	}
199	if !reflect.DeepEqual(saveMe1, saveMe) {
200		t.Fatalf("Expected %v, got %v", saveMe1, saveMe)
201	}
202
203	saveMe2 := myStruct{}
204	err = json.Unmarshal([]byte(config.String()), &saveMe2)
205	if err != nil {
206		t.Fatal(err)
207	}
208	if !reflect.DeepEqual(saveMe2, saveMe1) {
209		t.Fatalf("Expected %v, got %v", saveMe2, saveMe1)
210	}
211}
212
213func TestYAMLFormat(t *testing.T) {
214	testYAML := "test.yaml"
215	defer os.RemoveAll(testYAML)
216
217	type myStruct struct {
218		Version     string
219		User        string
220		Password    string
221		Directories []string
222	}
223
224	plainYAML := `version: "1"
225user: guest
226password: nopassword
227directories:
228- Work
229- Documents
230- Music
231`
232
233	if runtime.GOOS == "windows" {
234		plainYAML = strings.Replace(plainYAML, "\n", "\r\n", -1)
235	}
236
237	saveMe := myStruct{"1", "guest", "nopassword", []string{"Work", "Documents", "Music"}}
238
239	// Save format using
240	config, err := NewConfig(&saveMe, nil)
241	if err != nil {
242		t.Fatal(err)
243	}
244
245	err = config.Save(testYAML)
246	if err != nil {
247		t.Fatal(err)
248	}
249
250	// Check if the saved structure in actually an YAML format
251	b, err := ioutil.ReadFile(testYAML)
252	if err != nil {
253		t.Fatal(err)
254	}
255
256	if !bytes.Equal([]byte(plainYAML), b) {
257		t.Fatalf("Expected %v, got %v", plainYAML, string(b))
258	}
259
260	// Check if the loaded data is the same as the saved one
261	loadMe := myStruct{}
262	config, err = NewConfig(&loadMe, nil)
263	if err != nil {
264		t.Fatal(err)
265	}
266
267	err = config.Load(testYAML)
268	if err != nil {
269		t.Fatal(err)
270	}
271
272	if !reflect.DeepEqual(saveMe, loadMe) {
273		t.Fatalf("Expected %v, got %v", saveMe, loadMe)
274	}
275}
276
277func TestJSONFormat(t *testing.T) {
278	testJSON := "test.json"
279	defer os.RemoveAll(testJSON)
280
281	type myStruct struct {
282		Version     string
283		User        string
284		Password    string
285		Directories []string
286	}
287
288	plainJSON := `{
289	"Version": "1",
290	"User": "guest",
291	"Password": "nopassword",
292	"Directories": [
293		"Work",
294		"Documents",
295		"Music"
296	]
297}`
298
299	if runtime.GOOS == "windows" {
300		plainJSON = strings.Replace(plainJSON, "\n", "\r\n", -1)
301	}
302
303	saveMe := myStruct{"1", "guest", "nopassword", []string{"Work", "Documents", "Music"}}
304
305	// Save format using
306	config, err := NewConfig(&saveMe, nil)
307	if err != nil {
308		t.Fatal(err)
309	}
310
311	err = config.Save(testJSON)
312	if err != nil {
313		t.Fatal(err)
314	}
315
316	// Check if the saved structure in actually an JSON format
317	b, err := ioutil.ReadFile(testJSON)
318	if err != nil {
319		t.Fatal(err)
320	}
321
322	if !bytes.Equal([]byte(plainJSON), b) {
323		t.Fatalf("Expected %v, got %v", plainJSON, string(b))
324	}
325
326	// Check if the loaded data is the same as the saved one
327	loadMe := myStruct{}
328	config, err = NewConfig(&loadMe, nil)
329	if err != nil {
330		t.Fatal(err)
331	}
332	err = config.Load(testJSON)
333	if err != nil {
334		t.Fatal(err)
335	}
336
337	if !reflect.DeepEqual(saveMe, loadMe) {
338		t.Fatalf("Expected %v, got %v", saveMe, loadMe)
339	}
340}
341
342func TestSaveLoad(t *testing.T) {
343	defer os.RemoveAll("test.json")
344	type myStruct struct {
345		Version     string
346		User        string
347		Password    string
348		Directories []string
349	}
350	saveMe := myStruct{"1", "guest", "nopassword", []string{"Work", "Documents", "Music"}}
351	config, err := NewConfig(&saveMe, nil)
352	if err != nil {
353		t.Fatal(err)
354	}
355	err = config.Save("test.json")
356	if err != nil {
357		t.Fatal(err)
358	}
359
360	loadMe := myStruct{Version: "1"}
361	newConfig, err := NewConfig(&loadMe, nil)
362	if err != nil {
363		t.Fatal(err)
364	}
365	err = newConfig.Load("test.json")
366	if err != nil {
367		t.Fatal(err)
368	}
369
370	if !reflect.DeepEqual(config.Data(), newConfig.Data()) {
371		t.Fatalf("Expected %v, got %v", config.Data(), newConfig.Data())
372	}
373	if !reflect.DeepEqual(config.Data(), &loadMe) {
374		t.Fatalf("Expected %v, got %v", config.Data(), &loadMe)
375	}
376
377	mismatch := myStruct{"1.1", "guest", "nopassword", []string{"Work", "Documents", "Music"}}
378	if reflect.DeepEqual(config.Data(), &mismatch) {
379		t.Fatal("Expected to mismatch but succeeded instead")
380	}
381}
382
383func TestSaveBackup(t *testing.T) {
384	defer os.RemoveAll("test.json")
385	defer os.RemoveAll("test.json.old")
386	type myStruct struct {
387		Version     string
388		User        string
389		Password    string
390		Directories []string
391	}
392	saveMe := myStruct{"1", "guest", "nopassword", []string{"Work", "Documents", "Music"}}
393	config, err := NewConfig(&saveMe, nil)
394	if err != nil {
395		t.Fatal(err)
396	}
397	err = config.Save("test.json")
398	if err != nil {
399		t.Fatal(err)
400	}
401
402	loadMe := myStruct{Version: "1"}
403	newConfig, err := NewConfig(&loadMe, nil)
404	if err != nil {
405		t.Fatal(err)
406	}
407	err = newConfig.Load("test.json")
408	if err != nil {
409		t.Fatal(err)
410	}
411
412	if !reflect.DeepEqual(config.Data(), newConfig.Data()) {
413		t.Fatalf("Expected %v, got %v", config.Data(), newConfig.Data())
414	}
415	if !reflect.DeepEqual(config.Data(), &loadMe) {
416		t.Fatalf("Expected %v, got %v", config.Data(), &loadMe)
417	}
418
419	mismatch := myStruct{"1.1", "guest", "nopassword", []string{"Work", "Documents", "Music"}}
420	if reflect.DeepEqual(newConfig.Data(), &mismatch) {
421		t.Fatal("Expected to mismatch but succeeded instead")
422	}
423
424	config, err = NewConfig(&mismatch, nil)
425	if err != nil {
426		t.Fatal(err)
427	}
428	err = config.Save("test.json")
429	if err != nil {
430		t.Fatal(err)
431	}
432}
433
434func TestDiff(t *testing.T) {
435	type myStruct struct {
436		Version     string
437		User        string
438		Password    string
439		Directories []string
440	}
441	saveMe := myStruct{"1", "guest", "nopassword", []string{"Work", "Documents", "Music"}}
442	config, err := NewConfig(&saveMe, nil)
443	if err != nil {
444		t.Fatal(err)
445	}
446
447	type myNewConfigStruct struct {
448		Version string
449		// User     string
450		Password    string
451		Directories []string
452	}
453
454	mismatch := myNewConfigStruct{"1", "nopassword", []string{"Work", "documents", "Music"}}
455	newConfig, err := NewConfig(&mismatch, nil)
456	if err != nil {
457		t.Fatal(err)
458	}
459
460	fields, err := config.Diff(newConfig)
461	if err != nil {
462		t.Fatal(err)
463	}
464	if len(fields) != 1 {
465		t.Fatalf("Expected len 1, got %v", len(fields))
466	}
467
468	// Uncomment for debugging
469	//	for i, field := range fields {
470	//		fmt.Printf("Diff[%d]: %s=%v\n", i, field.Name(), field.Value())
471	//	}
472}
473
474func TestDeepDiff(t *testing.T) {
475	type myStruct struct {
476		Version     string
477		User        string
478		Password    string
479		Directories []string
480	}
481	saveMe := myStruct{"1", "guest", "nopassword", []string{"Work", "Documents", "Music"}}
482	config, err := NewConfig(&saveMe, nil)
483	if err != nil {
484		t.Fatal(err)
485	}
486
487	mismatch := myStruct{"1", "Guest", "nopassword", []string{"Work", "documents", "Music"}}
488	newConfig, err := NewConfig(&mismatch, nil)
489	if err != nil {
490		t.Fatal(err)
491	}
492
493	fields, err := config.DeepDiff(newConfig)
494	if err != nil {
495		t.Fatal(err)
496	}
497	if len(fields) != 2 {
498		t.Fatalf("Expected len 2, got %v", len(fields))
499	}
500
501	// Uncomment for debugging
502	//	for i, field := range fields {
503	//		fmt.Printf("DeepDiff[%d]: %s=%v\n", i, field.Name(), field.Value())
504	//	}
505}
506