TestOrganisationLogo asserted only that the system_files rows go; it now also waits for the commit and asserts the logo's and avatar's originals and thumbnails are gone from the bucket. The Postgres tests of the gap closure call t.Skip under -short instead of returning, so -short runs report them as skipped, not passed.
142 lines
5.8 KiB
Go
142 lines
5.8 KiB
Go
package user
|
|
|
|
import (
|
|
"bytes"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"regexp"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.golem15.com/golem15/sm-user-plugin/models"
|
|
"git.golem15.com/golem15/summercms/modules/compass"
|
|
)
|
|
|
|
// TestBackendLinkShowPost pins plan 08's link code transport (C-WR-01,
|
|
// C-IN-06): POST /backend-link/show answers exactly like the GET route (the
|
|
// data for a live code, 404 for an unknown, a used or an expired one), both
|
|
// with Cache-Control no-store, private; a code in the query string of the
|
|
// show or confirm POST is ignored (RC-IN-01); My account answers the link as
|
|
// <frontend_url>/link-admin#code=<64 hex> with no query string;
|
|
// golem15.user.backend_links.page_path changes the path, and a value
|
|
// without a leading slash gets one. Postgres only: skipped under
|
|
// -short.
|
|
func TestBackendLinkShowPost(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("Postgres")
|
|
}
|
|
env := newAdminEnv(t)
|
|
show := func(t *testing.T, code string) (get, post *struct {
|
|
status int
|
|
body string
|
|
cache string
|
|
}) {
|
|
t.Helper()
|
|
g := env.call(t, http.MethodGet, "/_user/api/v1/backend-link/"+code, "", nil)
|
|
p := env.call(t, http.MethodPost, "/_user/api/v1/backend-link/show", "", map[string]string{"code": code})
|
|
return &struct {
|
|
status int
|
|
body string
|
|
cache string
|
|
}{g.Code, g.Body.String(), g.Header().Get("Cache-Control")}, &struct {
|
|
status int
|
|
body string
|
|
cache string
|
|
}{p.Code, p.Body.String(), p.Header().Get("Cache-Control")}
|
|
}
|
|
same := func(t *testing.T, code string, status int) {
|
|
t.Helper()
|
|
get, post := show(t, code)
|
|
if get.status != status || post.status != status {
|
|
t.Fatalf("show %s: GET %d, POST %d, want %d", code[:8], get.status, post.status, status)
|
|
}
|
|
if !bytes.Equal([]byte(get.body), []byte(post.body)) {
|
|
t.Fatalf("POST show differs from GET:\nGET %s\nPOST %s", get.body, post.body)
|
|
}
|
|
if get.cache != "no-store, private" || post.cache != "no-store, private" {
|
|
t.Fatalf("Cache-Control: GET %q, POST %q", get.cache, post.cache)
|
|
}
|
|
}
|
|
|
|
t.Run("POST show answers like GET", func(t *testing.T) {
|
|
token := env.admin(t)
|
|
live := env.startLink(t, token)
|
|
same(t, live, http.StatusOK)
|
|
if _, post := show(t, live); !strings.Contains(post.body, `"admin"`) || !strings.Contains(post.body, `"expires_at"`) {
|
|
t.Fatalf("POST show data = %s", post.body)
|
|
}
|
|
same(t, strings.Repeat("a", 64), http.StatusNotFound)
|
|
|
|
used := env.startLink(t, env.admin(t))
|
|
if status, body := env.confirmLink(t, freshUserToken(t, env.seedUser(t, "show-used", nil)), used); status != http.StatusOK {
|
|
t.Fatalf("confirm = %d %s", status, body)
|
|
}
|
|
same(t, used, http.StatusNotFound)
|
|
|
|
expiring := env.admin(t)
|
|
expired := env.startLink(t, expiring)
|
|
if err := env.gdb.Model(&models.BackendLinkRequest{}).Where("backend_user_id = ?", adminID(t, expiring)).
|
|
Update("expires_at", time.Now().Add(-time.Minute)).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
same(t, expired, http.StatusNotFound)
|
|
})
|
|
|
|
t.Run("a code in the query string is ignored", func(t *testing.T) {
|
|
live := env.startLink(t, env.admin(t))
|
|
path := "/_user/api/v1/backend-link/show?code=" + live
|
|
for name, send := range map[string]func() *httptest.ResponseRecorder{
|
|
"no body": func() *httptest.ResponseRecorder { return env.send(t, http.MethodPost, path, "", nil, "", nil) },
|
|
"a JSON body without a code": func() *httptest.ResponseRecorder { return env.call(t, http.MethodPost, path, "", map[string]string{}) },
|
|
"a form body without a code": func() *httptest.ResponseRecorder {
|
|
return env.send(t, http.MethodPost, path, "", []byte("other=1"), "application/x-www-form-urlencoded", nil)
|
|
},
|
|
} {
|
|
if got := send(); got.Code != http.StatusNotFound {
|
|
t.Fatalf("show with ?code= and %s = %d %s, want 404", name, got.Code, got.Body.String())
|
|
}
|
|
}
|
|
user := env.seedUser(t, "show-query", nil)
|
|
rec := env.send(t, http.MethodPost, "/_user/api/v1/backend-link/confirm?code="+live, freshUserToken(t, user), []byte("other=1"), "application/x-www-form-urlencoded", nil)
|
|
if rec.Code != http.StatusNotFound {
|
|
t.Fatalf("confirm with ?code= = %d %s, want 404", rec.Code, rec.Body.String())
|
|
}
|
|
// A form body still carries the code, and the query string never did.
|
|
form := env.send(t, http.MethodPost, "/_user/api/v1/backend-link/show", "", []byte("code="+live), "application/x-www-form-urlencoded", nil)
|
|
if form.Code != http.StatusOK {
|
|
t.Fatalf("show with the code in a form body = %d %s", form.Code, form.Body.String())
|
|
}
|
|
if status, body := env.confirmLink(t, freshUserToken(t, user), live); status != http.StatusOK {
|
|
t.Fatalf("confirm with the code in the body after the refusals = %d %s", status, body)
|
|
}
|
|
})
|
|
|
|
t.Run("the My account URL carries the code in the fragment only", func(t *testing.T) {
|
|
var out struct {
|
|
URL string `json:"url"`
|
|
}
|
|
adminInto(t, env.expect(t, http.StatusOK, http.MethodPost, adminAPI("/account/contributions/frontend_account/actions/link"), env.admin(t), map[string]any{}), &out)
|
|
if !regexp.MustCompile(`^https://app\.example\.test/link-admin#code=[a-f0-9]{64}$`).MatchString(out.URL) || strings.Contains(out.URL, "?") {
|
|
t.Fatalf("link URL = %q", out.URL)
|
|
}
|
|
})
|
|
|
|
for _, c := range []struct{ value, want string }{{"/pl/polacz", "/pl/polacz"}, {"pl/polacz", "/pl/polacz"}} {
|
|
t.Run("page_path "+c.value, func(t *testing.T) {
|
|
custom := newAdminEnv(t, func(cfg *compass.Config) {
|
|
if err := cfg.Set("golem15.user.backend_links.page_path", c.value); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
})
|
|
var out struct {
|
|
URL string `json:"url"`
|
|
}
|
|
adminInto(t, custom.expect(t, http.StatusOK, http.MethodPost, adminAPI("/account/contributions/frontend_account/actions/link"), custom.admin(t), map[string]any{}), &out)
|
|
if !regexp.MustCompile(`^https://app\.example\.test` + regexp.QuoteMeta(c.want) + `#code=[a-f0-9]{64}$`).MatchString(out.URL) {
|
|
t.Fatalf("link URL with page_path %q = %q", c.value, out.URL)
|
|
}
|
|
})
|
|
}
|
|
}
|