Repository: mailgun/mailgun-go
Pull request #282 (2022-01-21 11:48:43)

Mocks for stats and tags


Patch:
diff --git a/mock.go b/mock.go
index fd98489..3dc6003 100644
--- a/mock.go
+++ b/mock.go
@@ -45,6 +45,8 @@ type mockServer struct {
 	complaints       []Complaint
 	bounces          []Bounce
 	credentials      []Credential
+	stats            []Stats
+	tags             []Tag
 	webhooks         WebHooksListResponse
 	mutex            sync.Mutex
 }
@@ -125,6 +127,8 @@ func NewMockServer() MockServer {
 		ms.addComplaintsRoutes(r)
 		ms.addBouncesRoutes(r)
 		ms.addCredentialsRoutes(r)
+		ms.addStatsRoutes(r)
+		ms.addTagsRoutes(r)
 	}(r.PathPrefix("/v3").Subrouter())
 	ms.addValidationRoutes(r)
 
diff --git a/mock_messages.go b/mock_messages.go
index b735d0f..1048758 100644
--- a/mock_messages.go
+++ b/mock_messages.go
@@ -83,6 +83,21 @@ func (ms *mockServer) createMessages(w http.ResponseWriter, r *http.Request) {
 		ms.mutex.Unlock()
 	}
 
+	tags := r.Form["o:tag"]
+	for _, newTag := range tags {
+		var tagExists bool
+		for _, existingTag := range ms.tags {
+			if newTag == existingTag.Value {
+				tagExists = true
+				break
+			}
+		}
+
+		if !tagExists {
+			ms.tags = append(ms.tags, Tag{Value: newTag})
+		}
+	}
+
 	toJSON(w, okResp{ID: "<" + id + ">", Message: "Queued. Thank you."})
 }
 
diff --git a/mock_stats.go b/mock_stats.go
new file mode 100644
index 0000000..47d0c6f
--- /dev/null
+++ b/mock_stats.go
@@ -0,0 +1,65 @@
+package mailgun
+
+import (
+	"net/http"
+	"time"
+
+	"github.com/gorilla/mux"
+)
+
+func (ms *mockServer) addStatsRoutes(r *mux.Router) {
+	r.HandleFunc("/{domain}/stats", ms.getStats).Methods(http.MethodGet)
+	r.HandleFunc("/{domain}/stats/total", ms.getStatsTotal).Methods(http.MethodGet)
+
+	ms.stats = append(ms.stats, Stats{
+		Time:         "",
+		Accepted:     Accepted{Total: 100, Incoming: 30, Outgoing: 70},
+		Delivered:    Delivered{Total: 100, Http: 100, Smtp: 0},
+		Failed:       Failed{Temporary: Temporary{Espblock: 0}, Permanent: Permanent{Total: 0}},
+		Stored:       Total{Total: 5},
+		Opened:       Total{Total: 40},
+		Clicked:      Total{Total: 50},
+		Unsubscribed: Total{Total: 5},
+		Complained:   Total{Total: 2},
+	})
+
+}
+
+func (ms *mockServer) getStats(w http.ResponseWriter, r *http.Request) {
+	w.WriteHeader(http.StatusNotFound)
+	w.Write([]byte("{\"message\": \"this route was deprecated 2 years ago and is no longer available\"}"))
+}
+
+func (ms *mockServer) getStatsTotal(w http.ResponseWriter, r *http.Request) {
+	defer ms.mutex.Unlock()
+	ms.mutex.Lock()
+
+	err := r.ParseForm()
+	if err != nil {
+		w.WriteHeader(http.StatusInternalServerError)
+		w.Write([]byte(err.Error()))
+		return
+	}
+
+	eventName := r.Form.Get("event")
+	if len(eventName) == 0 {
+		w.WriteHeader(http.StatusBadRequest)
+		w.Write([]byte("please provide at least one event: Missing mandatory parameter: event"))
+		return
+	}
+
+	resolution := r.Form.Get("resolution")
+	if len(resolution) > 0 && resolution != "day" && resolution != "hour" && resolution != "month" {
+		w.WriteHeader(http.StatusBadRequest)
+		w.Write([]byte("'resolution' parameter is invalid, valid values: 'month', 'day', 'hour'"))
+		return
+	}
+
+	end, _ := NewRFC2822Time(time.Now().String())
+	start, _ := NewRFC2822Time(time.Now().Add(-10 * time.Minute).String())
+	toJSON(w, &statsTotalResponse{
+		Stats: ms.stats,
+		End:   end.String(),
+		Start: start.String(),
+	})
+}
diff --git a/mock_tags.go b/mock_tags.go
new file mode 100644
index 0000000..aca42c2
--- /dev/null
+++ b/mock_tags.go
@@ -0,0 +1,149 @@
+package mailgun
+
+import (
+	"net/http"
+	"net/url"
+	"time"
+
+	"github.com/gorilla/mux"
+)
+
+func (ms *mockServer) addTagsRoutes(r *mux.Router) {
+	r.HandleFunc("/{domain}/tags", ms.listTags).Methods(http.MethodGet)
+	r.HandleFunc("/{domain}/tags/{tag}", ms.getTags).Methods(http.MethodGet)
+	r.HandleFunc("/{domain}/tags/{tag}", ms.deleteTags).Methods(http.MethodDelete)
+	r.HandleFunc("/{domain}/tags/{tag}", ms.createUpdateTags).Methods(http.MethodPut)
+
+	tenMinutesBefore := time.Now().Add(-10 * time.Minute)
+	now := time.Now()
+	ms.tags = append(ms.tags, Tag{
+		Value:       "test",
+		Description: "test description",
+		FirstSeen:   &tenMinutesBefore,
+		LastSeen:    &now,
+	})
+
+	ms.tags = append(ms.tags, Tag{
+		Value:       "test2",
+		Description: "test2 description",
+		FirstSeen:   &tenMinutesBefore,
+		LastSeen:    &now,
+	})
+}
+
+func (ms *mockServer) listTags(w http.ResponseWriter, r *http.Request) {
+	defer ms.mutex.Unlock()
+	ms.mutex.Lock()
+
+	var idx []string
+	for _, t := range ms.unsubscribes {
+		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 []Tag
+
+	if start != end {
+		results = ms.tags[start:end]
+		nextAddress = results[len(results)-1].Value
+		prevAddress = results[0].Value
+	} else {
+		results = []Tag{}
+		nextAddress = pivot
+		prevAddress = pivot
+	}
+
+	toJSON(w, tagsResponse{
+		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) getTags(w http.ResponseWriter, r *http.Request) {
+	defer ms.mutex.Unlock()
+	ms.mutex.Lock()
+	for _, tag := range ms.tags {
+		if tag.Value == mux.Vars(r)["tag"] {
+			toJSON(w, tag)
+			return
+		}
+	}
+	w.WriteHeader(http.StatusNotFound)
+	w.Write([]byte("{\"message\": \"Tag not found\"}"))
+}
+
+func (ms *mockServer) createUpdateTags(w http.ResponseWriter, r *http.Request) {
+	defer ms.mutex.Unlock()
+	ms.mutex.Lock()
+
+	if err := r.ParseForm(); err != nil {
+		w.WriteHeader(http.StatusInternalServerError)
+		w.Write([]byte(err.Error()))
+		return
+	}
+
+	tag := mux.Vars(r)["tag"]
+	description := r.FormValue("description")
+
+	var tagExists bool
+	for i, existingTag := range ms.tags {
+		if tag == existingTag.Value {
+			ms.tags[i].Description = description
+			tagExists = true
+		}
+	}
+
+	if !tagExists {
+		ms.tags = append(ms.tags, Tag{Value: tag, Description: description})
+	}
+
+	toJSON(w, map[string]interface{}{
+		"message": "Tag updated",
+	})
+}
+
+func (ms *mockServer) deleteTags(w http.ResponseWriter, r *http.Request) {
+	defer ms.mutex.Unlock()
+	ms.mutex.Lock()
+
+	for i, existingTag := range ms.tags {
+		if existingTag.Value == mux.Vars(r)["tag"] {
+			ms.tags = append(ms.tags[:i], ms.tags[i+1:len(ms.tags)]...)
+		}
+	}
+
+	toJSON(w, map[string]interface{}{
+		"message": "Tag deleted",
+	})
+}


Test patch:
diff --git a/stats_test.go b/stats_test.go
index ef7661b..633dcc0 100644
--- a/stats_test.go
+++ b/stats_test.go
@@ -1,19 +1,17 @@
-package mailgun
+package mailgun_test
 
 import (
 	"context"
 	"testing"
 
+	"github.com/mailgun/mailgun-go/v4"
+
 	"github.com/facebookgo/ensure"
 )
 
 func TestListStats(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()
 
 	stats, err := mg.GetStats(ctx, []string{"accepted", "delivered"}, nil)
@@ -26,15 +24,3 @@ func TestListStats(t *testing.T) {
 		t.Logf("Delivered Total: %d\n", firstStatsTotal.Delivered.Total)
 	}
 }
-
-func TestDeleteTag(t *testing.T) {
-	if reason := SkipNetworkTest(); reason != "" {
-		t.Skip(reason)
-	}
-
-	mg, err := NewMailgunFromEnv()
-	ctx := context.Background()
-
-	ensure.Nil(t, err)
-	ensure.Nil(t, mg.DeleteTag(ctx, "newsletter"))
-}
diff --git a/tags_test.go b/tags_test.go
index 201071e..6f71488 100644
--- a/tags_test.go
+++ b/tags_test.go
@@ -3,7 +3,6 @@ package mailgun_test
 import (
 	"context"
 	"log"
-	"os"
 	"testing"
 	"time"
 
@@ -19,20 +18,18 @@ const (
 )
 
 func TestTags(t *testing.T) {
-	if reason := mailgun.SkipNetworkTest(); reason != "" {
-		t.Skip(reason)
-	}
-	mg, err := mailgun.NewMailgunFromEnv()
-	msg := mg.NewMessage(fromUser, exampleSubject, exampleText, os.Getenv("MG_EMAIL_TO"))
-	msg.AddTag("newsletter")
-	msg.AddTag("homer")
-	msg.AddTag("bart")
-	msg.AddTag("disco-steve")
-	msg.AddTag("newsletter")
+	mg := mailgun.NewMailgun(testDomain, testKey)
+	mg.SetAPIBase(server.URL())
+	msg := mg.NewMessage(fromUser, exampleSubject, exampleText, "test@example.com")
+	ensure.Nil(t, msg.AddTag("newsletter"))
+	ensure.Nil(t, msg.AddTag("homer"))
+	ensure.Nil(t, msg.AddTag("bart"))
+	ensure.NotNil(t, msg.AddTag("disco-steve"))
+	ensure.NotNil(t, msg.AddTag("newsletter"))
 
 	ctx := context.Background()
 	// Create an email with some tags attached
-	_, _, err = mg.Send(ctx, msg)
+	_, _, err := mg.Send(ctx, msg)
 	ensure.Nil(t, err)
 
 	// Wait for the tag to show up
@@ -88,3 +85,11 @@ func waitForTag(mg mailgun.Mailgun, tag string) error {
 	}
 	return errors.Errorf("Waited to long for tag '%s' to show up", tag)
 }
+
+func TestDeleteTag(t *testing.T) {
+	mg := mailgun.NewMailgun(testDomain, testKey)
+	mg.SetAPIBase(server.URL())
+	ctx := context.Background()
+
+	ensure.Nil(t, mg.DeleteTag(ctx, "newsletter"))
+}