feat(cmd): shared buildProcessor + wire immediate web summarization
CI / Lint / Test / Vet (push) Successful in 20s
CI / Build & Import (push) Successful in 11s
CI / Mirror to GitHub (push) Failing after 3s

Extract the engine wiring (YouTube source, AI-router summarizer, store sink)
into buildProcessor, shared by cmdRun and cmdServe. It returns (nil, nil) — not
an error — on incomplete config, which is the queue-only fallback for serve.
engineProcessor adapts the engine to web.Processor: load the video row, run the
engine, clear the manual queue flag on a produced summary (mirrors the runner).
cmdServe wires it onto web.App.Processor; cmdRun reuses buildProcessor so the
wiring is no longer duplicated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-03 19:50:06 +02:00
co-authored by Claude Opus 4.8
parent 25215cbcbd
commit 8c6c7ca947
3 changed files with 157 additions and 21 deletions
+47
View File
@@ -0,0 +1,47 @@
package main
import (
"testing"
"gitea.d-ma.be/mathias/tapir/internal/config"
)
// TestBuildProcessorNilOnIncompleteConfig asserts the queue-only fallback: when a
// required input is missing, buildProcessor returns (nil, nil) — never an error —
// so the web UI degrades to queue-only instead of failing to start.
func TestBuildProcessorNilOnIncompleteConfig(t *testing.T) {
// A complete config (the fields buildProcessor gates on). The store is nil:
// buildProcessor must not touch it on the incomplete paths, and the complete
// path only stores the pointer (no connection), so nil is fine for this test.
complete := config.Config{
GatewayURL: "http://gw/v1",
YTClientID: "id",
YTClientSecret: "secret",
SecretsFile: "/tmp/secrets.json",
}
tests := []struct {
name string
mutate func(config.Config) config.Config
wantNil bool
}{
{"complete", func(c config.Config) config.Config { return c }, false},
{"no gateway url", func(c config.Config) config.Config { c.GatewayURL = ""; return c }, true},
{"no yt client id", func(c config.Config) config.Config { c.YTClientID = ""; return c }, true},
{"no yt client secret", func(c config.Config) config.Config { c.YTClientSecret = ""; return c }, true},
{"no secrets file", func(c config.Config) config.Config { c.SecretsFile = ""; return c }, true},
{"empty config", func(config.Config) config.Config { return config.Config{} }, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
engine, err := buildProcessor(tt.mutate(complete), nil)
if err != nil {
t.Fatalf("buildProcessor returned an error, want nil: %v", err)
}
if (engine == nil) != tt.wantNil {
t.Fatalf("engine == nil is %v, want %v", engine == nil, tt.wantNil)
}
})
}
}