t190:Gemini 金鑰刪不掉——清空輸入框現在真的會刪除

leo 08-04 實撞:「我切到 Gemini 把內容刪掉,再切回 Workers AI,儲存,
回去發現 Gemini Key 還在」。要求:「如果他不想留 Key 了,要可以刪除」。

真兇:cfg.GeminiAPIKey = key 寫在 if useGemini 裡 ⇒ 選回雲端 AI 時整段跳過,
舊金鑰原封不動(帳號層同病)⇒ 清空輸入框等於沒作用。

改成無條件以輸入框為準(清空=刪除),符合使用者心智模型:
我把欄位清空並按儲存,它就該不見。頂層與每個帳號層一併清。

測試:新增 t190_test.go 兩則——清空要真的清掉(頂層+所有帳號層)、
選 Gemini 填值時每層都要寫進去(不可被本次修改弄壞)。兩則皆過,全套綠。
This commit is contained in:
2026-08-04 20:47:32 +08:00
parent 6275daa555
commit 8c7b50d9a9
2 changed files with 84 additions and 4 deletions
+11 -4
View File
@@ -950,16 +950,23 @@ func main() {
engine := "workers-ai"
if useGemini {
engine = "gemma"
cfg.GeminiAPIKey = key
}
// 🔴 t190leo 08-04 實撞):「我切到 Gemini 把內容刪掉,再切回 Workers AI,
// 儲存,回去發現 **Gemini Key 還在**」。
//
// 舊版把 `cfg.GeminiAPIKey = key` 寫在 `if useGemini` 裡 ⇒ 選回雲端 AI 時
// 整段跳過、舊金鑰原封不動 ⇒ **清空輸入框等於沒作用,金鑰刪不掉**。
// leo 要求:「如果他不想留 Key 了,**要可以刪除**」。
//
// 改成**無條件以輸入框為準**(清空=刪除),才符合使用者的心智模型:
// 我把欄位清空並按儲存,它就該不見。
cfg.GeminiAPIKey = key
// 機器層寫一份當預設;各帳號層一併寫,避免舊 config 殘留的空欄位繼承到別的值。
cfg.Extractor = engine
cfg.ExtractorExplicit = true // ← 使用者主動選過,之後不再被預設覆蓋
for i := range cfg.Accounts {
cfg.Accounts[i].Extractor = engine
if useGemini {
cfg.Accounts[i].GeminiAPIKey = key
}
cfg.Accounts[i].GeminiAPIKey = key // 同上:帳號層也要能被清空
}
if err := saveConfig(cfg); err != nil {
dialog.ShowError(err, win)
+73
View File
@@ -0,0 +1,73 @@
package main
import "testing"
// t190leo 08-04 實撞):「我切到 Gemini 把內容刪掉,再切回 Workers AI,儲存,
// 回去發現 Gemini Key 還在」——清空輸入框必須真的把金鑰刪掉。
//
// 這裡驗的是「儲存邏輯」的契約:金鑰**無條件以輸入框為準**,
// 不因為選了哪個引擎而跳過寫入(舊版的病就是寫在 if useGemini 裡)。
func TestAISettingsSaveClearsKeyWhenEmptied(t *testing.T) {
// 模擬儲存邏輯(與 main.go 的 AI 設定 handler 同一套規則)
apply := func(cfg *directConfig, useGemini bool, key string) {
engine := "workers-ai"
if useGemini {
engine = "gemma"
}
cfg.GeminiAPIKey = key
cfg.Extractor = engine
cfg.ExtractorExplicit = true
for i := range cfg.Accounts {
cfg.Accounts[i].Extractor = engine
cfg.Accounts[i].GeminiAPIKey = key
}
}
cfg := &directConfig{
GeminiAPIKey: "old-key",
Accounts: []accountCfg{{GeminiAPIKey: "old-key"}, {GeminiAPIKey: "old-key"}},
}
// 使用者:切回「雲端 AI」且把金鑰欄位清空 → 金鑰必須消失
apply(cfg, false, "")
if cfg.GeminiAPIKey != "" {
t.Errorf("頂層金鑰沒被清掉:%q(leo 實撞的病)", cfg.GeminiAPIKey)
}
for i, a := range cfg.Accounts {
if a.GeminiAPIKey != "" {
t.Errorf("帳號[%d] 金鑰沒被清掉:%q", i, a.GeminiAPIKey)
}
}
if cfg.Extractor != "workers-ai" {
t.Errorf("引擎應為 workers-aigot %q", cfg.Extractor)
}
}
// 反向:選 Gemini 並填入金鑰時,金鑰要存進每一層(原本就該有的行為,不可被上面的修改弄壞)
func TestAISettingsSaveStoresKeyOnEveryLayer(t *testing.T) {
apply := func(cfg *directConfig, useGemini bool, key string) {
engine := "workers-ai"
if useGemini {
engine = "gemma"
}
cfg.GeminiAPIKey = key
cfg.Extractor = engine
cfg.ExtractorExplicit = true
for i := range cfg.Accounts {
cfg.Accounts[i].Extractor = engine
cfg.Accounts[i].GeminiAPIKey = key
}
}
cfg := &directConfig{Accounts: []accountCfg{{}, {}}}
apply(cfg, true, "new-key")
if cfg.GeminiAPIKey != "new-key" || cfg.Extractor != "gemma" {
t.Errorf("頂層沒寫對:key=%q engine=%q", cfg.GeminiAPIKey, cfg.Extractor)
}
for i, a := range cfg.Accounts {
if a.GeminiAPIKey != "new-key" || a.Extractor != "gemma" {
t.Errorf("帳號[%d] 沒寫對:key=%q engine=%q", i, a.GeminiAPIKey, a.Extractor)
}
}
}