mirror of
https://github.com/gabehf/Koito.git
synced 2026-04-22 20:11:50 -07:00
chore: initial public commit
This commit is contained in:
commit
fc9054b78c
250 changed files with 32809 additions and 0 deletions
734
engine/long_test.go
Normal file
734
engine/long_test.go
Normal file
|
|
@ -0,0 +1,734 @@
|
|||
package engine_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gabehf/koito/engine/handlers"
|
||||
"github.com/gabehf/koito/internal/cfg"
|
||||
"github.com/gabehf/koito/internal/db"
|
||||
"github.com/gabehf/koito/internal/models"
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var session string
|
||||
var apikey string
|
||||
var loginOnce sync.Once
|
||||
var apikeyOnce sync.Once
|
||||
|
||||
func login(t *testing.T) {
|
||||
loginOnce.Do(func() {
|
||||
formdata := url.Values{}
|
||||
formdata.Set("username", cfg.DefaultUsername())
|
||||
formdata.Set("password", cfg.DefaultPassword())
|
||||
encoded := formdata.Encode()
|
||||
resp, err := http.DefaultClient.Post(host()+"/apis/web/v1/login", "application/x-www-form-urlencoded", strings.NewReader(encoded))
|
||||
respBytes, _ := io.ReadAll(resp.Body)
|
||||
t.Logf("Login request response: %s - %s", resp.Status, respBytes)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, resp.Cookies(), 1)
|
||||
session = resp.Cookies()[0].Value
|
||||
require.NotEmpty(t, session)
|
||||
})
|
||||
}
|
||||
|
||||
func makeAuthRequest(t *testing.T, session, method, endpoint string, body io.Reader) (*http.Response, error) {
|
||||
req, err := http.NewRequest(method, host()+endpoint, body)
|
||||
require.NoError(t, err)
|
||||
req.AddCookie(&http.Cookie{
|
||||
Name: "koito_session",
|
||||
Value: session,
|
||||
})
|
||||
t.Logf("Making request to %s with session: %s", endpoint, session)
|
||||
return http.DefaultClient.Do(req)
|
||||
}
|
||||
|
||||
// Expects a valid session
|
||||
func getApiKey(t *testing.T, session string) {
|
||||
apikeyOnce.Do(func() {
|
||||
resp, err := makeAuthRequest(t, session, "GET", "/apis/web/v1/user/apikeys", nil)
|
||||
require.NoError(t, err)
|
||||
var keys []models.ApiKey
|
||||
err = json.NewDecoder(resp.Body).Decode(&keys)
|
||||
require.NoError(t, err)
|
||||
require.GreaterOrEqual(t, len(keys), 1)
|
||||
apikey = keys[0].Key
|
||||
})
|
||||
}
|
||||
|
||||
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 doSubmitListens(t *testing.T) {
|
||||
login(t)
|
||||
getApiKey(t, session)
|
||||
truncateTestData(t)
|
||||
bodies := []string{fmt.Sprintf(`{
|
||||
"listen_type": "single",
|
||||
"payload": [
|
||||
{
|
||||
"listened_at": %d,
|
||||
"track_metadata": {
|
||||
"additional_info": {
|
||||
"artist_mbids": [
|
||||
"efc787f0-046f-4a60-beff-77b398c8cdf4"
|
||||
],
|
||||
"artist_names": [
|
||||
"さユり"
|
||||
],
|
||||
"duration_ms": 275960,
|
||||
"recording_mbid": "21524d55-b1f8-45d1-b172-976cba447199",
|
||||
"release_group_mbid": "3281e0d9-fa44-4337-a8ce-6f264beeae16",
|
||||
"release_mbid": "eb790e90-0065-4852-b47d-bbeede4aa9fc",
|
||||
"submission_client": "navidrome",
|
||||
"submission_client_version": "0.56.1 (fa2cf362)"
|
||||
},
|
||||
"artist_name": "さユり",
|
||||
"release_name": "酸欠少女",
|
||||
"track_name": "花の塔"
|
||||
}
|
||||
}
|
||||
]
|
||||
}`, time.Now().Add(-2*time.Hour).Unix()), // yesterday
|
||||
fmt.Sprintf(`{
|
||||
"listen_type": "single",
|
||||
"payload": [
|
||||
{
|
||||
"listened_at": %d,
|
||||
"track_metadata": {
|
||||
"additional_info": {
|
||||
"artist_mbids": [
|
||||
"80b3cb83-b7a3-4f79-ad42-8325cefb3626"
|
||||
],
|
||||
"artist_names": [
|
||||
"キタニタツヤ"
|
||||
],
|
||||
"duration_ms": 197270,
|
||||
"recording_mbid": "4e909c21-e7a8-404d-b75a-0c8c2926efb0",
|
||||
"release_group_mbid": "89069d92-e495-462c-b189-3431551868ed",
|
||||
"release_mbid": "e16a49d6-77f3-4d73-b93c-cac855ce6ad5",
|
||||
"submission_client": "navidrome",
|
||||
"submission_client_version": "0.56.1 (fa2cf362)"
|
||||
},
|
||||
"artist_name": "キタニタツヤ",
|
||||
"release_name": "Where Our Blue Is",
|
||||
"track_name": "Where Our Blue Is"
|
||||
}
|
||||
}
|
||||
]
|
||||
}`, time.Now().Unix()),
|
||||
fmt.Sprintf(`{
|
||||
"listen_type": "single",
|
||||
"payload": [
|
||||
{
|
||||
"listened_at": %d,
|
||||
"track_metadata": {
|
||||
"additional_info": {
|
||||
"artist_mbids": [
|
||||
"1262ab85-308b-46e7-b0b5-91fef8e46b62"
|
||||
],
|
||||
"artist_names": [
|
||||
"ネクライトーキー"
|
||||
],
|
||||
"duration_ms": 241560,
|
||||
"recording_mbid": "8eec4f3f-a059-4217-aad1-fbf82e33e756",
|
||||
"release_group_mbid": "14f1aff0-dd19-4b42-82dd-720386b6d4c1",
|
||||
"release_mbid": "7762d7af-7b6c-454f-977e-1b261743e265",
|
||||
"submission_client": "navidrome",
|
||||
"submission_client_version": "0.56.1 (fa2cf362)"
|
||||
},
|
||||
"artist_name": "ネクライトーキー",
|
||||
"release_name": "ONE!",
|
||||
"track_name": "こんがらがった!"
|
||||
}
|
||||
}
|
||||
]
|
||||
}`, time.Now().Add(-1*time.Hour).Unix())}
|
||||
for _, body := range bodies {
|
||||
req, err := http.NewRequest("POST", host()+"/apis/listenbrainz/1/submit-listens", strings.NewReader(body))
|
||||
require.NoError(t, err)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Token %s", apikey))
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
require.NoError(t, err)
|
||||
respBytes, err := io.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, `{"status": "ok"}`, string(respBytes))
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetters(t *testing.T) {
|
||||
t.Run("Submit Listens", doSubmitListens)
|
||||
// Artist was saved
|
||||
resp, err := http.DefaultClient.Get(host() + "/apis/web/v1/artist?id=1")
|
||||
assert.NoError(t, err)
|
||||
var artist models.Artist
|
||||
err = json.NewDecoder(resp.Body).Decode(&artist)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "さユり", artist.Name)
|
||||
|
||||
// Album was saved
|
||||
resp, err = http.DefaultClient.Get(host() + "/apis/web/v1/album?id=1")
|
||||
assert.NoError(t, err)
|
||||
var album models.Album
|
||||
err = json.NewDecoder(resp.Body).Decode(&album)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "酸欠少女", album.Title)
|
||||
|
||||
// Track was saved
|
||||
resp, err = http.DefaultClient.Get(host() + "/apis/web/v1/track?id=1")
|
||||
assert.NoError(t, err)
|
||||
var track models.Track
|
||||
err = json.NewDecoder(resp.Body).Decode(&track)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "花の塔", track.Title)
|
||||
|
||||
// Listen was saved
|
||||
resp, err = http.DefaultClient.Get(host() + "/apis/web/v1/listens")
|
||||
assert.NoError(t, err)
|
||||
var listens db.PaginatedResponse[models.Listen]
|
||||
err = json.NewDecoder(resp.Body).Decode(&listens)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, listens.Items, 3)
|
||||
assert.EqualValues(t, 2, listens.Items[0].Track.ID)
|
||||
assert.Equal(t, "Where Our Blue Is", listens.Items[0].Track.Title)
|
||||
|
||||
resp, err = http.DefaultClient.Get(host() + "/apis/web/v1/top-artists")
|
||||
assert.NoError(t, err)
|
||||
var artists db.PaginatedResponse[models.Artist]
|
||||
err = json.NewDecoder(resp.Body).Decode(&artists)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, artists.Items, 3)
|
||||
|
||||
resp, err = http.DefaultClient.Get(host() + "/apis/web/v1/top-albums")
|
||||
assert.NoError(t, err)
|
||||
var albums db.PaginatedResponse[models.Album]
|
||||
err = json.NewDecoder(resp.Body).Decode(&albums)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, albums.Items, 3)
|
||||
|
||||
resp, err = http.DefaultClient.Get(host() + "/apis/web/v1/top-tracks")
|
||||
assert.NoError(t, err)
|
||||
var tracks db.PaginatedResponse[models.Track]
|
||||
err = json.NewDecoder(resp.Body).Decode(&tracks)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, tracks.Items, 3)
|
||||
|
||||
truncateTestData(t)
|
||||
}
|
||||
|
||||
func TestMerge(t *testing.T) {
|
||||
|
||||
t.Run("Submit Listens", doSubmitListens)
|
||||
|
||||
resp, err := makeAuthRequest(t, session, "POST", "/apis/web/v1/merge/tracks?from_id=1&to_id=2", nil)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 204, resp.StatusCode)
|
||||
|
||||
resp, err = http.DefaultClient.Get(host() + "/apis/web/v1/track?id=2")
|
||||
require.NoError(t, err)
|
||||
var track models.Track
|
||||
err = json.NewDecoder(resp.Body).Decode(&track)
|
||||
require.NoError(t, err)
|
||||
assert.EqualValues(t, 2, track.ListenCount)
|
||||
|
||||
truncateTestData(t)
|
||||
|
||||
t.Run("Submit Listens", doSubmitListens)
|
||||
|
||||
resp, err = makeAuthRequest(t, session, "POST", "/apis/web/v1/merge/artists?from_id=1&to_id=2", nil)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 204, resp.StatusCode)
|
||||
|
||||
resp, err = http.DefaultClient.Get(host() + "/apis/web/v1/artist?id=2")
|
||||
require.NoError(t, err)
|
||||
var artist models.Artist
|
||||
err = json.NewDecoder(resp.Body).Decode(&artist)
|
||||
require.NoError(t, err)
|
||||
assert.EqualValues(t, 2, artist.ListenCount)
|
||||
|
||||
truncateTestData(t)
|
||||
|
||||
t.Run("Submit Listens", doSubmitListens)
|
||||
|
||||
resp, err = makeAuthRequest(t, session, "POST", "/apis/web/v1/merge/albums?from_id=1&to_id=2", nil)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 204, resp.StatusCode)
|
||||
|
||||
resp, err = http.DefaultClient.Get(host() + "/apis/web/v1/album?id=2")
|
||||
require.NoError(t, err)
|
||||
var album models.Album
|
||||
err = json.NewDecoder(resp.Body).Decode(&album)
|
||||
require.NoError(t, err)
|
||||
assert.EqualValues(t, 2, album.ListenCount)
|
||||
|
||||
truncateTestData(t)
|
||||
}
|
||||
|
||||
func TestValidateToken(t *testing.T) {
|
||||
login(t)
|
||||
getApiKey(t, session)
|
||||
|
||||
req, err := http.NewRequest("GET", host()+"/apis/listenbrainz/1/validate-token", nil)
|
||||
require.NoError(t, err)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Token %s", apikey))
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
require.NoError(t, err)
|
||||
var actual handlers.LbzValidateResponse
|
||||
require.NoError(t, json.NewDecoder(resp.Body).Decode(&actual))
|
||||
t.Log(actual)
|
||||
var expected handlers.LbzValidateResponse
|
||||
expected.Code = 200
|
||||
expected.Message = "Token valid."
|
||||
expected.Valid = true
|
||||
expected.UserName = "test"
|
||||
assert.True(t, assert.ObjectsAreEqual(expected, actual))
|
||||
|
||||
req, err = http.NewRequest("GET", host()+"/apis/listenbrainz/1/validate-token", nil)
|
||||
require.NoError(t, err)
|
||||
req.Header.Add("Authorization", "Token thisisasuperinvalidtoken")
|
||||
resp, err = http.DefaultClient.Do(req)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 401, resp.StatusCode)
|
||||
|
||||
req, err = http.NewRequest("GET", host()+"/apis/listenbrainz/1/validate-token", nil)
|
||||
require.NoError(t, err)
|
||||
resp, err = http.DefaultClient.Do(req)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 401, resp.StatusCode)
|
||||
}
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
|
||||
t.Run("Submit Listens", doSubmitListens)
|
||||
|
||||
resp, err := makeAuthRequest(t, session, "DELETE", "/apis/web/v1/artist?id=1", nil)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 204, resp.StatusCode)
|
||||
|
||||
resp, err = http.DefaultClient.Get(host() + "/apis/web/v1/artist?id=1")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 404, resp.StatusCode)
|
||||
|
||||
resp, err = makeAuthRequest(t, session, "DELETE", "/apis/web/v1/album?id=1", nil)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 204, resp.StatusCode)
|
||||
|
||||
resp, err = http.DefaultClient.Get(host() + "/apis/web/v1/album?id=1")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 404, resp.StatusCode)
|
||||
|
||||
resp, err = makeAuthRequest(t, session, "DELETE", "/apis/web/v1/track?id=1", nil)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 204, resp.StatusCode)
|
||||
|
||||
resp, err = http.DefaultClient.Get(host() + "/apis/web/v1/track?id=1")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 404, resp.StatusCode)
|
||||
|
||||
truncateTestData(t)
|
||||
}
|
||||
|
||||
func TestAliasesAndSearch(t *testing.T) {
|
||||
|
||||
t.Run("Submit Listens", doSubmitListens)
|
||||
|
||||
resp, err := makeAuthRequest(t, session, "POST", "/apis/web/v1/aliases?artist_id=1&alias=Sayuri", nil)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 201, resp.StatusCode)
|
||||
|
||||
resp, err = http.DefaultClient.Get(host() + "/apis/web/v1/aliases?artist_id=1")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 200, resp.StatusCode)
|
||||
var actual []models.Alias
|
||||
require.NoError(t, json.NewDecoder(resp.Body).Decode(&actual))
|
||||
require.Len(t, actual, 2)
|
||||
assert.Equal(t, actual[0].Alias, "さユり")
|
||||
assert.Equal(t, actual[0].Source, "Canonical")
|
||||
assert.Equal(t, actual[1].Alias, "Sayuri")
|
||||
assert.Equal(t, actual[1].Source, "Manual")
|
||||
|
||||
resp, err = makeAuthRequest(t, session, "POST", "/apis/web/v1/aliases?album_id=1&alias=Sanketsu+Girl", nil)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 201, resp.StatusCode)
|
||||
|
||||
resp, err = http.DefaultClient.Get(host() + "/apis/web/v1/aliases?album_id=1")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 200, resp.StatusCode)
|
||||
actual = nil
|
||||
require.NoError(t, json.NewDecoder(resp.Body).Decode(&actual))
|
||||
require.Len(t, actual, 2)
|
||||
assert.Equal(t, actual[0].Alias, "酸欠少女")
|
||||
assert.Equal(t, actual[0].Source, "Canonical")
|
||||
assert.Equal(t, actual[1].Alias, "Sanketsu Girl")
|
||||
assert.Equal(t, actual[1].Source, "Manual")
|
||||
|
||||
resp, err = makeAuthRequest(t, session, "POST", "/apis/web/v1/aliases?track_id=1&alias=Tower+of+Flower", nil)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 201, resp.StatusCode)
|
||||
|
||||
resp, err = makeAuthRequest(t, session, "POST", "/apis/web/v1/aliases/primary?track_id=1&alias=Tower+of+Flower", nil)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 204, resp.StatusCode)
|
||||
|
||||
resp, err = http.DefaultClient.Get(host() + "/apis/web/v1/track?id=1")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 200, resp.StatusCode)
|
||||
var track models.Track
|
||||
require.NoError(t, json.NewDecoder(resp.Body).Decode(&track))
|
||||
require.Len(t, actual, 2)
|
||||
assert.Equal(t, track.Title, "Tower of Flower")
|
||||
|
||||
resp, err = makeAuthRequest(t, session, "POST", "/apis/web/v1/aliases/primary?artist_id=1&alias=Sayuri", nil)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 204, resp.StatusCode)
|
||||
|
||||
// make sure searching works with aliases
|
||||
|
||||
resp, err = http.DefaultClient.Get(host() + "/apis/web/v1/search?q=Sanketsu")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 200, resp.StatusCode)
|
||||
var results handlers.SearchResults
|
||||
require.NoError(t, json.NewDecoder(resp.Body).Decode(&results))
|
||||
require.Len(t, results.Albums, 1)
|
||||
assert.Equal(t, results.Albums[0].Title, "酸欠少女")
|
||||
|
||||
resp, err = http.DefaultClient.Get(host() + "/apis/web/v1/search?q=Sayuri")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 200, resp.StatusCode)
|
||||
results = handlers.SearchResults{}
|
||||
require.NoError(t, json.NewDecoder(resp.Body).Decode(&results))
|
||||
require.Len(t, results.Artists, 1)
|
||||
assert.Equal(t, results.Artists[0].Name, "Sayuri") // reflects the new primary alias
|
||||
|
||||
truncateTestData(t)
|
||||
}
|
||||
|
||||
func TestStats(t *testing.T) {
|
||||
// zeroes
|
||||
resp, err := http.DefaultClient.Get(host() + "/apis/web/v1/stats")
|
||||
t.Log(resp)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("Submit Listens", doSubmitListens)
|
||||
|
||||
resp, err = http.DefaultClient.Get(host() + "/apis/web/v1/stats")
|
||||
t.Log(resp)
|
||||
require.NoError(t, err)
|
||||
var actual handlers.StatsResponse
|
||||
require.NoError(t, json.NewDecoder(resp.Body).Decode(&actual))
|
||||
assert.EqualValues(t, 3, actual.ListenCount)
|
||||
assert.EqualValues(t, 3, actual.TrackCount)
|
||||
assert.EqualValues(t, 3, actual.AlbumCount)
|
||||
assert.EqualValues(t, 3, actual.ArtistCount)
|
||||
assert.EqualValues(t, 0, actual.HoursListened)
|
||||
}
|
||||
|
||||
func TestListenActivity(t *testing.T) {
|
||||
|
||||
// this test fails when run a bit after midnight
|
||||
// i'll figure out a better test later
|
||||
|
||||
// t.Run("Submit Listens", doSubmitListens)
|
||||
|
||||
// resp, err := http.DefaultClient.Get(host() + "/apis/web/v1/listen-activity?range=3")
|
||||
// t.Log(resp)
|
||||
// require.NoError(t, err)
|
||||
// var actual []db.ListenActivityItem
|
||||
// require.NoError(t, json.NewDecoder(resp.Body).Decode(&actual))
|
||||
// t.Log(actual)
|
||||
// require.Len(t, actual, 3)
|
||||
// assert.EqualValues(t, 3, actual[2].Listens)
|
||||
}
|
||||
|
||||
func TestAuth(t *testing.T) {
|
||||
// logs in a new session
|
||||
formdata := url.Values{}
|
||||
formdata.Set("username", cfg.DefaultUsername())
|
||||
formdata.Set("password", cfg.DefaultPassword())
|
||||
encoded := formdata.Encode()
|
||||
resp, err := http.DefaultClient.Post(host()+"/apis/web/v1/login", "application/x-www-form-urlencoded", strings.NewReader(encoded))
|
||||
respBytes, _ := io.ReadAll(resp.Body)
|
||||
t.Logf("Login request response: %s - %s", resp.Status, respBytes)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, resp.Cookies(), 1)
|
||||
s := resp.Cookies()[0].Value
|
||||
require.NotEmpty(t, s)
|
||||
|
||||
// test update user
|
||||
req, err := http.NewRequest("PATCH", host()+"/apis/web/v1/user?username=new&password=supersecret", nil)
|
||||
require.NoError(t, err)
|
||||
req.AddCookie(&http.Cookie{
|
||||
Name: "koito_session",
|
||||
Value: s,
|
||||
})
|
||||
resp, err = http.DefaultClient.Do(req)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 204, resp.StatusCode)
|
||||
|
||||
// test /me with updated info
|
||||
req, err = http.NewRequest("GET", host()+"/apis/web/v1/user/me", nil)
|
||||
require.NoError(t, err)
|
||||
req.AddCookie(&http.Cookie{
|
||||
Name: "koito_session",
|
||||
Value: s,
|
||||
})
|
||||
resp, err = http.DefaultClient.Do(req)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 200, resp.StatusCode)
|
||||
var me models.User
|
||||
require.NoError(t, json.NewDecoder(resp.Body).Decode(&me))
|
||||
require.Equal(t, "new", me.Username)
|
||||
|
||||
// login with old password fails
|
||||
formdata = url.Values{}
|
||||
formdata.Set("username", cfg.DefaultUsername())
|
||||
formdata.Set("password", cfg.DefaultPassword())
|
||||
encoded = formdata.Encode()
|
||||
resp, err = http.DefaultClient.Post(host()+"/apis/web/v1/login", "application/x-www-form-urlencoded", strings.NewReader(encoded))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 400, resp.StatusCode)
|
||||
|
||||
// reset update so other tests dont fail
|
||||
req, err = http.NewRequest("PATCH", host()+fmt.Sprintf("/apis/web/v1/user?username=%s&password=%s", cfg.DefaultUsername(), cfg.DefaultPassword()), nil)
|
||||
require.NoError(t, err)
|
||||
req.AddCookie(&http.Cookie{
|
||||
Name: "koito_session",
|
||||
Value: s,
|
||||
})
|
||||
resp, err = http.DefaultClient.Do(req)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 204, resp.StatusCode)
|
||||
|
||||
// creates api key
|
||||
req, err = http.NewRequest("POST", host()+"/apis/web/v1/user/apikeys?label=testing", nil)
|
||||
require.NoError(t, err)
|
||||
req.AddCookie(&http.Cookie{
|
||||
Name: "koito_session",
|
||||
Value: s,
|
||||
})
|
||||
resp, err = http.DefaultClient.Do(req)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 201, resp.StatusCode)
|
||||
var response struct {
|
||||
Key string `json:"key"`
|
||||
}
|
||||
require.NoError(t, json.NewDecoder(resp.Body).Decode(&response))
|
||||
require.NotEmpty(t, response.Key)
|
||||
|
||||
// validates api key
|
||||
req, err = http.NewRequest("GET", host()+"/apis/listenbrainz/1/validate-token", nil)
|
||||
require.NoError(t, err)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Token %s", response.Key))
|
||||
resp, err = http.DefaultClient.Do(req)
|
||||
require.NoError(t, err)
|
||||
var actual handlers.LbzValidateResponse
|
||||
require.NoError(t, json.NewDecoder(resp.Body).Decode(&actual))
|
||||
var expected handlers.LbzValidateResponse
|
||||
expected.Code = 200
|
||||
expected.Message = "Token valid."
|
||||
expected.Valid = true
|
||||
expected.UserName = "test"
|
||||
assert.True(t, assert.ObjectsAreEqual(expected, actual))
|
||||
|
||||
// changes api key label
|
||||
login(t) // i dont care about using the new session anymore
|
||||
resp, err = makeAuthRequest(t, s, "PATCH", "/apis/web/v1/user/apikeys?id=2&label=well+tested", nil)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 200, resp.StatusCode)
|
||||
resp, err = makeAuthRequest(t, s, "GET", "/apis/web/v1/user/apikeys", nil)
|
||||
require.NoError(t, err)
|
||||
var keys []models.ApiKey
|
||||
err = json.NewDecoder(resp.Body).Decode(&keys)
|
||||
require.NoError(t, err)
|
||||
require.GreaterOrEqual(t, len(keys), 2)
|
||||
require.NotNil(t, keys[1].Label)
|
||||
assert.Equal(t, "well tested", keys[1].Label)
|
||||
|
||||
// logs out
|
||||
req, err = http.NewRequest("POST", host()+"/apis/web/v1/logout", nil)
|
||||
require.NoError(t, err)
|
||||
req.AddCookie(&http.Cookie{
|
||||
Name: "koito_session",
|
||||
Value: s,
|
||||
})
|
||||
resp, err = http.DefaultClient.Do(req)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 204, resp.StatusCode)
|
||||
|
||||
// attempts to create an api key - unauthorized
|
||||
formdata = url.Values{}
|
||||
formdata.Set("label", "testing")
|
||||
encoded = formdata.Encode()
|
||||
req, err = http.NewRequest("POST", host()+"/apis/web/v1/user/apikeys", strings.NewReader(encoded))
|
||||
require.NoError(t, err)
|
||||
req.AddCookie(&http.Cookie{
|
||||
Name: "koito_session",
|
||||
Value: s,
|
||||
})
|
||||
resp, err = http.DefaultClient.Do(req)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 401, resp.StatusCode)
|
||||
}
|
||||
|
||||
func TestDeleteListen(t *testing.T) {
|
||||
login(t)
|
||||
getApiKey(t, session)
|
||||
|
||||
truncateTestData(t)
|
||||
|
||||
body := `{
|
||||
"listen_type": "single",
|
||||
"payload": [
|
||||
{
|
||||
"listened_at": 1749475719,
|
||||
"track_metadata": {
|
||||
"additional_info": {
|
||||
"artist_mbids": [
|
||||
"80b3cb83-b7a3-4f79-ad42-8325cefb3626"
|
||||
],
|
||||
"artist_names": [
|
||||
"キタニタツヤ"
|
||||
],
|
||||
"duration_ms": 197270,
|
||||
"recording_mbid": "4e909c21-e7a8-404d-b75a-0c8c2926efb0",
|
||||
"release_group_mbid": "89069d92-e495-462c-b189-3431551868ed",
|
||||
"release_mbid": "e16a49d6-77f3-4d73-b93c-cac855ce6ad5",
|
||||
"submission_client": "navidrome",
|
||||
"submission_client_version": "0.56.1 (fa2cf362)"
|
||||
},
|
||||
"artist_name": "キタニタツヤ",
|
||||
"release_name": "Where Our Blue Is",
|
||||
"track_name": "Where Our Blue Is"
|
||||
}
|
||||
}
|
||||
]
|
||||
}`
|
||||
|
||||
req, err := http.NewRequest("POST", host()+"/apis/listenbrainz/1/submit-listens", strings.NewReader(body))
|
||||
require.NoError(t, err)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Token %s", apikey))
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
require.NoError(t, err)
|
||||
respBytes, err := io.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, `{"status": "ok"}`, string(respBytes))
|
||||
|
||||
resp, err = makeAuthRequest(t, session, "DELETE", "/apis/web/v1/listen?track_id=1&unix=1749475719", nil)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 204, resp.StatusCode)
|
||||
|
||||
// deletes are idempotent
|
||||
resp, err = makeAuthRequest(t, session, "DELETE", "/apis/web/v1/listen?track_id=1&unix=1749475719", nil)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 204, resp.StatusCode)
|
||||
|
||||
// listen is deleted
|
||||
resp, err = http.DefaultClient.Get(host() + "/apis/web/v1/track?id=1")
|
||||
require.NoError(t, err)
|
||||
var track models.Track
|
||||
err = json.NewDecoder(resp.Body).Decode(&track)
|
||||
require.NoError(t, err)
|
||||
assert.EqualValues(t, 0, track.ListenCount)
|
||||
}
|
||||
|
||||
func TestArtistReplaceImage(t *testing.T) {
|
||||
|
||||
t.Run("Submit Listens", doSubmitListens)
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
mpw := multipart.NewWriter(buf)
|
||||
mpw.WriteField("artist_id", "1")
|
||||
w, err := mpw.CreateFormFile("image", path.Join("..", "static", "yuu.jpg"))
|
||||
require.NoError(t, err)
|
||||
f, err := os.Open(path.Join("..", "static", "yuu.jpg"))
|
||||
require.NoError(t, err)
|
||||
defer f.Close()
|
||||
_, err = io.Copy(w, f)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, mpw.Close())
|
||||
|
||||
req, err := http.NewRequest("POST", host()+"/apis/web/v1/replace-image", buf)
|
||||
require.NoError(t, err)
|
||||
req.AddCookie(&http.Cookie{
|
||||
Name: "koito_session",
|
||||
Value: session,
|
||||
})
|
||||
req.Header.Add("Content-Type", mpw.FormDataContentType())
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 200, resp.StatusCode)
|
||||
response := new(handlers.ReplaceImageResponse)
|
||||
require.NoError(t, json.NewDecoder(resp.Body).Decode(response))
|
||||
require.NotEmpty(t, response.Image)
|
||||
newid, err := uuid.Parse(response.Image)
|
||||
require.NoError(t, err)
|
||||
|
||||
a, err := store.GetArtist(context.Background(), db.GetArtistOpts{ID: 1})
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, a.Image)
|
||||
assert.Equal(t, newid, *a.Image)
|
||||
}
|
||||
|
||||
func TestAlbumReplaceImage(t *testing.T) {
|
||||
|
||||
t.Run("Submit Listens", doSubmitListens)
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
mpw := multipart.NewWriter(buf)
|
||||
mpw.WriteField("album_id", "1")
|
||||
w, err := mpw.CreateFormFile("image", path.Join("..", "static", "yuu.jpg"))
|
||||
require.NoError(t, err)
|
||||
f, err := os.Open(path.Join("..", "static", "yuu.jpg"))
|
||||
require.NoError(t, err)
|
||||
defer f.Close()
|
||||
_, err = io.Copy(w, f)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, mpw.Close())
|
||||
|
||||
req, err := http.NewRequest("POST", host()+"/apis/web/v1/replace-image", buf)
|
||||
require.NoError(t, err)
|
||||
req.AddCookie(&http.Cookie{
|
||||
Name: "koito_session",
|
||||
Value: session,
|
||||
})
|
||||
req.Header.Add("Content-Type", mpw.FormDataContentType())
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 200, resp.StatusCode)
|
||||
response := new(handlers.ReplaceImageResponse)
|
||||
require.NoError(t, json.NewDecoder(resp.Body).Decode(response))
|
||||
require.NotEmpty(t, response.Image)
|
||||
newid, err := uuid.Parse(response.Image)
|
||||
require.NoError(t, err)
|
||||
|
||||
a, err := store.GetAlbum(context.Background(), db.GetAlbumOpts{ID: 1})
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, a.Image)
|
||||
assert.Equal(t, newid, *a.Image)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue