package web import ( "net/http/httptest" "strings" "testing" "time" "gitea.dooplex.hu/admin/felhom-hub/internal/store" ) func resendPOST(s *Server, token string) *httptest.ResponseRecorder { rr := httptest.NewRecorder() s.handleBind(rr, httptest.NewRequest("POST", "/bind/"+token+"/resend", nil)) return rr } // R-719 (v0.126.0) — a returning customer's old link has expired; the page offers a fresh one. // The CONSEQUENCE asserted: a NEW link is mailed to the registered address, and it binds (a live token). // COMPANION RED-PROOF: make handleBindResend return before autoMintSelfBindIfWaiting → no link mailed. func TestR719_AnExpiredLinkOffersAndSendsAFreshOne(t *testing.T) { s, st := newTestServer(t) m := &stubMailer{} s.SetSelfBindMailer(m) seedForMint(t, st, "tester", "tester1@felhom.example") old := mintLink(t, st, "tester", -time.Hour) // expired a while ago if body := bindGET(t, s, old).Body.String(); !strings.Contains(body, "/bind/"+old+"/resend") || !strings.Contains(body, "Új linket kérek") { t.Fatal("the expired page does not offer a fresh link") } rr := resendPOST(s, old) if !strings.Contains(rr.Body.String(), "Kész.") { t.Fatalf("resend page: %s", rr.Body.String()) } if m.link == "" { t.Fatal("no fresh link was mailed for an expired link of a box-less customer") } fresh := m.link[strings.LastIndexByte(m.link, '/')+1:] if tok, _ := st.SelfBindTokenByHash(selfBindHash(fresh)); tok == nil || tok.Expired(time.Now()) { t.Fatal("the mailed link is not live") } } // No oracle, and no spam: an unknown token, a customer that already has a box, and a second press within // the hour all get the SAME page and NO mail. func TestR719_ResendIsNoOracleAndRateLimited(t *testing.T) { s, st := newTestServer(t) m := &stubMailer{} s.SetSelfBindMailer(m) seedForMint(t, st, "tester", "tester1@felhom.example") old := mintLink(t, st, "tester", -time.Hour) unknown := resendPOST(s, strings.Repeat("a", 64)).Body.String() if m.link != "" { t.Fatal("an unknown token mailed someone") } _ = resendPOST(s, old) // first press → mail first := m.link m.link = "" again := resendPOST(s, old).Body.String() if m.link != "" { t.Fatal("a second press within the hour mailed again") } if first == "" || unknown != again { t.Fatal("the answer differs between an unknown token and a real one — an oracle") } // A customer that already has a box gets no link either. seedForMint(t, st, "boxed", "b@felhom.example") if err := st.UpsertHost(&store.Host{HostID: "boxed-1", CustomerID: "boxed", APIKey: "hk"}); err != nil { t.Fatal(err) } b := mintLink(t, st, "boxed", -time.Hour) _ = resendPOST(s, b) if m.link != "" { t.Fatal("a customer with a box was mailed a bind link") } }