160 lines
4.6 KiB
Go
160 lines
4.6 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
|
|
"naviwatcher/internal/config"
|
|
"naviwatcher/internal/database"
|
|
"naviwatcher/internal/notifier"
|
|
"naviwatcher/internal/scanner"
|
|
"naviwatcher/internal/web"
|
|
)
|
|
|
|
// TestDataFlowSmoke seeds an in-memory DB with a monitored artist, one local
|
|
// album, and a missing external release, then exercises the full
|
|
// scan -> notify -> web pipeline end to end:
|
|
// - ScanAll reports the missing release
|
|
// - NotifyOnce (with a stub sender) sends exactly the missing release and
|
|
// marks it as sent, so a second run reports nothing
|
|
// - The Web UI dashboard requires auth and renders the artist + missing count,
|
|
// and the artist detail page renders the missing release
|
|
//
|
|
// This is the acceptance smoke check for Task 9.
|
|
func TestDataFlowSmoke(t *testing.T) {
|
|
db, err := database.New(":memory:")
|
|
if err != nil {
|
|
t.Fatalf("open :memory: db: %v", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
ctx := context.Background()
|
|
|
|
// Seed: one monitored artist with one local album ...
|
|
if err := database.SaveArtistSettings(db, &database.ArtistSettings{
|
|
ID: "ar1",
|
|
Name: "Pink Floyd",
|
|
MBID: "abcdef",
|
|
Monitored: true,
|
|
}); err != nil {
|
|
t.Fatalf("seed artist: %v", err)
|
|
}
|
|
if err := database.SaveLocalAlbum(db, &database.LocalAlbum{
|
|
ID: "al1",
|
|
ArtistID: "ar1",
|
|
Title: "The Wall",
|
|
}); err != nil {
|
|
t.Fatalf("seed local album: %v", err)
|
|
}
|
|
// ... and a missing external release (not present locally).
|
|
if err := database.SaveExternalRelease(db, &database.ExternalRelease{
|
|
RGID: "rg-missing",
|
|
ArtistID: "ar1",
|
|
Title: "Animals",
|
|
Type: "Album",
|
|
ReleaseDate: "1977-01-01",
|
|
}); err != nil {
|
|
t.Fatalf("seed external release: %v", err)
|
|
}
|
|
|
|
// 1) ScanAll should surface exactly the missing release.
|
|
missing, err := scanner.ScanAll(ctx, db, 0.85)
|
|
if err != nil {
|
|
t.Fatalf("ScanAll: %v", err)
|
|
}
|
|
if len(missing) != 1 {
|
|
t.Fatalf("expected 1 missing release, got %d", len(missing))
|
|
}
|
|
if missing[0].RGID != "rg-missing" || missing[0].Title != "Animals" {
|
|
t.Fatalf("unexpected missing release: %+v", missing[0])
|
|
}
|
|
|
|
// 2) Notifier: stub sender, first run notifies 1, second run notifies 0.
|
|
var sent []string
|
|
stub := stubSender{onSend: func(msg string) error {
|
|
sent = append(sent, msg)
|
|
return nil
|
|
}}
|
|
tgCfg := config.TelegramConfig{Enabled: true}
|
|
|
|
n1, err := notifier.NotifyOnce(ctx, db, stub, tgCfg, "http://localhost:8080", 0.85)
|
|
if err != nil {
|
|
t.Fatalf("NotifyOnce #1: %v", err)
|
|
}
|
|
if n1 != 1 {
|
|
t.Fatalf("expected NotifyOnce to send 1, got %d", n1)
|
|
}
|
|
if len(sent) != 1 || !strings.Contains(sent[0], "Animals") {
|
|
t.Fatalf("digest missing expected content: %v", sent)
|
|
}
|
|
|
|
n2, err := notifier.NotifyOnce(ctx, db, stub, tgCfg, "http://localhost:8080", 0.85)
|
|
if err != nil {
|
|
t.Fatalf("NotifyOnce #2: %v", err)
|
|
}
|
|
if n2 != 0 {
|
|
t.Fatalf("expected second NotifyOnce to send 0 (already sent), got %d", n2)
|
|
}
|
|
|
|
// 3) Web UI: dashboard requires basic auth and renders the artist.
|
|
srvCfg := &config.ServerConfig{
|
|
Host: "localhost",
|
|
Port: 0,
|
|
Username: "admin",
|
|
Password: "secret",
|
|
}
|
|
srv := web.NewServer(srvCfg, db, "http://localhost:8080", 0)
|
|
|
|
// Unauthenticated -> 401.
|
|
rec := httptest.NewRecorder()
|
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
|
srv.Handler().ServeHTTP(rec, req)
|
|
if rec.Code != http.StatusUnauthorized {
|
|
t.Fatalf("expected 401 for unauthenticated dashboard, got %d", rec.Code)
|
|
}
|
|
|
|
// Authenticated -> 200 with the artist name and missing count.
|
|
rec = httptest.NewRecorder()
|
|
req = httptest.NewRequest(http.MethodGet, "/", nil)
|
|
req.SetBasicAuth("admin", "secret")
|
|
srv.Handler().ServeHTTP(rec, req)
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("expected 200 for authenticated dashboard, got %d", rec.Code)
|
|
}
|
|
body := rec.Body.String()
|
|
if !strings.Contains(body, "Pink Floyd") {
|
|
t.Fatalf("dashboard did not render artist name; body head:\n%s", body[:min(400, len(body))])
|
|
}
|
|
|
|
// Artist detail page renders the missing release.
|
|
rec = httptest.NewRecorder()
|
|
req = httptest.NewRequest(http.MethodGet, "/artist/ar1", nil)
|
|
req.SetBasicAuth("admin", "secret")
|
|
srv.Handler().ServeHTTP(rec, req)
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("expected 200 for artist page, got %d", rec.Code)
|
|
}
|
|
if !strings.Contains(rec.Body.String(), "Animals") {
|
|
t.Fatalf("artist page did not render missing release 'Animals'")
|
|
}
|
|
}
|
|
|
|
func min(a, b int) int {
|
|
if a < b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
// stubSender is a test double for notifier.Sender.
|
|
type stubSender struct {
|
|
onSend func(message string) error
|
|
}
|
|
|
|
func (s stubSender) Send(_ context.Context, message string) error {
|
|
return s.onSend(message)
|
|
}
|