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()) } }