github.com/cli/cli@v1.14.1-0.20210902173923-1af6a669e342/internal/config/config_file_test.go (about) 1 package config 2 3 import ( 4 "bytes" 5 "fmt" 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 "runtime" 10 "testing" 11 12 "github.com/stretchr/testify/assert" 13 "gopkg.in/yaml.v3" 14 ) 15 16 func Test_parseConfig(t *testing.T) { 17 defer stubConfig(`--- 18 hosts: 19 github.com: 20 user: monalisa 21 oauth_token: OTOKEN 22 `, "")() 23 config, err := parseConfig("config.yml") 24 assert.NoError(t, err) 25 user, err := config.Get("github.com", "user") 26 assert.NoError(t, err) 27 assert.Equal(t, "monalisa", user) 28 token, err := config.Get("github.com", "oauth_token") 29 assert.NoError(t, err) 30 assert.Equal(t, "OTOKEN", token) 31 } 32 33 func Test_parseConfig_multipleHosts(t *testing.T) { 34 defer stubConfig(`--- 35 hosts: 36 example.com: 37 user: wronguser 38 oauth_token: NOTTHIS 39 github.com: 40 user: monalisa 41 oauth_token: OTOKEN 42 `, "")() 43 config, err := parseConfig("config.yml") 44 assert.NoError(t, err) 45 user, err := config.Get("github.com", "user") 46 assert.NoError(t, err) 47 assert.Equal(t, "monalisa", user) 48 token, err := config.Get("github.com", "oauth_token") 49 assert.NoError(t, err) 50 assert.Equal(t, "OTOKEN", token) 51 } 52 53 func Test_parseConfig_hostsFile(t *testing.T) { 54 defer stubConfig("", `--- 55 github.com: 56 user: monalisa 57 oauth_token: OTOKEN 58 `)() 59 config, err := parseConfig("config.yml") 60 assert.NoError(t, err) 61 user, err := config.Get("github.com", "user") 62 assert.NoError(t, err) 63 assert.Equal(t, "monalisa", user) 64 token, err := config.Get("github.com", "oauth_token") 65 assert.NoError(t, err) 66 assert.Equal(t, "OTOKEN", token) 67 } 68 69 func Test_parseConfig_hostFallback(t *testing.T) { 70 defer stubConfig(`--- 71 git_protocol: ssh 72 `, `--- 73 github.com: 74 user: monalisa 75 oauth_token: OTOKEN 76 example.com: 77 user: wronguser 78 oauth_token: NOTTHIS 79 git_protocol: https 80 `)() 81 config, err := parseConfig("config.yml") 82 assert.NoError(t, err) 83 val, err := config.Get("example.com", "git_protocol") 84 assert.NoError(t, err) 85 assert.Equal(t, "https", val) 86 val, err = config.Get("github.com", "git_protocol") 87 assert.NoError(t, err) 88 assert.Equal(t, "ssh", val) 89 val, err = config.Get("nonexistent.io", "git_protocol") 90 assert.NoError(t, err) 91 assert.Equal(t, "ssh", val) 92 } 93 94 func Test_parseConfig_migrateConfig(t *testing.T) { 95 defer stubConfig(`--- 96 github.com: 97 - user: keiyuri 98 oauth_token: 123456 99 `, "")() 100 101 mainBuf := bytes.Buffer{} 102 hostsBuf := bytes.Buffer{} 103 defer StubWriteConfig(&mainBuf, &hostsBuf)() 104 defer StubBackupConfig()() 105 106 _, err := parseConfig("config.yml") 107 assert.NoError(t, err) 108 109 expectedHosts := `github.com: 110 user: keiyuri 111 oauth_token: "123456" 112 ` 113 114 assert.Equal(t, expectedHosts, hostsBuf.String()) 115 assert.NotContains(t, mainBuf.String(), "github.com") 116 assert.NotContains(t, mainBuf.String(), "oauth_token") 117 } 118 119 func Test_parseConfigFile(t *testing.T) { 120 tests := []struct { 121 contents string 122 wantsErr bool 123 }{ 124 { 125 contents: "", 126 wantsErr: true, 127 }, 128 { 129 contents: " ", 130 wantsErr: false, 131 }, 132 { 133 contents: "\n", 134 wantsErr: false, 135 }, 136 } 137 138 for _, tt := range tests { 139 t.Run(fmt.Sprintf("contents: %q", tt.contents), func(t *testing.T) { 140 defer stubConfig(tt.contents, "")() 141 _, yamlRoot, err := parseConfigFile("config.yml") 142 if tt.wantsErr != (err != nil) { 143 t.Fatalf("got error: %v", err) 144 } 145 if tt.wantsErr { 146 return 147 } 148 assert.Equal(t, yaml.MappingNode, yamlRoot.Content[0].Kind) 149 assert.Equal(t, 0, len(yamlRoot.Content[0].Content)) 150 }) 151 } 152 } 153 154 func Test_ConfigDir(t *testing.T) { 155 tempDir := t.TempDir() 156 157 tests := []struct { 158 name string 159 onlyWindows bool 160 env map[string]string 161 output string 162 }{ 163 { 164 name: "HOME/USERPROFILE specified", 165 env: map[string]string{ 166 "GH_CONFIG_DIR": "", 167 "XDG_CONFIG_HOME": "", 168 "AppData": "", 169 "USERPROFILE": tempDir, 170 "HOME": tempDir, 171 }, 172 output: filepath.Join(tempDir, ".config", "gh"), 173 }, 174 { 175 name: "GH_CONFIG_DIR specified", 176 env: map[string]string{ 177 "GH_CONFIG_DIR": filepath.Join(tempDir, "gh_config_dir"), 178 }, 179 output: filepath.Join(tempDir, "gh_config_dir"), 180 }, 181 { 182 name: "XDG_CONFIG_HOME specified", 183 env: map[string]string{ 184 "XDG_CONFIG_HOME": tempDir, 185 }, 186 output: filepath.Join(tempDir, "gh"), 187 }, 188 { 189 name: "GH_CONFIG_DIR and XDG_CONFIG_HOME specified", 190 env: map[string]string{ 191 "GH_CONFIG_DIR": filepath.Join(tempDir, "gh_config_dir"), 192 "XDG_CONFIG_HOME": tempDir, 193 }, 194 output: filepath.Join(tempDir, "gh_config_dir"), 195 }, 196 { 197 name: "AppData specified", 198 onlyWindows: true, 199 env: map[string]string{ 200 "AppData": tempDir, 201 }, 202 output: filepath.Join(tempDir, "GitHub CLI"), 203 }, 204 { 205 name: "GH_CONFIG_DIR and AppData specified", 206 onlyWindows: true, 207 env: map[string]string{ 208 "GH_CONFIG_DIR": filepath.Join(tempDir, "gh_config_dir"), 209 "AppData": tempDir, 210 }, 211 output: filepath.Join(tempDir, "gh_config_dir"), 212 }, 213 { 214 name: "XDG_CONFIG_HOME and AppData specified", 215 onlyWindows: true, 216 env: map[string]string{ 217 "XDG_CONFIG_HOME": tempDir, 218 "AppData": tempDir, 219 }, 220 output: filepath.Join(tempDir, "gh"), 221 }, 222 } 223 224 for _, tt := range tests { 225 if tt.onlyWindows && runtime.GOOS != "windows" { 226 continue 227 } 228 t.Run(tt.name, func(t *testing.T) { 229 if tt.env != nil { 230 for k, v := range tt.env { 231 old := os.Getenv(k) 232 os.Setenv(k, v) 233 defer os.Setenv(k, old) 234 } 235 } 236 237 // Create directory to skip auto migration code 238 // which gets run when target directory does not exist 239 _ = os.MkdirAll(tt.output, 0755) 240 241 assert.Equal(t, tt.output, ConfigDir()) 242 }) 243 } 244 } 245 246 func Test_configFile_Write_toDisk(t *testing.T) { 247 configDir := filepath.Join(t.TempDir(), ".config", "gh") 248 _ = os.MkdirAll(configDir, 0755) 249 os.Setenv(GH_CONFIG_DIR, configDir) 250 defer os.Unsetenv(GH_CONFIG_DIR) 251 252 cfg := NewFromString(`pager: less`) 253 err := cfg.Write() 254 if err != nil { 255 t.Fatal(err) 256 } 257 258 expectedConfig := "pager: less\n" 259 if configBytes, err := ioutil.ReadFile(filepath.Join(configDir, "config.yml")); err != nil { 260 t.Error(err) 261 } else if string(configBytes) != expectedConfig { 262 t.Errorf("expected config.yml %q, got %q", expectedConfig, string(configBytes)) 263 } 264 265 if configBytes, err := ioutil.ReadFile(filepath.Join(configDir, "hosts.yml")); err != nil { 266 t.Error(err) 267 } else if string(configBytes) != "" { 268 t.Errorf("unexpected hosts.yml: %q", string(configBytes)) 269 } 270 } 271 272 func Test_autoMigrateConfigDir_noMigration_notExist(t *testing.T) { 273 homeDir := t.TempDir() 274 migrateDir := t.TempDir() 275 276 homeEnvVar := "HOME" 277 if runtime.GOOS == "windows" { 278 homeEnvVar = "USERPROFILE" 279 } 280 old := os.Getenv(homeEnvVar) 281 os.Setenv(homeEnvVar, homeDir) 282 defer os.Setenv(homeEnvVar, old) 283 284 err := autoMigrateConfigDir(migrateDir) 285 assert.Equal(t, errNotExist, err) 286 287 files, err := ioutil.ReadDir(migrateDir) 288 assert.NoError(t, err) 289 assert.Equal(t, 0, len(files)) 290 } 291 292 func Test_autoMigrateConfigDir_noMigration_samePath(t *testing.T) { 293 homeDir := t.TempDir() 294 migrateDir := filepath.Join(homeDir, ".config", "gh") 295 err := os.MkdirAll(migrateDir, 0755) 296 assert.NoError(t, err) 297 298 homeEnvVar := "HOME" 299 if runtime.GOOS == "windows" { 300 homeEnvVar = "USERPROFILE" 301 } 302 old := os.Getenv(homeEnvVar) 303 os.Setenv(homeEnvVar, homeDir) 304 defer os.Setenv(homeEnvVar, old) 305 306 err = autoMigrateConfigDir(migrateDir) 307 assert.Equal(t, errSamePath, err) 308 309 files, err := ioutil.ReadDir(migrateDir) 310 assert.NoError(t, err) 311 assert.Equal(t, 0, len(files)) 312 } 313 314 func Test_autoMigrateConfigDir_migration(t *testing.T) { 315 homeDir := t.TempDir() 316 migrateDir := t.TempDir() 317 homeConfigDir := filepath.Join(homeDir, ".config", "gh") 318 migrateConfigDir := filepath.Join(migrateDir, ".config", "gh") 319 320 homeEnvVar := "HOME" 321 if runtime.GOOS == "windows" { 322 homeEnvVar = "USERPROFILE" 323 } 324 old := os.Getenv(homeEnvVar) 325 os.Setenv(homeEnvVar, homeDir) 326 defer os.Setenv(homeEnvVar, old) 327 328 err := os.MkdirAll(homeConfigDir, 0755) 329 assert.NoError(t, err) 330 f, err := ioutil.TempFile(homeConfigDir, "") 331 assert.NoError(t, err) 332 f.Close() 333 334 err = autoMigrateConfigDir(migrateConfigDir) 335 assert.NoError(t, err) 336 337 _, err = ioutil.ReadDir(homeConfigDir) 338 assert.True(t, os.IsNotExist(err)) 339 340 files, err := ioutil.ReadDir(migrateConfigDir) 341 assert.NoError(t, err) 342 assert.Equal(t, 1, len(files)) 343 } 344 345 func Test_StateDir(t *testing.T) { 346 tempDir := t.TempDir() 347 348 tests := []struct { 349 name string 350 onlyWindows bool 351 env map[string]string 352 output string 353 }{ 354 { 355 name: "HOME/USERPROFILE specified", 356 env: map[string]string{ 357 "XDG_STATE_HOME": "", 358 "GH_CONFIG_DIR": "", 359 "XDG_CONFIG_HOME": "", 360 "LocalAppData": "", 361 "USERPROFILE": tempDir, 362 "HOME": tempDir, 363 }, 364 output: filepath.Join(tempDir, ".local", "state", "gh"), 365 }, 366 { 367 name: "XDG_STATE_HOME specified", 368 env: map[string]string{ 369 "XDG_STATE_HOME": tempDir, 370 }, 371 output: filepath.Join(tempDir, "gh"), 372 }, 373 { 374 name: "LocalAppData specified", 375 onlyWindows: true, 376 env: map[string]string{ 377 "LocalAppData": tempDir, 378 }, 379 output: filepath.Join(tempDir, "GitHub CLI"), 380 }, 381 { 382 name: "XDG_STATE_HOME and LocalAppData specified", 383 onlyWindows: true, 384 env: map[string]string{ 385 "XDG_STATE_HOME": tempDir, 386 "LocalAppData": tempDir, 387 }, 388 output: filepath.Join(tempDir, "gh"), 389 }, 390 } 391 392 for _, tt := range tests { 393 if tt.onlyWindows && runtime.GOOS != "windows" { 394 continue 395 } 396 t.Run(tt.name, func(t *testing.T) { 397 if tt.env != nil { 398 for k, v := range tt.env { 399 old := os.Getenv(k) 400 os.Setenv(k, v) 401 defer os.Setenv(k, old) 402 } 403 } 404 405 // Create directory to skip auto migration code 406 // which gets run when target directory does not exist 407 _ = os.MkdirAll(tt.output, 0755) 408 409 assert.Equal(t, tt.output, StateDir()) 410 }) 411 } 412 } 413 414 func Test_autoMigrateStateDir_noMigration_notExist(t *testing.T) { 415 homeDir := t.TempDir() 416 migrateDir := t.TempDir() 417 418 homeEnvVar := "HOME" 419 if runtime.GOOS == "windows" { 420 homeEnvVar = "USERPROFILE" 421 } 422 old := os.Getenv(homeEnvVar) 423 os.Setenv(homeEnvVar, homeDir) 424 defer os.Setenv(homeEnvVar, old) 425 426 err := autoMigrateStateDir(migrateDir) 427 assert.Equal(t, errNotExist, err) 428 429 files, err := ioutil.ReadDir(migrateDir) 430 assert.NoError(t, err) 431 assert.Equal(t, 0, len(files)) 432 } 433 434 func Test_autoMigrateStateDir_noMigration_samePath(t *testing.T) { 435 homeDir := t.TempDir() 436 migrateDir := filepath.Join(homeDir, ".config", "gh") 437 err := os.MkdirAll(migrateDir, 0755) 438 assert.NoError(t, err) 439 440 homeEnvVar := "HOME" 441 if runtime.GOOS == "windows" { 442 homeEnvVar = "USERPROFILE" 443 } 444 old := os.Getenv(homeEnvVar) 445 os.Setenv(homeEnvVar, homeDir) 446 defer os.Setenv(homeEnvVar, old) 447 448 err = autoMigrateStateDir(migrateDir) 449 assert.Equal(t, errSamePath, err) 450 451 files, err := ioutil.ReadDir(migrateDir) 452 assert.NoError(t, err) 453 assert.Equal(t, 0, len(files)) 454 } 455 456 func Test_autoMigrateStateDir_migration(t *testing.T) { 457 homeDir := t.TempDir() 458 migrateDir := t.TempDir() 459 homeConfigDir := filepath.Join(homeDir, ".config", "gh") 460 migrateStateDir := filepath.Join(migrateDir, ".local", "state", "gh") 461 462 homeEnvVar := "HOME" 463 if runtime.GOOS == "windows" { 464 homeEnvVar = "USERPROFILE" 465 } 466 old := os.Getenv(homeEnvVar) 467 os.Setenv(homeEnvVar, homeDir) 468 defer os.Setenv(homeEnvVar, old) 469 470 err := os.MkdirAll(homeConfigDir, 0755) 471 assert.NoError(t, err) 472 err = ioutil.WriteFile(filepath.Join(homeConfigDir, "state.yml"), nil, 0755) 473 assert.NoError(t, err) 474 475 err = autoMigrateStateDir(migrateStateDir) 476 assert.NoError(t, err) 477 478 files, err := ioutil.ReadDir(homeConfigDir) 479 assert.NoError(t, err) 480 assert.Equal(t, 0, len(files)) 481 482 files, err = ioutil.ReadDir(migrateStateDir) 483 assert.NoError(t, err) 484 assert.Equal(t, 1, len(files)) 485 assert.Equal(t, "state.yml", files[0].Name()) 486 } 487 488 func Test_DataDir(t *testing.T) { 489 tempDir := t.TempDir() 490 491 tests := []struct { 492 name string 493 onlyWindows bool 494 env map[string]string 495 output string 496 }{ 497 { 498 name: "HOME/USERPROFILE specified", 499 env: map[string]string{ 500 "XDG_DATA_HOME": "", 501 "GH_CONFIG_DIR": "", 502 "XDG_CONFIG_HOME": "", 503 "LocalAppData": "", 504 "USERPROFILE": tempDir, 505 "HOME": tempDir, 506 }, 507 output: filepath.Join(tempDir, ".local", "share", "gh"), 508 }, 509 { 510 name: "XDG_DATA_HOME specified", 511 env: map[string]string{ 512 "XDG_DATA_HOME": tempDir, 513 }, 514 output: filepath.Join(tempDir, "gh"), 515 }, 516 { 517 name: "LocalAppData specified", 518 onlyWindows: true, 519 env: map[string]string{ 520 "LocalAppData": tempDir, 521 }, 522 output: filepath.Join(tempDir, "GitHub CLI"), 523 }, 524 { 525 name: "XDG_DATA_HOME and LocalAppData specified", 526 onlyWindows: true, 527 env: map[string]string{ 528 "XDG_DATA_HOME": tempDir, 529 "LocalAppData": tempDir, 530 }, 531 output: filepath.Join(tempDir, "gh"), 532 }, 533 } 534 535 for _, tt := range tests { 536 if tt.onlyWindows && runtime.GOOS != "windows" { 537 continue 538 } 539 t.Run(tt.name, func(t *testing.T) { 540 if tt.env != nil { 541 for k, v := range tt.env { 542 old := os.Getenv(k) 543 os.Setenv(k, v) 544 defer os.Setenv(k, old) 545 } 546 } 547 548 assert.Equal(t, tt.output, DataDir()) 549 }) 550 } 551 }