package main import ( "bytes" "context" "path/filepath" "testing" "github.com/kami/maven/internal/audio" "github.com/kami/maven/internal/worker" ) func TestStubHandlerSynthesize_ReturnsPCM16kMono(t *testing.T) { h := &stubHandler{} resp, err := h.Synthesize(context.Background(), worker.SynthesizeReq{Text: "hello"}) if err != nil { t.Fatalf("unexpected error: %v", err) } if resp.Audio.Format != audio.PCM16kMono { t.Errorf("format = %v, want %v", resp.Audio.Format, audio.PCM16kMono) } } func TestStubHandlerSynthesize_NonEmptyAudio(t *testing.T) { h := &stubHandler{} resp, err := h.Synthesize(context.Background(), worker.SynthesizeReq{Text: "hello"}) if err != nil { t.Fatalf("unexpected error: %v", err) } if len(resp.Audio.Bytes) == 0 { t.Error("audio bytes are empty") } if len(resp.Audio.Bytes) != 6400 { t.Logf("audio bytes = %d (expected ~6400 for 200ms@16kHz)", len(resp.Audio.Bytes)) } } func TestStubHandlerSynthesize_DifferentTextDifferentAudio(t *testing.T) { h := &stubHandler{} a, _ := h.Synthesize(context.Background(), worker.SynthesizeReq{Text: "hello"}) b, _ := h.Synthesize(context.Background(), worker.SynthesizeReq{Text: "world"}) if bytes.Equal(a.Audio.Bytes, b.Audio.Bytes) { t.Error("different text produced identical audio") } } func TestStubHandlerSynthesize_EmptyTextProducesAudio(t *testing.T) { h := &stubHandler{} resp, err := h.Synthesize(context.Background(), worker.SynthesizeReq{Text: ""}) if err != nil { t.Fatalf("unexpected error: %v", err) } if len(resp.Audio.Bytes) == 0 { t.Error("empty text produced no audio") } } func TestResample22050To16000_EmptyInput(t *testing.T) { out := resample22050To16000(nil) if len(out) != 0 { t.Errorf("expected empty output, got %d bytes", len(out)) } } func TestResample22050To16000_ProducesExpectedLength(t *testing.T) { input := make([]byte, 44100) for i := range input { input[i] = byte(i) } out := resample22050To16000(input) expected := len(input) * 16000 / 22050 if diff := abs(len(out) - expected); diff > 4 { t.Errorf("output length = %d, want ~%d (diff %d)", len(out), expected, diff) } } func TestDefaultSocket_WithXdgRuntimeDir(t *testing.T) { t.Setenv("XDG_RUNTIME_DIR", "/run/user/1000") path := defaultSocket("tts.sock") want := "/run/user/1000/maven/tts.sock" if path != want { t.Errorf("got %q, want %q", path, want) } } func TestSynthesizeRoundTripViaSocket(t *testing.T) { sockDir := t.TempDir() sockPath := filepath.Join(sockDir, "tts.sock") h := &stubHandler{} srv := worker.NewSynthesizerServer(sockPath, h) if err := srv.Listen(); err != nil { t.Fatal(err) } go srv.Serve() defer srv.Close() client := worker.Dial(sockPath) defer client.Close() ctx := context.Background() resp, err := client.Synthesize(ctx, worker.SynthesizeReq{Text: "test"}) if err != nil { t.Fatalf("synthesize: %v", err) } if resp.Audio.Format != audio.PCM16kMono { t.Errorf("format = %v, want %v", resp.Audio.Format, audio.PCM16kMono) } if len(resp.Audio.Bytes) == 0 { t.Error("audio bytes are empty") } } func abs(n int) int { if n < 0 { return -n } return n }