test: Prozessgrenzen-Tests — talkd + talk als echte Prozesse gegen Fake-Provider und sink null
Primäre Seam laut Spec: talkd/talk gebuildet und als Prozesse gefahren, ElevenLabs-Fake per httptest (Ankünfte + Antworten mit Zeiten), TALK_PORT auf freie Ports (nie 7800), isoliertes Config-Dir. Getestet: Serialisierung/Priorität/now-Sprung, cancel (Synthese-Abbruch + Zahl), non-blocking + --wait, mute-Persistenz über Restart + offline geschriebene Datei, Degraded Mode (Lock-Serialisierung, mute, Lock-Freigabe), 3 parallele talk-Aufrufe, HTTP-Validierung (400/405), Status-Vertrag.
This commit is contained in:
1 parent
e1a3981d17
commit
1ae1f1262c
4 files changed
+1146
No files matched your search
@@ -0,0 +1,103 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// TestDegradedMuteDateiWirdVonDaemonGelesen: eine offline (CLI, ohne
|
||||
// Daemon) geschriebene Mute-Datei wirkt sofort im Daemon — beide lesen
|
||||
// dieselbe Quelle.
|
||||
func TestDegradedMuteDateiWirdVonDaemonGelesen(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")
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user