a1f6701bac
- gRPC service with MusicBrainz provider - PostgreSQL schema with migrations - Service layer with database-first caching - Repository pattern for data access - YAML configuration support - Research documentation for 17 music metadata projects
416 lines
9.1 KiB
Go
416 lines
9.1 KiB
Go
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")
|
|
}
|
|
}
|