158 lines
4.5 KiB
Go
158 lines
4.5 KiB
Go
package app
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"sync/atomic"
|
|
"testing"
|
|
|
|
"gitea.maximumdirect.net/eric/narratio/internal/adapters/whisperx"
|
|
"gitea.maximumdirect.net/eric/narratio/internal/config"
|
|
"gitea.maximumdirect.net/eric/narratio/internal/manifest"
|
|
"gitea.maximumdirect.net/eric/narratio/internal/stage"
|
|
)
|
|
|
|
func TestExecuteStagesDefaultWiringUsesWhisperXHTTPClient(t *testing.T) {
|
|
var calls atomic.Int32
|
|
var gotLanguage string
|
|
var gotFileBytes int
|
|
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
calls.Add(1)
|
|
if r.Method != http.MethodPost {
|
|
t.Fatalf("method = %s, want POST", r.Method)
|
|
}
|
|
if err := r.ParseMultipartForm(8 << 20); err != nil {
|
|
t.Fatalf("ParseMultipartForm() error = %v", err)
|
|
}
|
|
gotLanguage = r.FormValue("language")
|
|
file, _, err := r.FormFile("file")
|
|
if err != nil {
|
|
t.Fatalf("FormFile(file) error = %v", err)
|
|
}
|
|
defer file.Close()
|
|
data, err := io.ReadAll(file)
|
|
if err != nil {
|
|
t.Fatalf("ReadAll(file) error = %v", err)
|
|
}
|
|
gotFileBytes = len(data)
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_, _ = w.Write([]byte(`{"source":"httptest","segments":[{"speaker":"alice"}]}`))
|
|
}))
|
|
defer srv.Close()
|
|
|
|
cfg := testConfig(t)
|
|
setWhisperXConfig(cfg, srv.URL, 0)
|
|
|
|
summary, err := executeStages(context.Background(), cfg, transcribePipelineStages(), RunOptions{})
|
|
if err != nil {
|
|
t.Fatalf("executeStages() error = %v", err)
|
|
}
|
|
if calls.Load() != 1 {
|
|
t.Fatalf("whisperx calls = %d, want 1", calls.Load())
|
|
}
|
|
if gotLanguage != "en" {
|
|
t.Fatalf("language = %q, want en", gotLanguage)
|
|
}
|
|
if gotFileBytes == 0 {
|
|
t.Fatal("audio file payload was empty")
|
|
}
|
|
|
|
outPath := filepath.Join(cfg.Pipeline.Workspace.Root, "work", cfg.Session.SessionID, "transcripts", "raw", "alice.json")
|
|
data, err := os.ReadFile(outPath)
|
|
if err != nil {
|
|
t.Fatalf("ReadFile(%q) error = %v", outPath, err)
|
|
}
|
|
got := strings.TrimSpace(string(data))
|
|
if got == `{"schema":"speaker_transcript.v1","segments":[]}` {
|
|
t.Fatalf("got noop transcript output in default wiring: %q", got)
|
|
}
|
|
if !strings.Contains(got, `"source":"httptest"`) {
|
|
t.Fatalf("output = %q, want server response json", got)
|
|
}
|
|
|
|
store := &manifest.LocalStore{}
|
|
m, err := store.Load(context.Background(), summary.ManifestPath)
|
|
if err != nil {
|
|
t.Fatalf("Load manifest error = %v", err)
|
|
}
|
|
if m.Stages["transcribe"] == nil || m.Stages["transcribe"].Status != manifest.StatusSucceeded {
|
|
t.Fatalf("transcribe stage = %#v, want succeeded", m.Stages["transcribe"])
|
|
}
|
|
}
|
|
|
|
func TestExecuteStagesDefaultWiringWhisperXFailureMarksManifestFailed(t *testing.T) {
|
|
var calls atomic.Int32
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
calls.Add(1)
|
|
http.Error(w, "temporary", http.StatusInternalServerError)
|
|
}))
|
|
defer srv.Close()
|
|
|
|
cfg := testConfig(t)
|
|
setWhisperXConfig(cfg, srv.URL, 1)
|
|
|
|
summary, err := executeStages(context.Background(), cfg, transcribePipelineStages(), RunOptions{})
|
|
if err == nil {
|
|
t.Fatal("expected error, got nil")
|
|
}
|
|
if summary != nil {
|
|
t.Fatalf("summary = %#v, want nil on failure", summary)
|
|
}
|
|
if calls.Load() != 2 {
|
|
t.Fatalf("whisperx calls = %d, want 2 (retry + initial)", calls.Load())
|
|
}
|
|
|
|
store := &manifest.LocalStore{}
|
|
m, loadErr := store.Load(context.Background(), manifestPathFor(cfg))
|
|
if loadErr != nil {
|
|
t.Fatalf("Load manifest error = %v", loadErr)
|
|
}
|
|
if m.Stages["transcribe"] == nil || m.Stages["transcribe"].Status != manifest.StatusFailed {
|
|
t.Fatalf("transcribe stage = %#v, want failed", m.Stages["transcribe"])
|
|
}
|
|
}
|
|
|
|
func TestExecuteStagesExplicitWhisperXInjectionOverridesDefaultWiring(t *testing.T) {
|
|
cfg := testConfig(t)
|
|
setWhisperXConfig(cfg, "https://127.0.0.1:1/transcribe", 0)
|
|
|
|
fake := &whisperx.FakeClient{}
|
|
_, err := executeStages(
|
|
context.Background(),
|
|
cfg,
|
|
transcribePipelineStages(),
|
|
RunOptions{Env: &Env{WhisperX: fake}},
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("executeStages() error = %v", err)
|
|
}
|
|
if len(fake.Requests) != 1 {
|
|
t.Fatalf("fake whisperx requests = %d, want 1", len(fake.Requests))
|
|
}
|
|
}
|
|
|
|
func setWhisperXConfig(cfg *config.Config, url string, retries int) {
|
|
concurrency := 2
|
|
retryDelay := "1ms"
|
|
timeout := "2s"
|
|
cfg.Pipeline.WhisperX = config.WhisperXConfig{
|
|
TranscribeURL: url,
|
|
Language: "en",
|
|
Timeout: timeout,
|
|
Retries: &retries,
|
|
RetryDelay: retryDelay,
|
|
Concurrency: &concurrency,
|
|
}
|
|
}
|
|
|
|
func transcribePipelineStages() []stage.Stage {
|
|
all := BuildFullPlan()
|
|
return []stage.Stage{all[0], all[1]}
|
|
}
|