Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
118 lines
3.6 KiB
Go
118 lines
3.6 KiB
Go
package httpapi
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/descrybe/descrybe-v2/apps/api/internal/auth"
|
|
"github.com/descrybe/descrybe-v2/apps/api/internal/mail"
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
type recordingMailer struct {
|
|
enabled bool
|
|
sent []mail.Message
|
|
err error
|
|
}
|
|
|
|
func (m *recordingMailer) Enabled() bool { return m.enabled }
|
|
|
|
func (m *recordingMailer) Send(msg mail.Message) error {
|
|
if m.err != nil {
|
|
return m.err
|
|
}
|
|
m.sent = append(m.sent, msg)
|
|
return nil
|
|
}
|
|
|
|
func TestHandleAdminSendSetPasswordEmailsUnauthorized(t *testing.T) {
|
|
t.Parallel()
|
|
s := &Server{Mail: &recordingMailer{enabled: true}, Auth: &auth.Service{}}
|
|
req := httptest.NewRequest(http.MethodPost, "/api/admin/emails/set-password", bytes.NewBufferString("{}"))
|
|
rec := httptest.NewRecorder()
|
|
s.handleAdminSendSetPasswordEmails(rec, req)
|
|
if rec.Code != http.StatusUnauthorized {
|
|
t.Fatalf("status=%d want 401", rec.Code)
|
|
}
|
|
}
|
|
|
|
func TestHandleAdminSendSetPasswordEmailsMailerRequired(t *testing.T) {
|
|
t.Parallel()
|
|
adminID := uuid.New()
|
|
s := &Server{Auth: &auth.Service{}}
|
|
ctx := context.WithValue(context.Background(), ctxUserID, adminID)
|
|
req := httptest.NewRequest(http.MethodPost, "/api/admin/emails/set-password", bytes.NewBufferString("{}"))
|
|
req = req.WithContext(ctx)
|
|
rec := httptest.NewRecorder()
|
|
s.handleAdminSendSetPasswordEmails(rec, req)
|
|
if rec.Code != http.StatusServiceUnavailable {
|
|
t.Fatalf("status=%d want 503", rec.Code)
|
|
}
|
|
}
|
|
|
|
func TestHandleAdminSendSetPasswordEmailsRateLimited(t *testing.T) {
|
|
t.Parallel()
|
|
adminID := uuid.New()
|
|
s := &Server{
|
|
Mail: &recordingMailer{enabled: true},
|
|
Auth: &auth.Service{},
|
|
}
|
|
s.ensureAdminSetPasswordLimiters()
|
|
s.adminSetPasswordReqRL = newSlidingWindowLimiter(1, time.Minute)
|
|
reqKey := "admin-set-password:" + adminID.String()
|
|
if !s.adminSetPasswordReqRL.allow(reqKey) {
|
|
t.Fatal("setup: expected first allow")
|
|
}
|
|
|
|
ctx := context.WithValue(context.Background(), ctxUserID, adminID)
|
|
req := httptest.NewRequest(http.MethodPost, "/api/admin/emails/set-password", bytes.NewBufferString("{}"))
|
|
req = req.WithContext(ctx)
|
|
rec := httptest.NewRecorder()
|
|
s.handleAdminSendSetPasswordEmails(rec, req)
|
|
if rec.Code != http.StatusTooManyRequests {
|
|
t.Fatalf("status=%d want 429 body=%s", rec.Code, rec.Body.String())
|
|
}
|
|
if rec.Header().Get("Retry-After") == "" {
|
|
t.Fatal("expected Retry-After header")
|
|
}
|
|
raw := rec.Body.String()
|
|
if strings.Contains(raw, "@") {
|
|
t.Fatalf("rate-limit response must not include email addresses: %s", raw)
|
|
}
|
|
}
|
|
|
|
func TestHandleAdminSendSetPasswordEmailsSendRateLimitedSingleUser(t *testing.T) {
|
|
t.Parallel()
|
|
adminID := uuid.New()
|
|
targetID := uuid.New()
|
|
s := &Server{
|
|
Mail: &recordingMailer{enabled: true},
|
|
Auth: &auth.Service{},
|
|
}
|
|
s.ensureAdminSetPasswordLimiters()
|
|
s.adminSetPasswordReqRL = newSlidingWindowLimiter(10, time.Minute)
|
|
s.adminSetPasswordSendRL = newSlidingWindowLimiter(1, time.Minute)
|
|
sendKey := "admin-set-password-send:" + adminID.String()
|
|
if !s.adminSetPasswordSendRL.allow(sendKey) {
|
|
t.Fatal("setup: expected first send allow")
|
|
}
|
|
|
|
body := `{"user_id":"` + targetID.String() + `"}`
|
|
ctx := context.WithValue(context.Background(), ctxUserID, adminID)
|
|
req := httptest.NewRequest(http.MethodPost, "/api/admin/emails/set-password", bytes.NewBufferString(body))
|
|
req = req.WithContext(ctx)
|
|
rec := httptest.NewRecorder()
|
|
s.handleAdminSendSetPasswordEmails(rec, req)
|
|
if rec.Code != http.StatusTooManyRequests {
|
|
t.Fatalf("status=%d want 429 body=%s", rec.Code, rec.Body.String())
|
|
}
|
|
if rec.Header().Get("Retry-After") == "" {
|
|
t.Fatal("expected Retry-After header")
|
|
}
|
|
}
|