mirror of
https://github.com/gabehf/Koito.git
synced 2026-03-17 11:16:35 -07:00
chore: initial public commit
This commit is contained in:
commit
fc9054b78c
250 changed files with 32809 additions and 0 deletions
366
internal/catalog/catalog_test.go
Normal file
366
internal/catalog/catalog_test.go
Normal file
|
|
@ -0,0 +1,366 @@
|
|||
package catalog_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gabehf/koito/internal/catalog"
|
||||
"github.com/gabehf/koito/internal/cfg"
|
||||
"github.com/gabehf/koito/internal/db/psql"
|
||||
"github.com/gabehf/koito/internal/mbz"
|
||||
"github.com/gabehf/koito/internal/utils"
|
||||
_ "github.com/gabehf/koito/testing_init"
|
||||
"github.com/google/uuid"
|
||||
"github.com/ory/dockertest/v3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
mbzArtistData = map[uuid.UUID]*mbz.MusicBrainzArtist{
|
||||
uuid.MustParse("00000000-0000-0000-0000-000000000001"): {
|
||||
Name: "ATARASHII GAKKO!",
|
||||
SortName: "Atarashii Gakko",
|
||||
Aliases: []mbz.MusicBrainzArtistAlias{
|
||||
{
|
||||
Name: "新しい学校のリーダーズ",
|
||||
Type: "Artist name",
|
||||
Primary: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
mbzReleaseGroupData = map[uuid.UUID]*mbz.MusicBrainzReleaseGroup{
|
||||
uuid.MustParse("00000000-0000-0000-0000-000000000011"): {
|
||||
Title: "AG! Calling",
|
||||
Type: "Album",
|
||||
ArtistCredit: []mbz.MusicBrainzArtistCredit{
|
||||
{
|
||||
Artist: mbz.MusicBrainzArtist{
|
||||
Name: "ATARASHII GAKKO!",
|
||||
Aliases: []mbz.MusicBrainzArtistAlias{
|
||||
{
|
||||
Name: "新しい学校のリーダーズ",
|
||||
Type: "Artist name",
|
||||
Primary: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Name: "ATARASHII GAKKO!",
|
||||
},
|
||||
},
|
||||
Releases: []mbz.MusicBrainzRelease{
|
||||
{
|
||||
Title: "AG! Calling",
|
||||
ID: "00000000-0000-0000-0000-000000000101",
|
||||
ArtistCredit: []mbz.MusicBrainzArtistCredit{
|
||||
{
|
||||
Artist: mbz.MusicBrainzArtist{
|
||||
Name: "ATARASHII GAKKO!",
|
||||
Aliases: []mbz.MusicBrainzArtistAlias{
|
||||
{
|
||||
Name: "ATARASHII GAKKO!",
|
||||
Type: "Artist name",
|
||||
Primary: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Name: "ATARASHII GAKKO!",
|
||||
},
|
||||
},
|
||||
Status: "Official",
|
||||
},
|
||||
{
|
||||
Title: "AG! Calling - Alt Title",
|
||||
ID: "00000000-0000-0000-0000-000000000102",
|
||||
ArtistCredit: []mbz.MusicBrainzArtistCredit{
|
||||
{
|
||||
Artist: mbz.MusicBrainzArtist{
|
||||
Name: "ATARASHII GAKKO!",
|
||||
Aliases: []mbz.MusicBrainzArtistAlias{
|
||||
{
|
||||
Name: "ATARASHII GAKKO!",
|
||||
Type: "Artist name",
|
||||
Primary: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Name: "ATARASHII GAKKO!",
|
||||
},
|
||||
},
|
||||
Status: "Official",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
mbzReleaseData = map[uuid.UUID]*mbz.MusicBrainzRelease{
|
||||
uuid.MustParse("00000000-0000-0000-0000-000000000101"): {
|
||||
Title: "AG! Calling",
|
||||
ID: "00000000-0000-0000-0000-000000000101",
|
||||
ArtistCredit: []mbz.MusicBrainzArtistCredit{
|
||||
{
|
||||
Artist: mbz.MusicBrainzArtist{
|
||||
Name: "ATARASHII GAKKO!",
|
||||
Aliases: []mbz.MusicBrainzArtistAlias{
|
||||
{
|
||||
Name: "新しい学校のリーダーズ",
|
||||
Type: "Artist name",
|
||||
Primary: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Name: "ATARASHII GAKKO!",
|
||||
},
|
||||
},
|
||||
Status: "Official",
|
||||
},
|
||||
uuid.MustParse("00000000-0000-0000-0000-000000000202"): {
|
||||
Title: "EVANGELION FINALLY",
|
||||
ID: "00000000-0000-0000-0000-000000000202",
|
||||
ArtistCredit: []mbz.MusicBrainzArtistCredit{
|
||||
{
|
||||
Artist: mbz.MusicBrainzArtist{
|
||||
Name: "Various Artists",
|
||||
},
|
||||
Name: "Various Artists",
|
||||
},
|
||||
},
|
||||
Status: "Official",
|
||||
},
|
||||
}
|
||||
mbzTrackData = map[uuid.UUID]*mbz.MusicBrainzTrack{
|
||||
uuid.MustParse("00000000-0000-0000-0000-000000001001"): {
|
||||
Title: "Tokyo Calling",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
var store *psql.Psql
|
||||
|
||||
func getTestGetenv(resource *dockertest.Resource) func(string) string {
|
||||
dir, err := utils.GenerateRandomString(8)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return func(env string) string {
|
||||
switch env {
|
||||
case cfg.ENABLE_STRUCTURED_LOGGING_ENV:
|
||||
return "true"
|
||||
case cfg.LOG_LEVEL_ENV:
|
||||
return "debug"
|
||||
case cfg.DATABASE_URL_ENV:
|
||||
return fmt.Sprintf("postgres://postgres:secret@localhost:%s", resource.GetPort("5432/tcp"))
|
||||
case cfg.CONFIG_DIR_ENV:
|
||||
return dir
|
||||
case cfg.DISABLE_DEEZER_ENV, cfg.DISABLE_COVER_ART_ARCHIVE_ENV, cfg.DISABLE_MUSICBRAINZ_ENV, cfg.ENABLE_FULL_IMAGE_CACHE_ENV:
|
||||
return "true"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func truncateTestData(t *testing.T) {
|
||||
err := store.Exec(context.Background(),
|
||||
`TRUNCATE
|
||||
artists,
|
||||
artist_aliases,
|
||||
tracks,
|
||||
artist_tracks,
|
||||
releases,
|
||||
artist_releases,
|
||||
release_aliases,
|
||||
listens
|
||||
RESTART IDENTITY CASCADE`)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func setupTestDataWithMbzIDs(t *testing.T) {
|
||||
truncateTestData(t)
|
||||
|
||||
err := store.Exec(context.Background(),
|
||||
`INSERT INTO artists (musicbrainz_id)
|
||||
VALUES ('00000000-0000-0000-0000-000000000001')`)
|
||||
require.NoError(t, err)
|
||||
err = store.Exec(context.Background(),
|
||||
`INSERT INTO artist_aliases (artist_id, alias, source, is_primary)
|
||||
VALUES (1, 'ATARASHII GAKKO!', 'Testing', true)`)
|
||||
require.NoError(t, err)
|
||||
err = store.Exec(context.Background(),
|
||||
`INSERT INTO releases (musicbrainz_id)
|
||||
VALUES ('00000000-0000-0000-0000-000000000101')`)
|
||||
require.NoError(t, err)
|
||||
err = store.Exec(context.Background(),
|
||||
`INSERT INTO release_aliases (release_id, alias, source, is_primary)
|
||||
VALUES (1, 'AG! Calling', 'Testing', true)`)
|
||||
require.NoError(t, err)
|
||||
err = store.Exec(context.Background(),
|
||||
`INSERT INTO artist_releases (artist_id, release_id)
|
||||
VALUES (1, 1)`)
|
||||
require.NoError(t, err)
|
||||
err = store.Exec(context.Background(),
|
||||
`INSERT INTO tracks (release_id, musicbrainz_id)
|
||||
VALUES (1, '00000000-0000-0000-0000-000000001001')`)
|
||||
require.NoError(t, err)
|
||||
err = store.Exec(context.Background(),
|
||||
`INSERT INTO track_aliases (track_id, alias, source, is_primary)
|
||||
VALUES (1, 'Tokyo Calling', 'Testing', true)`)
|
||||
require.NoError(t, err)
|
||||
err = store.Exec(context.Background(),
|
||||
`INSERT INTO artist_tracks (artist_id, track_id)
|
||||
VALUES (1, 1)`)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func setupTestDataSansMbzIDs(t *testing.T) {
|
||||
truncateTestData(t)
|
||||
|
||||
err := store.Exec(context.Background(),
|
||||
`INSERT INTO artists (musicbrainz_id)
|
||||
VALUES (NULL)`)
|
||||
require.NoError(t, err)
|
||||
err = store.Exec(context.Background(),
|
||||
`INSERT INTO artist_aliases (artist_id, alias, source, is_primary)
|
||||
VALUES (1, 'ATARASHII GAKKO!', 'Testing', true)`)
|
||||
require.NoError(t, err)
|
||||
err = store.Exec(context.Background(),
|
||||
`INSERT INTO releases (musicbrainz_id)
|
||||
VALUES (NULL)`)
|
||||
require.NoError(t, err)
|
||||
err = store.Exec(context.Background(),
|
||||
`INSERT INTO release_aliases (release_id, alias, source, is_primary)
|
||||
VALUES (1, 'AG! Calling', 'Testing', true)`)
|
||||
require.NoError(t, err)
|
||||
err = store.Exec(context.Background(),
|
||||
`INSERT INTO artist_releases (artist_id, release_id)
|
||||
VALUES (1, 1)`)
|
||||
require.NoError(t, err)
|
||||
err = store.Exec(context.Background(),
|
||||
`INSERT INTO tracks (release_id, musicbrainz_id)
|
||||
VALUES (1, NULL)`)
|
||||
require.NoError(t, err)
|
||||
err = store.Exec(context.Background(),
|
||||
`INSERT INTO track_aliases (track_id, alias, source, is_primary)
|
||||
VALUES (1, 'Tokyo Calling', 'Testing', true)`)
|
||||
require.NoError(t, err)
|
||||
err = store.Exec(context.Background(),
|
||||
`INSERT INTO artist_tracks (artist_id, track_id)
|
||||
VALUES (1, 1)`)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
pool, err := dockertest.NewPool("")
|
||||
if err != nil {
|
||||
log.Fatalf("Could not construct pool: %s", err)
|
||||
}
|
||||
|
||||
if err := pool.Client.Ping(); err != nil {
|
||||
log.Fatalf("Could not connect to Docker: %s", err)
|
||||
}
|
||||
|
||||
resource, err := pool.Run("postgres", "latest", []string{"POSTGRES_PASSWORD=secret"})
|
||||
if err != nil {
|
||||
log.Fatalf("Could not start resource: %s", err)
|
||||
}
|
||||
|
||||
err = cfg.Load(getTestGetenv(resource))
|
||||
if err != nil {
|
||||
log.Fatalf("Could not load cfg: %s", err)
|
||||
}
|
||||
|
||||
if err := pool.Retry(func() error {
|
||||
var err error
|
||||
store, err = psql.New()
|
||||
if err != nil {
|
||||
log.Println("Failed to connect to test database, retrying...")
|
||||
return err
|
||||
}
|
||||
return store.Ping(context.Background())
|
||||
}); err != nil {
|
||||
log.Fatalf("Could not connect to database: %s", err)
|
||||
}
|
||||
|
||||
// insert a user into the db with id 1 to use for tests
|
||||
err = store.Exec(context.Background(), `INSERT INTO users (username, password) VALUES ('test', DECODE('abc123', 'hex'))`)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to insert test user: %v", err)
|
||||
}
|
||||
|
||||
code := m.Run()
|
||||
|
||||
// You can't defer this because os.Exit doesn't care for defer
|
||||
if err := pool.Purge(resource); err != nil {
|
||||
log.Fatalf("Could not purge resource: %s", err)
|
||||
}
|
||||
|
||||
err = os.RemoveAll(cfg.ConfigDir())
|
||||
if err != nil {
|
||||
log.Fatalf("Could not remove temporary config dir: %v", err)
|
||||
}
|
||||
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
// From: https://brandur.org/fragments/go-equal-time
|
||||
// EqualTime compares two times in a way that's safer and with better fail
|
||||
// output than a call to `require.Equal` would produce.
|
||||
//
|
||||
// It takes care to:
|
||||
//
|
||||
// - Strip off monotonic portions of timestamps so they aren't considered for
|
||||
// purposes of comparison.
|
||||
//
|
||||
// - Truncate nanoseconds in a functionally equivalent way to how pgx would do
|
||||
// it so that times that have round-tripped from Postgres can still be
|
||||
// compared. Postgres only stores times to the microsecond level.
|
||||
//
|
||||
// - Use formatted, human-friendly time outputs so that in case of a failure,
|
||||
// the discrepancy is easier to pick out.
|
||||
func EqualTime(t testing.TB, t1, t2 time.Time) {
|
||||
// Note that leaving off the nanosecond portion will have the effect of
|
||||
// truncating it rather than rounding to the nearest microsecond, which
|
||||
// functionally matches pgx's behavior while persisting.
|
||||
const rfc3339Micro = "2006-01-02T15:04:05.999999Z07:00"
|
||||
|
||||
require.Equal(t,
|
||||
t1.Format(rfc3339Micro),
|
||||
t2.Format(rfc3339Micro),
|
||||
)
|
||||
}
|
||||
|
||||
func TestArtistStringParse(t *testing.T) {
|
||||
type input struct {
|
||||
Name string
|
||||
Title string
|
||||
}
|
||||
cases := map[input][]string{
|
||||
// only one artist
|
||||
{"NELKE", ""}: {"NELKE"},
|
||||
{"The Brook & The Bluff", ""}: {"The Brook & The Bluff"},
|
||||
{"half·alive", ""}: {"half·alive"},
|
||||
// Earth, Wind, & Fire
|
||||
{"Earth, Wind & Fire", "The Very Best of Earth, Wind & Fire"}: {"Earth, Wind & Fire"},
|
||||
// only artists in artist string
|
||||
{"Carly Rae Jepsen feat. Rufus Wainwright", ""}: {"Carly Rae Jepsen", "Rufus Wainwright"},
|
||||
{"Mimi (feat. HATSUNE MIKU & KAFU)", ""}: {"Mimi", "HATSUNE MIKU", "KAFU"},
|
||||
{"Magnify Tokyo · Kanade Ishihara", ""}: {"Magnify Tokyo", "Kanade Ishihara"},
|
||||
{"Daft Punk [feat. Paul Williams]", ""}: {"Daft Punk", "Paul Williams"},
|
||||
// primary artist in artist string, features in title
|
||||
{"Tyler, The Creator", "CA (feat. Alice Smith, Leon Ware & Clem Creevy)"}: {"Tyler, The Creator", "Alice Smith", "Leon Ware", "Clem Creevy"},
|
||||
{"ONE OK ROCK", "C.U.R.I.O.S.I.T.Y. (feat. Paledusk and CHICO CARLITO)"}: {"ONE OK ROCK", "Paledusk", "CHICO CARLITO"},
|
||||
{"Rat Tally", "In My Car feat. Madeline Kenney"}: {"Rat Tally", "Madeline Kenney"},
|
||||
// artists in both
|
||||
{"Daft Punk feat. Julian Casablancas", "Instant Crush (feat. Julian Casablancas)"}: {"Daft Punk", "Julian Casablancas"},
|
||||
{"Paramore (feat. Joy Williams)", "Hate to See Your Heart Break feat. Joy Williams"}: {"Paramore", "Joy Williams"},
|
||||
}
|
||||
|
||||
for in, out := range cases {
|
||||
artists := catalog.ParseArtists(in.Name, in.Title)
|
||||
assert.ElementsMatch(t, out, artists)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue