115 lines
4.6 KiB
Go
115 lines
4.6 KiB
Go
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 and a second press within the hour get the SAME page and NO mail; a
|
|
// customer that already has a box gets no link (R-1203 below: its REAL link's holder is told why).
|
|
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")
|
|
}
|
|
}
|
|
|
|
// R-1203 (rehearsal 2026-10-20 B7): the household's old box is still linked. The holder of the household's REAL used
|
|
// link is told so in plain words (not „a mail comes in minutes"), the operator is told the host name once per hour,
|
|
// and no link is mailed. An unknown token still gets the generic page.
|
|
// COMPANION RED-PROOF: drop the GetHostByCustomer branch in handleBindResend → the page says „Kész." and no event.
|
|
func TestR1203_AHouseholdWhoseOldBoxIsStillLinkedIsToldSo(t *testing.T) {
|
|
s, st := newTestServer(t)
|
|
m := &stubMailer{}
|
|
s.SetSelfBindMailer(m)
|
|
type ev struct{ cust, typ, sev, msg, details string }
|
|
var evs []ev
|
|
s.emit = func(c, typ, sev, msg, details, src string) { evs = append(evs, ev{c, typ, sev, msg, details}) }
|
|
seedForMint(t, st, "Tester4", "tester4@felhom.example")
|
|
if err := st.UpsertHost(&store.Host{HostID: "Tester4-816e21", CustomerID: "Tester4", APIKey: "hk"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
used := mintLink(t, st, "Tester4", -time.Hour)
|
|
|
|
body := resendPOST(s, used).Body.String()
|
|
if !strings.Contains(body, "A régi dobozod még össze van kötve") || strings.Contains(body, "percen belül új e-mailt") {
|
|
t.Fatalf("the page does not say the old box is still linked:\n%s", body)
|
|
}
|
|
if m.link != "" {
|
|
t.Fatal("a link was mailed while the old box is linked")
|
|
}
|
|
if len(evs) != 1 || evs[0].typ != "bind_old_host_linked" || evs[0].sev != "warning" || !strings.Contains(evs[0].msg, "Tester4-816e21") {
|
|
t.Fatalf("operator events = %+v, want one warning naming the host", evs)
|
|
}
|
|
_ = resendPOST(s, used) // a second press within the hour: same page, no second mail
|
|
if len(evs) != 1 {
|
|
t.Fatalf("operator events after a second press = %d, want 1", len(evs))
|
|
}
|
|
if unk := resendPOST(s, strings.Repeat("b", 64)).Body.String(); strings.Contains(unk, "A régi dobozod") {
|
|
t.Fatal("an unknown token learns that a box is linked")
|
|
}
|
|
}
|