package server import ( "bytes" "encoding/json" "fmt" "io" "net/http" "net/http/httptest" "net/url" "os" "strings" "testing" "github.com/axllent/mailpit/config" "github.com/axllent/mailpit/internal/logger" "github.com/axllent/mailpit/internal/storage" "github.com/axllent/mailpit/server/apiv1" "github.com/jhillyerd/enmime" ) var ( putDataStruct struct { Read bool IDs []string } ) func TestAPIv1Messages(t *testing.T) { setup() defer storage.Close() r := apiRoutes() ts := httptest.NewServer(r) defer ts.Close() m, err := fetchMessages(ts.URL + "/api/v1/messages") if err != nil { t.Error(err.Error()) } // check count of empty database assertStatsEqual(t, ts.URL+"/api/v1/messages", 0, 0) // insert 100 t.Log("Insert 100 messages") insertEmailData(t) assertStatsEqual(t, ts.URL+"/api/v1/messages", 100, 100) m, err = fetchMessages(ts.URL + "/api/v1/messages") if err != nil { t.Error(err.Error()) } // read first 10 messages t.Log("Read first 10 messages including raw & headers") for idx, msg := range m.Messages { if idx == 10 { break } if _, err := clientGet(ts.URL + "/api/v1/message/" + msg.ID); err != nil { t.Error(err.Error()) } // get RAW if _, err := clientGet(ts.URL + "/api/v1/message/" + msg.ID + "/raw"); err != nil { t.Error(err.Error()) } // get headers if _, err := clientGet(ts.URL + "/api/v1/message/" + msg.ID + "/headers"); err != nil { t.Error(err.Error()) } } // 10 should be marked as read assertStatsEqual(t, ts.URL+"/api/v1/messages", 90, 100) // delete all t.Log("Delete all messages") _, err = clientDelete(ts.URL+"/api/v1/messages", "{}") if err != nil { t.Errorf("Expected nil, received %s", err.Error()) } assertStatsEqual(t, ts.URL+"/api/v1/messages", 0, 0) } func TestAPIv1ToggleReadStatus(t *testing.T) { setup() defer storage.Close() r := apiRoutes() ts := httptest.NewServer(r) defer ts.Close() m, err := fetchMessages(ts.URL + "/api/v1/messages") if err != nil { t.Error(err.Error()) } // check count of empty database assertStatsEqual(t, ts.URL+"/api/v1/messages", 0, 0) // insert 100 t.Log("Insert 100 messages") insertEmailData(t) assertStatsEqual(t, ts.URL+"/api/v1/messages", 100, 100) m, err = fetchMessages(ts.URL + "/api/v1/messages") if err != nil { t.Error(err.Error()) } // read first 10 IDs t.Log("Get first 10 IDs") putIDS := []string{} for idx, msg := range m.Messages { if idx == 10 { break } // store for later putIDS = append(putIDS, msg.ID) } assertStatsEqual(t, ts.URL+"/api/v1/messages", 100, 100) // mark first 10 as unread t.Log("Mark first 10 as read") putData := putDataStruct putData.Read = true putData.IDs = putIDS j, err := json.Marshal(putData) if err != nil { t.Error(err.Error()) } _, err = clientPut(ts.URL+"/api/v1/messages", string(j)) if err != nil { t.Error(err.Error()) } assertStatsEqual(t, ts.URL+"/api/v1/messages", 90, 100) // mark first 10 as read t.Log("Mark first 10 as unread") putData.Read = false j, err = json.Marshal(putData) if err != nil { t.Error(err.Error()) } _, err = clientPut(ts.URL+"/api/v1/messages", string(j)) if err != nil { t.Error(err.Error()) } assertStatsEqual(t, ts.URL+"/api/v1/messages", 100, 100) // mark all as read putData.Read = true putData.IDs = []string{} j, err = json.Marshal(putData) if err != nil { t.Error(err.Error()) } t.Log("Mark all read") _, err = clientPut(ts.URL+"/api/v1/messages", string(j)) if err != nil { t.Error(err.Error()) } assertStatsEqual(t, ts.URL+"/api/v1/messages", 0, 100) } func TestAPIv1Search(t *testing.T) { setup() defer storage.Close() r := apiRoutes() ts := httptest.NewServer(r) defer ts.Close() // insert 100 t.Log("Insert 100 messages & tag") insertEmailData(t) assertStatsEqual(t, ts.URL+"/api/v1/messages", 100, 100) // search assertSearchEqual(t, ts.URL+"/api/v1/search", "from-1@example.com", 1) assertSearchEqual(t, ts.URL+"/api/v1/search", "from:from-1@example.com", 1) assertSearchEqual(t, ts.URL+"/api/v1/search", "-from:from-1@example.com", 99) assertSearchEqual(t, ts.URL+"/api/v1/search", "-FROM:FROM-1@EXAMPLE.COM", 99) assertSearchEqual(t, ts.URL+"/api/v1/search", "to:from-1@example.com", 0) assertSearchEqual(t, ts.URL+"/api/v1/search", "from:@example.com", 100) assertSearchEqual(t, ts.URL+"/api/v1/search", "subject:\"Subject line\"", 100) assertSearchEqual(t, ts.URL+"/api/v1/search", "subject:\"SUBJECT LINE 17 END\"", 1) assertSearchEqual(t, ts.URL+"/api/v1/search", "!thisdoesnotexist", 100) assertSearchEqual(t, ts.URL+"/api/v1/search", "-ThisDoesNotExist", 100) assertSearchEqual(t, ts.URL+"/api/v1/search", "thisdoesnotexist", 0) assertSearchEqual(t, ts.URL+"/api/v1/search", "tag:\"Test tag 065\"", 1) assertSearchEqual(t, ts.URL+"/api/v1/search", "tag:\"TEST TAG 065\"", 1) assertSearchEqual(t, ts.URL+"/api/v1/search", "!tag:\"Test tag 023\"", 99) } func TestAPIv1Send(t *testing.T) { setup() defer storage.Close() r := apiRoutes() ts := httptest.NewServer(r) defer ts.Close() jsonData := `{ "From": { "Email": "john@example.com", "Name": "John Doe" }, "To": [ { "Email": "jane@example.com", "Name": "Jane Doe" } ], "Cc": [ { "Email": "manager1@example.com", "Name": "Manager 1" }, { "Email": "manager2@example.com", "Name": "Manager 2" } ], "Bcc": ["jack@example.com"], "Headers": { "X-IP": "1.2.3.4" }, "Subject": "Mailpit message via the HTTP API", "Text": "This is the text body", "HTML": "
Mailpit is awesome!
", "Attachments": [ { "Content": "VGhpcyBpcyBhIHBsYWluIHRleHQgYXR0YWNobWVudA==", "Filename": "Attached File.txt" }, { "Content": "iVBORw0KGgoAAAANSUhEUgAAAEEAAAA8CAMAAAAOlSdoAAAACXBIWXMAAAHrAAAB6wGM2bZBAAAAS1BMVEVHcEwRfnUkZ2gAt4UsSF8At4UtSV4At4YsSV4At4YsSV8At4YsSV4At4YsSV4sSV4At4YsSV4At4YtSV4At4YsSV4At4YtSV8At4YsUWYNAAAAGHRSTlMAAwoXGiktRE5dbnd7kpOlr7zJ0d3h8PD8PCSRAAACWUlEQVR42pXT4ZaqIBSG4W9rhqQYocG+/ys9Y0Z0Br+x3j8zaxUPewFh65K+7yrIMeIY4MT3wPfEJCidKXEMnLaVkxDiELiMz4WEOAZSFghxBIypCOlKiAMgXfIqTnBgSm8CIQ6BImxEUxEckClVQiHGj4Ba4AQHikAIClwTE9KtIghAhUJwoLkmLnCiAHJLRKgIMsEtVUKbBUIwoAg2C4QgQBE6l4VCnApBgSKYLLApCnCa0+96AEMW2BQcmC+Pr3nfp7o5Exy49gIADcIqUELGfeA+bp93LmAJp8QJoEcN3C7NY3sbVANixMyI0nku20/n5/ZRf3KI2k6JEDWQtxcbdGuAqu3TAXG+/799Oyyas1B1MnMiA+XyxHp9q0PUKGPiRAau1fZbLRZV09wZcT8/gHk8QQAxXn8VgaDqcUmU6O/r28nbVwXAqca2mRNtPAF5+zoP2MeN9Fy4NgC6RfcbgE7XITBRYTtOE3U3C2DVff7pk+PkUxgAbvtnPXJaD6DxulMLwOhPS/M3MQkgg1ZFrIXnmfaZoOfpKiFgzeZD/WuKqQEGrfJYkyWf6vlG3xUgTuscnkNkQsb599q124kdpMUjCa/XARHs1gZymVtGt3wLkiFv8rUgTxitYCex5EVGec0Y9VmoDTFBSQte2TfXGXlf7hbdaUM9Sk7fisEN9qfBBTK+FZcvM9fQSdkl2vj4W2oX/bRogO3XasiNH7R0eW7fgRM834ImTg+Lg6BEnx4vz81rhr+MYPBBQg1v8GndEOrthxaCTxNAOut8WKLGZQl+MPz88Q9tAO/hVuSeqQAAAABJRU5ErkJggg==", "Filename": "logo.png", "ContentID": "inline-cid", "ContentType": "overridden/type" } ], "ReplyTo": [ { "Email": "secretary@example.com", "Name": "Secretary" } ], "Tags": [ "Tag 1", "Tag 2" ] }` t.Log("Sending message via HTTP API") b, err := clientPost(ts.URL+"/api/v1/send", jsonData) if err != nil { t.Errorf("Expected nil, received %s", err.Error()) } resp := apiv1.SendMessageConfirmation{} if err := json.Unmarshal(b, &resp); err != nil { t.Error(err.Error()) return } t.Logf("Fetching response for message %s", resp.ID) msg, err := fetchMessage(ts.URL + "/api/v1/message/" + resp.ID) if err != nil { t.Error(err.Error()) } t.Logf("Testing response for message %s", resp.ID) assertEqual(t, `Mailpit message via the HTTP API`, msg.Subject, "wrong subject") assertEqual(t, `This is the text body`, msg.Text, "wrong text") assertEqual(t, `Mailpit is awesome!
`, msg.HTML, "wrong HTML") assertEqual(t, `"John Doe"