replace log.Fatal/os.Exit with error returns (#941)

* Use stdlib encoders
* Reduce some duplication
* Remove global pagination state
* Dedupe JSON detail types
* Bump golangci-lint

Reviewed-on: https://gitea.com/gitea/tea/pulls/941
Co-authored-by: techknowlogick <techknowlogick@gitea.com>
Co-committed-by: techknowlogick <techknowlogick@gitea.com>
This commit is contained in:
techknowlogick
2026-03-27 03:36:44 +00:00
committed by techknowlogick
parent 21881525a8
commit b05e03416b
124 changed files with 1610 additions and 759 deletions

View File

@@ -40,12 +40,21 @@ type LocalConfig struct {
var (
// config contain if loaded local tea config
config LocalConfig
loadConfigOnce sync.Once
config LocalConfig
loadConfigOnce sync.Once
configPathMu sync.Mutex
configPathTestOverride string
)
// GetConfigPath return path to tea config file
func GetConfigPath() string {
configPathMu.Lock()
override := configPathTestOverride
configPathMu.Unlock()
if override != "" {
return override
}
configFilePath, err := xdg.ConfigFile("tea/config.yml")
var exists bool
@@ -71,6 +80,13 @@ func GetConfigPath() string {
return configFilePath
}
// SetConfigPathForTesting overrides the config path used by helpers in tests.
func SetConfigPathForTesting(path string) {
configPathMu.Lock()
configPathTestOverride = path
configPathMu.Unlock()
}
// GetPreferences returns preferences based on the config file
func GetPreferences() Preferences {
_ = loadConfig()

View File

@@ -72,28 +72,36 @@ func TestConfigLock_MutexProtection(t *testing.T) {
}
}
func useTempConfigPath(t *testing.T) string {
t.Helper()
configPath := filepath.Join(t.TempDir(), "config.yml")
SetConfigPathForTesting(configPath)
t.Cleanup(func() {
SetConfigPathForTesting("")
})
return configPath
}
func TestReloadConfigFromDisk(t *testing.T) {
configPath := useTempConfigPath(t)
// Save original config state
originalConfig := config
// Create a temp config file
tmpDir, err := os.MkdirTemp("", "tea-reload-test")
if err != nil {
t.Fatalf("failed to create temp dir: %v", err)
config = LocalConfig{Logins: []Login{{Name: "stale"}}}
if err := os.WriteFile(configPath, []byte("logins:\n - name: test\n"), 0o600); err != nil {
t.Fatalf("failed to write temp config: %v", err)
}
defer os.RemoveAll(tmpDir)
// We can't easily change GetConfigPath, so we test that reloadConfigFromDisk
// handles a missing file gracefully (returns nil and resets config)
config = LocalConfig{Logins: []Login{{Name: "test"}}}
// Call reload - since the actual config path likely exists or doesn't,
// we just verify it doesn't panic and returns without error or with expected error
err = reloadConfigFromDisk()
// The function should either succeed or return an error, not panic
err := reloadConfigFromDisk()
if err != nil {
// This is acceptable - config file might not exist in test environment
t.Logf("reloadConfigFromDisk returned error (expected in test env): %v", err)
t.Fatalf("reloadConfigFromDisk returned error: %v", err)
}
if len(config.Logins) != 1 || config.Logins[0].Name != "test" {
t.Fatalf("expected config to reload test login, got %+v", config.Logins)
}
// Restore original config
@@ -101,6 +109,8 @@ func TestReloadConfigFromDisk(t *testing.T) {
}
func TestWithConfigLock(t *testing.T) {
useTempConfigPath(t)
executed := false
err := withConfigLock(func() error {
executed = true
@@ -115,6 +125,8 @@ func TestWithConfigLock(t *testing.T) {
}
func TestWithConfigLock_PropagatesError(t *testing.T) {
useTempConfigPath(t)
expectedErr := fmt.Errorf("test error")
err := withConfigLock(func() error {
return expectedErr
@@ -126,6 +138,8 @@ func TestWithConfigLock_PropagatesError(t *testing.T) {
}
func TestDoubleCheckedLocking_SimulatedRefresh(t *testing.T) {
useTempConfigPath(t)
// This test simulates the double-checked locking pattern
// by having multiple goroutines try to "refresh" simultaneously

View File

@@ -164,18 +164,17 @@ func SetDefaultLogin(name string) error {
}
// GetLoginByName get login by name (case insensitive)
func GetLoginByName(name string) *Login {
err := loadConfig()
if err != nil {
log.Fatal(err)
func GetLoginByName(name string) (*Login, error) {
if err := loadConfig(); err != nil {
return nil, err
}
for _, l := range config.Logins {
if strings.EqualFold(l.Name, name) {
return &l
for i := range config.Logins {
if strings.EqualFold(config.Logins[i].Name, name) {
return &config.Logins[i], nil
}
}
return nil
return nil, nil
}
// GetLoginByToken get login by token