package calendartest import ( "net/http" "net/http/httptest" "net/url" "strings" "testing" "time" calendar "amadan.net/oficina/calendar/internal/calendar" ) // The caps, the gate and the day bound (booking spec §4.2, §7). func fromIP(t *testing.T, h http.Handler, ip, target string, form url.Values) *httptest.ResponseRecorder { t.Helper() method := http.MethodGet if form != nil { method = http.MethodPost } req := calendar.RequestForTest(nil, method, target, form) req.Header.Set("X-Forwarded-For", ip) rec := httptest.NewRecorder() h.ServeHTTP(rec, req) return rec } func TestTheSixthOpenHoldFromOneIPIs429(t *testing.T) { app, _, path, _ := publishedPage(t) h := app.Handler() _, starts := futureDaySlots(t, h, path) for i := 0; i < 5; i++ { rec := fromIP(t, h, "203.0.113.9", path, holdForm(starts[i], "S", "s"+string(rune('a'+i))+"@example.com")) if rec.Code != 200 { t.Fatalf("hold %d: %d", i, rec.Code) } } rec := fromIP(t, h, "203.0.113.9", path, holdForm(starts[5], "S", "sx@example.com")) if rec.Code != http.StatusTooManyRequests { t.Errorf("sixth hold from one IP: %d, want 429", rec.Code) } // A different IP is fine. if rec := fromIP(t, h, "203.0.113.10", path, holdForm(starts[5], "S", "sy@example.com")); rec.Code != 200 { t.Errorf("hold from another IP: %d", rec.Code) } } func TestTheThirdOpenHoldForOneAddressIs429(t *testing.T) { app, _, path, _ := publishedPage(t) h := app.Handler() _, starts := futureDaySlots(t, h, path) for i := 0; i < 2; i++ { fromIP(t, h, "203.0.113."+string(rune('1'+i)), path, holdForm(starts[i], "S", "same@example.com")) } if rec := fromIP(t, h, "203.0.113.5", path, holdForm(starts[2], "S", "same@example.com")); rec.Code != http.StatusTooManyRequests { t.Errorf("third hold for one address: %d, want 429", rec.Code) } } func TestTheGateRefusesTheSixtyFirstRequestInAMinute(t *testing.T) { app, _, path, _ := publishedPage(t) h := app.Handler() var last int for i := 0; i < 61; i++ { last = fromIP(t, h, "198.51.100.7", path, nil).Code } if last != http.StatusTooManyRequests { t.Errorf("61st request: %d, want 429", last) } if rec := fromIP(t, h, "198.51.100.8", path, nil); rec.Code != 200 { t.Errorf("another IP after the gate closed on one: %d", rec.Code) } } func TestADayOutsideTheHorizonIs404AndAStrangeZoneFallsBack(t *testing.T) { app, _, path, _ := publishedPage(t) h := app.Handler() far := time.Now().AddDate(0, 0, 60).Format("2006-01-02") if rec := strangerGet(t, h, path+"?day="+far); rec.Code != http.StatusNotFound { t.Errorf("a day past the horizon: %d, want 404", rec.Code) } past := time.Now().AddDate(0, 0, -10).Format("2006-01-02") if rec := strangerGet(t, h, path+"?day="+past); rec.Code != http.StatusNotFound { t.Errorf("a day in the past: %d, want 404", rec.Code) } body := strangerGet(t, h, path+"?zone=Nowhere/Town").Body.String() if !strings.Contains(body, `