Skip to content

Commit

Permalink
Merge pull request #4 from Lai-YT/add-tests-of-database-accessor
Browse files Browse the repository at this point in the history
Test database accessor with in-memory SQLite
  • Loading branch information
Lai-YT authored Mar 30, 2024
2 parents b6e2af3 + 2a4bf88 commit 9b4dc11
Show file tree
Hide file tree
Showing 3 changed files with 206 additions and 0 deletions.
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ require (
github.com/sirupsen/logrus v1.9.3
go.uber.org/mock v0.4.0
gorm.io/driver/mysql v1.5.6
gorm.io/driver/sqlite v1.5.5
gorm.io/gorm v1.25.7
)

Expand All @@ -16,5 +17,6 @@ require (
github.com/go-sql-driver/mysql v1.8.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/mattn/go-sqlite3 v1.14.17 // indirect
golang.org/x/sys v0.18.0 // indirect
)
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo=
Expand All @@ -31,5 +33,7 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/mysql v1.5.6 h1:Ld4mkIickM+EliaQZQx3uOJDJHtrd70MxAUqWqlx3Y8=
gorm.io/driver/mysql v1.5.6/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
gorm.io/driver/sqlite v1.5.5 h1:7MDMtUZhV065SilG62E0MquljeArQZNfJnjd9i9gx3E=
gorm.io/driver/sqlite v1.5.5/go.mod h1:6NgQ7sQWAIFsPrJJl1lSNSu2TABh0ZZ/zm5fosATavE=
gorm.io/gorm v1.25.7 h1:VsD6acwRjz2zFxGO50gPO6AkNs7KKnvfzUjHQhZDz/A=
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
200 changes: 200 additions & 0 deletions storage/database_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
package storage

import (
"io"
"os"
"reflect"
"testing"
"todolist/core"

log "github.com/sirupsen/logrus"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)

func TestMain(m *testing.M) {
// So that we don't see log messages during tests.
log.SetOutput(io.Discard)
code := m.Run()
os.Exit(code)
}

// NOTE: Errors on the database panics because it means the test setup is incorrect.

func (dba *DatabaseAccessor) initTestDb() {
var err error
// NOTE: Using the in-memory SQLite database for testing purposes.
dba.db, err = gorm.Open(sqlite.Open("file::memory:"), &gorm.Config{
Logger: logger.Discard,
})
if err != nil {
panic(err)
}
err = dba.db.AutoMigrate(&TodoItemModel{})
if err != nil {
panic(err)
}
}

func (dba *DatabaseAccessor) closeTestDb() {
err := dba.db.Migrator().DropTable(&TodoItemModel{})
if err != nil {
panic(err)
}
dba.db = nil
}

// TestCreate Given a todo item, when Create is called, then the todo item should be created in the database and the id should be set and returned.
func TestCreate(t *testing.T) {
// arrange
dba := DatabaseAccessor{}
dba.initTestDb()
defer dba.closeTestDb()

// act
todo := core.TodoItem{Description: "Test description", Completed: false}
id, err := dba.Create(&todo)

// assert
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if todo.Id != id {
t.Errorf("Id not set on todo item correctly, expected %v, got %v", id, todo.Id)
}
want := []TodoItemModel{
{Id: id, Description: "Test description", Completed: false},
}
todosInDb := []TodoItemModel{}
dba.db.Find(&todosInDb)
if !reflect.DeepEqual(todosInDb, want) {
t.Errorf("want: %v, got: %v", want, todosInDb)
}
}

// TestRead Given some todo items in the database, when Read is called with a where clause that matches on the description of a todo item, then the todo item should be returned.
func TestRead(t *testing.T) {
// arrange
dba := DatabaseAccessor{}
dba.initTestDb()
defer dba.closeTestDb()
match := "Test description 1"
dba.db.Create(&[]TodoItemModel{
{Id: 1, Description: match, Completed: false},
{Id: 2, Description: "Test description 2", Completed: true},
})

// act
want := core.TodoItem{Id: 1, Description: "Test description 1", Completed: false}
got := dba.Read(func(item core.TodoItem) bool { return item.Description == match })

// assert
if len(got) != 1 {
t.Fatalf("expected 1 item, got %v", len(got))
}
if !reflect.DeepEqual(got[0], want) {
t.Errorf("want: %v, got: %v", want, got[0])
}
}

// TestUpdate Given some todo items in the database, when Update is called with the id of a todo item, then the todo item should be updated.
func TestUpdate(t *testing.T) {
// arrange
dba := DatabaseAccessor{}
dba.initTestDb()
defer dba.closeTestDb()
targetId := 2
dba.db.Create(&[]TodoItemModel{
{Id: 1, Description: "Test description 1", Completed: false},
{Id: targetId, Description: "Test description 2", Completed: true},
})

// act
updatedTodo := core.TodoItem{Id: targetId, Description: "Updated description", Completed: false}
err := dba.Update(updatedTodo)

// assert
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
want := []TodoItemModel{
{Id: 1, Description: "Test description 1", Completed: false},
{Id: targetId, Description: updatedTodo.Description, Completed: updatedTodo.Completed},
}
todosInDb := []TodoItemModel{}
dba.db.Find(&todosInDb)
if !reflect.DeepEqual(todosInDb, want) {
t.Errorf("want: %v, got: %v", want, todosInDb)
}
}

// TestUpdateNotFound Given some todo items in the database, when Update is called with an id that does not exist, then an error should be returned.
func TestUpdateNotFound(t *testing.T) {
// arrange
dba := DatabaseAccessor{}
dba.initTestDb()
defer dba.closeTestDb()
dba.db.Create(&[]TodoItemModel{
{Id: 1, Description: "Test description 1", Completed: false},
{Id: 2, Description: "Test description 2", Completed: true},
})

// act
nonExistentTodo := core.TodoItem{Id: 3, Description: "Updated description", Completed: false}
err := dba.Update(nonExistentTodo)

// assert
if err == nil {
t.Fatalf("expected error, got nil")
}
}

// TestDelete Given some todo items in the database, when Delete is called with the id of a todo item, then the todo item should be deleted.
func TestDelete(t *testing.T) {
// arrange
dba := DatabaseAccessor{}
dba.initTestDb()
defer dba.closeTestDb()
dba.db.Create(&[]TodoItemModel{
{Id: 1, Description: "Test description 1", Completed: false},
{Id: 2, Description: "Test description 2", Completed: true},
})

// act
err := dba.Delete(1)

// assert
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
want := []TodoItemModel{
{Id: 2, Description: "Test description 2", Completed: true},
}
todosInDb := []TodoItemModel{}
dba.db.Find(&todosInDb)
if !reflect.DeepEqual(todosInDb, want) {
t.Errorf("want: %v, got: %v", want, todosInDb)
}
}

// TestDeleteNotFound Given some todo items in the database, when Delete is called with an id that does not exist, then an error should be returned.
func TestDeleteNotFound(t *testing.T) {
// arrange
dba := DatabaseAccessor{}
dba.initTestDb()
defer dba.closeTestDb()
items := []TodoItemModel{
{Id: 1, Description: "Test description 1", Completed: false},
{Id: 2, Description: "Test description 2", Completed: true},
}
dba.db.Create(&items)

// act
err := dba.Delete(3)

// assert
if err == nil {
t.Fatalf("expected error, got nil")
}
}

0 comments on commit 9b4dc11

Please sign in to comment.