When I invoke the handler which is srv.handleCreateTicketOption(rec, req) it throws an error. I had written a code segment in a test file just like this in my old projects but didn't get an error. What does this have to do with invalid memory address or nil pointer dereference? Am I missing? Is it because of handleCreateTicketOption invokes a a service's function that executes a DB query?
func TestCreateTicketOptionHandler(t *testing.T) {
caseExpected, _ := json.Marshal(&ticket.Ticket{Id: 1, Name: "baris", Description: "test-desc", Allocation: 10})
srv := NewServer()
// expected := [][]byte{
// _, _ = json.Marshal(&ticket.Ticket{Id: 1, Name: "baris", Description: "test-desc", Allocation: 20}),
// // json.Marshal(&ticket.Ticket{Id: 1, Name: "baris", Description: "test-desc", Allocation: 20})
// }
tt := []struct {
name string
entry *ticket.Ticket
want []byte
code int
}{
{
"valid",
&ticket.Ticket{Name: "baris", Description: "test-desc", Allocation: 10},
caseExpected,
http.StatusOK,
},
}
var buf bytes.Buffer
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
json.NewEncoder(&buf).Encode(tc.entry)
req, err := http.NewRequest(http.MethodPost, "/ticket_options", &buf)
log.Println("1")
if err != nil {
log.Println("2")
t.Fatalf("could not create request: %v", err)
}
log.Println("3")
rec := httptest.NewRecorder()
log.Println("4")
srv.handleCreateTicketOption(rec, req)
log.Println("5")
if rec.Code != tc.code {
t.Fatalf("got status %d, want %v", rec.Code, tc.code)
}
log.Println("6")
if reflect.DeepEqual(rec.Body.Bytes(), tc.want) {
log.Println("7")
t.Fatalf("NAME:%v, got %v, want %v", tc.name, rec.Body.Bytes(), tc.want)
}
})
}
}
This is the output.
2022/09/07 04:37:56 1
2022/09/07 04:37:56 3
2022/09/07 04:37:56 4
--- FAIL: TestCreateTicketOptionHandler (0.00s)
--- FAIL: TestCreateTicketOptionHandler/valid (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=0x0 pc=0x12967bd]
goroutine 22 [running]:
testing.tRunner.func1.2({0x12cac40, 0x1522c10})
/usr/local/Cellar/go/1.18/libexec/src/testing/testing.go:1389 +0x24e
testing.tRunner.func1()
/usr/local/Cellar/go/1.18/libexec/src/testing/testing.go:1392 +0x39f
panic({0x12cac40, 0x1522c10})
/usr/local/Cellar/go/1.18/libexec/src/runtime/panic.go:838 +0x207
github.com/bariis/gowit-case-study/psql.(*TicketService).CreateTicketOption(0xc000091358, {0x1379588, 0xc0000ac008}, {0x0, {0xc0000acd20, 0x5}, {0xc0000acd25, 0x9}, 0xa})
/Users/barisertas/workspace/gowit-case-study/psql/ticket.go:24 +0xfd
github.com/bariis/gowit-case-study/http.(*Server).handleCreateTicketOption(0xc000091350, {0x1379150, 0xc000093240}, 0xc000136000)
/Users/barisertas/workspace/gowit-case-study/http/ticket.go:77 +0x10b
github.com/bariis/gowit-case-study/http.TestCreateTicketOptionHandler.func1(0xc000122680)
/Users/barisertas/workspace/gowit-case-study/http/ticket_test.go:68 +0x305
testing.tRunner(0xc000122680, 0xc0000ec920)
/usr/local/Cellar/go/1.18/libexec/src/testing/testing.go:1439 +0x102
created by testing.(*T).Run
/usr/local/Cellar/go/1.18/libexec/src/testing/testing.go:1486 +0x35f
exit status 2
This is the handler.
func (s *Server) handleCreateTicketOption(w http.ResponseWriter, r *http.Request) {
var t ticket.Ticket
body, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, er.ErrInternal.Error(), http.StatusInternalServerError)
return
}
err = json.Unmarshal(body, &t)
if err != nil {
http.Error(w, er.ErrInvalidData.Error(), http.StatusBadRequest)
return
}
ticket, err := s.TicketService.CreateTicketOption(r.Context(), t)
if err != nil {
http.Error(w, er.ErrInternal.Error(), http.StatusInternalServerError)
return
}
res, err := json.Marshal(ticket)
if err != nil {
http.Error(w, er.ErrInternal.Error(), http.StatusInternalServerError)
return
}
log.Printf("%v tickets allocated with name %v\n", t.Allocation, t.Name)
s.sendResponse(w, res, http.StatusOK)
}
This is the actual logic implemented that interacts with the db.
// CreateTicketOption creates a ticket option in database based on given attributes
// which are name, quantity, description.
func (t *TicketService) CreateTicketOption(ctx context.Context, ticket ticket.Ticket) (*ticket.Ticket, error) {
tx, err := t.db.dbPool.Begin(ctx)
if err != nil {
return nil, er.ErrInternal
}
defer tx.Rollback(ctx)
var id int
err = tx.QueryRow(ctx, `INSERT INTO ticket (name, description, allocation) VALUES ($1, $2, $3) RETURNING id`, ticket.Name, ticket.Description, ticket.Allocation).Scan(&id)
if err != nil {
return nil, er.ErrInternal
}
ticket.Id = id
return &ticket, tx.Commit(ctx)
}
I've written the same logic for the test file based on my old test file, and now it's giving me an error. Is it because of there is a DB operation where handler invokes
ticket, err := s.TicketService.CreateTicketOption(r.Context(), t)?