package test import ( "os" "path/filepath" "strings" "testing" "time" ) // TestMutePersistenzUeberRestart (Abnahmekriterium): `talk mute` → Daemon- // Restart → weiter stumm. Mute verwirft Ansagen dokumentiert mit // 202 {id:"", muted:true} und ohne jeden Provider-Kontakt. func TestMutePersistenzUeberRestart(t *testing.T) { root := t.TempDir() fake := newFakeProvider(t, 0) d := startDaemon(t, root, fake.srv.URL, nil) // Über die HTTP-API stummschalten: Datei mute liegt im Config-Dir. code, m := postPath(t, d, "/mute") if code != 200 || m["muted"] != true { t.Fatalf("POST /mute: %d %v", code, m) } muteFile := filepath.Join(configDir(root), "mute") b, err := os.ReadFile(muteFile) if err != nil { t.Fatalf("Mute-Datei fehlt im Config-Dir: %v", err) } if strings.TrimSpace(string(b)) != "true" { t.Errorf("Mute-Datei = %q, want true", b) } // Ansagen werden verworfen: 202, muted:true, leere ID, kein Treffer. code, m = speak(t, d, `{"text":"stille"}`) if code != 202 || m["muted"] != true || m["id"] != "" { t.Fatalf("speak bei Mute: %d %v, want 202 {id:, muted:true}", code, m) } fake.assertNoHits(t, 300*time.Millisecond) if st := getStatus(t, d); !st.Muted { t.Fatal("/status meldet muted=false") } // Daemon-Restart mit demselben Config-Dir: weiter stumm. d.stop(t) d2 := startDaemon(t, root, fake.srv.URL, nil) if st := getStatus(t, d2); !st.Muted { t.Fatalf("nach Restart muted = false, want true (persistent)") } code, m = speak(t, d2, `{"text":"immer noch stumm"}`) if code != 202 || m["muted"] != true { t.Fatalf("speak nach Restart bei Mute: %d %v", code, m) } fake.assertNoHits(t, 300*time.Millisecond) // Unmute: Ansagen laufen wieder. code, m = postPath(t, d2, "/unmute") if code != 200 || m["muted"] != false { t.Fatalf("POST /unmute: %d %v", code, m) } code, m = speak(t, d2, `{"text":"wieder da"}`) if code != 202 || m["id"] == "" { t.Fatalf("speak nach unmute: %d %v, want 202 mit id", code, m) } hits := fake.waitHits(t, 1) if hits[0].Text != "wieder da" { t.Errorf("Treffer = %q, want wieder da", hits[0].Text) } } // TestOfflineMuteDateiWirdVonDaemonGelesen: eine offline (CLI, ohne // Daemon) geschriebene Mute-Datei wirkt sofort im Daemon — beide lesen // dieselbe Quelle. func TestOfflineMuteDateiWirdVonDaemonGelesen(t *testing.T) { root := t.TempDir() fake := newFakeProvider(t, 0) d := startDaemon(t, root, fake.srv.URL, nil) deadEnv := daemonEnv(root, 1, fake.srv.URL, nil) // Offline stummschalten (talk mute ohne Daemon → Datei direkt). res := runTalk(t, deadEnv, "mute") if res.code != 0 || res.stdout != "muted\n" { t.Fatalf("talk mute (offline): %d %q — stderr: %s", res.code, res.stdout, res.stderr) } // Der laufende Daemon sieht den Zustand bei der nächsten Ansage. if st := getStatus(t, d); !st.Muted { t.Fatal("Daemon sieht offline geschriebene Mute-Datei nicht") } code, m := speak(t, d, `{"text":"soll verworfen werden"}`) if code != 202 || m["muted"] != true { t.Fatalf("speak: %d %v, want verworfen", code, m) } fake.assertNoHits(t, 300*time.Millisecond) res = runTalk(t, deadEnv, "unmute") if res.code != 0 { t.Fatalf("talk unmute (offline): %d — stderr: %s", res.code, res.stderr) } if st := getStatus(t, d); st.Muted { t.Fatal("Daemon sieht offline geschriebene Unmute-Datei nicht") } }