Remove legacy question storage (#7)
Deletes obsolete question/answer/vote persistence and the compatibility answer endpoint. Existing databases drop the legacy tables through migration 009. Plumber replies now notify the root homeowner even when nested beneath another plumber reply. Post and reply forms prevent duplicate submissions and show progress while posting. Reviewed-on: #7 Co-authored-by: codegirl-007 <s.raide@gmail.com>
This commit was merged in pull request #7.
This commit is contained in:
+16
-89
@@ -19,7 +19,6 @@ import (
|
||||
|
||||
"plumber"
|
||||
"plumber/internal/blob"
|
||||
"plumber/internal/mail"
|
||||
"plumber/internal/pacific"
|
||||
"plumber/internal/store"
|
||||
)
|
||||
@@ -169,6 +168,16 @@ func TestRegisterLoginAsk(t *testing.T) {
|
||||
if rec.Code != 200 {
|
||||
t.Fatalf("submit form %d", rec.Code)
|
||||
}
|
||||
for _, want := range []string{
|
||||
`src="/static/app.js"`,
|
||||
`id="submit-progress"`,
|
||||
`data-submit-once`,
|
||||
`data-submit-button`,
|
||||
} {
|
||||
if !strings.Contains(rec.Body.String(), want) {
|
||||
t.Fatalf("submit form missing %q: %s", want, rec.Body.String())
|
||||
}
|
||||
}
|
||||
csrf := csrfFrom(rec.Body.String())
|
||||
form := strings.NewReader("_csrf=" + csrf + "&title=Leaky+faucet&body=Drip+all+night.&city=Oakland")
|
||||
req = httptest.NewRequest(http.MethodPost, "/submit", form)
|
||||
@@ -483,13 +492,12 @@ func TestProfileAdminAnsweredListAndAvatarUpload(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMutationsVoteAnswerHideAndCSRF(t *testing.T) {
|
||||
recording := &mail.Recording{}
|
||||
srv, mem := newTestServer(t, Config{Mail: recording})
|
||||
func TestMutationsVoteHideAndCSRF(t *testing.T) {
|
||||
srv, mem := newTestServer(t, Config{})
|
||||
h := srv.Handler()
|
||||
adminName := uniq("admin")
|
||||
userName := uniq("user")
|
||||
admin := seedUser(t, mem, adminName, "hunter22", store.RoleAdmin)
|
||||
seedUser(t, mem, adminName, "hunter22", store.RoleAdmin)
|
||||
user := seedUser(t, mem, userName, "hunter22", store.RoleUser)
|
||||
adminCookies := loginUser(t, h, adminName, "hunter22")
|
||||
userCookies := loginUser(t, h, userName, "hunter22")
|
||||
@@ -562,92 +570,11 @@ func TestMutationsVoteAnswerHideAndCSRF(t *testing.T) {
|
||||
t.Fatalf("vote not applied: %+v %v", got, err)
|
||||
}
|
||||
|
||||
// Non-admin answer rejected
|
||||
rec = httptest.NewRecorder()
|
||||
req = httptest.NewRequest(http.MethodGet, "/questions/"+q.ID, nil)
|
||||
for _, c := range userCookies {
|
||||
req.AddCookie(c)
|
||||
}
|
||||
req = httptest.NewRequest(http.MethodPost, "/questions/"+q.ID+"/answer", nil)
|
||||
h.ServeHTTP(rec, req)
|
||||
csrf = csrfFrom(rec.Body.String())
|
||||
form = strings.NewReader("_csrf=" + csrf + "&body=Nope")
|
||||
req = httptest.NewRequest(http.MethodPost, "/questions/"+q.ID+"/answer", form)
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
for _, c := range userCookies {
|
||||
req.AddCookie(c)
|
||||
}
|
||||
rec = httptest.NewRecorder()
|
||||
h.ServeHTTP(rec, req)
|
||||
if rec.Code != http.StatusForbidden {
|
||||
t.Fatalf("non-admin answer want 403, got %d", rec.Code)
|
||||
}
|
||||
|
||||
// Admin answer compatibility route creates a reply and redirects the thread.
|
||||
rec = httptest.NewRecorder()
|
||||
req = httptest.NewRequest(http.MethodGet, "/questions/"+q.ID, nil)
|
||||
for _, c := range adminCookies {
|
||||
req.AddCookie(c)
|
||||
}
|
||||
h.ServeHTTP(rec, req)
|
||||
csrf = csrfFrom(rec.Body.String())
|
||||
form = strings.NewReader("_csrf=" + csrf + "&body=Tighten+the+nuts.")
|
||||
req = httptest.NewRequest(http.MethodPost, "/questions/"+q.ID+"/answer", form)
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
req.Header.Set("HX-Request", "true")
|
||||
for _, c := range adminCookies {
|
||||
req.AddCookie(c)
|
||||
}
|
||||
rec = httptest.NewRecorder()
|
||||
h.ServeHTTP(rec, req)
|
||||
if rec.Code != http.StatusSeeOther {
|
||||
t.Fatalf("admin answer: %d %s", rec.Code, rec.Body.String())
|
||||
}
|
||||
thread, err := mem.GetPostThread(context.Background(), q.ID)
|
||||
if err != nil || len(thread.Replies) != 1 {
|
||||
t.Fatalf("admin reply missing: %+v %v", thread, err)
|
||||
}
|
||||
adminReply := thread.Replies[0]
|
||||
if adminReply.AuthorID != admin.ID || adminReply.Body != "Tighten the nuts." {
|
||||
t.Fatalf("unexpected admin reply: %+v", adminReply)
|
||||
}
|
||||
if got := rec.Header().Get("HX-Redirect"); got != "/questions/"+q.ID+"#post-"+adminReply.ID {
|
||||
t.Fatalf("admin answer redirect = %q", got)
|
||||
}
|
||||
msgs := waitForMail(t, recording, 1)
|
||||
if msg := msgs[0]; msg.ToEmail != user.Email ||
|
||||
msg.RootID != q.ID ||
|
||||
msg.ReplyID != adminReply.ID ||
|
||||
msg.ReplyBody != adminReply.Body {
|
||||
t.Fatalf("compatibility reply notification = %+v", msg)
|
||||
}
|
||||
|
||||
rec = httptest.NewRecorder()
|
||||
req = httptest.NewRequest(http.MethodGet, "/questions/"+q.ID, nil)
|
||||
for _, c := range adminCookies {
|
||||
req.AddCookie(c)
|
||||
}
|
||||
h.ServeHTTP(rec, req)
|
||||
if body := rec.Body.String(); !strings.Contains(body, "Tighten the nuts.") ||
|
||||
!strings.Contains(body, "<summary>Edit</summary>") ||
|
||||
!strings.Contains(body, ">Tighten the nuts.</textarea>") ||
|
||||
!strings.Contains(body, `type="reset" class="btn btn-ghost"`) ||
|
||||
!strings.Contains(body, `removeAttribute('open')`) ||
|
||||
strings.Contains(body, `<details class="post-composer" open`) {
|
||||
t.Fatalf("admin reply editor is not collapsed and populated: %s", body)
|
||||
}
|
||||
|
||||
// The public reply is visible to the root author, but editing remains admin-only.
|
||||
rec = httptest.NewRecorder()
|
||||
req = httptest.NewRequest(http.MethodGet, "/questions/"+q.ID, nil)
|
||||
for _, c := range userCookies {
|
||||
req.AddCookie(c)
|
||||
}
|
||||
h.ServeHTTP(rec, req)
|
||||
if rec.Code != 200 || !strings.Contains(rec.Body.String(), "Tighten the nuts.") {
|
||||
t.Fatalf("question author cannot see answer: %d %s", rec.Code, rec.Body.String())
|
||||
}
|
||||
if strings.Contains(rec.Body.String(), `/posts/`+adminReply.ID+`/edit`) {
|
||||
t.Fatalf("question author can edit admin reply: %s", rec.Body.String())
|
||||
if rec.Code != http.StatusNotFound {
|
||||
t.Fatalf("removed answer endpoint want 404, got %d", rec.Code)
|
||||
}
|
||||
|
||||
// Hide invalid id
|
||||
|
||||
Reference in New Issue
Block a user