6.0 KB
emailers_test.go
package emailers
import (
"embed"
"encoding/json"
"html/template"
"io"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
//go:embed all:emails
var testEmails embed.FS
func TestNewLogger(t *testing.T) {
l := NewLogger(testEmails, nil)
if l == nil {
t.Fatal("expected logger to be created")
}
}
func TestNewLogger_WithFuncs(t *testing.T) {
funcs := template.FuncMap{
"upper": strings.ToUpper,
}
l := NewLogger(testEmails, funcs)
if l == nil {
t.Fatal("expected logger to be created with custom funcs")
}
}
func TestLogger_Send(t *testing.T) {
l := NewLogger(testEmails, nil)
err := l.Send(
"user@example.com",
"Welcome!",
"welcome.html",
map[string]any{"name": "Alice"},
)
if err != nil {
t.Fatalf("expected no error, got: %v", err)
}
}
func TestLogger_Send_MultipleTemplates(t *testing.T) {
l := NewLogger(testEmails, nil)
err := l.Send(
"user@example.com",
"Reset Password",
"reset.html",
map[string]any{"name": "Bob", "link": "https://example.com/reset/abc123"},
)
if err != nil {
t.Fatalf("expected no error, got: %v", err)
}
}
func TestLogger_Send_InvalidTemplate(t *testing.T) {
l := NewLogger(testEmails, nil)
err := l.Send(
"user@example.com",
"Test",
"nonexistent.html",
nil,
)
if err == nil {
t.Error("expected error for nonexistent template")
}
}
func TestNewResend(t *testing.T) {
r := NewResend(testEmails, "re_test_key", "noreply@example.com", nil)
if r == nil {
t.Fatal("expected resend emailer to be created")
}
if r.apiKey != "re_test_key" {
t.Errorf("expected apiKey 're_test_key', got %q", r.apiKey)
}
if r.from != "noreply@example.com" {
t.Errorf("expected from 'noreply@example.com', got %q", r.from)
}
}
func TestNewResend_WithFuncs(t *testing.T) {
funcs := template.FuncMap{
"upper": strings.ToUpper,
}
r := NewResend(testEmails, "re_test_key", "noreply@example.com", funcs)
if r == nil {
t.Fatal("expected resend emailer to be created with custom funcs")
}
}
func TestResend_Send(t *testing.T) {
// Create a mock Resend API server
var receivedBody map[string]any
var receivedAuth string
var receivedContentType string
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
receivedAuth = r.Header.Get("Authorization")
receivedContentType = r.Header.Get("Content-Type")
body, err := io.ReadAll(r.Body)
if err != nil {
t.Errorf("failed to read request body: %v", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
if err := json.Unmarshal(body, &receivedBody); err != nil {
t.Errorf("failed to unmarshal request body: %v", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"id": "test-email-id"}`))
}))
defer server.Close()
// Override http.DefaultClient to redirect requests to our test server
originalTransport := http.DefaultClient.Transport
http.DefaultClient.Transport = &testTransport{
targetURL: server.URL,
}
defer func() {
http.DefaultClient.Transport = originalTransport
}()
r := NewResend(testEmails, "re_test_api_key", "sender@example.com", nil)
err := r.Send(
"recipient@example.com",
"Welcome!",
"welcome.html",
map[string]any{"name": "Alice"},
)
if err != nil {
t.Fatalf("expected no error, got: %v", err)
}
// Verify authorization header
if receivedAuth != "Bearer re_test_api_key" {
t.Errorf("expected Authorization 'Bearer re_test_api_key', got %q", receivedAuth)
}
// Verify content type
if receivedContentType != "application/json" {
t.Errorf("expected Content-Type 'application/json', got %q", receivedContentType)
}
// Verify payload
if receivedBody["from"] != "sender@example.com" {
t.Errorf("expected from 'sender@example.com', got %v", receivedBody["from"])
}
if receivedBody["subject"] != "Welcome!" {
t.Errorf("expected subject 'Welcome!', got %v", receivedBody["subject"])
}
// Verify 'to' is an array with recipient
toSlice, ok := receivedBody["to"].([]any)
if !ok {
t.Fatalf("expected 'to' to be an array, got %T", receivedBody["to"])
}
if len(toSlice) != 1 || toSlice[0] != "recipient@example.com" {
t.Errorf("expected to ['recipient@example.com'], got %v", toSlice)
}
// Verify HTML body was rendered
htmlBody, ok := receivedBody["html"].(string)
if !ok {
t.Fatal("expected 'html' field in payload")
}
if !strings.Contains(htmlBody, "Alice") {
t.Error("expected rendered HTML to contain 'Alice'")
}
if !strings.Contains(htmlBody, "Welcome") {
t.Error("expected rendered HTML to contain 'Welcome'")
}
}
func TestResend_Send_APIError(t *testing.T) {
// Create a mock server that returns an error
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte(`{"message": "Invalid API key"}`))
}))
defer server.Close()
// Override http.DefaultClient transport
originalTransport := http.DefaultClient.Transport
http.DefaultClient.Transport = &testTransport{
targetURL: server.URL,
}
defer func() {
http.DefaultClient.Transport = originalTransport
}()
r := NewResend(testEmails, "invalid_key", "sender@example.com", nil)
err := r.Send(
"recipient@example.com",
"Test",
"welcome.html",
map[string]any{"name": "Bob"},
)
if err == nil {
t.Error("expected error for API error response")
}
if !strings.Contains(err.Error(), "401") {
t.Errorf("expected error to mention status code, got: %v", err)
}
}
func TestResend_Send_InvalidTemplate(t *testing.T) {
r := NewResend(testEmails, "re_test_key", "sender@example.com", nil)
err := r.Send(
"recipient@example.com",
"Test",
"nonexistent.html",
nil,
)
if err == nil {
t.Error("expected error for nonexistent template")
}
}
// testTransport redirects all HTTP requests to the test server URL.
type testTransport struct {
targetURL string
}
func (t *testTransport) RoundTrip(req *http.Request) (*http.Response, error) {
// Redirect the request to our test server
req.URL.Scheme = "http"
req.URL.Host = strings.TrimPrefix(t.targetURL, "http://")
return http.DefaultTransport.RoundTrip(req)
}