// // tank // // Import and Playlist Daemon for autoradio project // // // Copyright (C) 2017-2018 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" "os" "os/user" "testing" "github.com/jinzhu/gorm" ) var ( testBasePath = "run/aura-tank_testing" testDBType = "mysql" testDBConnection = "tank:aura@tcp(127.0.0.1:3306)/tank?charset=utf8&parseTime=True&loc=Local" // testDBType = "postgres" // testDBConnection = "host=127.0.0.1 port=5432 user=tank dbname=tank password=aura sslmode=disable" testGroup1 = "test1" testGroup2 = "test2" testUser1 = "user1" testUser2 = "user2" ) func stringInSlice(slice []string, search string) bool { for _, element := range slice { if element == search { return true } } return false } 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) // testDBConnection = filepath.Join(testBasePath, ".db.sqlite") os.Exit(m.Run()) } // // Testing // func TestOpen(t *testing.T) { // base-path is non-existing directory cfg := &Config{} cfg.BasePath = "/nonexistend/" cfg.DB.Type = testDBType cfg.DB.Connection = testDBConnection 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) } /// exisiting but non-database file (only possible with sqlite...) // if f, err := os.Create(testDBConnection); err != nil { // t.Fatalf("unexpected error: %v", err) // } else { // io.WriteString(f, "this is not a sqlite db.") // f.Close() // } // if _, err := NewStore(cfg); err == nil { // t.Fatalf("opening store using a invalid database should throw an error") // } /// create new db and reopen it // os.Remove(testDBConnection) store, err := NewStore(cfg) if err != nil { t.Fatalf("creating new store failed: %v", err) } store.Close() store, err = NewStore(cfg) if err != nil { t.Fatalf("re-opening existing store failed: %v", err) } store.Close() } func TestMigrations(t *testing.T) { cfg := &Config{} cfg.BasePath = testBasePath cfg.DB.Type = testDBType cfg.DB.Connection = testDBConnection // 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() != "201806161853" { t.Fatalf("for now new databases should have revision %q: got %q", "201806161853", 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 groups (name) VALUES ($1)", testGroup1) //// mysql: db.Exec("INSERT INTO groups (name) VALUES (?)", testGroup1) //// //// 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 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 groups (name) VALUES (?)", testGroup1) // if res.Error != nil { // t.Fatalf("adding group '%s' shouldn't fail: %v", testGroup1, res.Error) // } // if res = db.Exec("INSERT INTO groups (name) VALUES (?)", testGroup1); res.Error == nil { // t.Fatalf("re-adding the same group should fail") // } // if res = db.Exec("INSERT INTO files (group_name, size) VALUES (?, ?)", testGroup1, 500); res.Error != nil { // t.Fatalf("re-adding the same group should fail") // } // } // // Groups // // // func TestGroups(t *testing.T) { // os.Remove(testDBPath) // store, err := NewStore(&Config{testBasePath}) // if err != nil { // t.Fatalf("unexpected error: %v", err) // } // groups, err := store.ListGroups() // if err != nil { // t.Fatalf("listing groups failed: %v", err) // } // if len(groups) != 1 && groups[0] != publicGroupBn { // t.Fatalf("a newly created store should contain a single group '%s' but ListGroups returned: %q", publicGroupBn, groups) // } // if err = store.createGroup(testGroup1); err != nil { // t.Fatalf("creating group failed: %v", err) // } // groups, err = store.ListGroups() // if err != nil { // t.Fatalf("unexpected error: %v", err) // } // groupsSorted := sort.StringSlice(groups) // groupsSorted.Sort() // groupsExpected := sort.StringSlice([]string{publicGroupBn, testGroup1}) // groupsExpected.Sort() // if !reflect.DeepEqual(groupsSorted, groupsExpected) { // t.Fatalf("store should contain groups %q but ListGroups returned: %q", groupsExpected, groupsSorted) // } // if err = store.createGroup(testGroup2); err != nil { // t.Fatalf("creating group failed: %v", err) // } // groups, err = store.ListGroups() // if err != nil { // t.Fatalf("unexpected error: %v", err) // } // groupsSorted = sort.StringSlice(groups) // groupsSorted.Sort() // groupsExpected = sort.StringSlice([]string{publicGroupBn, testGroup1, testGroup2}) // groupsExpected.Sort() // if !reflect.DeepEqual(groupsSorted, groupsExpected) { // t.Fatalf("store should contain groups %q but ListGroups returned: %q", groupsExpected, groupsSorted) // } // } // // Files // // // func TestFilesListAndCreate(t *testing.T) { // os.Remove(testDBPath) // store, err := NewStore(&Config{testBasePath}) // if err != nil { // t.Fatalf("unexpected error: %v", err) // } // files, err := store.ListFiles(publicGroupBn) // if err != nil { // t.Fatalf("listing files of public group failed: %v", err) // } // if len(files) != 0 { // t.Fatalf("a newly created store should contain no files in public group but ListFiles returned: %v", files) // } // files, err = store.ListFiles("notexistend") // if err != nil { // t.Fatalf("listing files of not existing group shouldn't throw an error but returned: %v", err) // } // if len(files) != 0 { // t.Fatalf("listing files of not existing group should return and empty list but ListFiles returned: %v", files) // } // _, err = store.CreateFile(publicGroupBn, File{}) // if err != nil { // t.Fatalf("creating file in public group failed: %v", err) // } // files, err = store.ListFiles(publicGroupBn) // 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(testGroup1, File{}) // if err != nil { // t.Fatalf("creating file in not existing group shouldn't throw an error but CreateFile returned: %v", err) // } // groups, err := store.ListGroups() // if err != nil { // t.Fatalf("unexpected error: %v", err) // } // if !stringInSlice(groups, testGroup1) { // t.Fatalf("creating file in not existing group '%s' should create that group but ListGroups returned : %q", testGroup1, groups) // } // } // func TestNewAndChangedFile(t *testing.T) { // file := NewFile(testUser1) // if file.Created.User != testUser1 { // t.Fatalf("new file should be created by user '%s' but is: '%s'", testUser1, file.Created.User) // } // age := time.Since(file.Created.Timestamp) // if age < 0 || age > 3*time.Second { // t.Fatalf("new file timestamp is off: %s", file.Created.Timestamp) // } // if file.LastChanged.User != "" || !file.LastChanged.Timestamp.IsZero() { // t.Fatalf("new file's changed value should be empty but is: %+v", file.LastChanged) // } // orig := *file // file.Changed(testUser2) // if file.Created.User != orig.Created.User { // t.Fatalf("marking file as changed shouldn't change Created.User or Created.Timestamp, was '%+v' but is now '%+v'", orig.Created, file.Created) // } // if file.LastChanged.User != testUser2 { // t.Fatalf("changed file should have changed by user '%s' but is: '%s'", testUser2, file.LastChanged.User) // } // age = time.Since(file.LastChanged.Timestamp) // if age < 0 || age > 3*time.Second { // t.Fatalf("changed file timestamp is off: %s", file.LastChanged.Timestamp) // } // } // func TestFilesCreateAndGet(t *testing.T) { // os.Remove(testDBPath) // store, err := NewStore(&Config{testBasePath}) // if err != nil { // t.Fatalf("unexpected error: %v", err) // } // _, err = store.GetFile(testGroup1, InvalidID) // if err != ErrNotFound { // t.Fatalf("getting file in not-existing group should return ErrNotFound, but GetFile returned: %v", err) // } // expected := NewFile(testUser1) // fileID, err := store.CreateFile(testGroup1, *expected) // if err != nil { // t.Fatalf("unexpected error: %v", err) // } // _, err = store.GetFile(testGroup1, fileID) // //file, err := store.GetFile(testGroup1, fileID) // if err != nil { // t.Fatalf("getting existing file from store shouldn't return an error, but GetFile returned: %v", err) // } // // This does not work because DeepEqual does not use time.Equal when comparing timestamps... // // if !reflect.DeepEqual(expected, file) { // // t.Fatalf("GetFile returned different file than expected. Got %v, expected %v", file, expected) // // } // _, err = store.GetFile(testGroup1, InvalidID) // if err != ErrNotFound { // t.Fatalf("getting not-existing file in existing group should return ErrNotFound, but GetFile returned: %v", err) // } // } // func TestFilesUpdate(t *testing.T) { // os.Remove(testDBPath) // store, err := NewStore(&Config{testBasePath}) // if err != nil { // t.Fatalf("unexpected error: %v", err) // } // err = store.UpdateFile(testGroup1, InvalidID, File{}) // if err != ErrNotFound { // t.Fatalf("updateing not-existing file should return ErrNotFound, but UpdateFile returned: %v", err) // } // orig := NewFile(testUser1) // fileID, err := store.CreateFile(testGroup1, *orig) // if err != nil { // t.Fatalf("unexpected error: %v", err) // } // updated := *orig // updated.Changed(testUser2) // err = store.UpdateFile(testGroup1, fileID, updated) // if err != nil { // t.Fatalf("updating file failed, UpdateFile returned: %v", err) // } // // TODO: check if file was successfully updated // } // func TestFilesDelete(t *testing.T) { // os.Remove(testDBPath) // store, err := NewStore(&Config{testBasePath}) // if err != nil { // t.Fatalf("unexpected error: %v", err) // } // err = store.DeleteFile(testGroup1, InvalidID) // if err != ErrNotFound { // t.Fatalf("deleting not-existing file should return ErrNotFound, but DeleteFile returned: %v", err) // } // file := NewFile(testUser1) // fileID, err := store.CreateFile(testGroup1, *file) // if err != nil { // t.Fatalf("unexpected error: %v", err) // } // err = store.DeleteFile(testGroup1, fileID) // if err != nil { // t.Fatalf("deleting file failed, UpdateFile returned: %v", err) // } // err = store.DeleteFile(testGroup1, fileID) // if err != ErrNotFound { // t.Fatalf("repeated deletion of file should return ErrNotFound, but UpdateFile returned: %v", err) // } // // test file usage // fileID, err = store.CreateFile(testGroup1, *file) // if err != nil { // t.Fatalf("unexpected error: %v", err) // } // file.UsedBy = []uint64{42} // file.Changed(testUser1) // if err = store.UpdateFile(testGroup1, fileID, *file); err != nil { // t.Fatalf("unexpected error: %v", err) // } // err = store.DeleteFile(testGroup1, fileID) // if err != ErrFileInUse { // t.Fatalf("deleting file should return ErrFileInUse, but DeleteFile returned: %v", err) // } // }