This could be really primitive question but I can't figure it out how to test POST method in API build by Go echo.
I wrote my test following some documents and it seems decent. but I ended up with "invalid memory address or nil pointer" error.
here is my code.
/main.go
package main
import "go-auth-api/model"
func main() {
model.Init()
defer model.DB.Close()
router := newRouter()
router.Logger.Fatal(router.Start(":8080"))
}
/router.go
package main
import (
"github.com/labstack/echo"
"github.com/labstack/echo/middleware"
"go-auth-api/handler"
"net/http"
)
func newRouter() *echo.Echo {
e := echo.New()
e.HTTPErrorHandler = handler.ErrorHandler
e.Pre(middleware.RemoveTrailingSlash())
e.Use(middleware.Logger())
e.Use(middleware.Recover())
// index
e.GET("/", func(c echo.Context) error {
return c.String(http.StatusOK, "Go auth API")
})
// user
e.POST("/sign-up", handler.SignUp)
e.POST("/login", handler.Login)
api := e.Group("/api")
// authentication is required from here
api.Use(middleware.JWTWithConfig(handler.Config))
api.GET("/user", handler.GetUserInfo)
api.GET("/todo", handler.GetUserTodos)
api.POST("/todo", handler.CreateTodo)
api.PUT("/todo/:id", handler.PutTodo)
api.DELETE("/todo/:id", handler.DeleteTodo)
return e
}
/model/db.go
package model
import (
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/postgres"
)
var DB *gorm.DB
func Init() {
var err error
DB, err = gorm.Open("postgres", "host=db port=5432 user=postgres dbname=app password=postgres sslmode=disable")
if err != nil {
panic(err.Error())
}
DB.AutoMigrate(&User{})
DB.AutoMigrate(&Todo{})
}
/model/user.go
package model
type User struct {
ID int `json:"id" gorm:"primary_key auto_increment"`
Name string `json:"name"`
Password string `json:"password"`
Todos []Todo
}
func CreateUser(user *User) {
DB.Create(user)
}
func GetUser(u *User) User {
var user User
var todos []Todo
DB.Where(u).First(&user)
DB.Where(Todo{UserID: uint(user.ID)}).Find(&todos)
user.Todos = todos
return user
}
/handler/auth.go
package handler
import (
"github.com/dgrijalva/jwt-go"
"github.com/labstack/echo"
"github.com/labstack/echo/middleware"
"go-auth-api/model"
"net/http"
"time"
)
type jwtCustomClaims struct {
ID int `json:"id"`
Name string `json:"name"`
jwt.StandardClaims
}
var signingKey = []byte("test1test2test3")
var Config = middleware.JWTConfig{
Claims: &jwtCustomClaims{},
SigningKey: signingKey,
}
func SignUp(c echo.Context) error {
user := new(model.User)
if err := c.Bind(user); err != nil {
return err
}
if user.Name == "" || user.Password == "" {
return &echo.HTTPError{
Code: http.StatusBadRequest,
Message: "invalid name or password",
}
}
if u := model.GetUser(&model.User{Name: user.Name}); u.Name == user.Name {
return &echo.HTTPError{
Code: http.StatusConflict,
Message: "name already exists",
}
}
model.CreateUser(user)
user.Password = "****"
return c.JSON(http.StatusCreated, user)
}
func Login(c echo.Context) error {
u := new(model.User)
if err := c.Bind(u); err != nil {
return err
}
user := model.GetUser(&model.User{Name: u.Name})
if user.Password != u.Password {
return &echo.HTTPError{
Code: http.StatusBadRequest,
Message: "invalid name or password",
}
}
claims := jwtCustomClaims{
user.ID,
user.Name,
jwt.StandardClaims{
ExpiresAt: time.Now().Add(time.Hour * 72).Unix(),
},
}
t := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
token, err := t.SignedString(signingKey)
if err != nil {
return err
}
return c.JSON(http.StatusOK, map[string]string{
"Token": token,
})
}
func retrieveUserIdFromToken(c echo.Context) int {
user := c.Get("user").(*jwt.Token)
claims := user.Claims.(*jwtCustomClaims)
id := claims.ID
return id
}
and finally, this is the test I wrote.
/handler/auth_test.go
package handler
import (
"github.com/labstack/echo"
"github.com/stretchr/testify/assert"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
func TestConnection(t *testing.T) {
e := echo.New()
req := httptest.NewRequest(http.MethodGet, "/", nil)
rec := httptest.NewRecorder()
_ = e.NewContext(req, rec)
assert.Equal(t, http.StatusOK, rec.Code)
}
func TestSignUp(t *testing.T) {
userJson := `{"name": "test", "password": "test1234"}`
e := echo.New()
req := httptest.NewRequest(http.MethodPost, "/sign-up", strings.NewReader(userJson))
req.Header.Set("Content-Type", "application/json")
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
if assert.NoError(t, SignUp(c)) {
assert.Equal(t, http.StatusCreated, rec.Code)
}
}
then I got this following error after running the test
=== RUN TestConnect
--- PASS: TestConnect (0.00s)
=== RUN TestSignUp
--- FAIL: TestSignUp (0.00s)
panic: runtime error: invalid memory address or nil pointer dereference [recovered]
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0xb0 pc=0x80e4d6]
goroutine 21 [running]:
testing.tRunner.func1(0xc0001a0d00)
/usr/local/go/src/testing/testing.go:874 +0x3a3
panic(0x916940, 0xdb6920)
/usr/local/go/src/runtime/panic.go:679 +0x1b2
github.com/jinzhu/gorm.(*DB).clone(0x0, 0xc0001d5dd8)
/go/pkg/mod/github.com/jinzhu/gorm@v1.9.11/main.go:821 +0x26
github.com/jinzhu/gorm.(*DB).Where(0x0, 0x8d13a0, 0xc0001a5340, 0x0, 0x0, 0x0, 0x7c0cb3)
/go/pkg/mod/github.com/jinzhu/gorm@v1.9.11/main.go:232 +0x2f
go-auth-api/model.GetUser(0xc0001a5340, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
/app/model/user.go:17 +0xb2
go-auth-api/handler.SignUp(0xa74dc0, 0xc0001b76c0, 0x0, 0xde6450)
/app/handler/auth.go:38 +0x133
go-auth-api/handler.TestSignUp(0xc0001a0d00)
/app/handler/auth_test.go:34 +0x33d
testing.tRunner(0xc0001a0d00, 0x9cbf48)
/usr/local/go/src/testing/testing.go:909 +0xc9
created by testing.(*T).Run
/usr/local/go/src/testing/testing.go:960 +0x350
FAIL go-auth-api/handler 0.021s
FAIL
Api server is working perfect. this error only happens when I execute test.
Am I missing something in the test? or is my architecture wrong?
sorry for such a fundamental question but I'm really stuck.
Aucun commentaire:
Enregistrer un commentaire