Manager: use in-memory SQLite database for testing

The on-disk database that was used before caused issues with tests running
in parallel. Not only is there the theoretical issue of tests seeing each
other's data (this didn't happen, but could), there was also the practical
issue of one test running while the other tried to erase the database file
(which fails on Windows due to file locking).
This commit is contained in:
Sybren A. Stüvel 2022-03-03 13:51:55 +01:00
parent 9b9c6bffff
commit 8824489980
4 changed files with 71 additions and 39 deletions

View File

@ -33,7 +33,8 @@ import (
) )
func TestStoreAuthoredJob(t *testing.T) { func TestStoreAuthoredJob(t *testing.T) {
db := CreateTestDB(t) db, dbCloser := CreateTestDB(t)
defer dbCloser()
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel() defer cancel()
@ -74,8 +75,8 @@ func TestStoreAuthoredJob(t *testing.T) {
} }
func TestJobHasTasksInStatus(t *testing.T) { func TestJobHasTasksInStatus(t *testing.T) {
ctx, ctxCancel, db, job, _ := jobTasksTestFixtures(t) ctx, close, db, job, _ := jobTasksTestFixtures(t)
defer ctxCancel() defer close()
hasTasks, err := db.JobHasTasksInStatus(ctx, job, api.TaskStatusQueued) hasTasks, err := db.JobHasTasksInStatus(ctx, job, api.TaskStatusQueued)
assert.NoError(t, err) assert.NoError(t, err)
@ -87,8 +88,8 @@ func TestJobHasTasksInStatus(t *testing.T) {
} }
func TestCountTasksOfJobInStatus(t *testing.T) { func TestCountTasksOfJobInStatus(t *testing.T) {
ctx, ctxCancel, db, job, authoredJob := jobTasksTestFixtures(t) ctx, close, db, job, authoredJob := jobTasksTestFixtures(t)
defer ctxCancel() defer close()
numQueued, numTotal, err := db.CountTasksOfJobInStatus(ctx, job, api.TaskStatusQueued) numQueued, numTotal, err := db.CountTasksOfJobInStatus(ctx, job, api.TaskStatusQueued)
assert.NoError(t, err) assert.NoError(t, err)
@ -118,8 +119,8 @@ func TestCountTasksOfJobInStatus(t *testing.T) {
} }
func TestUpdateJobsTaskStatuses(t *testing.T) { func TestUpdateJobsTaskStatuses(t *testing.T) {
ctx, ctxCancel, db, job, authoredJob := jobTasksTestFixtures(t) ctx, close, db, job, authoredJob := jobTasksTestFixtures(t)
defer ctxCancel() defer close()
err := db.UpdateJobsTaskStatuses(ctx, job, api.TaskStatusSoftFailed, "testing æctivity") err := db.UpdateJobsTaskStatuses(ctx, job, api.TaskStatusSoftFailed, "testing æctivity")
assert.NoError(t, err) assert.NoError(t, err)
@ -147,8 +148,8 @@ func TestUpdateJobsTaskStatuses(t *testing.T) {
} }
func TestUpdateJobsTaskStatusesConditional(t *testing.T) { func TestUpdateJobsTaskStatusesConditional(t *testing.T) {
ctx, ctxCancel, db, job, authoredJob := jobTasksTestFixtures(t) ctx, close, db, job, authoredJob := jobTasksTestFixtures(t)
defer ctxCancel() defer close()
getTask := func(taskIndex int) *Task { getTask := func(taskIndex int) *Task {
task, err := db.FetchTask(ctx, authoredJob.Tasks[taskIndex].UUID) task, err := db.FetchTask(ctx, authoredJob.Tasks[taskIndex].UUID)
@ -192,8 +193,8 @@ func TestUpdateJobsTaskStatusesConditional(t *testing.T) {
} }
func TestTaskAssignToWorker(t *testing.T) { func TestTaskAssignToWorker(t *testing.T) {
ctx, ctxCancel, db, _, authoredJob := jobTasksTestFixtures(t) ctx, close, db, _, authoredJob := jobTasksTestFixtures(t)
defer ctxCancel() defer close()
task, err := db.FetchTask(ctx, authoredJob.Tasks[1].UUID) task, err := db.FetchTask(ctx, authoredJob.Tasks[1].UUID)
assert.NoError(t, err) assert.NoError(t, err)
@ -206,8 +207,8 @@ func TestTaskAssignToWorker(t *testing.T) {
} }
func TestFetchTasksOfWorkerInStatus(t *testing.T) { func TestFetchTasksOfWorkerInStatus(t *testing.T) {
ctx, ctxCancel, db, _, authoredJob := jobTasksTestFixtures(t) ctx, close, db, _, authoredJob := jobTasksTestFixtures(t)
defer ctxCancel() defer close()
task, err := db.FetchTask(ctx, authoredJob.Tasks[1].UUID) task, err := db.FetchTask(ctx, authoredJob.Tasks[1].UUID)
assert.NoError(t, err) assert.NoError(t, err)
@ -288,9 +289,12 @@ func createTestAuthoredJobWithTasks() job_compilers.AuthoredJob {
} }
func jobTasksTestFixtures(t *testing.T) (context.Context, context.CancelFunc, *DB, *Job, job_compilers.AuthoredJob) { func jobTasksTestFixtures(t *testing.T) (context.Context, context.CancelFunc, *DB, *Job, job_compilers.AuthoredJob) {
db := CreateTestDB(t) db, dbCloser := CreateTestDB(t)
ctx, ctxCancel := context.WithTimeout(context.Background(), 1*time.Second)
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) cancel := func() {
ctxCancel()
dbCloser()
}
authoredJob := createTestAuthoredJobWithTasks() authoredJob := createTestAuthoredJobWithTasks()
err := db.StoreAuthoredJob(ctx, authoredJob) err := db.StoreAuthoredJob(ctx, authoredJob)

View File

@ -33,7 +33,8 @@ import (
) )
func TestNoTasks(t *testing.T) { func TestNoTasks(t *testing.T) {
db := CreateTestDB(t) db, dbCloser := CreateTestDB(t)
defer dbCloser()
ctx, ctxCancel := context.WithTimeout(context.Background(), 100*time.Millisecond) ctx, ctxCancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer ctxCancel() defer ctxCancel()
@ -45,7 +46,8 @@ func TestNoTasks(t *testing.T) {
} }
func TestOneJobOneTask(t *testing.T) { func TestOneJobOneTask(t *testing.T) {
db := CreateTestDB(t) db, dbCloser := CreateTestDB(t)
defer dbCloser()
ctx, ctxCancel := context.WithTimeout(context.Background(), 100*time.Millisecond) ctx, ctxCancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer ctxCancel() defer ctxCancel()
@ -81,7 +83,8 @@ func TestOneJobOneTask(t *testing.T) {
} }
func TestOneJobThreeTasksByPrio(t *testing.T) { func TestOneJobThreeTasksByPrio(t *testing.T) {
db := CreateTestDB(t) db, dbCloser := CreateTestDB(t)
defer dbCloser()
ctx, ctxCancel := context.WithTimeout(context.Background(), 100*time.Millisecond) ctx, ctxCancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer ctxCancel() defer ctxCancel()
@ -113,7 +116,8 @@ func TestOneJobThreeTasksByPrio(t *testing.T) {
} }
func TestOneJobThreeTasksByDependencies(t *testing.T) { func TestOneJobThreeTasksByDependencies(t *testing.T) {
db := CreateTestDB(t) db, dbCloser := CreateTestDB(t)
defer dbCloser()
ctx, ctxCancel := context.WithTimeout(context.Background(), 100*time.Millisecond) ctx, ctxCancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer ctxCancel() defer ctxCancel()
@ -140,7 +144,8 @@ func TestOneJobThreeTasksByDependencies(t *testing.T) {
} }
func TestTwoJobsThreeTasks(t *testing.T) { func TestTwoJobsThreeTasks(t *testing.T) {
db := CreateTestDB(t) db, dbCloser := CreateTestDB(t)
defer dbCloser()
ctx, ctxCancel := context.WithTimeout(context.Background(), 100*time.Millisecond) ctx, ctxCancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer ctxCancel() defer ctxCancel()

View File

@ -22,33 +22,56 @@ package persistence
* ***** END GPL LICENSE BLOCK ***** */ * ***** END GPL LICENSE BLOCK ***** */
import ( import (
"database/sql"
"os" "os"
"testing" "testing"
"time"
"github.com/stretchr/testify/assert" "github.com/glebarez/sqlite"
"golang.org/x/net/context" "github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"gorm.io/gorm"
) )
const TestDSN = "flamenco-test.sqlite" // Change this to a filename if you want to run a single test and inspect the
// resulting database.
func CreateTestDB(t *testing.T) *DB { const TestDSN = "file::memory:"
// Creating a new database should be fast.
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
func CreateTestDB(t *testing.T) (db *DB, closer func()) {
// Delete the SQLite file if it exists on disk.
if _, err := os.Stat(TestDSN); err == nil { if _, err := os.Stat(TestDSN); err == nil {
// File exists.
if err := os.Remove(TestDSN); err != nil { if err := os.Remove(TestDSN); err != nil {
t.Fatalf("unable to remove %s: %v", TestDSN, err) t.Fatalf("unable to remove %s: %v", TestDSN, err)
} }
} }
db, err := openDB(ctx, TestDSN) var err error
assert.NoError(t, err)
dblogger := NewDBLogger(log.Level(zerolog.InfoLevel).Output(os.Stdout))
sqliteConn, err := sql.Open(sqlite.DriverName, TestDSN)
if err != nil {
t.Fatalf("opening SQLite connection: %v", err)
}
config := gorm.Config{
Logger: dblogger,
ConnPool: sqliteConn,
}
db, err = openDBWithConfig(TestDSN, &config)
if err != nil {
t.Fatalf("opening DB: %v", err)
}
err = db.migrate() err = db.migrate()
assert.NoError(t, err) if err != nil {
t.Fatalf("migrating DB: %v", err)
}
return db closer = func() {
if err := sqliteConn.Close(); err != nil {
t.Fatalf("closing DB: %v", err)
}
}
return db, closer
} }

View File

@ -33,8 +33,8 @@ import (
) )
func TestCreateFetchWorker(t *testing.T) { func TestCreateFetchWorker(t *testing.T) {
db := CreateTestDB(t) db, dbCloser := CreateTestDB(t)
defer dbCloser()
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel() defer cancel()
@ -69,8 +69,8 @@ func TestCreateFetchWorker(t *testing.T) {
} }
func TestSaveWorker(t *testing.T) { func TestSaveWorker(t *testing.T) {
db := CreateTestDB(t) db, dbCloser := CreateTestDB(t)
defer dbCloser()
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel() defer cancel()