fix panic when submitting invalid form on guestbook page

This commit is contained in:
yequari 2025-06-09 22:28:36 -07:00
parent b9842ddd5e
commit 861c953d01
4 changed files with 91 additions and 6 deletions

View File

@ -179,9 +179,7 @@ func (app *application) postGuestbookCommentCreate(w http.ResponseWriter, r *htt
form.CheckField(validator.NotBlank(form.AuthorName), "authorName", "This field cannot be blank") form.CheckField(validator.NotBlank(form.AuthorName), "authorName", "This field cannot be blank")
form.CheckField(validator.MaxChars(form.AuthorName, 256), "authorName", "This field cannot be more than 256 characters long") form.CheckField(validator.MaxChars(form.AuthorName, 256), "authorName", "This field cannot be more than 256 characters long")
form.CheckField(validator.NotBlank(form.AuthorEmail), "authorEmail", "This field cannot be blank")
form.CheckField(validator.MaxChars(form.AuthorEmail, 256), "authorEmail", "This field cannot be more than 256 characters long") form.CheckField(validator.MaxChars(form.AuthorEmail, 256), "authorEmail", "This field cannot be more than 256 characters long")
form.CheckField(validator.NotBlank(form.AuthorSite), "authorSite", "This field cannot be blank")
form.CheckField(validator.MaxChars(form.AuthorSite, 256), "authorSite", "This field cannot be more than 256 characters long") form.CheckField(validator.MaxChars(form.AuthorSite, 256), "authorSite", "This field cannot be more than 256 characters long")
form.CheckField(validator.NotBlank(form.Content), "content", "This field cannot be blank") form.CheckField(validator.NotBlank(form.Content), "content", "This field cannot be blank")
@ -193,7 +191,8 @@ func (app *application) postGuestbookCommentCreate(w http.ResponseWriter, r *htt
return return
} }
data := app.newCommonData(r) data := app.newCommonData(r)
views.GuestbookView("Guestbook", data, website, website.Guestbook, comments, forms.CommentCreateForm{}).Render(r.Context(), w) w.WriteHeader(http.StatusUnprocessableEntity)
views.GuestbookView("Guestbook", data, website, website.Guestbook, comments, form).Render(r.Context(), w)
return return
} }
@ -203,7 +202,7 @@ func (app *application) postGuestbookCommentCreate(w http.ResponseWriter, r *htt
app.serverError(w, r, err) app.serverError(w, r, err)
return return
} }
// app.sessionManager.Put(r.Context(), "flash", "Comment successfully posted!") app.sessionManager.Put(r.Context(), "flash", "Comment successfully posted!")
http.Redirect(w, r, fmt.Sprintf("/websites/%s/guestbook", slug), http.StatusSeeOther) http.Redirect(w, r, fmt.Sprintf("/websites/%s/guestbook", slug), http.StatusSeeOther)
} }

View File

@ -3,6 +3,7 @@ package main
import ( import (
"fmt" "fmt"
"net/http" "net/http"
"net/url"
"testing" "testing"
"git.32bit.cafe/32bitcafe/guestbook/internal/assert" "git.32bit.cafe/32bitcafe/guestbook/internal/assert"
@ -58,3 +59,88 @@ func TestGetGuestbookView(t *testing.T) {
}) })
} }
} }
func TestPostGuestbookCommentCreate(t *testing.T) {
app := newTestApplication(t)
ts := newTestServer(t, app.routes())
defer ts.Close()
_, _, body := ts.get(t, fmt.Sprintf("/websites/%s/guestbook", shortIdToSlug(1)))
validCSRFToken := extractCSRFToken(t, body)
const (
validAuthorName = "John Test"
validAuthorEmail = "test@example.com"
validAuthorSite = "example.com"
validContent = "This is a comment"
)
tests := []struct {
name string
authorName string
authorEmail string
authorSite string
content string
csrfToken string
wantCode int
}{
{
name: "Valid input",
authorName: validAuthorName,
authorEmail: validAuthorEmail,
authorSite: validAuthorSite,
content: validContent,
csrfToken: validCSRFToken,
wantCode: http.StatusSeeOther,
},
{
name: "Blank name",
authorName: "",
authorEmail: validAuthorEmail,
authorSite: validAuthorSite,
content: validContent,
csrfToken: validCSRFToken,
wantCode: http.StatusUnprocessableEntity,
},
{
name: "Blank email",
authorName: validAuthorName,
authorEmail: "",
authorSite: validAuthorSite,
content: validContent,
csrfToken: validCSRFToken,
wantCode: http.StatusSeeOther,
},
{
name: "Blank site",
authorName: validAuthorName,
authorEmail: validAuthorEmail,
authorSite: "",
content: validContent,
csrfToken: validCSRFToken,
wantCode: http.StatusSeeOther,
},
{
name: "Blank content",
authorName: validAuthorName,
authorEmail: validAuthorEmail,
authorSite: validAuthorSite,
content: "",
csrfToken: validCSRFToken,
wantCode: http.StatusUnprocessableEntity,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
form := url.Values{}
form.Add("authorname", tt.authorName)
form.Add("authoremail", tt.authorEmail)
form.Add("authorsite", tt.authorSite)
form.Add("content", tt.content)
form.Add("csrf_token", tt.csrfToken)
code, _, body := ts.postForm(t, fmt.Sprintf("/websites/%s/guestbook/comments/create", shortIdToSlug(1)), form)
assert.Equal(t, code, tt.wantCode)
assert.Equal(t, body, body)
})
}
}

View File

@ -17,7 +17,7 @@ func (app *application) routes() http.Handler {
standard := alice.New(app.recoverPanic, app.logRequest, commonHeaders) standard := alice.New(app.recoverPanic, app.logRequest, commonHeaders)
mux.Handle("/{$}", dynamic.ThenFunc(app.home)) mux.Handle("/{$}", dynamic.ThenFunc(app.home))
mux.Handle("POST /websites/{id}/guestbook/comments/create", standard.ThenFunc(app.postGuestbookCommentCreate)) mux.Handle("POST /websites/{id}/guestbook/comments/create", dynamic.ThenFunc(app.postGuestbookCommentCreate))
mux.Handle("GET /websites/{id}/guestbook", dynamic.ThenFunc(app.getGuestbook)) mux.Handle("GET /websites/{id}/guestbook", dynamic.ThenFunc(app.getGuestbook))
mux.Handle("GET /users/register", dynamic.ThenFunc(app.getUserRegister)) mux.Handle("GET /users/register", dynamic.ThenFunc(app.getUserRegister))
mux.Handle("POST /users/register", dynamic.ThenFunc(app.postUserRegister)) mux.Handle("POST /users/register", dynamic.ThenFunc(app.postUserRegister))

View File

@ -19,7 +19,7 @@ type CommentCreateForm struct {
AuthorName string `schema:"authorname"` AuthorName string `schema:"authorname"`
AuthorEmail string `schema:"authoremail"` AuthorEmail string `schema:"authoremail"`
AuthorSite string `schema:"authorsite"` AuthorSite string `schema:"authorsite"`
Content string `schema:"content,required"` Content string `schema:"content"`
validator.Validator `schema:"-"` validator.Validator `schema:"-"`
} }