Skip to content

Commit

Permalink
add some tests
Browse files Browse the repository at this point in the history
  • Loading branch information
yakuter committed Jun 28, 2020
1 parent 5b43fee commit ff5f6b6
Show file tree
Hide file tree
Showing 9 changed files with 373 additions and 8 deletions.
6 changes: 4 additions & 2 deletions cmd/passwall-server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ func main() {
log.Fatal(err)
}

s, err := storage.New(&cfg.Database)
db, err := storage.DBConn(&cfg.Database)
if err != nil {
logger.Fatalf("failed to open storage: %s\n", err)
log.Fatal(err)
}

s := storage.New(db)

// Migrate database tables
// TODO: Migrate should be in storege.New functions of categories
app.MigrateSystemTables(s)
Expand Down
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/didip/tollbooth v4.0.2+incompatible
github.com/gin-gonic/gin v1.6.2
github.com/go-playground/validator/v10 v10.2.0
github.com/go-test/deep v1.0.6
github.com/golang/protobuf v1.4.0 // indirect
github.com/gorilla/mux v1.7.4
github.com/heroku/x v0.0.22
Expand All @@ -22,6 +23,7 @@ require (
github.com/satori/go.uuid v1.2.0
github.com/sethvargo/go-password v0.1.3
github.com/spf13/viper v1.6.2
github.com/stretchr/testify v1.5.1
github.com/ulule/limiter/v3 v3.5.0
github.com/urfave/negroni v1.0.0
golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ github.com/go-redis/redis/v7 v7.2.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRf
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-test/deep v1.0.6 h1:UHSEyLZUwX9Qoi99vVwvewiMC8mM2bf7XEM2nqvzEn8=
github.com/go-test/deep v1.0.6/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8=
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
Expand Down
8 changes: 7 additions & 1 deletion internal/api/health_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package api

import (
"log"
"net/http"
"net/http/httptest"
"testing"
Expand All @@ -22,7 +23,12 @@ func TestHealthCheck(t *testing.T) {
LogMode: false,
}

db, err := storage.New(mockDBConfig)
mockDB, err := storage.DBConn(mockDBConfig)
if err != nil {
log.Fatal(err)
}

db := storage.New(mockDB)

req, err := http.NewRequest("GET", "/health", nil)
if err != nil {
Expand Down
21 changes: 19 additions & 2 deletions internal/api/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package api

import (
"encoding/json"
"fmt"
"net/http"
"strconv"

Expand All @@ -16,7 +17,7 @@ const (
LoginDeleteSuccess = "Login deleted successfully!"
)

// FindAll ...
// FindAllLogins ...
func FindAllLogins(s storage.Store) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var err error
Expand All @@ -38,7 +39,7 @@ func FindAllLogins(s storage.Store) http.HandlerFunc {
}
}

// FindByID ...
// FindLoginsByID ...
func FindLoginsByID(s storage.Store) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
Expand Down Expand Up @@ -153,3 +154,19 @@ func DeleteLogin(s storage.Store) http.HandlerFunc {
RespondWithJSON(w, http.StatusOK, response)
}
}

// Test endpoint ...
func TestLogin(s storage.Store) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {

schema := r.Context().Value("schema").(string)
fmt.Println(schema)

response := model.Response{
Code: http.StatusOK,
Status: Success,
Message: "Test success!",
}
RespondWithJSON(w, http.StatusOK, response)
}
}
255 changes: 255 additions & 0 deletions internal/api/login_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
package api

import (
"context"
"database/sql/driver"
"encoding/json"
"net/http"
"net/http/httptest"
"regexp"
"testing"
"time"

"github.com/DATA-DOG/go-sqlmock"
"github.com/go-test/deep"
"github.com/gorilla/mux"
"github.com/jinzhu/gorm"
"github.com/pass-wall/passwall-server/internal/storage"
"github.com/pass-wall/passwall-server/model"
"github.com/stretchr/testify/assert"
)

func TestFindAllLogins(t *testing.T) {
w := httptest.NewRecorder()

// Create mock db
mockDB, mock := dbSetup()

// Initialize router
r := routersSetup(mockDB)

// Generate dummy login
var logins []model.Login
login := model.Login{
ID: 1,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
DeletedAt: nil,
Title: "Dummy Title",
URL: "http://dummy.com",
Username: "dummyuser",
Password: "GRr4f5bWKolEVw8EjXSryNPvVLEorL3VILyYhMUkZiize6FlBvP4C1I=", // Encrypted "dummypassword"
}
logins = append(logins, login)

// Add dummy login to dummy db table
rows := sqlmock.
NewRows([]string{"id", "created_at", "updated_at", "deleted_at", "title", "url", "username", "password"}).
AddRow(login.ID, login.CreatedAt, login.UpdatedAt, login.DeletedAt, login.Title, login.URL, login.Username, login.Password)

// Define expected query
const sqlSelectOne = `SELECT * FROM "user-test"."logins"`
mock.ExpectQuery(regexp.QuoteMeta(sqlSelectOne)).
WillReturnRows(rows)

// Make request
r.ServeHTTP(w, httptest.NewRequest("GET", "/api/logins", nil))

// Check status code
assert.Equal(t, http.StatusOK, w.Code, "Did not get expected HTTP status code, got")

// Unmarshall response
var resultLogins []model.Login
decoder := json.NewDecoder(w.Body)
if err := decoder.Decode(&resultLogins); err != nil {
t.Error(err)
}

// Compare response and table data
assert.Nil(t, deep.Equal(logins, resultLogins))

// we make sure that all expectations were met
if err := mock.ExpectationsWereMet(); err != nil {
t.Errorf("there were unfulfilled expectations: %s", err)
}

}

func TestFindLoginsByID(t *testing.T) {
w := httptest.NewRecorder()

// Create mock db
mockDB, mock := dbSetup()

// Initialize router
r := routersSetup(mockDB)

// Generate dummy login
loginDTO := &model.LoginDTO{
ID: 1,
Title: "Dummy Title",
URL: "http://dummy.com",
Username: "dummyuser",
Password: "dummypassword",
}

encryptedPassword := "GRr4f5bWKolEVw8EjXSryNPvVLEorL3VILyYhMUkZiize6FlBvP4C1I="

// Add dummy login to dummy db table
rows := sqlmock.
NewRows([]string{"id", "created_at", "updated_at", "deleted_at", "title", "url", "username", "password"}).
AddRow(loginDTO.ID, time.Now(), time.Now(), nil, loginDTO.Title, loginDTO.URL, loginDTO.Username, encryptedPassword)

// Define expected query
const sqlSelectOne = `SELECT * FROM "user-test"."logins" WHERE "user-test"."logins"."deleted_at" IS NULL AND ((id = $1))`
mock.ExpectQuery(regexp.QuoteMeta(sqlSelectOne)).
WithArgs(loginDTO.ID).
WillReturnRows(rows)

// Make request
r.ServeHTTP(w, httptest.NewRequest("GET", "/api/logins/1", nil))

// Check status code
assert.Equal(t, http.StatusOK, w.Code, "Did not get expected HTTP status code, got")

// Unmarshall response
resultLogin := new(model.LoginDTO)
decoder := json.NewDecoder(w.Body)
if err := decoder.Decode(&resultLogin); err != nil {
t.Error(err)
}

// Compare response and table data
assert.Nil(t, deep.Equal(loginDTO, resultLogin))

// we make sure that all expectations were met
if err := mock.ExpectationsWereMet(); err != nil {
t.Errorf("there were unfulfilled expectations: %s", err)
}
}

func dbSetup() (*gorm.DB, sqlmock.Sqlmock) {
db, mock, _ := sqlmock.New()
DB, _ := gorm.Open("postgres", db)
// DB.LogMode(true)

return DB, mock
}

func routersSetup(db *gorm.DB) *mux.Router {

// Create storage with mock db
store := storage.New(db)

// Initialize router
apiRouter := mux.NewRouter().PathPrefix("/api").Subrouter()

// Login endpoints
apiRouter.Handle("/logins", contextMiddleware(FindAllLogins(store))).Methods(http.MethodGet)
apiRouter.Handle("/logins", contextMiddleware(CreateLogin(store))).Methods(http.MethodPost)
apiRouter.Handle("/logins/{id:[0-9]+}", contextMiddleware(FindLoginsByID(store))).Methods(http.MethodGet)
apiRouter.Handle("/logins/{id:[0-9]+}", contextMiddleware(UpdateLogin(store))).Methods(http.MethodPut)
apiRouter.Handle("/logins/{id:[0-9]+}", contextMiddleware(DeleteLogin(store))).Methods(http.MethodDelete)

return apiRouter
}

func contextMiddleware(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
ctxWithID := context.WithValue(ctx, "id", 1)
ctxWithAuthorized := context.WithValue(ctxWithID, "authorized", true)
ctxWithSchema := context.WithValue(ctxWithAuthorized, "schema", "user-test")

h.ServeHTTP(w, r.WithContext(ctxWithSchema))
})
}

// func TestDeleteLogin(t *testing.T) {
// w := httptest.NewRecorder()

// // Create mock db
// mockDB, mock := dbSetup()

// // Initialize router
// r := routersSetup(mockDB)

// // Generate dummy login
// loginDTO := &model.LoginDTO{
// ID: 1,
// Title: "Dummy Title",
// URL: "http://dummy.com",
// Username: "dummyuser",
// Password: "dummypassword",
// }

// encryptedPassword := "GRr4f5bWKolEVw8EjXSryNPvVLEorL3VILyYhMUkZiize6FlBvP4C1I="

// // Add dummy login to dummy db table
// rows := sqlmock.
// NewRows([]string{"id", "created_at", "updated_at", "deleted_at", "title", "url", "username", "password"}).
// AddRow(loginDTO.ID, time.Now(), time.Now(), nil, loginDTO.Title, loginDTO.URL, loginDTO.Username, encryptedPassword)

// // Define expected query
// const sqlSelectOne = `SELECT * FROM "user-test"."logins" WHERE "user-test"."logins"."deleted_at" IS NULL AND ((id = $1))`
// mock.ExpectQuery(regexp.QuoteMeta(sqlSelectOne)).
// WithArgs(loginDTO.ID).
// WillReturnRows(rows)

// // Define expected query
// const sqlDeleteOne = `DELETE FROM "user-test"."logins" WHERE "user-test"."logins"."deleted_at" IS NULL AND ((id = $1))`
// mock.ExpectBegin() // start transaction
// mock.ExpectQuery(regexp.QuoteMeta(sqlDeleteOne)).
// WithArgs(loginDTO.ID).
// WillReturnRows(rows)
// mock.ExpectCommit() // commit transaction

// // Make request
// r.ServeHTTP(w, httptest.NewRequest("DELETE", "/api/logins/1", nil))

// // Check status code
// assert.Equal(t, http.StatusOK, w.Code, "Did not get expected HTTP status code, got")

// fmt.Println(w.Body.String())
// }

/* func TestCreateLogin(t *testing.T) {
w := httptest.NewRecorder()
// Create mock db
mockDB, mock := dbSetup()
mockDB.LogMode(true)
// Initialize router
r := routersSetup(mockDB)
// Generate dummy login
loginDTO := &model.LoginDTO{
ID: 1,
Title: "Dummy Title",
URL: "http://dummy.com",
Username: "dummyuser",
Password: "dummypassword",
}
const sqlInsert = `INSERT INTO "user-test"."logins" ("created_at","updated_at","deleted_at","title","url","username","password") VALUES ($1,$2,$3,$4,$5,$6,$7) RETURNING "user-test"."logins"."id"`
mock.ExpectBegin() // start transaction
mock.ExpectQuery(regexp.QuoteMeta(sqlInsert)).
WithArgs(AnyTime{}, AnyTime{}, nil, loginDTO.Title, loginDTO.URL, loginDTO.Username, loginDTO.Password).
WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(loginDTO.ID))
mock.ExpectCommit() // commit transaction
// Make request
data, _ := json.Marshal(loginDTO)
req, _ := http.NewRequest("POST", "/api/logins", bytes.NewBuffer(data))
req.Header.Set("Content-Type", "application/json")
r.ServeHTTP(w, req)
} */

type AnyTime struct{}

func (a AnyTime) Match(v driver.Value) bool {
_, ok := v.(time.Time)
return ok
}
1 change: 1 addition & 0 deletions internal/router/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ func (r *Router) initRoutes() {
apiRouter := mux.NewRouter().PathPrefix("/api").Subrouter()

// Login endpoints
apiRouter.HandleFunc("/login-test", api.TestLogin(r.store)).Methods(http.MethodGet)
apiRouter.HandleFunc("/logins", api.FindAllLogins(r.store)).Methods(http.MethodGet)
apiRouter.HandleFunc("/logins", api.CreateLogin(r.store)).Methods(http.MethodPost)
apiRouter.HandleFunc("/logins/{id:[0-9]+}", api.FindLoginsByID(r.store)).Methods(http.MethodGet)
Expand Down
Loading

0 comments on commit ff5f6b6

Please sign in to comment.