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 }