package memoriatest import ( "io" "net/http" "net/http/httptest" "strings" "testing" ) // B5 (polish audit, 2026-09-23): the sign-in page said nothing after any // sign-in attempt. It switched on ?outcome=, a word nothing writes; // rastrillo's auth handlers and memoria's own beginGuard redirect with // ?sent=1 and ?err=rate|address|expired|keymail|1. So "Send me a link" // came back to the same blank form, and a rate limit, an unparseable // address and a dead link were all silent. // // Every case here drives the REAL flow and follows the Location it was // given — never a hand-built query. A hand-built query is written from // the same model as the switch that reads it, which is exactly how the // old ?outcome= test-free code agreed with itself for months. Each case // also asserts the Location's query, so if rastrillo renames a code the // failure names the new spelling rather than a missing sentence. // // Verified red 2026-09-23 against the old localSignin (switch on // "outcome"): every subtest failed with the blank form. // followToSignin performs the redirect a browser would and returns the // page body. It refuses anything that is not a redirect to /signin, so a // changed status cannot pass as "the page rendered without the flash". func followToSignin(t *testing.T, h http.Handler, rr *httptest.ResponseRecorder, wantQuery string) string { t.Helper() if rr.Code != http.StatusSeeOther && rr.Code != http.StatusFound { t.Fatalf("status = %d, want a redirect to the sign-in page; body %q", rr.Code, rr.Body.String()) } loc := rr.Header().Get("Location") if !strings.HasPrefix(loc, "/signin?") { t.Fatalf("Location = %q, want /signin?…", loc) } if got := strings.TrimPrefix(loc, "/signin?"); got != wantQuery { t.Fatalf("Location query = %q, want %q — rastrillo's vocabulary moved; "+ "signinOutcome must read the new word", got, wantQuery) } req := httptest.NewRequest(http.MethodGet, loc, nil) page := httptest.NewRecorder() h.ServeHTTP(page, req) if page.Code != http.StatusOK { t.Fatalf("GET %s = %d", loc, page.Code) } b, _ := io.ReadAll(page.Body) return string(b) } func assertFlash(t *testing.T, body, tone, sentence string) { t.Helper() if !strings.Contains(body, `rst-tone="`+tone+`"`) { t.Errorf("no %s-toned callout on the page the redirect landed on", tone) } if !strings.Contains(body, sentence) { t.Errorf("page does not say %q", sentence) } } func TestSigninPageSaysWhatHappened(t *testing.T) { t.Run("a sent link says check your email", func(t *testing.T) { // Both writers of ?sent=1: rastrillo after mailing an owner's // link, and beginGuard answering a stranger with the owner's // answer by design. // // Since the sign-in door (2026-09-29) a sent link is the door's // own check-your-email notice (estilo-signin-sent), which REPLACES // the form rather than toning a flash above it — so the state // asserted is the notice, with no form under it. var bodies []string for _, address := range []string{dialTestOwner, "stranger@attacker.example"} { h, _ := signinApp(t) rr := beginSignin(t, h, address) body := followToSignin(t, h, rr, "sent=1") if !strings.Contains(body, `
If that address can sign in here, we’ve sent it a link. It works once and expires in 15 minutes.
`) { t.Errorf("the sent page is not the door's check-your-email notice:\n%s", body) } if !strings.Contains(body, "