package email import ( "context" "encoding/json" "errors" "io" "net/http" "net/http/httptest" "strings" "testing" ) func TestPostmarkSender_Send(t *testing.T) { var gotBody postmarkRequest var gotToken string srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { gotToken = r.Header.Get("X-Postmark-Server-Token") body, _ := io.ReadAll(r.Body) _ = json.Unmarshal(body, &gotBody) w.Header().Set("Content-Type", "application/json") _, _ = w.Write([]byte(`{"ErrorCode":0,"Message":"OK","To":"alice@example.com"}`)) })) defer srv.Close() sender := NewPostmarkSender("test-token", "noreply@example.com", srv.URL, testLogger(t)) if err := sender.Send(context.Background(), []string{"alice@example.com"}, "Hello", "World"); err != nil { t.Fatalf("Send: %v", err) } if gotToken != "test-token" { t.Errorf("token = %q, want test-token", gotToken) } if gotBody.From != "noreply@example.com" { t.Errorf("from = %q", gotBody.From) } if gotBody.To != "alice@example.com" { t.Errorf("to = %q", gotBody.To) } if gotBody.Subject != "Hello" { t.Errorf("subject = %q", gotBody.Subject) } if gotBody.TextBody != "World" { t.Errorf("body = %q", gotBody.TextBody) } } func TestPostmarkSender_ErrorCode(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") _, _ = w.Write([]byte(`{"ErrorCode":406,"Message":"Inactive recipient"}`)) })) defer srv.Close() sender := NewPostmarkSender("tok", "noreply@example.com", srv.URL, testLogger(t)) err := sender.Send(context.Background(), []string{"x@x.com"}, "s", "b") if err == nil { t.Fatal("expected error, got nil") } if !strings.Contains(err.Error(), "406") { t.Errorf("error should mention code 406: %v", err) } } func TestPostmarkSender_ServerError_Temporary(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusInternalServerError) _, _ = w.Write([]byte("boom")) })) defer srv.Close() sender := NewPostmarkSender("tok", "noreply@example.com", srv.URL, testLogger(t)) err := sender.Send(context.Background(), []string{"x@x.com"}, "s", "b") if err == nil { t.Fatal("expected error") } var temp *TemporaryError if !errors.As(err, &temp) { t.Errorf("expected TemporaryError, got %T: %v", err, err) } } func TestPostmarkSender_Validation(t *testing.T) { sender := NewPostmarkSender("", "noreply@example.com", "", testLogger(t)) if err := sender.Send(context.Background(), []string{"x@x.com"}, "s", "b"); err == nil { t.Fatal("expected error for missing token") } sender = NewPostmarkSender("tok", "", "", testLogger(t)) if err := sender.Send(context.Background(), []string{"x@x.com"}, "s", "b"); err == nil { t.Fatal("expected error for missing from") } sender = NewPostmarkSender("tok", "noreply@example.com", "", testLogger(t)) if err := sender.Send(context.Background(), nil, "s", "b"); err == nil { t.Fatal("expected error for empty recipients") } } func TestPostmarkSender_MultipleRecipients(t *testing.T) { var gotBody postmarkRequest srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { body, _ := io.ReadAll(r.Body) _ = json.Unmarshal(body, &gotBody) _, _ = w.Write([]byte(`{"ErrorCode":0,"Message":"OK"}`)) })) defer srv.Close() sender := NewPostmarkSender("tok", "noreply@example.com", srv.URL, testLogger(t)) if err := sender.Send(context.Background(), []string{"a@x.com", "b@x.com"}, "s", "b"); err != nil { t.Fatalf("Send: %v", err) } if !strings.Contains(gotBody.To, "a@x.com") || !strings.Contains(gotBody.To, "b@x.com") { t.Errorf("to = %q, expected both recipients", gotBody.To) } }