package web
import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"net/url"
"strings"
"testing"
"craftdesign.group/quarry/internal/api"
"craftdesign.group/quarry/internal/ops"
)
// An edit the channel refuses is answered in the channel (#378): back to
// the page it was made on with the refusal as a notice, never a bare
// 403 page that takes the chat away and the typed edit with it. The
// page it goes back to is a local path or the channel itself.
func TestARefusedChatEditStaysInTheChannel(t *testing.T) {
type editIn struct {
Repo string `json:"repo"`
Seq uint64 `json:"seq"`
Text string `json:"text"`
}
type editOut struct {
OK bool `json:"ok"`
}
r := ops.NewRegistry()
if err := ops.Register(r, ops.Op[editIn, editOut]{
Name: "chat.edit", Caps: "C", Doc: "test stub", Kind: ops.Command, Auth: ops.Token,
Route: "POST /api/v1/r/{repo}/chat/{seq}/edit",
Run: func(context.Context, editIn) (editOut, error) {
return editOut{}, fmt.Errorf("a message takes edits for five minutes after it is sent, and this one is older: %w", ops.ErrForbidden)
},
}); err != nil {
t.Fatal(err)
}
verify := Verify(func(repo, user, pass string, need ops.Auth) api.Decision { return api.Allowed })
h := HandlerWith(r, nil, nil, []Option{WithSessions(fakeSessions{user: "dan", roles: map[string]ops.Auth{"alpha": ops.Admin}})}, verify)
srv := httptest.NewServer(h)
t.Cleanup(srv.Close)
client := &http.Client{CheckRedirect: func(*http.Request, []*http.Request) error { return http.ErrUseLastResponse }}
edit := func(back string) *http.Response {
req, _ := http.NewRequest("POST", srv.URL+"/r/alpha/chat/7/edit",
strings.NewReader(url.Values{"text": {"changed"}, "back": {back}}.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.AddCookie(&http.Cookie{Name: sessionCookie, Value: "good"})
resp, err := client.Do(req)
if err != nil {
t.Fatal(err)
}
resp.Body.Close()
return resp
}
resp := edit("/r/crew/alpha/chat")
if resp.StatusCode != http.StatusSeeOther || resp.Header.Get("Location") != "/r/crew/alpha/chat" {
t.Fatalf("FAIL: a refused edit answered %d to %q, want 303 back to the page", resp.StatusCode, resp.Header.Get("Location"))
}
var flash string
for _, c := range resp.Cookies() {
if c.Name == flashCookie {
flash = c.Value
}
}
if !strings.Contains(flash, "five minutes") || strings.Contains(flash, "forbidden") {
t.Errorf("FAIL: the refusal carried back is %q", flash)
}
if resp := edit("//elsewhere.example/x"); resp.Header.Get("Location") != "/r/alpha/chat" {
t.Errorf("FAIL: an off-site back went to %q", resp.Header.Get("Location"))
}
t.Log("ok: a refused edit returns to the channel with its reason")
}