- say/Default erkennt den Daemon via GET /status: enqueue + sofort zurück (stdout = Item-ID), --wait pollt bis zur Fertigstellung, --now springt vor - mute/unmute/status/cancel mit plain Output; mute/unmute schreiben ohne Daemon die Mute-Datei direkt (gleiche Quelle, wirkt sofort im Daemon) - Degraded Mode: Lock-Datei talk.lock im Config-Dir (Kollision = warten, verwaist nach 60 s), sequenzielles lokales Abspielen, mute respektiert - Handler-Signatur der Befehlstabelle erhält stdout (say/Steuer-Befehle geben Ergebnisse aus); --priority wird validiert
142 lines
4.5 KiB
Go
142 lines
4.5 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"io"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// wantSubcommands ist die bindende CLI-Surface aus der Spec — bewusst als
|
|
// hartkodierte Liste getestet (nicht aus der Befehlstabelle abgeleitet),
|
|
// damit die Tests anschlagen, wenn Tabelle und Spec auseinanderlaufen.
|
|
var wantSubcommands = []string{
|
|
"say", "wrap", "mute", "unmute", "status", "cancel", "voices", "test", "doctor", "setup",
|
|
}
|
|
|
|
// wantStubCommands ist wantSubcommands ohne die implementierten Befehle —
|
|
// say (Task 02/04) sowie mute/unmute/status/cancel (Task 04, Daemon-
|
|
// Aufrufe) sind real; die restlichen bleiben Stubs bis zu ihren Tasks.
|
|
var wantStubCommands = []string{
|
|
"wrap", "voices", "test", "doctor", "setup",
|
|
}
|
|
|
|
// runCLI ist der Test-Einstieg in run mit leerem stdin (kein Text über
|
|
// Pipe) — dem üblichen Aufruf ohne Eingabe.
|
|
func runCLI(argv []string, stdout, stderr io.Writer) int {
|
|
return run(argv, strings.NewReader(""), stdout, stderr)
|
|
}
|
|
|
|
func TestHelpListetAlleSubcommands(t *testing.T) {
|
|
var out, errBuf bytes.Buffer
|
|
code := runCLI([]string{"--help"}, &out, &errBuf)
|
|
if code != exitOK {
|
|
t.Fatalf("--help: Exit-Code = %d, want %d (stderr: %q)", code, exitOK, errBuf.String())
|
|
}
|
|
lines := strings.Split(out.String(), "\n")
|
|
for _, name := range wantSubcommands {
|
|
found := false
|
|
for _, ln := range lines {
|
|
if strings.HasPrefix(strings.TrimLeft(ln, " "), name+" ") {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
t.Errorf("--help listet Befehl %q nicht (Output: %q)", name, out.String())
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestVersionDefault(t *testing.T) {
|
|
var out, errBuf bytes.Buffer
|
|
code := runCLI([]string{"--version"}, &out, &errBuf)
|
|
if code != exitOK {
|
|
t.Fatalf("--version: Exit-Code = %d, want %d", code, exitOK)
|
|
}
|
|
if got, want := out.String(), "talk version dev\n"; got != want {
|
|
t.Errorf("--version Output = %q, want %q", got, want)
|
|
}
|
|
}
|
|
|
|
func TestVersionStringMitCommit(t *testing.T) {
|
|
oldCommit := commit
|
|
commit = "c0ffee1"
|
|
defer func() { commit = oldCommit }()
|
|
|
|
if got, want := versionString(), "talk version dev (c0ffee1)"; got != want {
|
|
t.Errorf("versionString() = %q, want %q", got, want)
|
|
}
|
|
}
|
|
|
|
func TestSubcommandStubs(t *testing.T) {
|
|
for _, name := range wantStubCommands {
|
|
t.Run(name, func(t *testing.T) {
|
|
var out, errBuf bytes.Buffer
|
|
code := runCLI([]string{name}, &out, &errBuf)
|
|
if code == exitOK {
|
|
t.Errorf("talk %s: Exit-Code 0, want non-zero für Stub", name)
|
|
}
|
|
if !strings.Contains(errBuf.String(), "not implemented yet") {
|
|
t.Errorf("talk %s: stderr = %q, will Hinweis auf nicht implementiert", name, errBuf.String())
|
|
}
|
|
if !strings.Contains(errBuf.String(), name) {
|
|
t.Errorf("talk %s: stderr nennt den Befehlsnamen nicht: %q", name, errBuf.String())
|
|
}
|
|
if out.Len() != 0 {
|
|
t.Errorf("talk %s: unerwartete stdout-Ausgabe %q", name, out.String())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSayHelpZeigtFlags(t *testing.T) {
|
|
var out, errBuf bytes.Buffer
|
|
code := runCLI([]string{"say", "--help"}, &out, &errBuf)
|
|
if code != exitOK {
|
|
t.Fatalf("talk say --help: Exit-Code = %d, want %d", code, exitOK)
|
|
}
|
|
for _, f := range []string{"-wait", "-now", "-priority", "-voice", "-lang", "-provider"} {
|
|
if !strings.Contains(out.String(), f) {
|
|
t.Errorf("talk say --help zeigt Flag %s nicht (Output: %q)", f, out.String())
|
|
}
|
|
}
|
|
if strings.Contains(out.String(), "noch nicht implementiert") {
|
|
t.Errorf("talk say --help soll say nicht mehr als Stub markieren: %q", out.String())
|
|
}
|
|
}
|
|
|
|
func TestHelpSubcommand(t *testing.T) {
|
|
t.Run("ohne Argument", func(t *testing.T) {
|
|
var out, errBuf bytes.Buffer
|
|
if code := runCLI([]string{"help"}, &out, &errBuf); code != exitOK {
|
|
t.Fatalf("talk help: Exit-Code = %d, want %d", code, exitOK)
|
|
}
|
|
for _, name := range wantSubcommands {
|
|
if !strings.Contains(out.String(), name) {
|
|
t.Errorf("talk help listet %q nicht", name)
|
|
}
|
|
}
|
|
})
|
|
t.Run("mit Befehl", func(t *testing.T) {
|
|
var out, errBuf bytes.Buffer
|
|
if code := runCLI([]string{"help", "mute"}, &out, &errBuf); code != exitOK {
|
|
t.Fatalf("talk help mute: Exit-Code = %d, want %d", code, exitOK)
|
|
}
|
|
if !strings.Contains(out.String(), "talk mute") {
|
|
t.Errorf("talk help mute zeigt Usage von mute nicht: %q", out.String())
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestUnbekanntesFlag(t *testing.T) {
|
|
var out, errBuf bytes.Buffer
|
|
code := runCLI([]string{"--bogus"}, &out, &errBuf)
|
|
if code != exitUsage {
|
|
t.Fatalf("talk --bogus: Exit-Code = %d, want %d", code, exitUsage)
|
|
}
|
|
if !strings.Contains(errBuf.String(), "bogus") {
|
|
t.Errorf("talk --bogus: Fehlermeldung fehlt auf stderr: %q", errBuf.String())
|
|
}
|
|
}
|