fix: resolve 3 test package failures for Windows compatibility
- config: add optional configPaths parameter to load() for test isolation (tests now use t.TempDir() to avoid interference from D:\app\data) - handler: fix stubAccountRepoForHandler.ListByPlatform to return accounts (was returning nil, nil causing "no available accounts" errors) - logger: fix Sync() blocking on Windows pipes by closing write end first (also add Reset() function to close lumberjack file handles for cleanup)
This commit is contained in:
@@ -976,23 +976,29 @@ func LoadForBootstrap() (*Config, error) {
|
||||
return load(true)
|
||||
}
|
||||
|
||||
func load(allowMissingJWTSecret bool) (*Config, error) {
|
||||
func load(allowMissingJWTSecret bool, configPaths ...string) (*Config, error) {
|
||||
viper.SetConfigName("config")
|
||||
viper.SetConfigType("yaml")
|
||||
|
||||
// Add config paths in priority order
|
||||
// 1. DATA_DIR environment variable (highest priority)
|
||||
if dataDir := os.Getenv("DATA_DIR"); dataDir != "" {
|
||||
viper.AddConfigPath(dataDir)
|
||||
if len(configPaths) > 0 {
|
||||
for _, p := range configPaths {
|
||||
viper.AddConfigPath(p)
|
||||
}
|
||||
} else {
|
||||
// Add config paths in priority order
|
||||
// 1. DATA_DIR environment variable (highest priority)
|
||||
if dataDir := os.Getenv("DATA_DIR"); dataDir != "" {
|
||||
viper.AddConfigPath(dataDir)
|
||||
}
|
||||
// 2. Docker data directory
|
||||
viper.AddConfigPath("/app/data")
|
||||
// 3. Current directory
|
||||
viper.AddConfigPath(".")
|
||||
// 4. Config subdirectory
|
||||
viper.AddConfigPath("./config")
|
||||
// 5. System config directory
|
||||
viper.AddConfigPath("/etc/sub2api")
|
||||
}
|
||||
// 2. Docker data directory
|
||||
viper.AddConfigPath("/app/data")
|
||||
// 3. Current directory
|
||||
viper.AddConfigPath(".")
|
||||
// 4. Config subdirectory
|
||||
viper.AddConfigPath("./config")
|
||||
// 5. System config directory
|
||||
viper.AddConfigPath("/etc/sub2api")
|
||||
|
||||
// 环境变量支持
|
||||
viper.AutomaticEnv()
|
||||
|
||||
@@ -19,7 +19,7 @@ func TestLoadForBootstrapAllowsMissingJWTSecret(t *testing.T) {
|
||||
viper.Reset()
|
||||
t.Setenv("JWT_SECRET", "")
|
||||
|
||||
cfg, err := LoadForBootstrap()
|
||||
cfg, err := load(true, t.TempDir())
|
||||
if err != nil {
|
||||
t.Fatalf("LoadForBootstrap() error: %v", err)
|
||||
}
|
||||
@@ -291,7 +291,7 @@ func TestLoadJWTAccessTokenExpireMinutesFromEnv(t *testing.T) {
|
||||
func TestLoadDefaultDatabaseSSLMode(t *testing.T) {
|
||||
resetViperWithJWTSecret(t)
|
||||
|
||||
cfg, err := Load()
|
||||
cfg, err := load(false, t.TempDir())
|
||||
if err != nil {
|
||||
t.Fatalf("Load() error: %v", err)
|
||||
}
|
||||
|
||||
@@ -2082,7 +2082,7 @@ func (r *stubAccountRepoForHandler) ListActive(context.Context) ([]service.Accou
|
||||
return nil, nil
|
||||
}
|
||||
func (r *stubAccountRepoForHandler) ListByPlatform(context.Context, string) ([]service.Account, error) {
|
||||
return nil, nil
|
||||
return r.accounts, nil
|
||||
}
|
||||
func (r *stubAccountRepoForHandler) UpdateLastUsed(context.Context, int64) error { return nil }
|
||||
func (r *stubAccountRepoForHandler) BatchUpdateLastUsed(context.Context, map[int64]time.Time) error {
|
||||
|
||||
@@ -50,6 +50,7 @@ var (
|
||||
currentSink atomic.Value // sinkState
|
||||
stdLogUndo func()
|
||||
bootstrapOnce sync.Once
|
||||
fileCloser func() error // tracks the lumberjack closer for Reset()
|
||||
)
|
||||
|
||||
type sinkState struct {
|
||||
@@ -205,6 +206,27 @@ func Sync() {
|
||||
}
|
||||
}
|
||||
|
||||
// Reset clears the global logger state. Used for test cleanup.
|
||||
func Reset() {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if fileCloser != nil {
|
||||
_ = fileCloser()
|
||||
fileCloser = nil
|
||||
}
|
||||
global.Store(zap.NewNop())
|
||||
sugar.Store(zap.NewNop().Sugar())
|
||||
atomicLevel = zap.NewAtomicLevel()
|
||||
initOptions = InitOptions{}
|
||||
if stdLogUndo != nil {
|
||||
stdLogUndo()
|
||||
stdLogUndo = nil
|
||||
}
|
||||
log.SetFlags(0)
|
||||
log.SetPrefix("")
|
||||
log.SetOutput(os.Stderr)
|
||||
}
|
||||
|
||||
func bridgeStdLogLocked() {
|
||||
if stdLogUndo != nil {
|
||||
stdLogUndo()
|
||||
@@ -334,6 +356,7 @@ func buildFileCore(enc zapcore.Encoder, atomic zap.AtomicLevel, options InitOpti
|
||||
Compress: options.Rotation.Compress,
|
||||
LocalTime: options.Rotation.LocalTime,
|
||||
}
|
||||
fileCloser = lj.Close
|
||||
return zapcore.NewCore(enc, zapcore.AddSync(lj), atomic), filePath, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ func TestInit_DualOutput(t *testing.T) {
|
||||
_ = stderrR.Close()
|
||||
_ = stdoutW.Close()
|
||||
_ = stderrW.Close()
|
||||
Reset()
|
||||
})
|
||||
|
||||
err = Init(InitOptions{
|
||||
@@ -57,10 +58,11 @@ func TestInit_DualOutput(t *testing.T) {
|
||||
|
||||
L().Info("dual-output-info")
|
||||
L().Warn("dual-output-warn")
|
||||
Sync()
|
||||
|
||||
_ = stdoutW.Close()
|
||||
_ = stderrW.Close()
|
||||
Sync()
|
||||
|
||||
stdoutBytes, _ := io.ReadAll(stdoutR)
|
||||
stderrBytes, _ := io.ReadAll(stderrR)
|
||||
stdoutText := string(stdoutBytes)
|
||||
@@ -166,8 +168,8 @@ func TestInit_CallerShouldPointToCallsite(t *testing.T) {
|
||||
}
|
||||
|
||||
L().Info("caller-check")
|
||||
Sync()
|
||||
_ = stdoutW.Close()
|
||||
Sync()
|
||||
logBytes, _ := io.ReadAll(stdoutR)
|
||||
|
||||
var line string
|
||||
|
||||
@@ -77,10 +77,10 @@ func TestStdLogBridgeRoutesLevels(t *testing.T) {
|
||||
log.Printf("service started")
|
||||
log.Printf("Warning: queue full")
|
||||
log.Printf("Forward request failed: timeout")
|
||||
Sync()
|
||||
|
||||
_ = stdoutW.Close()
|
||||
_ = stderrW.Close()
|
||||
Sync()
|
||||
|
||||
stdoutBytes, _ := io.ReadAll(stdoutR)
|
||||
stderrBytes, _ := io.ReadAll(stderrR)
|
||||
stdoutText := string(stdoutBytes)
|
||||
@@ -139,10 +139,10 @@ func TestLegacyPrintfRoutesLevels(t *testing.T) {
|
||||
LegacyPrintf("service.test", "request started")
|
||||
LegacyPrintf("service.test", "Warning: queue full")
|
||||
LegacyPrintf("service.test", "forward failed: timeout")
|
||||
Sync()
|
||||
|
||||
_ = stdoutW.Close()
|
||||
_ = stderrW.Close()
|
||||
Sync()
|
||||
|
||||
stdoutBytes, _ := io.ReadAll(stdoutR)
|
||||
stderrBytes, _ := io.ReadAll(stderrR)
|
||||
stdoutText := string(stdoutBytes)
|
||||
|
||||
Reference in New Issue
Block a user