Files
llmgw/internal/web/signup_test.go
T
Ilker ManapandClaude Opus 5.5 66f702d5c1 A forgotten password is set through a one-time link
/forgot mails a link to set a new password when the cluster's mail settings
say how - SMTP relay, sender, and the public base_url the link points at,
never the request's Host - and says the same whether or not the address has
an account. /reset sets it once, closing every session. Links are kept by
their hash in password_resets, good for an hour, a newer one replacing an
older. Without mail, /forgot says to ask an operator, and llmgw users
reset-link makes the link to hand over. Single sign-on accounts get none.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-07 13:20:11 +02:00

100 lines
3.6 KiB
Go

package web
import (
"context"
"errors"
"log/slog"
"net/http"
"net/http/httptest"
"net/url"
"strings"
"testing"
)
// signupAccounts is an account service whose sign-up answers with err.
type signupAccounts struct {
fakeAccounts
err error
}
func (s signupAccounts) SignUp(context.Context, string, string, string) error { return s.err }
func (signupAccounts) SignupOpen() bool { return true }
func signupBody(t *testing.T, err error) string {
t.Helper()
mux := http.NewServeMux()
NewAccountPages(signupAccounts{err: err}, nil, nil, "v0", slog.New(slog.DiscardHandler)).Register(mux)
form := url.Values{"email": {"a@b.c"}, "name": {"A"}, "password": {"long enough password"}}
req := httptest.NewRequest(http.MethodPost, "/signup", strings.NewReader(form.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
rec := httptest.NewRecorder()
mux.ServeHTTP(rec, req)
return rec.Body.String()
}
func TestASignupThatFailsSaysSoWithAReferenceAndNoDatabaseWords(t *testing.T) {
body := signupBody(t, errors.New(`duplicate key value violates unique constraint "users_email_key"`))
if strings.Contains(body, "users_email_key") || strings.Contains(body, "constraint") {
t.Errorf("the database's own words reached the page: %s", body)
}
if !strings.Contains(body, "was not created") || !strings.Contains(body, "reference: err-") {
t.Errorf("no plain failure with a reference to give an operator: %s", body)
}
}
func TestASignupSaysWhatToDoIfTheAddressHadAnAccount(t *testing.T) {
// The service answers a taken address as it does a new one, so nobody
// can find out from this page who has an account; what the page can do
// is say what to do in either case.
body := signupBody(t, nil)
if !strings.Contains(body, "already had an account") || !strings.Contains(body, `href="/login"`) ||
!strings.Contains(body, `href="/forgot"`) {
t.Errorf("the done page leaves someone who already had an account stuck: %s", body)
}
}
func TestTheSignInFormWantsItsOwnTokenAndRationsAnAddress(t *testing.T) {
mux := http.NewServeMux()
NewAccountPages(signupAccounts{}, nil, nil, "v0", slog.New(slog.DiscardHandler)).Register(mux)
post := func(form url.Values, cookie *http.Cookie) *httptest.ResponseRecorder {
req := httptest.NewRequest(http.MethodPost, "/login", strings.NewReader(form.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.RemoteAddr = "203.0.113.9:1234"
if cookie != nil {
req.AddCookie(cookie)
}
rec := httptest.NewRecorder()
mux.ServeHTTP(rec, req)
return rec
}
// No token: another site's form.
if rec := post(url.Values{"email": {"a@b.c"}, "password": {"x"}}, nil); rec.Code != http.StatusBadRequest {
t.Errorf("a sign-in without the form's token: %d", rec.Code)
}
// The form's token, matched by its cookie, reaches the sign-in.
rec := httptest.NewRecorder()
mux.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/login", nil))
var cookie *http.Cookie
for _, c := range rec.Result().Cookies() {
if c.Name == loginCSRFCookie {
cookie = c
}
}
if cookie == nil || !strings.Contains(rec.Body.String(), cookie.Value) {
t.Fatal("the form carries no token")
}
if rec := post(url.Values{"email": {"a@b.c"}, "password": {"x"}, "csrf": {cookie.Value}}, cookie); rec.Code == http.StatusBadRequest {
t.Errorf("the form's own token was refused")
}
// A script posting it is rationed.
limited := false
for range 40 {
if post(url.Values{"csrf": {cookie.Value}}, cookie).Code == http.StatusTooManyRequests {
limited = true
}
}
if !limited {
t.Error("forty sign-ins a moment from one address were all taken")
}
}