package services import ( "bytes" "context" "strings" "testing" "github.com/stormlightlabs/noteleaf/internal/models" "github.com/stormlightlabs/noteleaf/internal/shared" ) func TestMediaServices(t *testing.T) { t.Run("MovieService", func(t *testing.T) { t.Run("Search", func(t *testing.T) { t.Run("successful search", func(t *testing.T) { cleanup := SetupSuccessfulMovieMocks(t) defer cleanup() service := CreateMovieService() TestMovieSearch(t, service, "Fantastic Four", "Fantastic Four") }) t.Run("search returns error", func(t *testing.T) { cleanup := SetupFailureMocks(t, "search error") defer cleanup() service := CreateMovieService() _, err := service.Search(context.Background(), "error", 1, 10) shared.AssertErrorContains(t, err, "search error", "") }) }) t.Run("Get", func(t *testing.T) { t.Run("successful get", func(t *testing.T) { cleanup := SetupSuccessfulMovieMocks(t) defer cleanup() service := CreateMovieService() result, err := service.Get(context.Background(), "some-url") if err != nil { t.Fatalf("Get failed: %v", err) } movie, ok := (*result).(*models.Movie) if !ok { t.Fatalf("expected a movie model, got %T", *result) } if movie.Title == "" { t.Error("expected non-empty movie title") } }) t.Run("get returns error", func(t *testing.T) { cleanup := SetupFailureMocks(t, "fetch error") defer cleanup() service := CreateMovieService() _, err := service.Get(context.Background(), "error") shared.AssertErrorContains(t, err, "fetch error", "") }) }) t.Run("Check", func(t *testing.T) { t.Run("successful check", func(t *testing.T) { cleanup := SetupSuccessfulMovieMocks(t) defer cleanup() service := CreateMovieService() err := service.Check(context.Background()) if err != nil { t.Fatalf("Check failed: %v", err) } }) t.Run("check returns error", func(t *testing.T) { cleanup := SetupFailureMocks(t, "html fetch error") defer cleanup() service := CreateMovieService() err := service.Check(context.Background()) shared.AssertErrorContains(t, err, "html fetch error", "") }) }) t.Run("Parse Search results", func(t *testing.T) { results, err := ParseSearch(bytes.NewReader(SearchSample)) if err != nil { t.Fatalf("ParseSearch failed: %v", err) } if len(results) == 0 { t.Fatal("expected non-empty search results") } }) t.Run("Parse Search error", func(t *testing.T) { results, err := ParseSearch(strings.NewReader("\x00bad html")) if err != nil { t.Fatalf("unexpected error for malformed HTML: %v", err) } if len(results) != 0 { t.Errorf("expected 0 results for malformed HTML, got %d", len(results)) } html := `Test` results, err = ParseSearch(strings.NewReader(html)) if err != nil { t.Fatalf("unexpected error: %v", err) } if len(results) != 0 { t.Errorf("expected 0 results, got %d", len(results)) } }) t.Run("Extract Metadata", func(t *testing.T) { movie, err := ExtractMovieMetadata(bytes.NewReader(MovieSample)) if err != nil { t.Fatalf("ExtractMovieMetadata failed: %v", err) } if movie.Type != "Movie" { t.Errorf("expected Type=Movie, got %s", movie.Type) } if movie.Name == "" { t.Error("expected non-empty Name") } }) t.Run("Extract Metadata Errors", func(t *testing.T) { if _, err := ExtractMovieMetadata(strings.NewReader("not html")); err == nil { t.Error("expected error for invalid HTML") } html := `` if _, err := ExtractMovieMetadata(strings.NewReader(html)); err == nil || !strings.Contains(err.Error(), "no Movie JSON-LD") { t.Errorf("expected 'no Movie JSON-LD', got %v", err) } html = `` if _, err := ExtractMovieMetadata(strings.NewReader(html)); err == nil { t.Error("expected error for invalid JSON") } }) t.Run("Extract TV Series Metadata", func(t *testing.T) { series, err := ExtractTVSeriesMetadata(bytes.NewReader(SeriesSample)) if err != nil { t.Fatalf("ExtractTVSeriesMetadata failed: %v", err) } if series.Type != "TVSeries" { t.Errorf("expected Type=TVSeries, got %s", series.Type) } if series.NumberOfSeasons <= 0 { t.Error("expected NumberOfSeasons > 0") } }) t.Run("Extract TV Series Metadata Errors", func(t *testing.T) { if _, err := ExtractTVSeriesMetadata(strings.NewReader("not html")); err == nil { t.Error("expected error for invalid HTML") } html := `` if _, err := ExtractTVSeriesMetadata(strings.NewReader(html)); err == nil || !strings.Contains(err.Error(), "no TVSeries JSON-LD") { t.Errorf("expected 'no TVSeries JSON-LD', got %v", err) } }) t.Run("Extract TV Series Season metadata", func(t *testing.T) { season, err := ExtractTVSeasonMetadata(bytes.NewReader(SeasonSample)) if err != nil { t.Fatalf("ExtractTVSeasonMetadata failed: %v", err) } if season.Type != "TVSeason" { t.Errorf("expected Type=TVSeason, got %s", season.Type) } if season.SeasonNumber <= 0 { t.Error("expected SeasonNumber > 0") } if season.PartOfSeries.Name == "" { t.Error("expected non-empty PartOfSeries.Name") } }) t.Run("Extract TV Series Season errors", func(t *testing.T) { if _, err := ExtractTVSeasonMetadata(strings.NewReader("not html")); err == nil { t.Error("expected error for invalid HTML") } html := `` if _, err := ExtractTVSeasonMetadata(strings.NewReader(html)); err == nil || !strings.Contains(err.Error(), "no TVSeason JSON-LD") { t.Errorf("expected 'no TVSeason JSON-LD', got %v", err) } }) t.Run("Fetch HTML errors", func(t *testing.T) { if _, err := FetchHTML("://bad-url"); err == nil { t.Error("expected error for invalid URL") } }) }) t.Run("TVService", func(t *testing.T) { t.Run("Search", func(t *testing.T) { t.Run("successful search", func(t *testing.T) { cleanup := SetupSuccessfulTVMocks(t) defer cleanup() service := CreateTVService() TestTVSearch(t, service, "peacemaker", "Peacemaker") }) t.Run("search returns error", func(t *testing.T) { cleanup := SetupFailureMocks(t, "search error") defer cleanup() service := CreateTVService() _, err := service.Search(context.Background(), "error", 1, 10) shared.AssertErrorContains(t, err, "search error", "") }) }) t.Run("Get", func(t *testing.T) { t.Run("successful get", func(t *testing.T) { cleanup := SetupSuccessfulTVMocks(t) defer cleanup() service := CreateTVService() result, err := service.Get(context.Background(), "some-url") if err != nil { t.Fatalf("Get failed: %v", err) } show, ok := (*result).(*models.TVShow) if !ok { t.Fatalf("expected a tv show model, got %T", *result) } if show.Title == "" { t.Error("expected non-empty TV show title") } }) t.Run("get returns error", func(t *testing.T) { cleanup := SetupFailureMocks(t, "fetch error") defer cleanup() service := CreateTVService() _, err := service.Get(context.Background(), "error") shared.AssertErrorContains(t, err, "fetch error", "") }) }) t.Run("Check", func(t *testing.T) { t.Run("successful check", func(t *testing.T) { cleanup := SetupSuccessfulTVMocks(t) defer cleanup() service := CreateTVService() err := service.Check(context.Background()) if err != nil { t.Fatalf("Check failed: %v", err) } }) t.Run("check returns error", func(t *testing.T) { cleanup := SetupFailureMocks(t, "html fetch error") defer cleanup() service := CreateTVService() err := service.Check(context.Background()) shared.AssertErrorContains(t, err, "html fetch error", "") }) }) }) }