d55c590c5a
gates / gates (push) Successful in 5m25s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
88 lines
2.7 KiB
Go
88 lines
2.7 KiB
Go
package api
|
|
|
|
import (
|
|
"io"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
|
|
"gitea.dooplex.hu/admin/felhom-hub/internal/mailhold"
|
|
)
|
|
|
|
// MAIL-HOLD on the two API send paths (the dispatcher's paths: internal/notify/mailhold_test.go).
|
|
// COMPANION RED-PROOF (REPORT): remove the h.mailHold.Check block from handleNotify / handleMail → these fail with the
|
|
// mail handed to Resend (the recorded transport / the fake SMTP sender).
|
|
|
|
func heldHold(t *testing.T) *mailhold.Hold {
|
|
t.Helper()
|
|
dir := t.TempDir()
|
|
if err := os.WriteFile(filepath.Join(dir, mailhold.FileName), nil, 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return mailhold.New(dir, nil)
|
|
}
|
|
|
|
// recordingTransport stands in for api.resend.com: it records each request and never leaves the process.
|
|
type recordingTransport struct {
|
|
mu sync.Mutex
|
|
calls int
|
|
}
|
|
|
|
func (rt *recordingTransport) RoundTrip(r *http.Request) (*http.Response, error) {
|
|
rt.mu.Lock()
|
|
rt.calls++
|
|
rt.mu.Unlock()
|
|
return &http.Response{StatusCode: 200, Body: io.NopCloser(strings.NewReader(`{"id":"x"}`)), Header: http.Header{}}, nil
|
|
}
|
|
|
|
func TestMailHold_NotifySendsNothingWhileHeld(t *testing.T) {
|
|
h, st := newEventTestHandler(t)
|
|
rt := &recordingTransport{}
|
|
h.httpClient = &http.Client{Transport: rt}
|
|
h.resendAPIKey = "test-key"
|
|
if err := st.SaveNotificationPrefs("c1", "household@example.hu", []string{"backup_failed"}, 6); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
h.SetMailHold(heldHold(t))
|
|
|
|
rr := do(h, http.MethodPost, "/notify", "ckey", `{"customer_id":"c1","event_type":"backup_failed","severity":"error","message":"m"}`)
|
|
if rr.Code != http.StatusOK {
|
|
t.Fatalf("status = %d, body=%s", rr.Code, rr.Body.String())
|
|
}
|
|
if rt.calls != 0 {
|
|
t.Fatalf("MAIL-HOLD present but /notify handed %d mail(s) to Resend", rt.calls)
|
|
}
|
|
if !strings.Contains(rr.Body.String(), `"reason":"mail_hold"`) {
|
|
t.Fatalf("the answer must say the mail was held: %s", rr.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestMailHold_AppMailRelayRefusesWhileHeld(t *testing.T) {
|
|
h, st, _ := newTestHandler(t)
|
|
withCustomer(t, st, "c1", "ckey")
|
|
fake := &fakeSender{}
|
|
h.SetMailRelay(fake, 30, []string{"felhom.eu"})
|
|
hold := heldHold(t)
|
|
h.SetMailHold(hold)
|
|
|
|
rr := do(h, "POST", "/mail", "ckey", mailBody(t, "vaultwarden@felhom.eu"))
|
|
if fake.callCount() != 0 {
|
|
t.Fatalf("MAIL-HOLD present but the relay sent %d mail(s)", fake.callCount())
|
|
}
|
|
if rr.Code != http.StatusServiceUnavailable {
|
|
t.Fatalf("held relay: status %d, want 503", rr.Code)
|
|
}
|
|
|
|
// Released: the relay sends again.
|
|
if err := hold.Release(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
rr = do(h, "POST", "/mail", "ckey", mailBody(t, "vaultwarden@felhom.eu"))
|
|
if rr.Code != http.StatusOK || fake.callCount() != 1 {
|
|
t.Fatalf("after release: status %d, sends %d — want 200 and 1", rr.Code, fake.callCount())
|
|
}
|
|
}
|