2025-12-08 10:14:47 +01:00
|
|
|
package config
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"os"
|
|
|
|
|
"path/filepath"
|
|
|
|
|
"testing"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func TestConfig_SetHost(t *testing.T) {
|
|
|
|
|
cfg := &Config{}
|
|
|
|
|
|
|
|
|
|
hostConfig := HostConfig{
|
|
|
|
|
Hostname: "codeberg.org",
|
|
|
|
|
Token: "test-token",
|
|
|
|
|
User: "testuser",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cfg.SetHost("codeberg.org", hostConfig)
|
|
|
|
|
|
|
|
|
|
if cfg.Hosts == nil {
|
|
|
|
|
t.Fatal("Hosts map should not be nil")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(cfg.Hosts) != 1 {
|
|
|
|
|
t.Errorf("Expected 1 host, got %d", len(cfg.Hosts))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
host, ok := cfg.Hosts["codeberg.org"]
|
|
|
|
|
if !ok {
|
|
|
|
|
t.Fatal("Host codeberg.org not found")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if host.Hostname != "codeberg.org" {
|
|
|
|
|
t.Errorf("Expected hostname 'codeberg.org', got '%s'", host.Hostname)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if host.Token != "test-token" {
|
|
|
|
|
t.Errorf("Expected token 'test-token', got '%s'", host.Token)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestConfig_GetHost(t *testing.T) {
|
|
|
|
|
cfg := &Config{
|
|
|
|
|
Hosts: map[string]HostConfig{
|
|
|
|
|
"codeberg.org": {
|
|
|
|
|
Hostname: "codeberg.org",
|
|
|
|
|
Token: "test-token",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-23 12:39:51 -06:00
|
|
|
host, err := cfg.GetHost("codeberg.org", "", "")
|
2025-12-08 10:14:47 +01:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if host.Hostname != "codeberg.org" {
|
|
|
|
|
t.Errorf("Expected hostname 'codeberg.org', got '%s'", host.Hostname)
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-23 12:39:51 -06:00
|
|
|
_, err = cfg.GetHost("nonexistent.org", "", "")
|
2025-12-08 10:14:47 +01:00
|
|
|
if err == nil {
|
|
|
|
|
t.Error("Expected error for nonexistent host")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestGetConfigDir(t *testing.T) {
|
|
|
|
|
dir, err := GetConfigDir()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if dir == "" {
|
|
|
|
|
t.Error("Config directory should not be empty")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-12 15:44:24 +01:00
|
|
|
func TestGetConfigDir_XDG(t *testing.T) {
|
|
|
|
|
t.Setenv("XDG_CONFIG_HOME", "/custom/config")
|
|
|
|
|
|
|
|
|
|
dir, err := GetConfigDir()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-26 08:16:52 -06:00
|
|
|
expected := "/custom/config/fj"
|
2026-03-12 15:44:24 +01:00
|
|
|
if dir != expected {
|
|
|
|
|
t.Errorf("Expected %q, got %q", expected, dir)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-08 10:14:47 +01:00
|
|
|
func TestGetConfigPath(t *testing.T) {
|
|
|
|
|
path, err := GetConfigPath()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if path == "" {
|
|
|
|
|
t.Error("Config path should not be empty")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestConfig_SaveAndLoad(t *testing.T) {
|
|
|
|
|
// Create a temp directory for testing
|
|
|
|
|
tempDir := t.TempDir()
|
|
|
|
|
tempFile := filepath.Join(tempDir, "config.yaml")
|
|
|
|
|
|
|
|
|
|
// Create a config with test data
|
|
|
|
|
cfg := &Config{
|
|
|
|
|
Hosts: map[string]HostConfig{
|
|
|
|
|
"codeberg.org": {
|
|
|
|
|
Hostname: "codeberg.org",
|
|
|
|
|
Token: "test-token-123",
|
|
|
|
|
User: "testuser",
|
|
|
|
|
GitProtocol: "ssh",
|
|
|
|
|
},
|
|
|
|
|
"github.com": {
|
|
|
|
|
Hostname: "github.com",
|
|
|
|
|
Token: "github-token-456",
|
|
|
|
|
User: "githubuser",
|
|
|
|
|
GitProtocol: "https",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Test Save
|
|
|
|
|
err := cfg.SaveToPath(tempFile)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("Failed to save config: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Verify file exists
|
|
|
|
|
if _, err := os.Stat(tempFile); os.IsNotExist(err) {
|
|
|
|
|
t.Fatal("Config file was not created")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Test Load
|
|
|
|
|
loadedCfg, err := LoadFromPath(tempFile)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("Failed to load config: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Verify loaded config matches saved config
|
|
|
|
|
if len(loadedCfg.Hosts) != 2 {
|
|
|
|
|
t.Errorf("Expected 2 hosts, got %d", len(loadedCfg.Hosts))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check codeberg.org host
|
|
|
|
|
codebergHost, ok := loadedCfg.Hosts["codeberg.org"]
|
|
|
|
|
if !ok {
|
|
|
|
|
t.Fatal("codeberg.org host not found in loaded config")
|
|
|
|
|
}
|
|
|
|
|
if codebergHost.Token != "test-token-123" {
|
|
|
|
|
t.Errorf("Expected token 'test-token-123', got '%s'", codebergHost.Token)
|
|
|
|
|
}
|
|
|
|
|
if codebergHost.User != "testuser" {
|
|
|
|
|
t.Errorf("Expected user 'testuser', got '%s'", codebergHost.User)
|
|
|
|
|
}
|
|
|
|
|
if codebergHost.GitProtocol != "ssh" {
|
|
|
|
|
t.Errorf("Expected git_protocol 'ssh', got '%s'", codebergHost.GitProtocol)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check github.com host
|
|
|
|
|
githubHost, ok := loadedCfg.Hosts["github.com"]
|
|
|
|
|
if !ok {
|
|
|
|
|
t.Fatal("github.com host not found in loaded config")
|
|
|
|
|
}
|
|
|
|
|
if githubHost.Token != "github-token-456" {
|
|
|
|
|
t.Errorf("Expected token 'github-token-456', got '%s'", githubHost.Token)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestConfig_LoadNonexistentFile(t *testing.T) {
|
|
|
|
|
tempDir := t.TempDir()
|
|
|
|
|
tempFile := filepath.Join(tempDir, "nonexistent.yaml")
|
|
|
|
|
|
|
|
|
|
// Test Load with nonexistent file
|
|
|
|
|
cfg, err := LoadFromPath(tempFile)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("Load should not error on nonexistent file: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Should return empty config
|
|
|
|
|
if cfg == nil {
|
|
|
|
|
t.Fatal("Config should not be nil")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if cfg.Hosts == nil {
|
|
|
|
|
t.Fatal("Hosts map should be initialized")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(cfg.Hosts) != 0 {
|
|
|
|
|
t.Errorf("Expected empty hosts map, got %d entries", len(cfg.Hosts))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestConfig_LoadInvalidYAML(t *testing.T) {
|
|
|
|
|
tempDir := t.TempDir()
|
|
|
|
|
tempFile := filepath.Join(tempDir, "invalid.yaml")
|
|
|
|
|
|
|
|
|
|
// Write invalid YAML
|
|
|
|
|
err := os.WriteFile(tempFile, []byte("invalid: yaml: content: [[["), 0600)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("Failed to write invalid YAML: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Test Load with invalid YAML
|
|
|
|
|
_, err = LoadFromPath(tempFile)
|
|
|
|
|
if err == nil {
|
|
|
|
|
t.Error("Expected error when loading invalid YAML")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestConfig_SaveCreatesDirectory(t *testing.T) {
|
|
|
|
|
tempDir := t.TempDir()
|
|
|
|
|
tempFile := filepath.Join(tempDir, "subdir", "config.yaml")
|
|
|
|
|
|
|
|
|
|
cfg := &Config{
|
|
|
|
|
Hosts: map[string]HostConfig{
|
|
|
|
|
"test.org": {
|
|
|
|
|
Hostname: "test.org",
|
|
|
|
|
Token: "token",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Save should create the directory
|
|
|
|
|
err := cfg.SaveToPath(tempFile)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("Failed to save config: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Verify directory was created
|
|
|
|
|
subdir := filepath.Dir(tempFile)
|
|
|
|
|
if _, err := os.Stat(subdir); os.IsNotExist(err) {
|
|
|
|
|
t.Error("Save() should have created the directory")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Verify file exists
|
|
|
|
|
if _, err := os.Stat(tempFile); os.IsNotExist(err) {
|
|
|
|
|
t.Error("Config file was not created")
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-12-15 15:07:42 +01:00
|
|
|
|
|
|
|
|
func TestConfig_SetHost_NilMap(t *testing.T) {
|
|
|
|
|
cfg := &Config{
|
|
|
|
|
Hosts: nil,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hostConfig := HostConfig{
|
|
|
|
|
Hostname: "test.org",
|
|
|
|
|
Token: "test-token",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Should not panic even with nil map
|
|
|
|
|
cfg.SetHost("test.org", hostConfig)
|
|
|
|
|
|
|
|
|
|
if cfg.Hosts == nil {
|
|
|
|
|
t.Fatal("Hosts map should be initialized")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(cfg.Hosts) != 1 {
|
|
|
|
|
t.Errorf("Expected 1 host, got %d", len(cfg.Hosts))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestConfig_GetHost_EmptyString(t *testing.T) {
|
|
|
|
|
cfg := &Config{
|
|
|
|
|
Hosts: map[string]HostConfig{
|
|
|
|
|
"codeberg.org": {
|
|
|
|
|
Hostname: "codeberg.org",
|
|
|
|
|
Token: "test-token",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Empty hostname should default to codeberg.org
|
2026-03-23 12:39:51 -06:00
|
|
|
host, err := cfg.GetHost("", "", "")
|
2025-12-15 15:07:42 +01:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if host.Hostname != "codeberg.org" {
|
|
|
|
|
t.Errorf("Expected default hostname 'codeberg.org', got '%s'", host.Hostname)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestConfig_GetHost_WhitespaceString(t *testing.T) {
|
|
|
|
|
cfg := &Config{
|
|
|
|
|
Hosts: map[string]HostConfig{
|
|
|
|
|
"codeberg.org": {
|
|
|
|
|
Hostname: "codeberg.org",
|
|
|
|
|
Token: "test-token",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Whitespace-only hostname should default to codeberg.org
|
2026-03-23 12:39:51 -06:00
|
|
|
host, err := cfg.GetHost(" ", "", "")
|
2025-12-15 15:07:42 +01:00
|
|
|
if err == nil {
|
|
|
|
|
t.Logf("Got host: %+v (this may be expected behavior)", host)
|
|
|
|
|
} else {
|
|
|
|
|
t.Logf("Got error: %v (this may be expected behavior)", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestConfig_SetHost_EmptyToken(t *testing.T) {
|
|
|
|
|
cfg := &Config{}
|
|
|
|
|
|
|
|
|
|
hostConfig := HostConfig{
|
|
|
|
|
Hostname: "codeberg.org",
|
|
|
|
|
Token: "", // Empty token
|
|
|
|
|
User: "testuser",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cfg.SetHost("codeberg.org", hostConfig)
|
|
|
|
|
|
2026-03-23 12:39:51 -06:00
|
|
|
host, err := cfg.GetHost("codeberg.org", "", "")
|
2025-12-15 15:07:42 +01:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if host.Token != "" {
|
|
|
|
|
t.Errorf("Expected empty token, got '%s'", host.Token)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestConfig_SetHost_OverwriteExisting(t *testing.T) {
|
|
|
|
|
cfg := &Config{
|
|
|
|
|
Hosts: map[string]HostConfig{
|
|
|
|
|
"codeberg.org": {
|
|
|
|
|
Hostname: "codeberg.org",
|
|
|
|
|
Token: "old-token",
|
|
|
|
|
User: "olduser",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Overwrite with new config
|
|
|
|
|
newConfig := HostConfig{
|
|
|
|
|
Hostname: "codeberg.org",
|
|
|
|
|
Token: "new-token",
|
|
|
|
|
User: "newuser",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cfg.SetHost("codeberg.org", newConfig)
|
|
|
|
|
|
2026-03-23 12:39:51 -06:00
|
|
|
host, err := cfg.GetHost("codeberg.org", "", "")
|
2025-12-15 15:07:42 +01:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if host.Token != "new-token" {
|
|
|
|
|
t.Errorf("Expected token 'new-token', got '%s'", host.Token)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if host.User != "newuser" {
|
|
|
|
|
t.Errorf("Expected user 'newuser', got '%s'", host.User)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestConfig_MultipleHosts(t *testing.T) {
|
|
|
|
|
cfg := &Config{}
|
|
|
|
|
|
|
|
|
|
hosts := []struct {
|
|
|
|
|
hostname string
|
|
|
|
|
token string
|
|
|
|
|
user string
|
|
|
|
|
}{
|
|
|
|
|
{"codeberg.org", "token1", "user1"},
|
|
|
|
|
{"github.com", "token2", "user2"},
|
|
|
|
|
{"gitlab.com", "token3", "user3"},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add multiple hosts
|
|
|
|
|
for _, h := range hosts {
|
|
|
|
|
cfg.SetHost(h.hostname, HostConfig{
|
|
|
|
|
Hostname: h.hostname,
|
|
|
|
|
Token: h.token,
|
|
|
|
|
User: h.user,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Verify all hosts are stored
|
|
|
|
|
if len(cfg.Hosts) != 3 {
|
|
|
|
|
t.Errorf("Expected 3 hosts, got %d", len(cfg.Hosts))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Verify each host can be retrieved correctly
|
|
|
|
|
for _, h := range hosts {
|
2026-03-23 12:39:51 -06:00
|
|
|
host, err := cfg.GetHost(h.hostname, "", "")
|
2025-12-15 15:07:42 +01:00
|
|
|
if err != nil {
|
|
|
|
|
t.Errorf("Failed to get host %s: %v", h.hostname, err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if host.Token != h.token {
|
|
|
|
|
t.Errorf("Host %s: expected token '%s', got '%s'", h.hostname, h.token, host.Token)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if host.User != h.user {
|
|
|
|
|
t.Errorf("Host %s: expected user '%s', got '%s'", h.hostname, h.user, host.User)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestConfig_GitProtocol(t *testing.T) {
|
|
|
|
|
cfg := &Config{}
|
|
|
|
|
|
|
|
|
|
// Test SSH protocol
|
|
|
|
|
cfg.SetHost("test-ssh.org", HostConfig{
|
|
|
|
|
Hostname: "test-ssh.org",
|
|
|
|
|
Token: "token",
|
|
|
|
|
GitProtocol: "ssh",
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// Test HTTPS protocol
|
|
|
|
|
cfg.SetHost("test-https.org", HostConfig{
|
|
|
|
|
Hostname: "test-https.org",
|
|
|
|
|
Token: "token",
|
|
|
|
|
GitProtocol: "https",
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// Verify protocols are stored correctly
|
2026-03-23 12:39:51 -06:00
|
|
|
sshHost, _ := cfg.GetHost("test-ssh.org", "", "")
|
2025-12-15 15:07:42 +01:00
|
|
|
if sshHost.GitProtocol != "ssh" {
|
|
|
|
|
t.Errorf("Expected git_protocol 'ssh', got '%s'", sshHost.GitProtocol)
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-23 12:39:51 -06:00
|
|
|
httpsHost, _ := cfg.GetHost("test-https.org", "", "")
|
2025-12-15 15:07:42 +01:00
|
|
|
if httpsHost.GitProtocol != "https" {
|
|
|
|
|
t.Errorf("Expected git_protocol 'https', got '%s'", httpsHost.GitProtocol)
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-03-23 12:39:51 -06:00
|
|
|
|
|
|
|
|
func TestResolveHostByPath(t *testing.T) {
|
|
|
|
|
cfg := &Config{
|
|
|
|
|
Hosts: map[string]HostConfig{
|
|
|
|
|
"forgejo.zerova.net": {
|
|
|
|
|
Hostname: "forgejo.zerova.net",
|
|
|
|
|
Token: "token1",
|
2026-04-26 08:16:52 -06:00
|
|
|
MatchDirs: []string{"/Users/sid/repos/fj", "/Users/sid/repos/zerova"},
|
2026-03-23 12:39:51 -06:00
|
|
|
},
|
|
|
|
|
"codeberg.org": {
|
|
|
|
|
Hostname: "codeberg.org",
|
|
|
|
|
Token: "token2",
|
|
|
|
|
MatchDirs: []string{"/"},
|
|
|
|
|
},
|
|
|
|
|
"gitea.example.com": {
|
|
|
|
|
Hostname: "gitea.example.com",
|
|
|
|
|
Token: "token3",
|
|
|
|
|
// no match_dirs — should never be selected by path
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tests := []struct {
|
|
|
|
|
name string
|
|
|
|
|
cwd string
|
|
|
|
|
want string
|
|
|
|
|
}{
|
2026-04-26 08:16:52 -06:00
|
|
|
{"exact dir match", "/Users/sid/repos/fj", "forgejo.zerova.net"},
|
|
|
|
|
{"nested dir match", "/Users/sid/repos/fj/cmd/root.go", "forgejo.zerova.net"},
|
2026-03-23 12:39:51 -06:00
|
|
|
{"second match dir", "/Users/sid/repos/zerova/pkg", "forgejo.zerova.net"},
|
2026-04-26 08:16:52 -06:00
|
|
|
{"longest prefix wins over /", "/Users/sid/repos/fj/internal", "forgejo.zerova.net"},
|
2026-03-23 12:39:51 -06:00
|
|
|
{"/ as global catch-all", "/tmp", "codeberg.org"},
|
|
|
|
|
{"/ matches root itself", "/", "codeberg.org"},
|
|
|
|
|
{"no match_dirs host not selected", "/some/random/path", "codeberg.org"},
|
|
|
|
|
{"empty cwd returns empty", "", ""},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
|
got := cfg.ResolveHostByPath(tt.cwd)
|
|
|
|
|
if got != tt.want {
|
|
|
|
|
t.Errorf("ResolveHostByPath(%q) = %q, want %q", tt.cwd, got, tt.want)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestResolveHostByPath_LongestPrefixAcrossHosts(t *testing.T) {
|
|
|
|
|
cfg := &Config{
|
|
|
|
|
Hosts: map[string]HostConfig{
|
|
|
|
|
"broad.org": {
|
|
|
|
|
Hostname: "broad.org",
|
|
|
|
|
Token: "t1",
|
|
|
|
|
MatchDirs: []string{"/Users/sid"},
|
|
|
|
|
},
|
|
|
|
|
"specific.org": {
|
|
|
|
|
Hostname: "specific.org",
|
|
|
|
|
Token: "t2",
|
|
|
|
|
MatchDirs: []string{"/Users/sid/repos/myproject"},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
got := cfg.ResolveHostByPath("/Users/sid/repos/myproject/main.go")
|
|
|
|
|
if got != "specific.org" {
|
|
|
|
|
t.Errorf("expected specific.org, got %q", got)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
got = cfg.ResolveHostByPath("/Users/sid/other")
|
|
|
|
|
if got != "broad.org" {
|
|
|
|
|
t.Errorf("expected broad.org, got %q", got)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestGetHost_MatchDirsIntegration(t *testing.T) {
|
|
|
|
|
cfg := &Config{
|
|
|
|
|
Hosts: map[string]HostConfig{
|
|
|
|
|
"forgejo.zerova.net": {
|
|
|
|
|
Hostname: "forgejo.zerova.net",
|
|
|
|
|
Token: "token1",
|
2026-04-26 08:16:52 -06:00
|
|
|
MatchDirs: []string{"/Users/sid/repos/fj"},
|
2026-03-23 12:39:51 -06:00
|
|
|
},
|
|
|
|
|
"codeberg.org": {
|
|
|
|
|
Hostname: "codeberg.org",
|
|
|
|
|
Token: "token2",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// cwd match should resolve to forgejo.zerova.net
|
2026-04-26 08:16:52 -06:00
|
|
|
host, err := cfg.GetHost("", "", "/Users/sid/repos/fj/cmd")
|
2026-03-23 12:39:51 -06:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("unexpected error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if host.Hostname != "forgejo.zerova.net" {
|
|
|
|
|
t.Errorf("expected forgejo.zerova.net, got %s", host.Hostname)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// no cwd match falls through to codeberg.org default
|
|
|
|
|
host, err = cfg.GetHost("", "", "/tmp")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("unexpected error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if host.Hostname != "codeberg.org" {
|
|
|
|
|
t.Errorf("expected codeberg.org, got %s", host.Hostname)
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-03-23 12:50:49 -06:00
|
|
|
|
|
|
|
|
func TestResolveHostByPath_TildeExpansion(t *testing.T) {
|
|
|
|
|
home, err := os.UserHomeDir()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Skip("cannot determine home directory")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cfg := &Config{
|
|
|
|
|
Hosts: map[string]HostConfig{
|
|
|
|
|
"tilde.org": {
|
|
|
|
|
Hostname: "tilde.org",
|
|
|
|
|
Token: "t1",
|
|
|
|
|
MatchDirs: []string{"~/repos"},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
got := cfg.ResolveHostByPath(filepath.Join(home, "repos", "myproject"))
|
|
|
|
|
if got != "tilde.org" {
|
|
|
|
|
t.Errorf("expected tilde.org, got %q", got)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
got = cfg.ResolveHostByPath(filepath.Join(home, "other"))
|
|
|
|
|
if got != "" {
|
|
|
|
|
t.Errorf("expected empty, got %q", got)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-23 12:55:49 -06:00
|
|
|
func TestResolveHostByPath_TieBreakByConfigOrder(t *testing.T) {
|
|
|
|
|
cfg := &Config{
|
|
|
|
|
Hosts: map[string]HostConfig{
|
|
|
|
|
"second.org": {
|
|
|
|
|
Hostname: "second.org",
|
|
|
|
|
Token: "t2",
|
|
|
|
|
MatchDirs: []string{"/shared/path"},
|
|
|
|
|
Order: 1,
|
|
|
|
|
},
|
|
|
|
|
"first.org": {
|
|
|
|
|
Hostname: "first.org",
|
|
|
|
|
Token: "t1",
|
|
|
|
|
MatchDirs: []string{"/shared/path"},
|
|
|
|
|
Order: 0,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
got := cfg.ResolveHostByPath("/shared/path/subdir")
|
|
|
|
|
if got != "first.org" {
|
|
|
|
|
t.Errorf("expected first.org (earlier in config), got %q", got)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestAssignHostOrder(t *testing.T) {
|
|
|
|
|
yamlData := []byte(`hosts:
|
|
|
|
|
alpha.org:
|
|
|
|
|
hostname: alpha.org
|
|
|
|
|
token: t1
|
|
|
|
|
beta.org:
|
|
|
|
|
hostname: beta.org
|
|
|
|
|
token: t2
|
|
|
|
|
gamma.org:
|
|
|
|
|
hostname: gamma.org
|
|
|
|
|
token: t3
|
|
|
|
|
`)
|
|
|
|
|
cfg, err := LoadFromPath(writeTempConfig(t, yamlData))
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("unexpected error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if cfg.Hosts["alpha.org"].Order != 0 {
|
|
|
|
|
t.Errorf("alpha.org order = %d, want 0", cfg.Hosts["alpha.org"].Order)
|
|
|
|
|
}
|
|
|
|
|
if cfg.Hosts["beta.org"].Order != 1 {
|
|
|
|
|
t.Errorf("beta.org order = %d, want 1", cfg.Hosts["beta.org"].Order)
|
|
|
|
|
}
|
|
|
|
|
if cfg.Hosts["gamma.org"].Order != 2 {
|
|
|
|
|
t.Errorf("gamma.org order = %d, want 2", cfg.Hosts["gamma.org"].Order)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func writeTempConfig(t *testing.T, data []byte) string {
|
|
|
|
|
t.Helper()
|
|
|
|
|
path := filepath.Join(t.TempDir(), "config.yaml")
|
|
|
|
|
if err := os.WriteFile(path, data, 0600); err != nil {
|
|
|
|
|
t.Fatalf("failed to write temp config: %v", err)
|
|
|
|
|
}
|
|
|
|
|
return path
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-23 12:50:49 -06:00
|
|
|
func TestExpandHome(t *testing.T) {
|
|
|
|
|
home, err := os.UserHomeDir()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Skip("cannot determine home directory")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tests := []struct {
|
|
|
|
|
input string
|
|
|
|
|
want string
|
|
|
|
|
}{
|
|
|
|
|
{"~/repos", filepath.Join(home, "repos")},
|
|
|
|
|
{"~", home},
|
|
|
|
|
{"/absolute/path", "/absolute/path"},
|
|
|
|
|
{"relative/path", "relative/path"},
|
|
|
|
|
{"~other", "~other"}, // only ~/... is expanded, not ~user
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
|
got := expandHome(tt.input)
|
|
|
|
|
if got != tt.want {
|
|
|
|
|
t.Errorf("expandHome(%q) = %q, want %q", tt.input, got, tt.want)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|