package runtimeconfig import ( "errors" "os" "path/filepath" "testing" ) // TestLoadVoiceTTSLaunchTuning 锁定 voice-tts-launch.json 的四条读取口径: // 缺失 = 没配过(不是错误、**不启用**)、全空白 = 没配过、合法 = 逐键读出、 // 坏文件 = 可分类错误(调用方据此回落"不启用")。 func TestLoadVoiceTTSLaunchTuning(t *testing.T) { dir := t.TempDir() path := filepath.Join(dir, "voice-tts-launch.json") // ① 缺失 ⇒ 零值 + nil(默认关:这把开关会起进程,不能靠"猜"打开) if tuning, err := LoadVoiceTTSLaunchTuning(path); err != nil || tuning.EnabledOn() { t.Fatalf("缺失: tuning=%+v err=%v, want 零值 + nil", tuning, err) } // ② 全空白 ⇒ 同样当作没配过 if err := os.WriteFile(path, []byte(" \n\t "), 0o600); err != nil { t.Fatalf("write: %v", err) } if tuning, err := LoadVoiceTTSLaunchTuning(path); err != nil || tuning.EnabledOn() { t.Fatalf("空白: tuning=%+v err=%v, want 零值 + nil", tuning, err) } // ③ 合法 ⇒ 逐键读出(脚本路径含中文,正是主人那台机器的形状) body := `{"enabled":true,"scriptPath":"D:\\Project\\Service\\emotionTTS_v5.1.0\\一键启动.bat", "probeUrl":"http://127.0.0.1:9880","probeTimeoutMs":250,"cooldownSec":90}` if err := os.WriteFile(path, []byte(body), 0o600); err != nil { t.Fatalf("write: %v", err) } tuning, err := LoadVoiceTTSLaunchTuning(path) if err != nil { t.Fatalf("合法文件: %v", err) } if !tuning.EnabledOn() { t.Fatal("enabled:true 没被读出") } if tuning.ProbeTimeoutMs != 250 || tuning.CooldownSec != 90 { t.Fatalf("超时/冷却没读出: %+v", tuning) } if tuning.ProbeURL != "http://127.0.0.1:9880" { t.Fatalf("probeUrl = %q", tuning.ProbeURL) } if filepath.Base(tuning.ScriptPath) != "一键启动.bat" { t.Fatalf("scriptPath 没读出: %q", tuning.ScriptPath) } // ④ 显式 false ⇒ 与"没配过"同效(EnabledOn 为 false) if err := os.WriteFile(path, []byte(`{"enabled":false}`), 0o600); err != nil { t.Fatalf("write: %v", err) } if tuning, err = LoadVoiceTTSLaunchTuning(path); err != nil || tuning.EnabledOn() { t.Fatalf("显式 false: tuning=%+v err=%v, want 不启用", tuning, err) } // ⑤ 坏文件 ⇒ 分类错误(调用方据此 WARN + 不启用),且绝不覆盖原文件 if err := os.WriteFile(path, []byte(`{"enabled":`), 0o600); err != nil { t.Fatalf("write: %v", err) } if _, err = LoadVoiceTTSLaunchTuning(path); !errors.Is(err, ErrInvalidVoiceTTSLaunch) { t.Fatalf("坏文件: err=%v, want ErrInvalidVoiceTTSLaunch", err) } if raw, rerr := os.ReadFile(path); rerr != nil || string(raw) != `{"enabled":` { t.Fatalf("坏文件被改写了: %q err=%v", raw, rerr) } } // TestDefaultVoiceTTSLaunchPath 钉住落点:`$DATA_DIR/config/voice-tts-launch.json` // —— 它是"零新 .env 键"这一条的物理形态(开关只可能来自面板文件)。 func TestDefaultVoiceTTSLaunchPath(t *testing.T) { got := DefaultVoiceTTSLaunchPath("/data") want := filepath.Join("/data", "config", "voice-tts-launch.json") if got != want { t.Fatalf("落点 = %q, want %q", got, want) } if VoiceTTSLaunchConfigRelPath != "config/voice-tts-launch.json" { t.Fatalf("相对路径 = %q", VoiceTTSLaunchConfigRelPath) } }