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