Skip to content

Commit e4e70ae

Browse files
committed
Added some db unit tests
1 parent d555af6 commit e4e70ae

File tree

7 files changed

+198
-55
lines changed

7 files changed

+198
-55
lines changed

backend/go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module npm
33
go 1.20
44

55
require (
6+
github.com/DATA-DOG/go-sqlmock v1.5.0
67
github.com/alexflint/go-arg v1.4.3
78
github.com/amacneil/dbmate/v2 v2.3.0
89
github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible

backend/go.sum

Lines changed: 3 additions & 49 deletions
Large diffs are not rendered by default.

backend/internal/api/handler/users.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,11 @@ func DeleteUser() func(http.ResponseWriter, *http.Request) {
139139
case sql.ErrNoRows:
140140
h.NotFound(w, r)
141141
case nil:
142-
h.ResultResponseJSON(w, r, http.StatusOK, item.Delete())
142+
if err := item.Delete(); err != nil {
143+
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
144+
} else {
145+
h.ResultResponseJSON(w, r, http.StatusOK, true)
146+
}
143147
default:
144148
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
145149
}

backend/internal/database/db.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ func GetDB() *gorm.DB {
3737
return dbInstance
3838
}
3939

40+
// SetDB will set the dbOnstance to this
41+
// Used by unit testing to set the db to a mock database
42+
func SetDB(db *gorm.DB) {
43+
dbInstance = db
44+
}
45+
4046
func connect() (*gorm.DB, error) {
4147
var d gorm.Dialector
4248
dsn := config.Configuration.DB.GetGormConnectURL()

backend/internal/entity/user/model.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,13 @@ func (m *Model) LoadByEmail(email string) error {
6464

6565
// Save will save this model to the DB
6666
func (m *Model) Save() error {
67-
// Ensure email is nice
68-
m.Email = strings.TrimSpace(strings.ToLower(m.Email))
6967
if m.IsSystem {
7068
return errors.ErrSystemUserReadonly
7169
}
7270

71+
// Ensure email is nice
72+
m.Email = strings.TrimSpace(strings.ToLower(m.Email))
73+
7374
// Check if an existing user with this email exists
7475
if m2, err := GetByEmail(m.Email); err == nil && m.ID != m2.ID {
7576
return errors.ErrDuplicateEmailUser
@@ -81,14 +82,14 @@ func (m *Model) Save() error {
8182
}
8283

8384
// Delete will mark a user as deleted
84-
func (m *Model) Delete() bool {
85+
func (m *Model) Delete() error {
8586
if m.ID == 0 {
8687
// Can't delete a new object
87-
return false
88+
return eris.New("Unable to delete a new object")
8889
}
8990
db := database.GetDB()
9091
result := db.Delete(m)
91-
return result.Error == nil
92+
return result.Error
9293
}
9394

9495
// SetPermissions will wipe out any existing permissions and add new ones for this user
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
package user
2+
3+
import (
4+
"regexp"
5+
"testing"
6+
7+
"npm/internal/errors"
8+
"npm/internal/model"
9+
"npm/internal/test"
10+
11+
"github.com/DATA-DOG/go-sqlmock"
12+
"github.com/stretchr/testify/assert"
13+
"github.com/stretchr/testify/require"
14+
"github.com/stretchr/testify/suite"
15+
)
16+
17+
// +------------+
18+
// | Setup |
19+
// +------------+
20+
21+
type testsuite struct {
22+
suite.Suite
23+
mock sqlmock.Sqlmock
24+
singleRow *sqlmock.Rows
25+
}
26+
27+
// SetupTest is executed before each test
28+
func (s *testsuite) SetupTest() {
29+
var err error
30+
s.mock, err = test.Setup()
31+
require.NoError(s.T(), err)
32+
33+
s.singleRow = sqlmock.NewRows([]string{
34+
"id",
35+
"name",
36+
"nickname",
37+
"email",
38+
"is_disabled",
39+
"is_system",
40+
}).AddRow(
41+
10,
42+
"John Doe",
43+
"Jonny",
44+
45+
false,
46+
false,
47+
)
48+
}
49+
50+
// In order for 'go test' to run this suite, we need to create
51+
// a normal test function and pass our suite to suite.Run
52+
func TestExampleTestSuite(t *testing.T) {
53+
suite.Run(t, new(testsuite))
54+
}
55+
56+
// +------------+
57+
// | Tests |
58+
// +------------+
59+
60+
func (s *testsuite) TestLoadByID() {
61+
s.mock.
62+
ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "user" WHERE "user"."id" = $1 AND "user"."is_deleted" = $2 ORDER BY "user"."id" LIMIT 1`)).
63+
WithArgs(10, 0).
64+
WillReturnRows(s.singleRow)
65+
66+
m := Model{}
67+
err := m.LoadByID(10)
68+
require.NoError(s.T(), err)
69+
require.NoError(s.T(), s.mock.ExpectationsWereMet())
70+
}
71+
72+
func (s *testsuite) TestLoadByEmail() {
73+
s.mock.
74+
ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "user" WHERE email = $1 AND is_system = $2 AND "user"."is_deleted" = $3 ORDER BY "user"."id" LIMIT 1`)).
75+
WithArgs("[email protected]", false, 0).
76+
WillReturnRows(s.singleRow)
77+
78+
m := Model{}
79+
err := m.LoadByEmail("[email protected]")
80+
require.NoError(s.T(), err)
81+
require.NoError(s.T(), s.mock.ExpectationsWereMet())
82+
}
83+
84+
func (s *testsuite) TestSave() {
85+
s.mock.
86+
ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "user" WHERE email = $1 AND is_system = $2 AND "user"."is_deleted" = $3 ORDER BY "user"."id" LIMIT 1`)).
87+
WithArgs("[email protected]", false, 0).
88+
WillReturnRows(s.singleRow)
89+
90+
s.mock.ExpectBegin()
91+
s.mock.ExpectQuery(regexp.QuoteMeta(`INSERT INTO "user" ("created_at","updated_at","is_deleted","name","nickname","email","is_disabled","is_system") VALUES ($1,$2,$3,$4,$5,$6,$7,$8) RETURNING "id"`)).
92+
WithArgs(
93+
sqlmock.AnyArg(),
94+
sqlmock.AnyArg(),
95+
0,
96+
"John Doe",
97+
"Jonny",
98+
99+
false,
100+
false,
101+
).
102+
WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow("11"))
103+
s.mock.ExpectCommit()
104+
105+
// New model, as system
106+
m := Model{
107+
Name: "John Doe",
108+
Nickname: "Jonny",
109+
Email: "[email protected]", // mixed case on purpose
110+
IsSystem: true,
111+
}
112+
err := m.Save()
113+
assert.Equal(s.T(), errors.ErrSystemUserReadonly.Error(), err.Error())
114+
115+
// Remove system and try again. Expect error due to duplicate email
116+
m.IsSystem = false
117+
err = m.Save()
118+
assert.Equal(s.T(), errors.ErrDuplicateEmailUser.Error(), err.Error())
119+
120+
// Change email and try again. Expect success
121+
m.Email = "[email protected]"
122+
err = m.Save()
123+
require.NoError(s.T(), err)
124+
require.NoError(s.T(), s.mock.ExpectationsWereMet())
125+
}
126+
127+
func (s *testsuite) TestDelete() {
128+
s.mock.ExpectBegin()
129+
s.mock.
130+
ExpectExec(regexp.QuoteMeta(`UPDATE "user" SET "is_deleted"=$1 WHERE "user"."id" = $2 AND "user"."is_deleted" = $3`)).
131+
WithArgs(1, 10, 0).
132+
WillReturnResult(sqlmock.NewResult(0, 1))
133+
s.mock.ExpectCommit()
134+
135+
m := Model{}
136+
err := m.Delete()
137+
assert.Equal(s.T(), "Unable to delete a new object", err.Error())
138+
139+
m2 := Model{
140+
ModelBase: model.ModelBase{
141+
ID: 10,
142+
},
143+
Name: "John Doe",
144+
}
145+
err2 := m2.Delete()
146+
require.NoError(s.T(), err2)
147+
require.NoError(s.T(), s.mock.ExpectationsWereMet())
148+
}
149+
150+
func (s *testsuite) TestGenerateGravatar() {
151+
m := Model{Email: "[email protected]"}
152+
m.generateGravatar()
153+
assert.Equal(s.T(), "https://www.gravatar.com/avatar/dc36565cc2376197358fa27ed4c47253?d=mm&r=pg&s=128", m.GravatarURL)
154+
}

backend/internal/test/suite.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package test
2+
3+
import (
4+
"npm/internal/database"
5+
6+
"github.com/DATA-DOG/go-sqlmock"
7+
"gorm.io/driver/postgres"
8+
"gorm.io/gorm"
9+
)
10+
11+
func Setup() (sqlmock.Sqlmock, error) {
12+
db, mock, err := sqlmock.New()
13+
if err != nil {
14+
return nil, err
15+
}
16+
dialector := postgres.New(postgres.Config{
17+
Conn: db,
18+
DriverName: "postgres",
19+
})
20+
gormDB, err := gorm.Open(dialector, &gorm.Config{})
21+
database.SetDB(gormDB)
22+
return mock, err
23+
}

0 commit comments

Comments
 (0)