mirror of
https://github.com/warmbly/warmbly.git
synced 2026-10-13 16:02:42 +00:00
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
155 lines
6.3 KiB
Go
155 lines
6.3 KiB
Go
package handler
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
"github.com/jackc/pgx/v5"
|
|
"github.com/jackc/pgx/v5/pgconn"
|
|
"github.com/rs/zerolog"
|
|
"github.com/rs/zerolog/log"
|
|
"github.com/warmbly/warmbly/internal/api/middleware"
|
|
"github.com/warmbly/warmbly/internal/repository"
|
|
)
|
|
|
|
type internalArrivalMap struct {
|
|
calls, legacyCalls int
|
|
err error
|
|
}
|
|
|
|
func TestInternalArrivalOwnershipLossIsNotDurableAdmission(t *testing.T) {
|
|
mailbox := uuid.NewString()
|
|
for _, tc := range []struct {
|
|
name, mailbox, marker string
|
|
err error
|
|
}{
|
|
{"ownership-loss", mailbox, mailbox, fmt.Errorf("wrapped: %w", repository.ErrArrivalMailboxOwnershipLost)},
|
|
{"database-failure", mailbox, "", &pgconn.PgError{Code: "08006", Message: "private payload"}},
|
|
{"unrelated-missing-row", mailbox, "", pgx.ErrNoRows},
|
|
{"unconfirmed", mailbox, "", repository.ErrArrivalAdmissionUnconfirmed},
|
|
{"invalid-id", "invalid", "", repository.ErrArrivalMailboxOwnershipLost},
|
|
{"nil-id", uuid.Nil.String(), "", repository.ErrArrivalMailboxOwnershipLost},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
m := &internalArrivalMap{err: tc.err}
|
|
h := &Handler{EmailMessageMap: m}
|
|
r := gin.New()
|
|
r.POST("/arrival", h.InternalAdmitEmailArrival)
|
|
w := httptest.NewRecorder()
|
|
r.ServeHTTP(w, httptest.NewRequest(http.MethodPost, "/arrival", strings.NewReader(fmt.Sprintf(`{"map":{"email_id":%q},"pending":{}}`, tc.mailbox))))
|
|
if m.calls != 1 || w.Code != 503 || w.Body.Len() != 0 || w.Header().Get("X-Warmbly-Arrival-Durable") != "" || w.Header().Get("X-Warmbly-Arrival-Ownership-Lost") != tc.marker {
|
|
t.Fatal("failure acknowledged or ownership marker inferred without authority")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestInternalArrivalLogsOnlySafeDiagnosticsWithoutAcknowledging(t *testing.T) {
|
|
var output bytes.Buffer
|
|
original := log.Logger
|
|
log.Logger = zerolog.New(&output)
|
|
t.Cleanup(func() { log.Logger = original })
|
|
private := "private-provider-payload-and-key"
|
|
m := &internalArrivalMap{err: &pgconn.PgError{Code: "22021", Message: private, Detail: private}}
|
|
h := &Handler{EmailMessageMap: m}
|
|
r := gin.New()
|
|
requestID, user, email, arrival := uuid.NewString(), uuid.NewString(), uuid.NewString(), uuid.NewString()
|
|
r.Use(func(c *gin.Context) { c.Set(middleware.RequestIDContextKey, requestID) })
|
|
r.POST("/arrival", h.InternalAdmitEmailArrival)
|
|
body := fmt.Sprintf(`{"map":{"user_id":%q,"email_id":%q,"id":%q,"message_id":%q,"thread_id":%q},"pending":{}}`, user, email, arrival, private, private)
|
|
w := httptest.NewRecorder()
|
|
r.ServeHTTP(w, httptest.NewRequest(http.MethodPost, "/arrival", strings.NewReader(body)))
|
|
if w.Code != 503 || w.Body.Len() != 0 || w.Header().Get("X-Warmbly-Arrival-Durable") != "" || strings.Contains(output.String(), private) {
|
|
t.Fatal("failed admission was acknowledged or exposed private data")
|
|
}
|
|
var diagnostic map[string]any
|
|
if err := json.Unmarshal(output.Bytes(), &diagnostic); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for key, expected := range map[string]string{"admission_stage": "dependency", "sqlstate": "22021", "user_id": user, "email_id": email, "arrival_id": arrival, "request_id": requestID} {
|
|
if diagnostic[key] != expected {
|
|
t.Fatalf("missing diagnostic field %s", key)
|
|
}
|
|
}
|
|
if diagnostic["time"] == nil {
|
|
t.Fatal("diagnostic has no observation time")
|
|
}
|
|
output.Reset()
|
|
requestID = "client-trace_123"
|
|
r.ServeHTTP(httptest.NewRecorder(), httptest.NewRequest(http.MethodPost, "/arrival", strings.NewReader(`{"map":{"user_id":"private","email_id":"private","id":"private"},"pending":{}}`)))
|
|
if strings.Contains(output.String(), `"email_id"`) || strings.Contains(output.String(), `"user_id"`) || strings.Contains(output.String(), `"arrival_id"`) {
|
|
t.Fatal("invalid identities were logged")
|
|
}
|
|
if !strings.Contains(output.String(), `"request_id":"client-trace_123"`) {
|
|
t.Fatal("accepted non-UUID trace identifier was lost")
|
|
}
|
|
for _, invalid := range []string{"bad/request/id", "invalid\ntrace", strings.Repeat("a", 129)} {
|
|
output.Reset()
|
|
requestID = invalid
|
|
r.ServeHTTP(httptest.NewRecorder(), httptest.NewRequest(http.MethodPost, "/arrival", strings.NewReader(`{"map":{},"pending":{}}`)))
|
|
if strings.Contains(output.String(), `"request_id"`) {
|
|
t.Fatal("unsafe or oversized trace identifier was logged")
|
|
}
|
|
}
|
|
}
|
|
|
|
func (m *internalArrivalMap) Add(context.Context, repository.EmailMessageData) error {
|
|
m.legacyCalls++
|
|
return nil
|
|
}
|
|
func (*internalArrivalMap) Get(context.Context, uuid.UUID, uuid.UUID, string) (*repository.EmailMessageData, error) {
|
|
return nil, nil
|
|
}
|
|
func (*internalArrivalMap) Del(context.Context, uuid.UUID, uuid.UUID, string, uuid.UUID) error {
|
|
return nil
|
|
}
|
|
func (m *internalArrivalMap) AdmitArrival(context.Context, repository.EmailMessageData, *repository.PendingArrival) error {
|
|
m.calls++
|
|
return m.err
|
|
}
|
|
|
|
func TestInternalArrivalAcknowledgesOnlyCompletedAdmission(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
name string
|
|
err error
|
|
status int
|
|
ack string
|
|
}{
|
|
{"success", nil, 204, "1"}, {"failure", errors.New("private payload or cipher failure"), 503, ""},
|
|
{"unconfirmed collision", repository.ErrArrivalAdmissionUnconfirmed, 503, ""},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
m := &internalArrivalMap{err: tc.err}
|
|
h := &Handler{EmailMessageMap: m}
|
|
r := gin.New()
|
|
r.POST("/arrival", h.InternalAdmitEmailArrival)
|
|
w := httptest.NewRecorder()
|
|
r.ServeHTTP(w, httptest.NewRequest(http.MethodPost, "/arrival", strings.NewReader(`{"map":{},"pending":{}}`)))
|
|
if m.calls != 1 || w.Code != tc.status || w.Header().Get("X-Warmbly-Arrival-Durable") != tc.ack || w.Body.Len() != 0 {
|
|
t.Fatalf("calls=%d status=%d body=%q", m.calls, w.Code, w.Body.String())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestInternalArrivalPreservesLegacyWorkerProtocol(t *testing.T) {
|
|
m := &internalArrivalMap{}
|
|
h := &Handler{EmailMessageMap: m}
|
|
r := gin.New()
|
|
r.PUT("/map", h.InternalPutEmailMessageMap)
|
|
w := httptest.NewRecorder()
|
|
r.ServeHTTP(w, httptest.NewRequest(http.MethodPut, "/map", strings.NewReader(`{"user_id":"fixture","email_id":"fixture","message_id":"fixture","id":"fixture","thread_id":"fixture"}`)))
|
|
if w.Code != 204 || m.legacyCalls != 1 || m.calls != 0 || w.Header().Get("X-Warmbly-Arrival-Durable") != "" {
|
|
t.Fatalf("legacy contract changed: %d", w.Code)
|
|
}
|
|
}
|