239 lines
7.0 KiB
Go
239 lines
7.0 KiB
Go
package web
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
"testing"
|
|
|
|
"plumber/internal/events"
|
|
"plumber/internal/pacific"
|
|
"plumber/internal/store"
|
|
)
|
|
|
|
func TestPostHandlersPublishEvents(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
rec := &events.Recording{}
|
|
srv, mem := newTestServer(t, Config{
|
|
Events: rec,
|
|
BaseURL: "https://www.askaplumberfirst.com",
|
|
})
|
|
handler := srv.Handler()
|
|
homeowner := seedUser(t, mem, uniq("homeowner"), "hunter22", store.RoleUser)
|
|
admin := seedUser(t, mem, uniq("admin"), "hunter22", store.RoleAdmin)
|
|
homeownerCookies := loginUser(t, handler, homeowner.Username, "hunter22")
|
|
adminCookies := loginUser(t, handler, admin.Username, "hunter22")
|
|
homeownerCSRF := csrfForCookies(t, handler, homeownerCookies)
|
|
adminCSRF := csrfForCookies(t, handler, adminCookies)
|
|
|
|
submit := postForm(handler, "/submit", url.Values{
|
|
"_csrf": {homeownerCSRF},
|
|
"title": {"Leaky sink"},
|
|
"body": {"Water under the cabinet."},
|
|
"city": {"Oakland"},
|
|
}, homeownerCookies)
|
|
if submit.Code != http.StatusSeeOther {
|
|
t.Fatalf("submit status = %d: %s", submit.Code, submit.Body.String())
|
|
}
|
|
|
|
create := postForm(handler, "/posts", url.Values{
|
|
"_csrf": {homeownerCSRF},
|
|
"title": {"Second question"},
|
|
"body": {"Another leak."},
|
|
"city": {"Berkeley"},
|
|
}, homeownerCookies)
|
|
if create.Code != http.StatusSeeOther {
|
|
t.Fatalf("create status = %d: %s", create.Code, create.Body.String())
|
|
}
|
|
|
|
roots, err := mem.ListRootPosts(context.Background(), pacific.Today(), homeowner.ID)
|
|
if err != nil || len(roots) != 2 {
|
|
t.Fatalf("roots = %+v, %v", roots, err)
|
|
}
|
|
var submitRoot, createRoot store.Post
|
|
for _, root := range roots {
|
|
switch root.Title {
|
|
case "Leaky sink":
|
|
submitRoot = root
|
|
case "Second question":
|
|
createRoot = root
|
|
}
|
|
}
|
|
if submitRoot.ID == "" || createRoot.ID == "" {
|
|
t.Fatalf("missing created roots: %+v", roots)
|
|
}
|
|
|
|
reply := postForm(handler, "/posts", url.Values{
|
|
"_csrf": {adminCSRF},
|
|
"parent_id": {createRoot.ID},
|
|
"body": {"Replace the cartridge."},
|
|
}, adminCookies)
|
|
if reply.Code != http.StatusSeeOther {
|
|
t.Fatalf("reply status = %d: %s", reply.Code, reply.Body.String())
|
|
}
|
|
thread, err := mem.GetPostThread(context.Background(), createRoot.ID)
|
|
if err != nil || len(thread.Replies) != 1 {
|
|
t.Fatalf("thread = %+v, %v", thread, err)
|
|
}
|
|
adminReply := thread.Replies[0]
|
|
|
|
edit := postForm(handler, "/posts/"+createRoot.ID+"/edit", url.Values{
|
|
"_csrf": {homeownerCSRF},
|
|
"body": {"Updated leak description."},
|
|
}, homeownerCookies)
|
|
if edit.Code != http.StatusSeeOther {
|
|
t.Fatalf("edit status = %d: %s", edit.Code, edit.Body.String())
|
|
}
|
|
|
|
empty := postForm(handler, "/posts", url.Values{
|
|
"_csrf": {homeownerCSRF},
|
|
"title": {"Missing body"},
|
|
}, homeownerCookies)
|
|
if empty.Code != http.StatusBadRequest {
|
|
t.Fatalf("empty body status = %d, want 400", empty.Code)
|
|
}
|
|
|
|
hidden := &store.Post{
|
|
AuthorID: homeowner.ID,
|
|
Title: "Hidden thread",
|
|
Body: "Not public.",
|
|
PostDate: pacific.Today(),
|
|
PostState: store.PostStateHidden,
|
|
}
|
|
if err := mem.CreatePost(context.Background(), hidden); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
hiddenReply := postForm(handler, "/posts", url.Values{
|
|
"_csrf": {homeownerCSRF},
|
|
"parent_id": {hidden.ID},
|
|
"body": {"Should not publish."},
|
|
}, homeownerCookies)
|
|
if hiddenReply.Code != http.StatusNotFound {
|
|
t.Fatalf("hidden reply status = %d, want 404", hiddenReply.Code)
|
|
}
|
|
hiddenEdit := postForm(handler, "/posts/"+hidden.ID+"/edit", url.Values{
|
|
"_csrf": {homeownerCSRF},
|
|
"body": {"Still hidden."},
|
|
}, homeownerCookies)
|
|
if hiddenEdit.Code != http.StatusSeeOther {
|
|
t.Fatalf("hidden edit status = %d: %s", hiddenEdit.Code, hiddenEdit.Body.String())
|
|
}
|
|
|
|
got := rec.Snapshot()
|
|
if len(got) != 4 {
|
|
t.Fatalf("published %d events, want 4: %#v", len(got), got)
|
|
}
|
|
|
|
submitEv, ok := got[0].(events.PostCreated)
|
|
if !ok {
|
|
t.Fatalf("first event %T, want PostCreated", got[0])
|
|
}
|
|
assertPostEvent(t, submitEv.PostEvent, events.PostEvent{
|
|
PostID: submitRoot.ID,
|
|
RootID: submitRoot.ID,
|
|
Title: "Leaky sink",
|
|
Body: "Water under the cabinet.",
|
|
City: "Oakland",
|
|
AuthorID: homeowner.ID,
|
|
AuthorName: homeowner.Name,
|
|
AuthorRole: string(store.RoleUser),
|
|
Permalink: "https://www.askaplumberfirst.com/questions/" + submitRoot.ID + "#post-" + submitRoot.ID,
|
|
})
|
|
|
|
createEv, ok := got[1].(events.PostCreated)
|
|
if !ok {
|
|
t.Fatalf("second event %T, want PostCreated", got[1])
|
|
}
|
|
assertPostEvent(t, createEv.PostEvent, events.PostEvent{
|
|
PostID: createRoot.ID,
|
|
RootID: createRoot.ID,
|
|
Title: "Second question",
|
|
Body: "Another leak.",
|
|
City: "Berkeley",
|
|
AuthorID: homeowner.ID,
|
|
AuthorName: homeowner.Name,
|
|
AuthorRole: string(store.RoleUser),
|
|
Permalink: "https://www.askaplumberfirst.com/questions/" + createRoot.ID + "#post-" + createRoot.ID,
|
|
})
|
|
|
|
replyEv, ok := got[2].(events.PostCreated)
|
|
if !ok {
|
|
t.Fatalf("third event %T, want PostCreated", got[2])
|
|
}
|
|
assertPostEvent(t, replyEv.PostEvent, events.PostEvent{
|
|
PostID: adminReply.ID,
|
|
RootID: createRoot.ID,
|
|
ParentID: createRoot.ID,
|
|
Body: "Replace the cartridge.",
|
|
AuthorID: admin.ID,
|
|
AuthorName: admin.Name,
|
|
AuthorRole: string(store.RoleAdmin),
|
|
Permalink: "https://www.askaplumberfirst.com/questions/" + createRoot.ID + "#post-" + adminReply.ID,
|
|
})
|
|
|
|
editEv, ok := got[3].(events.PostUpdated)
|
|
if !ok {
|
|
t.Fatalf("fourth event %T, want PostUpdated", got[3])
|
|
}
|
|
assertPostEvent(t, editEv.PostEvent, events.PostEvent{
|
|
PostID: createRoot.ID,
|
|
RootID: createRoot.ID,
|
|
Title: "Second question",
|
|
Body: "Updated leak description.",
|
|
City: "Berkeley",
|
|
AuthorID: homeowner.ID,
|
|
AuthorName: homeowner.Name,
|
|
AuthorRole: string(store.RoleUser),
|
|
Permalink: "https://www.askaplumberfirst.com/questions/" + createRoot.ID + "#post-" + createRoot.ID,
|
|
})
|
|
|
|
for i, ev := range got {
|
|
raw, err := json.Marshal(ev)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if strings.Contains(strings.ToLower(string(raw)), "discord") {
|
|
t.Fatalf("event %d contains discord fields: %s", i, raw)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestStoreCreateDoesNotPublish(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
rec := &events.Recording{}
|
|
_, mem := newTestServer(t, Config{Events: rec})
|
|
homeowner := seedUser(t, mem, uniq("homeowner"), "hunter22", store.RoleUser)
|
|
if err := mem.CreatePost(context.Background(), &store.Post{
|
|
AuthorID: homeowner.ID,
|
|
Title: "Direct write",
|
|
Body: "No handler.",
|
|
PostDate: pacific.Today(),
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if rec.Len() != 0 {
|
|
t.Fatalf("store.CreatePost published %d events", rec.Len())
|
|
}
|
|
}
|
|
|
|
func assertPostEvent(t *testing.T, got, want events.PostEvent) {
|
|
t.Helper()
|
|
if got.PostID != want.PostID ||
|
|
got.RootID != want.RootID ||
|
|
got.ParentID != want.ParentID ||
|
|
got.Title != want.Title ||
|
|
got.Body != want.Body ||
|
|
got.City != want.City ||
|
|
got.AuthorID != want.AuthorID ||
|
|
got.AuthorName != want.AuthorName ||
|
|
got.AuthorRole != want.AuthorRole ||
|
|
got.Permalink != want.Permalink ||
|
|
len(got.Images) != 0 {
|
|
t.Fatalf("event = %+v, want %+v", got, want)
|
|
}
|
|
}
|