<|start_filename|>resources/time_test.go<|end_filename|>
package resources

import (
	"bytes"
	"testing"
	"time"
)

func TestMarshalJSON(t *testing.T) {
	dateTime, _ := time.Parse(time.RFC3339, "2016-10-14T12:42:05Z")
	dt := RFC3339DateTime{dateTime}

	want := []byte(`"2016-10-14T12:42:05Z"`)
	got, err := dt.MarshalJSON()

	if err != nil {
		t.Error("unexpected error:", err)
	}

	if !bytes.Equal(got, want) {
		t.Errorf("expected %v", want)
		t.Errorf("     got %v", got)
	}
}

func TestUnmarshalJSON(t *testing.T) {
	data := []byte(`"2016-10-14T12:42:05Z"`)
	var dt RFC3339DateTime

	err := dt.UnmarshalJSON(data)
	if err != nil {
		t.Error("unexpected error:", err)
	}

	if dt.UnixNano() != 1476448925000000000 {
		t.Errorf("expected: %v", 1476448925000000000)
		t.Errorf("     got: %v", dt.UnixNano())
	}
}

func TestUnmarshalEmptyJSON(t *testing.T) {
	var data []byte
	var dt RFC3339DateTime

	err := dt.UnmarshalJSON(data)
	if err != nil {
		t.Error("unexpected error:", err)
	}

	data = []byte{34, 34}
	err = dt.UnmarshalJSON(data)
	if err != nil {
		t.Error("unexpected error:", err)
	}
}

func TestUnmarshalBrokenJSON(t *testing.T) {
	data := []byte{1, 1}
	var dt RFC3339DateTime

	err := dt.UnmarshalJSON(data)
	if err == nil {
		t.Error("error expected")
	}
}


<|start_filename|>core_test.go<|end_filename|>
package mailjet

import (
	"bytes"
	"encoding/json"
	"fmt"
	"runtime"
	"strings"
	"testing"
)

func TestCreateRequest(t *testing.T) {
	req, err := createRequest("GET", apiBase, nil, nil)
	if err != nil {
		t.Fatal("Unexpected error:", err)
	}
	if req.Method != "GET" {
		t.Fatal("Wrong method:", req.Method)
	}
	if req.URL.String() != apiBase {
		t.Fatal("Wrong URL:", req.URL.String())
	}
	ua := fmt.Sprintf("%s/%s;%s",
		UserAgentBase,
		UserAgentVersion,
		runtime.Version(),
	)
	if req.Header["User-Agent"] == nil || req.Header["User-Agent"][0] != ua {
		t.Fatal("Wrong User-agent:", req.Header["User-Agent"][0])
	}
}

func TestConvertPayload(t *testing.T) {
	type Test struct {
		ID           int64 `mailjet:"read_only"`
		Name         string
		Email        string
		Address      string            `json:",omitempty" mailjet:"read_only"`
		TextPart     string            `json:"Text-Part,omitempty"`
		Header       map[string]string `json:",omitempty"`
		MjCampaignID int64             `json:"Mj-CampaignID,omitempty" mailjet:"read_only"`
	}
	test := &Test{
		ID:       -42,
		Email:    "<EMAIL>",
		TextPart: "This is text",
	}
	resMap := make(map[string]interface{})
	resMap["Email"] = "<EMAIL>"
	resMap["Text-Part"] = "This is text"
	body, err := convertPayload(test, []string{"Email", "TextPart"})
	if err != nil {
		t.Fatal("Unexpected error:", err)
	}
	res, _ := json.Marshal(resMap)
	if !bytes.Equal(body, res) {
		t.Fatal("Wrong body:", string(body), string(res))
	}

	resMap["Name"] = ""
	body, err = convertPayload(&test, nil)
	if err != nil {
		t.Fatal("Unexpected error:", err)
	}
	res, _ = json.Marshal(resMap)
	if !bytes.Equal(body, res) {
		t.Fatal("Wrong body:", string(body), string(res))
	}
}

func TestBuildUrl(t *testing.T) {
	info := &Request{
		Resource: "contactslist",
		ID:       1,
		Action:   "managemanycontacts",
		ActionID: 5,
	}
	expected := "https://api.mailjet.com/v3/REST/contactslist/1/managemanycontacts/5"
	res := buildURL(apiBase, info)
	if res != expected {
		t.Fatal("Fail to build URL:", res)
	}
}

func TestReadJsonEmptyResult(t *testing.T) {
	type TestStruct struct {
		Email string
	}
	var data []TestStruct
	body := `{"Count":0,"Data":[],"Total":0}`
	_, _, err := readJSONResult(strings.NewReader(body), &data)
	if err != nil {
		t.Fatal("Unexpected error:", err)
	}
}

func TestReadJsonResult(t *testing.T) {
	type TestStruct struct {
		Email string
	}
	var data []TestStruct
	body := `{"Count":2,"Data":[{"Email":"<EMAIL>"},{"Email":"<EMAIL>"}],"Total":1}`
	count, total, err := readJSONResult(strings.NewReader(body), &data)
	if err != nil {
		t.Fatal("Unexpected error:", err)
	}
	if count != 2 {
		t.Fatalf("Wrong count: %d != %d", count, 2)
	}
	if total != 1 {
		t.Fatalf("Wrong total: %d != %d", total, 2)
	}
	if data != nil {
		if data[0].Email != "<EMAIL>" {
			t.Fatalf("Fail to unmarshal JSON: %s != %s", data[0].Email, "<EMAIL>")
		}
		if data[1].Email != "<EMAIL>" {
			t.Fatalf("Fail to unmarshal JSON: %s != %s", data[1].Email, "<EMAIL>")
		}
	} else {
		t.Fatal("Fail to unmarshal JSON: empty res")
	}
}


<|start_filename|>smtp_client_mock.go<|end_filename|>
package mailjet

import (
	"errors"
)

// SMTPClientMock def
type SMTPClientMock struct {
	valid bool
}

// NewSMTPClientMock returns a new smtp client mock
func NewSMTPClientMock(valid bool) *SMTPClientMock {
	return &SMTPClientMock{
		valid: valid,
	}
}

// SendMail wraps smtp.SendMail
func (s SMTPClientMock) SendMail(from string, to []string, msg []byte) error {
	if s.valid == true {
		return nil
	}
	return errors.New("smtp send error")
}


<|start_filename|>smtp_client_decl.go<|end_filename|>
package mailjet

// SMTPClientInterface def
type SMTPClientInterface interface {
	SendMail(from string, to []string, msg []byte) error
}


<|start_filename|>http_client_decl.go<|end_filename|>
package mailjet

import "net/http"

// HTTPClientInterface method definition
type HTTPClientInterface interface {
	APIKeyPublic() string
	APIKeyPrivate() string
	Client() *http.Client
	SetClient(client *http.Client)
	Send(req *http.Request) HTTPClientInterface
	SendMailV31(req *http.Request) (*http.Response, error)
	With(headers map[string]string) HTTPClientInterface
	Read(response interface{}) HTTPClientInterface
	Call() (count int, total int, err error)
}


<|start_filename|>smtp_client.go<|end_filename|>
package mailjet

import (
	"fmt"
	"net/smtp"
)

// SMTPClient is the wrapper for smtp
type SMTPClient struct {
	host string
	auth smtp.Auth
}

// Hostname and port for the SMTP client.
const (
	HostSMTP = "in-v3.mailjet.com"
	PortSMTP = 587
)

// NewSMTPClient returns a new smtp client wrapper
func NewSMTPClient(apiKeyPublic, apiKeyPrivate string) *SMTPClient {
	auth := smtp.PlainAuth(
		"",
		apiKeyPublic,
		apiKeyPrivate,
		HostSMTP,
	)
	return &SMTPClient{
		host: fmt.Sprintf("%s:%d", HostSMTP, PortSMTP),
		auth: auth,
	}
}

// SendMail wraps smtp.SendMail
func (s SMTPClient) SendMail(from string, to []string, msg []byte) error {
	return smtp.SendMail(s.host, s.auth, from, to, msg)
}


<|start_filename|>mailjet_resources.go<|end_filename|>
package mailjet

import (
	"fmt"
	"net/http"
	"net/textproto"
	"sync"

	"encoding/json"
)

/*
** API structures
 */

// Client bundles data needed by a large number
// of methods in order to interact with the Mailjet API.
type Client struct {
	apiBase    string
	httpClient HTTPClientInterface
	smtpClient SMTPClientInterface
	sync.Mutex
}

// Request bundles data needed to build the URL.
type Request struct {
	Resource string
	ID       int64
	AltID    string
	Action   string
	ActionID int64
}

// DataRequest bundles data needed to build the DATA URL.
type DataRequest struct {
	SourceType   string
	SourceTypeID int64
	DataType     string
	MimeType     string
	DataTypeID   int64
	LastID       bool
}

// FullRequest is the same as a Request but with a payload.
type FullRequest struct {
	Info    *Request
	Payload interface{}
}

// FullDataRequest is the same as a DataRequest but with a payload.
type FullDataRequest struct {
	Info    *DataRequest
	Payload interface{}
}

// RequestOptions are functional options that modify the specified request.
type RequestOptions func(*http.Request)

// RequestResult is the JSON result sent by the API.
type RequestResult struct {
	Count int
	Data  interface{}
	Total int
}

// RequestError is the error returned by the API.
type RequestError struct {
	ErrorInfo    string
	ErrorMessage string
	StatusCode   int
}

func (e RequestError) Error() string {
	return fmt.Sprintf("Unexpected server response code: %d: %s (%s)", e.StatusCode, e.ErrorMessage, e.ErrorInfo)
}

// RequestErrorV31 is the error returned by the API.
type RequestErrorV31 struct {
	ErrorInfo       string
	ErrorMessage    string
	StatusCode      int
	ErrorIdentifier string
}

/*
** Send API structures
 */

// InfoSendMail bundles data used by the Send API.
type InfoSendMail struct {
	FromEmail                string
	FromName                 string
	Sender                   string      `json:",omitempty"`
	Recipients               []Recipient `json:",omitempty"`
	To                       string      `json:",omitempty"`
	Cc                       string      `json:",omitempty"`
	Bcc                      string      `json:",omitempty"`
	Subject                  string
	TextPart                 string            `json:"Text-part,omitempty"`
	HTMLPart                 string            `json:"Html-part,omitempty"`
	Attachments              []Attachment      `json:",omitempty"`
	InlineAttachments        []Attachment      `json:"Inline_attachments,omitempty"`
	MjPrio                   int               `json:"Mj-prio,omitempty"`
	MjCampaign               string            `json:"Mj-campaign,omitempty"`
	MjDeduplicateCampaign    bool              `json:"Mj-deduplicatecampaign,omitempty"`
	MjCustomID               string            `json:"Mj-CustomID,omitempty"`
	MjTemplateID             string            `json:"Mj-TemplateID,omitempty"`
	MjTemplateErrorReporting string            `json:"MJ-TemplateErrorReporting,omitempty"`
	MjTemplateLanguage       string            `json:"Mj-TemplateLanguage,omitempty"`
	MjTemplateErrorDeliver   string            `json:"MJ-TemplateErrorDeliver,omitempty"`
	MjEventPayLoad           string            `json:"Mj-EventPayLoad,omitempty"`
	Headers                  map[string]string `json:",omitempty"`
	Vars                     interface{}       `json:",omitempty"`
	Messages                 []InfoSendMail    `json:",omitempty"`
}

// Recipient bundles data on the target of the mail.
type Recipient struct {
	Email string
	Name  string
	Vars  interface{} `json:",omitempty"`
}

// Attachment bundles data on the file attached to the mail.
type Attachment struct {
	ContentType string `json:"Content-Type"`
	Content     string
	Filename    string
}

// SentResult is the JSON result sent by the Send API.
type SentResult struct {
	Sent []struct {
		Email     string
		MessageID int64
	}
}

/*
** SMTP mail sending structures
 */

// InfoSMTP contains mandatory informations to send a mail via SMTP.
type InfoSMTP struct {
	From       string
	Recipients []string
	Header     textproto.MIMEHeader
	Content    []byte
}

/*
** Send API v3.1 structures
 */

// MessagesV31 definition
type MessagesV31 struct {
	Info        []InfoMessagesV31 `json:"Messages,omitempty"`
	SandBoxMode bool              `json:",omitempty"`
}

// InfoMessagesV31 represents the payload input taken by send API v3.1
type InfoMessagesV31 struct {
	From                     *RecipientV31          `json:",omitempty"`
	ReplyTo                  *RecipientV31          `json:",omitempty"`
	Sender                   *RecipientV31          `json:",omitempty"`
	To                       *RecipientsV31         `json:",omitempty"`
	Cc                       *RecipientsV31         `json:",omitempty"`
	Bcc                      *RecipientsV31         `json:",omitempty"`
	Attachments              *AttachmentsV31        `json:",omitempty"`
	InlinedAttachments       *InlinedAttachmentsV31 `json:",omitempty"`
	Subject                  string                 `json:",omitempty"`
	TextPart                 string                 `json:",omitempty"`
	HTMLPart                 string                 `json:",omitempty"`
	Priority                 int                    `json:",omitempty"`
	CustomCampaign           string                 `json:",omitempty"`
	StatisticsContactsListID int                    `json:",omitempty"`
	MonitoringCategory       string                 `json:",omitempty"`
	DeduplicateCampaign      bool                   `json:",omitempty"`
	TrackClicks              string                 `json:",omitempty"`
	TrackOpens               string                 `json:",omitempty"`
	CustomID                 string                 `json:",omitempty"`
	Variables                map[string]interface{} `json:",omitempty"`
	EventPayload             string                 `json:",omitempty"`
	TemplateID               interface{}            `json:",omitempty"`
	TemplateLanguage         bool                   `json:",omitempty"`
	TemplateErrorReporting   *RecipientV31          `json:",omitempty"`
	TemplateErrorDeliver     bool                   `json:",omitempty"`
	Headers                  map[string]interface{} `json:",omitempty"`
}

// RecipientV31 struct handle users input
type RecipientV31 struct {
	Email string `json:",omitempty"`
	Name  string `json:",omitempty"`
}

// RecipientsV31 is a collection of emails
type RecipientsV31 []RecipientV31

// AttachmentV31 struct represent a content attachment
type AttachmentV31 struct {
	ContentType   string `json:"ContentType,omitempty"`
	Base64Content string `json:"Base64Content,omitempty"`
	Filename      string `json:"Filename,omitempty"`
}

// AttachmentsV31 collection
type AttachmentsV31 []AttachmentV31

// InlinedAttachmentV31 struct represent the content of an inline attachement
type InlinedAttachmentV31 struct {
	AttachmentV31 `json:",omitempty"`
	ContentID     string `json:"ContentID,omitempty"`
}

// InlinedAttachmentsV31 collection
type InlinedAttachmentsV31 []InlinedAttachmentV31

// ErrorInfoV31 struct
type ErrorInfoV31 struct {
	Identifier string `json:"ErrorIdentifier,omitempty"`
	Info       string `json:"ErrorInfo"`
	Message    string `json:"ErrorMessage"`
	StatusCode int    `json:"StatusCode"`
}

func (err *ErrorInfoV31) Error() string {
	raw, _ := json.Marshal(err)
	return string(raw)
}

// APIErrorDetailsV31 contains the information details describing a specific error
type APIErrorDetailsV31 struct {
	ErrorClass     string
	ErrorMessage   string
	ErrorRelatedTo []string
	StatusCode     int
}

// APIFeedbackErrorV31 struct is composed of an error definition and the payload associated
type APIFeedbackErrorV31 struct {
	Errors []APIErrorDetailsV31
}

// APIFeedbackErrorsV31 defines the error when a validation error is being sent by the API
type APIFeedbackErrorsV31 struct {
	Messages []APIFeedbackErrorV31
}

func (api *APIFeedbackErrorsV31) Error() string {
	raw, _ := json.Marshal(api)
	return string(raw)
}

// GeneratedMessageV31 contains info to retrieve a generated email
type GeneratedMessageV31 struct {
	Email       string
	MessageUUID string
	MessageID   int64
	MessageHref string
}

// ResultV31 bundles the results of a sent email
type ResultV31 struct {
	Status   string
	CustomID string `json:",omitempty"`
	To       []GeneratedMessageV31
	Cc       []GeneratedMessageV31
	Bcc      []GeneratedMessageV31
}

// ResultsV31 bundles several results when several mails are sent
type ResultsV31 struct {
	ResultsV31 []ResultV31 `json:"Messages"`
}
