eintopf.info@v0.13.16/test/mailhog.go (about)

     1  // Copyright (C) 2024 The Eintopf authors
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Affero General Public License as
     5  // published by the Free Software Foundation, either version 3 of the
     6  // License, or (at your option) any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU Affero General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Affero General Public License
    14  // along with this program.  If not, see <https://www.gnu.org/licenses/>.
    15  
    16  package test
    17  
    18  import (
    19  	"encoding/json"
    20  	"fmt"
    21  	"io"
    22  	"net/http"
    23  	"net/smtp"
    24  	"net/url"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/ory/dockertest/v3"
    29  )
    30  
    31  func RunMailHog(t *testing.T, pool *dockertest.Pool) (string, MailHogClient) {
    32  	// n, err := pool.CreateNetwork("mailhog")
    33  	// if err != nil {
    34  	// 	t.Fatalf("Cloud not create mailhog network: %s", err)
    35  	// }
    36  	mailhog, err := pool.RunWithOptions(&dockertest.RunOptions{
    37  		Repository: "mailhog/mailhog",
    38  		Tag:        "latest",
    39  		// Networks:   []*dockertest.Network{n},
    40  	})
    41  	if err != nil {
    42  		t.Fatalf("Could not start resource: %s", err)
    43  	}
    44  	t.Cleanup(func() {
    45  		if err := pool.Purge(mailhog); err != nil {
    46  			t.Fatalf("Could not purge resource: %s", err)
    47  		}
    48  	})
    49  	client := MailHogClient{URL: fmt.Sprintf("http://localhost:%s", mailhog.GetPort("8025/tcp"))}
    50  
    51  	// exponential backoff-retry, because the application in the container might not be ready to accept connections yet
    52  	if err := pool.Retry(func() error {
    53  		_, err := client.V1GetMessages()
    54  		return err
    55  	}); err != nil {
    56  		t.Fatalf("Could not connect to mailhog: %s", err)
    57  	}
    58  
    59  	smtpHost := fmt.Sprintf("localhost:%s", mailhog.GetPort("1025/tcp"))
    60  	// stunnel, err := pool.RunWithOptions(&dockertest.RunOptions{
    61  	// 	Repository: "dweomer/stunnel@sha256",
    62  	// 	// Tag:        "sha256:2d883cdcc3c65298a4ec24e07ba6415304d4b579c0826e5d9e0cadad6343ce43",
    63  	// 	Tag: "2d883cdcc3c65298a4ec24e07ba6415304d4b579c0826e5d9e0cadad6343ce43",
    64  	// 	Env: []string{
    65  	// 		"STUNNEL_SERVICE=smtps",
    66  	// 		"STUNNEL_ACCEPT=465",
    67  	// 		fmt.Sprintf("STUNNEL_CONNECT=%s:%s", mailhog.GetIPInNetwork(n), mailhog.GetPort("1025/tcp")),
    68  	// 	},
    69  	// 	ExposedPorts: []string{"465"},
    70  	// 	Networks:     []*dockertest.Network{n},
    71  	// })
    72  	// if err != nil {
    73  	// 	t.Fatalf("Could not start resource: %s", err)
    74  	// }
    75  	// t.Cleanup(func() {
    76  	// 	if err := pool.Purge(stunnel); err != nil {
    77  	// 		t.Fatalf("Could not purge resource: %s", err)
    78  	// 	}
    79  	// })
    80  	// smtpHost := fmt.Sprintf("localhost:%s", stunnel.GetPort("465/tcp"))
    81  	// log.Println("hre", smtpHost)
    82  	// // exponential backoff-retry, because the application in the container might not be ready to accept connections yet
    83  	// if err := pool.Retry(func() error {
    84  	// 	return dialEmail(smtpHost)
    85  	// }); err != nil {
    86  	// 	t.Fatalf("Could not connect to mailhog via stunnel: %s", err)
    87  	// }
    88  	// // time.Sleep(time.Second * 5)
    89  	// log.Println("hrea")
    90  	// t.Fatal("here")
    91  
    92  	return smtpHost, client
    93  }
    94  
    95  func dialEmail(smtpHost string) error {
    96  	_, err := smtp.Dial(smtpHost)
    97  	if err != nil {
    98  		return err
    99  	}
   100  	// host, _, _ := net.SplitHostPort(smtpHost)
   101  	// tlsconfig := &tls.Config{InsecureSkipVerify: true, ServerName: host}
   102  	// if err := c.StartTLS(tlsconfig); err != nil {
   103  	// 	return err
   104  	// }
   105  	// if err := c.StartTLS(tlsconfig); err != nil {
   106  	// 	return err
   107  	// }
   108  	//
   109  	// w, err := c.Data()
   110  	// if err != nil {
   111  	// 	return err
   112  	// }
   113  	// if _, err := w.Write([]byte{}); err != nil {
   114  	// 	return err
   115  	// }
   116  	// if err := w.Close(); err != nil {
   117  	// 	return err
   118  	// }
   119  
   120  	// return c.Quit()
   121  	return nil
   122  }
   123  
   124  func (c MailHogClient) WantLastEmailToBe(t *testing.T, from string, to string, subject string, body string) {
   125  	t.Helper()
   126  
   127  	messages, err := c.V1GetMessages()
   128  	if err != nil {
   129  		t.Error(err)
   130  	}
   131  
   132  	if len(messages) == 0 {
   133  		t.Error("no emails")
   134  		return
   135  	}
   136  
   137  	m := messages[len(messages)-1]
   138  	if m.Raw.From != from {
   139  		t.Errorf("want FROM to be %s, got %s", from, messages[0].Raw.From)
   140  	}
   141  	if m.Raw.To[0] != to {
   142  		t.Errorf("want TO to be %s, got %s", to, messages[0].Raw.To[0])
   143  	}
   144  	if m.Content.Headers["Subject"][0] != subject {
   145  		t.Errorf("want SUBJECT to be '%s', got '%s'", subject, messages[0].Content.Headers["Subject"][0])
   146  	}
   147  	if m.Content.Body != body {
   148  		t.Errorf("want BODY to be '%s', got '%s'", body, messages[0].Content.Body)
   149  	}
   150  }
   151  
   152  func (c MailHogClient) GetLastEmail() (MailHogMessage, error) {
   153  	messages, err := c.V1GetMessages()
   154  	if err != nil {
   155  		return MailHogMessage{}, err
   156  	}
   157  
   158  	if len(messages) == 0 {
   159  		return MailHogMessage{}, fmt.Errorf("no emails received")
   160  	}
   161  
   162  	return messages[len(messages)-1], nil
   163  }
   164  
   165  type MailHogMessage struct {
   166  	ID   string
   167  	From struct {
   168  		Relays  string
   169  		Mailbox string
   170  		Domain  string
   171  		Params  string
   172  	}
   173  	To []struct {
   174  		Relays  string
   175  		Mailbox string
   176  		Domain  string
   177  		Params  string
   178  	}
   179  	Content struct {
   180  		Headers map[string][]string
   181  		Body    string
   182  		Size    int
   183  		Mime    string
   184  	}
   185  	Created time.Time
   186  	Mime    string
   187  	Raw     struct {
   188  		From string
   189  		To   []string
   190  		Data string
   191  		Helo string
   192  	}
   193  }
   194  
   195  type MailHogClient struct {
   196  	URL string
   197  }
   198  
   199  func (m MailHogClient) V1GetMessages() ([]MailHogMessage, error) {
   200  	path, err := url.JoinPath(m.URL, "api/v1/messages")
   201  	if err != nil {
   202  		return nil, err
   203  	}
   204  	resp, err := http.Get(path)
   205  	if err != nil {
   206  		return nil, err
   207  	}
   208  	data, err := io.ReadAll(resp.Body)
   209  	if err != nil {
   210  		return nil, err
   211  	}
   212  	var messages []MailHogMessage
   213  	err = json.Unmarshal(data, &messages)
   214  	if err != nil {
   215  		return nil, err
   216  	}
   217  	return messages, nil
   218  }