Repository: mailgun/mailgun-go
Pull request #279 (2021-12-24 09:30:21)

Complaints mocks


Patch:
diff --git a/mock.go b/mock.go
index 656334b..c10462c 100644
--- a/mock.go
+++ b/mock.go
@@ -42,6 +42,7 @@ type mockServer struct {
 	templates        []Template
 	templateVersions map[string][]TemplateVersion
 	unsubscribes     []Unsubscribe
+	complaints       []Complaint
 	webhooks         WebHooksListResponse
 	mutex            sync.Mutex
 }
@@ -119,6 +120,7 @@ func NewMockServer() MockServer {
 		ms.addTemplateRoutes(r)
 		ms.addTemplateVersionRoutes(r)
 		ms.addUnsubscribesRoutes(r)
+		ms.addComplaintsRoutes(r)
 	}(r.PathPrefix("/v3").Subrouter())
 	ms.addValidationRoutes(r)
 
diff --git a/mock_complaints.go b/mock_complaints.go
new file mode 100644
index 0000000..e6f89b4
--- /dev/null
+++ b/mock_complaints.go
@@ -0,0 +1,179 @@
+package mailgun
+
+import (
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"net/url"
+	"time"
+
+	"github.com/gorilla/mux"
+)
+
+func (ms *mockServer) addComplaintsRoutes(r *mux.Router) {
+	r.HandleFunc("/{domain}/complaints", ms.listComplaints).Methods(http.MethodGet)
+	r.HandleFunc("/{domain}/complaints/{address}", ms.getComplaint).Methods(http.MethodGet)
+	r.HandleFunc("/{domain}/complaints/{address}", ms.deleteComplaint).Methods(http.MethodDelete)
+	r.HandleFunc("/{domain}/complaints", ms.createComplaint).Methods(http.MethodPost)
+
+	ms.complaints = append(ms.complaints, Complaint{
+		CreatedAt: RFC2822Time(time.Now()),
+		Address:   "foo@mailgun.test",
+	})
+
+	ms.complaints = append(ms.complaints, Complaint{
+		CreatedAt: RFC2822Time(time.Now()),
+		Address:   "alice@example.com",
+	})
+}
+
+func (ms *mockServer) listComplaints(w http.ResponseWriter, r *http.Request) {
+	defer ms.mutex.Unlock()
+	ms.mutex.Lock()
+
+	var idx []string
+	for _, t := range ms.complaints {
+		idx = append(idx, t.Address)
+	}
+
+	limit := stringToInt(r.FormValue("limit"))
+	if limit == 0 {
+		limit = 100
+	}
+
+	page := r.FormValue("page")
+	var pivot string
+	if len(page) != 0 {
+		pivot = r.FormValue("p")
+		if pivot == "" {
+			w.WriteHeader(http.StatusBadRequest)
+			w.Write([]byte("{\"message\": \"Invalid parameter: pivot \"}"))
+			return
+		}
+	}
+	start, end := pageOffsets(idx, page, pivot, limit)
+	var nextAddress, prevAddress string
+	var results []Complaint
+
+	if start != end {
+		results = ms.complaints[start:end]
+		nextAddress = results[len(results)-1].Address
+		prevAddress = results[0].Address
+	} else {
+		results = []Complaint{}
+		nextAddress = pivot
+		prevAddress = pivot
+	}
+
+	toJSON(w, complaintsResponse{
+		Paging: Paging{
+			First: getPageURL(r, url.Values{
+				"page": []string{"first"},
+			}),
+			Last: getPageURL(r, url.Values{
+				"page": []string{"last"},
+			}),
+			Next: getPageURL(r, url.Values{
+				"page": []string{"next"},
+				"p":    []string{nextAddress},
+			}),
+			Previous: getPageURL(r, url.Values{
+				"page": []string{"prev"},
+				"p":    []string{prevAddress},
+			}),
+		},
+		Items: results,
+	})
+}
+
+func (ms *mockServer) getComplaint(w http.ResponseWriter, r *http.Request) {
+	defer ms.mutex.Unlock()
+	ms.mutex.Lock()
+
+	for _, complaint := range ms.complaints {
+		if complaint.Address == mux.Vars(r)["address"] {
+			toJSON(w, complaint)
+			return
+		}
+	}
+	w.WriteHeader(http.StatusNotFound)
+	w.Write([]byte("{\"message\": \"Address not found in complaints table\"}"))
+}
+
+func (ms *mockServer) createComplaint(w http.ResponseWriter, r *http.Request) {
+	defer ms.mutex.Unlock()
+	ms.mutex.Lock()
+
+	var complaints []Complaint
+	if r.Header.Get("Content-Type") == "application/json" {
+		body, err := ioutil.ReadAll(r.Body)
+		if err != nil {
+			w.WriteHeader(http.StatusInternalServerError)
+			w.Write([]byte("{\"message\": \"Can't read request body\"}"))
+			return
+		}
+
+		err = json.Unmarshal(body, &complaints)
+		if err != nil {
+			w.WriteHeader(http.StatusBadRequest)
+			w.Write([]byte(fmt.Sprintf("{\"message\": \"Invalid json: %s\"}", err.Error())))
+			return
+		}
+	} else {
+		if err := r.ParseForm(); err != nil {
+			w.WriteHeader(http.StatusInternalServerError)
+			w.Write([]byte(err.Error()))
+			return
+		}
+
+		address := r.FormValue("address")
+		if len(address) == 0 {
+			w.WriteHeader(http.StatusBadRequest)
+			w.Write([]byte("{\"message\": \"Invalid format for parameter address: \"}"))
+			return
+		}
+
+		complaints = append(complaints, Complaint{Address: address, CreatedAt: RFC2822Time(time.Now())})
+	}
+
+	for _, complaint := range complaints {
+		var addressExist bool
+		for _, existingComplaint := range ms.complaints {
+			if existingComplaint.Address == complaint.Address {
+				addressExist = true
+			}
+		}
+
+		if !addressExist {
+			complaint.CreatedAt = RFC2822Time(time.Now())
+			ms.complaints = append(ms.complaints, complaint)
+		}
+	}
+
+	toJSON(w, map[string]interface{}{
+		"message": "Address has been added to the complaints table",
+		"address": fmt.Sprint(complaints),
+	})
+}
+
+func (ms *mockServer) deleteComplaint(w http.ResponseWriter, r *http.Request) {
+	defer ms.mutex.Unlock()
+	ms.mutex.Lock()
+
+	for i, complaint := range ms.complaints {
+		if complaint.Address == mux.Vars(r)["address"] {
+			ms.complaints = append(ms.complaints[:i], ms.complaints[i+1:len(ms.complaints)]...)
+
+			toJSON(w, map[string]interface{}{
+				"message": "Complaint has been removed",
+			})
+			return
+		}
+	}
+
+	toJSON(w, map[string]interface{}{
+		"message": "Address not found in complaints table",
+	})
+	return
+}
diff --git a/mock_unsubscribes.go b/mock_unsubscribes.go
index 556eddb..b961cc6 100644
--- a/mock_unsubscribes.go
+++ b/mock_unsubscribes.go
@@ -114,7 +114,7 @@ func (ms *mockServer) createUnsubscribe(w http.ResponseWriter, r *http.Request)
 		body, err := ioutil.ReadAll(r.Body)
 		if err != nil {
 			w.WriteHeader(http.StatusInternalServerError)
-			w.Write([]byte("{\"message\": \"Add multiple unsubscribes is not yet implemented\"}"))
+			w.Write([]byte("{\"message\": \"Can't read request body\"}"))
 			return
 		}
 


Test patch:
diff --git a/spam_complaints_test.go b/spam_complaints_test.go
index b82ed5b..5f0c143 100644
--- a/spam_complaints_test.go
+++ b/spam_complaints_test.go
@@ -1,4 +1,4 @@
-package mailgun
+package mailgun_test
 
 import (
 	"context"
@@ -7,57 +7,47 @@ import (
 	"testing"
 
 	"github.com/facebookgo/ensure"
+	"github.com/mailgun/mailgun-go/v4"
 )
 
 func TestGetComplaints(t *testing.T) {
-	if reason := SkipNetworkTest(); reason != "" {
-		t.Skip(reason)
-	}
+	mg := mailgun.NewMailgun(testDomain, testKey)
+	mg.SetAPIBase(server.URL())
 
-	mg, err := NewMailgunFromEnv()
-	ensure.Nil(t, err)
 	ctx := context.Background()
 
 	it := mg.ListComplaints(nil)
-	var page []Complaint
+	var page []mailgun.Complaint
 	for it.Next(ctx, &page) {
 		//spew.Dump(page)
 	}
-	ensure.Nil(t, err)
+	ensure.Nil(t, it.Err())
 }
 
 func TestGetComplaintFromRandomNoComplaint(t *testing.T) {
-	if reason := SkipNetworkTest(); reason != "" {
-		t.Skip(reason)
-	}
-
-	mg, err := NewMailgunFromEnv()
-	ensure.Nil(t, err)
+	mg := mailgun.NewMailgun(testDomain, testKey)
+	mg.SetAPIBase(server.URL())
 	ctx := context.Background()
 
-	_, err = mg.GetComplaint(ctx, randomString(64, "")+"@example.com")
+	_, err := mg.GetComplaint(ctx, randomString(64, "")+"@example.com")
 	ensure.NotNil(t, err)
 
-	ure, ok := err.(*UnexpectedResponseError)
+	ure, ok := err.(*mailgun.UnexpectedResponseError)
 	ensure.True(t, ok)
 	ensure.DeepEqual(t, ure.Actual, http.StatusNotFound)
 }
 
 func TestCreateDeleteComplaint(t *testing.T) {
-	if reason := SkipNetworkTest(); reason != "" {
-		t.Skip(reason)
-	}
-
-	mg, err := NewMailgunFromEnv()
-	ensure.Nil(t, err)
+	mg := mailgun.NewMailgun(testDomain, testKey)
+	mg.SetAPIBase(server.URL())
 	ctx := context.Background()
 
 	var hasComplaint = func(email string) bool {
 		t.Logf("hasComplaint: %s\n", email)
 		it := mg.ListComplaints(nil)
-		ensure.Nil(t, err)
+		ensure.Nil(t, it.Err())
 
-		var page []Complaint
+		var page []mailgun.Complaint
 		for it.Next(ctx, &page) {
 			for _, complaint := range page {
 				t.Logf("Complaint Address: %s\n", complaint.Address)