Recently Written · git

recentlywritten

my writings (done recently)

git clone https://github.com/equwal/recentlywritten

Log | Files | Refs


accounts/mail_test.go (3322 bytes)

1 package main
2 
3 import (
4 	"bytes"
5 	"io"
6 	"mime"
7 	"mime/quotedprintable"
8 	"net/mail"
9 	"strings"
10 	"testing"
11 	"time"
12 	"unicode/utf8"
13 
14 	"pgregory.net/rapid"
15 )
16 
17 var testFrom = &mail.Address{Name: "Recently Written", Address: "posts@example.com"}
18 
19 // TestFormatMessageProperty checks that any subject and body give a
20 // message that parses, has exactly the expected headers, and decodes back
21 // to the same subject and body.
22 func TestFormatMessageProperty(t *testing.T) {
23 	rapid.Check(t, func(t *rapid.T) {
24 		valid := rapid.String().Filter(utf8.ValidString)
25 		m := message{
26 			To:          "reader@example.com",
27 			Subject:     valid.Draw(t, "subject"),
28 			Body:        valid.Draw(t, "body"),
29 			Unsubscribe: rapid.SampledFrom([]string{"", "https://example.com/account/unsubscribe?t=ABC"}).Draw(t, "unsub"),
30 		}
31 		data, err := formatMessage(testFrom, m, time.Unix(0, 0))
32 		if err != nil {
33 			t.Fatal(err)
34 		}
35 		msg, err := mail.ReadMessage(bytes.NewReader(data))
36 		if err != nil {
37 			t.Fatalf("message does not parse: %v\n%q", err, data)
38 		}
39 		wantHeaders := 8
40 		if m.Unsubscribe != "" {
41 			wantHeaders += 2
42 		}
43 		if len(msg.Header) != wantHeaders {
44 			t.Fatalf("got headers %v, want %d (a line break in the subject must not add one)", msg.Header, wantHeaders)
45 		}
46 		subject, err := new(mime.WordDecoder).DecodeHeader(msg.Header.Get("Subject"))
47 		if err != nil {
48 			t.Fatal(err)
49 		}
50 		wantSubject := strings.NewReplacer("\r", " ", "\n", " ").Replace(m.Subject)
51 		// net/mail removes spaces at the two ends of a plain header value,
52 		// but not in an encoded word.
53 		if strings.Trim(subject, " \t") != strings.Trim(wantSubject, " \t") {
54 			t.Fatalf("subject: got %q, want %q", subject, wantSubject)
55 		}
56 		body, err := io.ReadAll(quotedprintable.NewReader(msg.Body))
57 		if err != nil {
58 			t.Fatal(err)
59 		}
60 		lf := strings.NewReplacer("\r\n", "\n", "\r", "\n")
61 		if got := lf.Replace(string(body)); got != lf.Replace(m.Body) {
62 			t.Fatalf("body: got %q, want %q", got, m.Body)
63 		}
64 		if got := msg.Header.Get("List-Unsubscribe"); m.Unsubscribe != "" && got != "<"+m.Unsubscribe+">" {
65 			t.Fatalf("List-Unsubscribe = %q", got)
66 		}
67 	})
68 }
69 
70 func TestFormatMessageRejectsBadAddress(t *testing.T) {
71 	if _, err := formatMessage(testFrom, message{To: "not an address"}, time.Now()); err == nil {
72 		t.Fatal("no error for a bad address")
73 	}
74 }
75 
76 func TestLoadConfigNamesMissingVariables(t *testing.T) {
77 	env := map[string]string{"RW_BASE_URL": "https://example.com/", "SMTP_PORT": "2525"}
78 	_, err := loadConfig(func(k string) string { return env[k] }, false)
79 	if err == nil || err.Error() != "set these environment variables: RW_SITE_DIR, RW_DB_PATH, RW_MAIL_FROM, SMTP_HOST" {
80 		t.Fatalf("err = %v", err)
81 	}
82 
83 	env["RW_SITE_DIR"], env["RW_DB_PATH"] = "/srv/site", "/var/lib/rw/rw.db"
84 	env["RW_MAIL_FROM"], env["SMTP_HOST"] = "Recently Written <posts@example.com>", "smtp.example.com"
85 	c, err := loadConfig(func(k string) string { return env[k] }, false)
86 	if err != nil {
87 		t.Fatal(err)
88 	}
89 	if c.BaseURL != "https://example.com" || c.SMTPAddr != "smtp.example.com:2525" ||
90 		c.MailFrom.Address != "posts@example.com" || c.Listen != "127.0.0.1:8081" {
91 		t.Fatalf("config = %+v", c)
92 	}
93 
94 	delete(env, "SMTP_HOST")
95 	if _, err := loadConfig(func(k string) string { return env[k] }, true); err != nil {
96 		t.Fatalf("dev mode needs no SMTP: %v", err)
97 	}
98 }