Something went wrong on our end
-
Christian Pointner authoredChristian Pointner authored
store_test.go 18.93 KiB
//
// tank
//
// Import and Playlist Daemon for autoradio project
//
//
// Copyright (C) 2017-2019 Christian Pointner <equinox@helsinki.at>
//
// This file is part of tank.
//
// tank is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// tank is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with tank. If not, see <http://www.gnu.org/licenses/>.
//
package store
import (
"fmt"
"io"
"os"
"os/user"
"path/filepath"
"reflect"
"testing"
//"github.com/jinzhu/gorm"
)
var (
testBasePath = "run/aura-tank_testing"
testPublicDirPath = filepath.Join(testBasePath, publicShowName)
testDBPort = uint16(0)
testDBUsername = "tank"
testDBPassword = "aura"
testDBDB = "tank"
testShow1 = "test1"
testShow2 = "test2"
testUser1 = "user1"
testUser2 = "user2"
testSourceURI1 = "upload://test1.mp3"
testSourceURI2 = "upload://test2.ogg"
testFileArtist1 = "solo artist"
testFileArtist2 = "band of 2"
testFileAlbum1 = "first album"
testFileAlbum2 = "second album"
testFileTitle1 = "this is one title"
testFileTitle2 = "this is not two title's"
)
func testDBConfig() (cfg DBConfig) {
cfg.Type = os.Getenv("AURA_TANK_TEST_DB_TYPE")
if cfg.Type == "" {
cfg.Type = "mysql"
}
cfg.Host = os.Getenv("AURA_TANK_TEST_DB_HOST")
if cfg.Host == "" {
cfg.Host = "127.0.0.1"
}
switch cfg.Type {
case "mysql":
cfg.TLS = "false"
case "postgres":
cfg.TLS = "disable"
}
cfg.Port = testDBPort
cfg.Username = testDBUsername
cfg.Password = testDBPassword
cfg.DB = testDBDB
return
}
func newTestStore(t *testing.T) *Store {
cfg := Config{}
cfg.BasePath = testBasePath
cfg.DB = testDBConfig()
// TODO: drop database (all tables and constrains)
store, err := NewStore(cfg)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
return store
}
func TestMain(m *testing.M) {
u, err := user.Current()
if err != nil {
os.Exit(-1)
}
testBasePath = fmt.Sprintf("/run/user/%s/aura-tank_testing", u.Uid)
os.RemoveAll(testBasePath)
testPublicDirPath = filepath.Join(testBasePath, publicShowName)
os.Exit(m.Run())
}
//
// Testing
//
func TestOpen(t *testing.T) {
// base-path is non-existing directory
cfg := Config{}
cfg.BasePath = "/nonexistend/"
cfg.DB = testDBConfig()
if _, err := NewStore(cfg); err == nil {
t.Fatalf("opening store in nonexisting directory should throw an error")
}
cfg.BasePath = testBasePath
if err := os.MkdirAll(testBasePath, 0700); err != nil {
t.Fatalf("unexpected error: %v", err)
}
store, err := NewStore(cfg)
if err != nil {
t.Fatalf("creating new store failed: %v", err)
}
store.Close()
if store, err = NewStore(cfg); err != nil {
t.Fatalf("re-opening existing store failed: %v", err)
}
store.Close()
if err := os.RemoveAll(testPublicDirPath); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if f, err := os.Create(testPublicDirPath); err != nil {
t.Fatalf("unexpected error: %v", err)
} else {
io.WriteString(f, "this is not a directory")
f.Close()
}
if _, err = NewStore(cfg); err == nil {
t.Fatalf("opening store where path to public show dir is not a directory should throw an error")
}
if err := os.RemoveAll(testPublicDirPath); err != nil {
t.Fatalf("unexpected error: %v", err)
}
}
func TestMigrations(t *testing.T) {
cfg := Config{}
cfg.BasePath = testBasePath
cfg.DB = testDBConfig()
// os.Remove(testDBConnection)
store, err := NewStore(cfg)
if err != nil {
t.Fatalf("creating new store failed: %v", err)
}
// TODO: This is only true for now!!
if store.GetRevision() != "201903131716" {
t.Fatalf("for now new databases should have revision %q: got %q", "201903131716", store.GetRevision())
}
store.Close()
// TODO: needs more testing!!!!
}
//// Arrrrgh!!!!!
//// both database/sql and gorm are incredibly stupid!!!!
//// using database/sql alone it is needlessly hard to write portable sql queries:
//// postgres: db.Exec("INSERT INTO shows (name) VALUES ($1)", testShow1)
//// mysql: db.Exec("INSERT INTO shows (name) VALUES (?)", testShow1)
////
//// using gorm db.Exec() will deal with that but i couldn't find a good way to get
//// the last inserted id without using the model code of gorm and we specifically
//// don't want to use the model code since it is not clear whether the constraints
//// are actually enforced by the DBMS or by some gorm callbacks...
////
// func TestDBConstraints(t *testing.T) {
// // we don't want to use the model but hand written SQL commands here since we want to check
// // whether the constraints are really enforced by the DBMS and not by some magic bullshit in gorm!
// // This is even worse since gorm.AutoUpdate will not deal with foreign key constraints and we
// // need do it ourselves at the migration scripts. It would be really nice to be able to check
// // whether the migrations do the right thing!!!
// db, err := gorm.Open(testDBType, testDBConnection)
// if err != nil {
// t.Fatalf("unexpected error: %v", err)
// }
// if err = db.DB().Ping(); err != nil {
// t.Fatalf("unexpected error: %v", err)
// }
// res := db.Exec("INSERT INTO shows (name) VALUES (?)", testShow1)
// if res.Error != nil {
// t.Fatalf("adding show '%s' shouldn't fail: %v", testShow1, res.Error)
// }
// if res = db.Exec("INSERT INTO shows (name) VALUES (?)", testShow1); res.Error == nil {
// t.Fatalf("re-adding the same show should fail")
// }
// if res = db.Exec("INSERT INTO files (show_name, size) VALUES (?, ?)", testShow1, 500); res.Error != nil {
// t.Fatalf("re-adding the same show should fail")
// }
// }
// Shows
//
func checkShows(t *testing.T, shows Shows, expected []string) {
// if len(shows) != len(expected) {
// t.Fatalf("expected %d shows in store but got %d: %v", len(expected), len(shows), shows)
// }
for _, gname := range expected {
found := false
for _, g := range shows {
if gname == g.Name {
found = true
break
}
}
if !found {
t.Fatalf("expected show '%s' to be in store but got: %v", gname, shows)
}
if st, err := os.Stat(filepath.Join(testBasePath, gname)); err != nil {
t.Fatalf("can't open show directory for show '%s': %v", gname, err)
} else if !st.IsDir() {
t.Fatalf("path of show '%s' is not a directory", gname)
}
}
}
func TestShows(t *testing.T) {
store := newTestStore(t)
shows, err := store.ListShows()
if err != nil {
t.Fatalf("listing shows failed: %v", err)
}
checkShows(t, shows, []string{publicShowName})
if _, err = store.CreateShow(testShow1); err != nil {
t.Fatalf("creating show failed: %v", err)
}
if _, err = store.CreateShow(testShow2); err != nil {
t.Fatalf("creating show failed: %v", err)
}
shows, err = store.ListShows()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
checkShows(t, shows, []string{publicShowName, testShow1, testShow2})
if err = store.DeleteShow(testShow1); err != nil {
t.Fatalf("deleting show '%s' failed: %v", testShow1, err)
}
shows, err = store.ListShows()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
checkShows(t, shows, []string{publicShowName, testShow2})
if err = store.DeleteShow(testShow2); err != nil {
t.Fatalf("deleteing show '%s' failed: %v", testShow2, err)
}
checkShows(t, shows, []string{publicShowName})
}
// Files
//
func TestFilesListCreateDelete(t *testing.T) {
store := newTestStore(t)
files, err := store.ListFiles(publicShowName)
if err != nil {
t.Fatalf("listing files of public show failed: %v", err)
}
if len(files) != 0 {
t.Fatalf("a newly created store should contain no files in public show but ListFiles returned: %v", files)
}
files, err = store.ListFiles("notexistend")
if err != nil {
t.Fatalf("listing files of not existing show shouldn't throw an error but returned: %v", err)
}
if len(files) != 0 {
t.Fatalf("listing files of not existing show should return and empty list but ListFiles returned: %v", files)
}
publicFile, err := store.CreateFile(publicShowName, File{})
if err != nil {
t.Fatalf("creating file in public show failed: %v", err)
}
files, err = store.ListFiles(publicShowName)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if len(files) != 1 {
t.Fatalf("ListFiles should return a single file but returned: %v", files)
}
_, err = store.CreateFile(testShow1, File{Size: 17})
if err != nil {
t.Fatalf("creating file in not existing show shouldn't throw an error but CreateFile returned: %v", err)
}
_, err = store.CreateFile(testShow1, File{Size: 23})
if err != nil {
t.Fatalf("creating file in not existing show shouldn't throw an error but CreateFile returned: %v", err)
}
shows, err := store.ListShows()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
checkShows(t, shows, []string{publicShowName, testShow1})
files, err = store.ListFiles(testShow1)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if len(files) != 2 {
t.Fatalf("ListFiles should return a two files but returned: %v", files)
}
// clean up so next test can run with a clean DB, TODO: remove as soon as newTestStore() can re-init the DB
if err = store.DeleteShow(testShow1); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if err = store.DeleteFile(publicShowName, publicFile.ID); err != nil {
t.Fatalf("deleteing file %d of show '%s' failed: %v", publicFile.ID, publicShowName, err)
}
checkShows(t, shows, []string{publicShowName})
}
func fileEqual(a, b *File) bool {
// TODO: comparing the whole file struct using DeepEqual does not work because it does not use time.Equal when comparing timestamps...
return (a.Size == b.Size || reflect.DeepEqual(a.Source, b.Source) || reflect.DeepEqual(a.Metadata, b.Metadata))
}
func TestFilesCreateAndGet(t *testing.T) {
store := newTestStore(t)
if _, err := store.GetFile(testShow1, 0); err != ErrNotFound {
t.Fatalf("getting file in not-existing show should return ErrNotFound, but GetFile returned: %v", err)
}
file1 := File{Size: 12345}
file1.Source.URI = testSourceURI1
file1.Metadata.Artist = testFileArtist1
file1.Metadata.Album = testFileAlbum1
file1.Metadata.Title = testFileTitle1
in1, err := store.CreateFile(testShow1, file1)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
out1, err := store.GetFile(testShow1, in1.ID)
if err != nil {
t.Fatalf("getting existing file from store shouldn't return an error, but GetFile returned: %v", err)
}
if !fileEqual(in1, out1) {
t.Fatalf("GetFile returned different file than expected. Got %+v, expected %+v", out1, in1)
}
if _, err = store.GetFile(testShow1, 0); err != ErrNotFound {
t.Fatalf("getting not-existing file in existing show should return ErrNotFound, but GetFile returned: %v", err)
}
file2 := File{Size: 54321}
file2.Source.URI = testSourceURI2
file2.Metadata.Artist = testFileArtist2
file2.Metadata.Album = testFileAlbum2
file2.Metadata.Title = testFileTitle2
in2, err := store.CreateFile(testShow1, file2)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
out2, err := store.GetFile(testShow1, in2.ID)
if err != nil {
t.Fatalf("getting existing file from store shouldn't return an error, but GetFile returned: %v", err)
}
if !fileEqual(in2, out2) {
t.Fatalf("GetFile returned different file than expected. Got %+v, expected %+v", out2, in2)
}
files, err := store.ListFiles(testShow1)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if len(files) != 2 {
t.Fatalf("show '%s' should contain 2 files but ListFiles returned: %+v", testShow1, files)
}
for _, file := range files {
if file.ID == in1.ID {
if !fileEqual(in1, out1) {
t.Fatalf("ListFile returned different file than expected. Got %+v, expected %+v", out1, in1)
}
} else if file.ID == in2.ID {
if !fileEqual(in2, out2) {
t.Fatalf("ListFile returned different file than expected. Got %+v, expected %+v", out2, in2)
}
} else {
t.Fatalf("show '%s' should only contain files %d and %d but ListFiles returned: %+v", testShow1, in1.ID, in2.ID, files)
}
}
// clean up so next test can run with a clean DB, TODO: remove as soon as newTestStore() can re-init the DB
if err = store.DeleteShow(testShow1); err != nil {
t.Fatalf("unexpected error: %v", err)
}
}
func TestFilesUpdate(t *testing.T) {
store := newTestStore(t)
if _, err := store.UpdateFile(publicShowName, 0, File{}); err != ErrNotFound {
t.Fatalf("updateting not-existing file hould return ErrNotFound, but UpdateFile returned: %v", err)
}
file := File{Size: 12345}
file.Source.URI = testSourceURI1
file.Metadata.Artist = testFileArtist1
file.Metadata.Album = testFileAlbum1
file.Metadata.Title = testFileTitle1
in, err := store.CreateFile(testShow2, file)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
out, err := store.GetFile(testShow2, in.ID)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !fileEqual(in, out) {
t.Fatalf("GetFile returned different file than expected. Got %+v, expected %+v", out, in)
}
out.Size = 54321
out.Source.URI = testSourceURI2
out.Metadata.Artist = testFileArtist2
out.Metadata.Album = testFileAlbum2
out.Metadata.Title = testFileTitle2
if _, err = store.UpdateFile(testShow2, in.ID, *out); err != nil {
t.Fatalf("updateting an existing file shouldn't fail but UpdateFile returned: %v", err)
}
files, err := store.ListFiles(testShow2)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if len(files) != 1 {
t.Fatalf("show '%s' should contain 1 file but ListFiles returned: %+v", testShow2, files)
}
if files[0].ID != in.ID || !fileEqual(out, &(files[0])) {
t.Fatalf("ListFile returned different file than expected. Got %+v, expected %+v", out, files[0])
}
// clean up so next test can run with a clean DB, TODO: remove as soon as newTestStore() can re-init the DB
if err = store.DeleteShow(testShow2); err != nil {
t.Fatalf("unexpected error: %v", err)
}
}
func TestFilesDelete(t *testing.T) {
store := newTestStore(t)
if err := store.DeleteFile(testShow1, 0); err != ErrNotFound {
t.Fatalf("deleting not-existing file should return ErrNotFound, but DeleteFile returned: %v", err)
}
file, err := store.CreateFile(testShow1, File{Size: 12345})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if err = store.DeleteFile(testShow1, file.ID); err != nil {
t.Fatalf("deleting file failed, DeleteFile returned: %v", err)
}
if err = store.DeleteFile(testShow1, file.ID); err != ErrNotFound {
t.Fatalf("repeated deletion of file should return ErrNotFound, but DeleteFile returned: %v", err)
}
// clean up so next test can run with a clean DB, TODO: remove as soon as newTestStore() can re-init the DB
if err = store.DeleteShow(testShow1); err != nil {
t.Fatalf("unexpected error: %v", err)
}
}
// Playlists
//
func generateTestPlaylist(uris ...string) (p Playlist) {
for _, u := range uris {
e := PlaylistEntry{URI: u}
p.Entries = append(p.Entries, e)
}
return p
}
func TestPlaylistsListCreateDelete(t *testing.T) {
store := newTestStore(t)
playlists, err := store.ListPlaylists(publicShowName)
if err != nil {
t.Fatalf("listing playlists of public show failed: %v", err)
}
if len(playlists) != 0 {
t.Fatalf("a newly created store should contain no playlists in public show but ListPlaylists returned: %v", playlists)
}
playlists, err = store.ListPlaylists("notexistend")
if err != nil {
t.Fatalf("listing playlists of not existing show shouldn't throw an error but returned: %v", err)
}
if len(playlists) != 0 {
t.Fatalf("listing playlists of not existing show should return and empty list but ListPlaylists returned: %v", playlists)
}
in := generateTestPlaylist("audioin://1", "http://stream.example.com/live.mp")
publicPlaylist, err := store.CreatePlaylist(publicShowName, in)
if err != nil {
t.Fatalf("creating playlist in public show failed: %v", err)
}
playlists, err = store.ListPlaylists(publicShowName)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if len(playlists) != 1 {
t.Fatalf("ListPlaylists should return a single playlist but returned: %v", playlists)
}
in1 := generateTestPlaylist("audioin://1", "http://stream.example.com/live.mp3")
_, err = store.CreatePlaylist(testShow1, in1)
if err != nil {
t.Fatalf("creating playlist in not existing show shouldn't throw an error but CreatePlaylist returned: %v", err)
}
in2 := generateTestPlaylist("https://stream.example.com/other.ogg", "audioin://2")
_, err = store.CreatePlaylist(testShow1, in2)
if err != nil {
t.Fatalf("creating playlist in not existing show shouldn't throw an error but CreatePlaylist returned: %v", err)
}
shows, err := store.ListShows()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
checkShows(t, shows, []string{publicShowName, testShow1})
playlists, err = store.ListPlaylists(testShow1)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if len(playlists) != 2 {
t.Fatalf("ListPlaylists should return two playlists but returned: %v", playlists)
}
// clean up so next test can run with a clean DB, TODO: remove as soon as newTestStore() can re-init the DB
if err = store.DeleteShow(testShow1); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if err = store.DeletePlaylist(publicShowName, publicPlaylist.ID); err != nil {
t.Fatalf("deleteing playlist %d of show '%s' failed: %v", publicPlaylist.ID, publicShowName, err)
}
}
func TestPlaylistsCreateAndGet(t *testing.T) {
store := newTestStore(t)
f := File{Size: 12345}
f.Source.URI = testSourceURI1
f.Metadata.Artist = testFileArtist1
f.Metadata.Album = testFileAlbum1
f.Metadata.Title = testFileTitle1
file1, err := store.CreateFile(testShow1, f)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if _, err := store.GetPlaylist(testShow1, 0); err != ErrNotFound {
t.Fatalf("getting playlist in not-existing show should return ErrNotFound, but GetPlaylist returned: %v", err)
}
p := generateTestPlaylist("http://stream.example.com/stream.mp3")
p.Entries = append(p.Entries, PlaylistEntry{File: &File{ShowName: file1.ShowName, ID: file1.ID}})
list1, err := store.CreatePlaylist(testShow1, p)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if _, err := store.GetPlaylist(testShow1, list1.ID); err != nil {
t.Fatalf("getting existing playlist from store shouldn't return an error, but GetPlaylist returned: %v", err)
}
// TODO: check if playlists are equal
p = generateTestPlaylist("http://stream.example.com/other.mp3", fmt.Sprintf("file://%s/%d", file1.ShowName, file1.ID))
if _, err = store.CreatePlaylist(testShow1, p); err != nil {
t.Fatalf("unexpected error: %v", err)
}
playlists, err := store.ListPlaylists(testShow1)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if len(playlists) != 2 {
t.Fatalf("ListPlaylists should return two playlists but returned: %v", playlists)
}
// TODO: check playlists contains both lists
}