package e2e import ( "context" "net" "testing" "time" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "github.com/metadata-agregator/internal/server" metadatav1 "github.com/metadata-agregator/pkg/gen/metadata/v1" ) const ( radioheadMBID = "a74b1b7f-71a5-4011-9441-d0b5e4122711" okComputerMBID = "b1392450-e666-3926-a536-22c65f834433" paranoidAndroid = "9f9cf187-d6f9-437f-9d98-d59cdbd52757" ) type testServer struct { addr string server *grpc.Server } func startTestServer(t *testing.T) *testServer { t.Helper() lis, err := net.Listen("tcp", "localhost:0") if err != nil { t.Fatalf("failed to listen: %v", err) } grpcServer := grpc.NewServer() metadatav1.RegisterMetadataServiceServer(grpcServer, server.NewMetadataServer()) go func() { if err := grpcServer.Serve(lis); err != nil { t.Logf("server stopped: %v", err) } }() return &testServer{ addr: lis.Addr().String(), server: grpcServer, } } func (s *testServer) stop() { s.server.GracefulStop() } func newClient(t *testing.T, addr string) metadatav1.MetadataServiceClient { t.Helper() conn, err := grpc.NewClient(addr, grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { t.Fatalf("failed to connect: %v", err) } t.Cleanup(func() { conn.Close() }) return metadatav1.NewMetadataServiceClient(conn) } func TestSearchArtists(t *testing.T) { if testing.Short() { t.Skip("skipping e2e test in short mode") } srv := startTestServer(t) defer srv.stop() client := newClient(t, srv.addr) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() resp, err := client.SearchArtists(ctx, &metadatav1.SearchArtistsRequest{ Query: "Radiohead", Limit: 5, }) if err != nil { t.Fatalf("SearchArtists failed: %v", err) } if len(resp.Artists) == 0 { t.Fatal("expected at least one artist") } found := false for _, a := range resp.Artists { if a.Id == radioheadMBID { found = true if a.Name != "Radiohead" { t.Errorf("expected name 'Radiohead', got %q", a.Name) } if a.Country != "GB" { t.Errorf("expected country 'GB', got %q", a.Country) } break } } if !found { t.Error("Radiohead not found in search results") } } func TestGetArtist(t *testing.T) { if testing.Short() { t.Skip("skipping e2e test in short mode") } srv := startTestServer(t) defer srv.stop() client := newClient(t, srv.addr) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() artist, err := client.GetArtist(ctx, &metadatav1.GetArtistRequest{ Identifier: &metadatav1.GetArtistRequest_Id{Id: radioheadMBID}, }) if err != nil { t.Fatalf("GetArtist failed: %v", err) } if artist.Id != radioheadMBID { t.Errorf("expected ID %q, got %q", radioheadMBID, artist.Id) } if artist.Name != "Radiohead" { t.Errorf("expected name 'Radiohead', got %q", artist.Name) } if artist.ArtistType != "Group" { t.Errorf("expected type 'Group', got %q", artist.ArtistType) } if len(artist.Genres) == 0 { t.Error("expected genres to be populated") } if len(artist.ExternalIds) == 0 { t.Error("expected external IDs to be populated") } hasMusicBrainz := false for _, ext := range artist.ExternalIds { if ext.Source == "musicbrainz" { hasMusicBrainz = true break } } if !hasMusicBrainz { t.Error("expected musicbrainz external ID") } } func TestGetArtistAlbums(t *testing.T) { if testing.Short() { t.Skip("skipping e2e test in short mode") } srv := startTestServer(t) defer srv.stop() client := newClient(t, srv.addr) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() resp, err := client.GetArtistAlbums(ctx, &metadatav1.GetArtistAlbumsRequest{ ArtistId: radioheadMBID, Limit: 10, }) if err != nil { t.Fatalf("GetArtistAlbums failed: %v", err) } if len(resp.Albums) == 0 { t.Fatal("expected at least one album") } if resp.Total == 0 { t.Error("expected total to be greater than 0") } foundOKComputer := false for _, album := range resp.Albums { if album.Id == okComputerMBID { foundOKComputer = true if album.Title != "OK Computer" { t.Errorf("expected title 'OK Computer', got %q", album.Title) } break } } if !foundOKComputer { t.Log("OK Computer not in first 10 results (pagination)") } for _, album := range resp.Albums { if album.AlbumType == "" { t.Errorf("album %q missing type", album.Title) } if len(album.Artists) == 0 { t.Errorf("album %q missing artists", album.Title) } } } func TestGetAlbum(t *testing.T) { if testing.Short() { t.Skip("skipping e2e test in short mode") } srv := startTestServer(t) defer srv.stop() client := newClient(t, srv.addr) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() album, err := client.GetAlbum(ctx, &metadatav1.GetAlbumRequest{ Identifier: &metadatav1.GetAlbumRequest_Id{Id: okComputerMBID}, }) if err != nil { t.Fatalf("GetAlbum failed: %v", err) } if album.Id != okComputerMBID { t.Errorf("expected ID %q, got %q", okComputerMBID, album.Id) } if album.Title != "OK Computer" { t.Errorf("expected title 'OK Computer', got %q", album.Title) } if album.AlbumType != "Album" { t.Errorf("expected type 'Album', got %q", album.AlbumType) } if album.ReleaseDate == "" { t.Error("expected release date to be populated") } if len(album.Artists) == 0 { t.Error("expected artists to be populated") } } func TestGetAlbumTracks(t *testing.T) { if testing.Short() { t.Skip("skipping e2e test in short mode") } srv := startTestServer(t) defer srv.stop() client := newClient(t, srv.addr) ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) defer cancel() resp, err := client.GetAlbumTracks(ctx, &metadatav1.GetAlbumTracksRequest{ AlbumId: okComputerMBID, }) if err != nil { t.Fatalf("GetAlbumTracks failed: %v", err) } if len(resp.Tracks) == 0 { t.Fatal("expected tracks") } if len(resp.Tracks) != 12 { t.Errorf("expected 12 tracks for OK Computer, got %d", len(resp.Tracks)) } expectedTracks := []string{ "Airbag", "Paranoid Android", "Subterranean Homesick Alien", "Exit Music (for a Film)", "Let Down", "Karma Police", "Fitter Happier", "Electioneering", "Climbing Up the Walls", "No Surprises", "Lucky", "The Tourist", } for i, track := range resp.Tracks { if track.TrackNumber != int32(i+1) { t.Errorf("track %d: expected track number %d, got %d", i, i+1, track.TrackNumber) } if i < len(expectedTracks) && track.Title != expectedTracks[i] { t.Errorf("track %d: expected title %q, got %q", i+1, expectedTracks[i], track.Title) } if track.DurationMs == 0 { t.Errorf("track %q: missing duration", track.Title) } if track.Isrc == "" { t.Errorf("track %q: missing ISRC", track.Title) } } } func TestGetTrack(t *testing.T) { if testing.Short() { t.Skip("skipping e2e test in short mode") } srv := startTestServer(t) defer srv.stop() client := newClient(t, srv.addr) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() track, err := client.GetTrack(ctx, &metadatav1.GetTrackRequest{ Identifier: &metadatav1.GetTrackRequest_Id{Id: paranoidAndroid}, }) if err != nil { t.Fatalf("GetTrack failed: %v", err) } if track.Id != paranoidAndroid { t.Errorf("expected ID %q, got %q", paranoidAndroid, track.Id) } if track.Title != "Paranoid Android" { t.Errorf("expected title 'Paranoid Android', got %q", track.Title) } if track.DurationMs == 0 { t.Error("expected duration to be populated") } if track.Isrc == "" { t.Error("expected ISRC to be populated") } if len(track.Artists) == 0 { t.Error("expected artists to be populated") } } func TestGetTrackByISRC(t *testing.T) { if testing.Short() { t.Skip("skipping e2e test in short mode") } srv := startTestServer(t) defer srv.stop() client := newClient(t, srv.addr) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() track, err := client.GetTrack(ctx, &metadatav1.GetTrackRequest{ Identifier: &metadatav1.GetTrackRequest_Isrc{Isrc: "GBAYE9701376"}, }) if err != nil { t.Fatalf("GetTrack by ISRC failed: %v", err) } if track.Title != "Paranoid Android" { t.Errorf("expected title 'Paranoid Android', got %q", track.Title) } } func TestProviderSelection(t *testing.T) { if testing.Short() { t.Skip("skipping e2e test in short mode") } srv := startTestServer(t) defer srv.stop() client := newClient(t, srv.addr) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() resp, err := client.SearchArtists(ctx, &metadatav1.SearchArtistsRequest{ Query: "Radiohead", Limit: 1, Provider: metadatav1.Provider_PROVIDER_MUSICBRAINZ, }) if err != nil { t.Fatalf("SearchArtists with provider failed: %v", err) } if len(resp.Artists) == 0 { t.Fatal("expected at least one artist") } hasMB := false for _, ext := range resp.Artists[0].ExternalIds { if ext.Source == "musicbrainz" { hasMB = true break } } if !hasMB { t.Error("expected musicbrainz source when provider=MUSICBRAINZ") } }