package notify
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
)
// A forge that can send its own invites can tell the companion how, so a
// self-hoster does not have to POST a credential into notify by hand
// before any email works at all.
func TestEnsureEmailIntegrationCreatesOneWhenThereIsNone(t *testing.T) {
var posted map[string]any
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch {
case r.Method == "GET" && r.URL.Path == "/v1/integrations":
w.Write([]byte(`[{"channel":"push","identifier":"webpush"}]`))
case r.Method == "POST" && r.URL.Path == "/v1/integrations/email":
json.NewDecoder(r.Body).Decode(&posted)
w.WriteHeader(http.StatusCreated)
w.Write([]byte(`{"id":"1"}`))
default:
t.Errorf("FAIL: unexpected %s %s", r.Method, r.URL.Path)
}
}))
defer srv.Close()
made, err := New(srv.URL, "k").EnsureEmailIntegration(context.Background(),
EmailSender{Provider: "resend", Identifier: "quarry-mail",
From: "quarry <[email protected]>", Key: "re_secret"})
if err != nil || !made {
t.Fatalf("FAIL: made=%v err=%v", made, err)
}
if posted["provider"] != "resend" || posted["from"] != "quarry <[email protected]>" {
t.Errorf("FAIL: the sender was not carried across: %+v", posted)
}
if posted["apiKey"] != "re_secret" {
t.Error("FAIL: the credential did not reach the companion")
}
if posted["primary"] != true {
t.Error("FAIL: the only email integration is not the primary one")
}
t.Log("ok: a forge with a mailer gives the companion one too")
}
// An operator who configured their own provider meant it, and a forge
// restart must not replace their choice.
func TestEnsureEmailIntegrationLeavesAnExistingOneAlone(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == "POST" {
t.Error("FAIL: an existing email integration was overwritten")
}
w.Write([]byte(`[{"channel":"email","identifier":"their-smtp"}]`))
}))
defer srv.Close()
made, err := New(srv.URL, "k").EnsureEmailIntegration(context.Background(),
EmailSender{Provider: "resend", From: "quarry <[email protected]>", Key: "re_secret"})
if err != nil || made {
t.Fatalf("FAIL: made=%v err=%v", made, err)
}
t.Log("ok: an operator's own provider survives a restart")
}
// A forge with no mailer of its own asks for nothing: there is no
// credential to pass on, and inventing one is not this function's job.
func TestEnsureEmailIntegrationIsSilentWithoutACredential(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
t.Errorf("FAIL: asked the companion anything at all: %s %s", r.Method, r.URL.Path)
}))
defer srv.Close()
c := New(srv.URL, "k")
for _, s := range []EmailSender{
{Provider: "resend", From: "quarry <[email protected]>"},
{Provider: "resend", Key: "re_secret"},
} {
if made, err := c.EnsureEmailIntegration(context.Background(), s); made || err != nil {
t.Errorf("FAIL: %+v gave made=%v err=%v", s, made, err)
}
}
t.Log("ok: no credential, no integration, no noise")
}
// notify's subscriber upsert REPLACES the row, so an address has to ride
// along every time or the one already stored is blanked — and a
// subscriber with no address is one no email can reach.
func TestEnsureSubscriberCarriesTheAddress(t *testing.T) {
var got map[string]string
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
json.NewDecoder(r.Body).Decode(&got)
w.WriteHeader(http.StatusCreated)
w.Write([]byte(`{}`))
}))
defer srv.Close()
if err := New(srv.URL, "k").EnsureSubscriber(context.Background(), "dan", "[email protected]"); err != nil {
t.Fatal(err)
}
if got["subscriberId"] != "dan" || got["email"] != "[email protected]" {
t.Errorf("FAIL: sent %+v", got)
}
t.Log("ok: the address travels with the subscriber")
}