Pulse/internal/config/watcher_extra_test.go
rcourtman ed78509f92 Fix flaky tests and improve coverage across alerts, api, and config packages
- Fix deadlock and race conditions in internal/alerts
- Add comprehensive error path tests for internal/config
- Fix 401 handling in internal/api
- Fix Docker Swarm task filtering test logic
2026-01-03 18:36:17 +00:00

109 lines
2.9 KiB
Go

package config
import (
"os"
"path/filepath"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestConfigWatcher_WatchForChanges_Live(t *testing.T) {
tempDir := t.TempDir()
envPath := filepath.Join(tempDir, ".env")
apiTokensPath := filepath.Join(tempDir, "api_tokens.json")
mockEnvPath := filepath.Join(tempDir, "mock.env")
require.NoError(t, os.WriteFile(envPath, []byte("PULSE_AUTH_USER=initial"), 0644))
require.NoError(t, os.WriteFile(apiTokensPath, []byte("[]"), 0644))
require.NoError(t, os.WriteFile(mockEnvPath, []byte("PULSE_MOCK_TEST=1"), 0644))
t.Setenv("PULSE_AUTH_CONFIG_DIR", tempDir)
cfg := &Config{}
cw, err := NewConfigWatcher(cfg)
require.NoError(t, err)
cw.mockEnvPath = mockEnvPath // Force mock.env path for test
// Setup callbacks
mockReloaded := make(chan bool, 1)
tokensReloaded := make(chan bool, 1)
cw.SetMockReloadCallback(func() { mockReloaded <- true })
cw.SetAPITokenReloadCallback(func() { tokensReloaded <- true })
// Start watching
err = cw.Start()
require.NoError(t, err)
defer cw.Stop()
// Give watcher time to start
time.Sleep(100 * time.Millisecond)
// 1. Test .env change
require.NoError(t, os.WriteFile(envPath, []byte("PULSE_AUTH_USER=something-different"), 0644))
require.Eventually(t, func() bool {
Mu.RLock()
defer Mu.RUnlock()
return cfg.AuthUser == "something-different"
}, 5*time.Second, 200*time.Millisecond)
// 2. Test api_tokens.json change
// Mock global persistence for API token reloads
p := NewConfigPersistence(tempDir)
originalPersistence := globalPersistence
globalPersistence = p
defer func() { globalPersistence = originalPersistence }()
// Write empty tokens list but it MUST be valid JSON
require.NoError(t, os.WriteFile(apiTokensPath, []byte("[]"), 0644))
select {
case <-tokensReloaded:
// Success
case <-time.After(5 * time.Second):
t.Error("Timed out waiting for API token reload")
}
// 3. Test mock.env change
require.NoError(t, os.WriteFile(mockEnvPath, []byte("PULSE_MOCK_TEST=2"), 0644))
select {
case <-mockReloaded:
// Success
case <-time.After(2 * time.Second):
t.Error("Timed out waiting for mock reload")
}
}
func TestConfigWatcher_WatchForChanges_ErrorHandling(t *testing.T) {
tempDir := t.TempDir()
envPath := filepath.Join(tempDir, ".env")
require.NoError(t, os.WriteFile(envPath, []byte(""), 0644))
t.Setenv("PULSE_AUTH_CONFIG_DIR", tempDir)
cfg := &Config{}
cw, err := NewConfigWatcher(cfg)
require.NoError(t, err)
// Inject error into watcher channel
go func() {
cw.watcher.Errors <- os.ErrPermission
}()
// Start normally but we want to see it Doesn't crash on error
go cw.watchForChanges()
defer cw.Stop()
// Wait a bit to ensure loop handles error
time.Sleep(100 * time.Millisecond)
}
func TestConfigWatcher_CalculateFileHash_NotFound(t *testing.T) {
cw := &ConfigWatcher{}
hash, err := cw.calculateFileHash("/path/to/nothing")
assert.Error(t, err)
assert.Empty(t, hash)
}