Recently Written · git

recentlywritten

my writings (done recently)

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

Log | Files | Refs


accounts/helpers_test.go (2881 bytes)

1 package main
2 
3 import (
4 	"errors"
5 	"io"
6 	"log"
7 	"os"
8 	"path/filepath"
9 	"strings"
10 	"sync"
11 	"testing"
12 	"time"
13 )
14 
15 // captureMailer keeps each message. If fail is true, Send returns an error
16 // and keeps nothing.
17 type captureMailer struct {
18 	mu   sync.Mutex
19 	sent []message
20 	fail bool
21 }
22 
23 func (c *captureMailer) Send(m message) error {
24 	c.mu.Lock()
25 	defer c.mu.Unlock()
26 	if c.fail {
27 		return errors.New("smtp is down")
28 	}
29 	c.sent = append(c.sent, m)
30 	return nil
31 }
32 
33 // clock is a time source that a test moves forward.
34 type clock struct{ t time.Time }
35 
36 func (c *clock) now() time.Time { return c.t }
37 
38 type testEnv struct {
39 	app   *app
40 	mail  *captureMailer
41 	clock *clock
42 }
43 
44 // fataler is the part of testing.TB that the helpers use. *rapid.T has
45 // these methods too.
46 type fataler interface {
47 	Helper()
48 	Fatal(args ...any)
49 	Fatalf(format string, args ...any)
50 }
51 
52 // newTestEnv makes an app with a :memory: database and a site directory
53 // that has the posts in index.
54 func newTestEnv(t *testing.T, index []Post) *testEnv {
55 	t.Helper()
56 	db, err := openDB(":memory:")
57 	if err != nil {
58 		t.Fatal(err)
59 	}
60 	t.Cleanup(func() { db.Close() })
61 	site := t.TempDir()
62 	if err := os.MkdirAll(filepath.Join(site, "postdata"), 0o755); err != nil {
63 		t.Fatal(err)
64 	}
65 	e := &testEnv{
66 		mail:  &captureMailer{},
67 		clock: &clock{t: time.Date(2026, 9, 1, 12, 0, 0, 0, time.UTC)},
68 	}
69 	e.app = &app{
70 		cfg:  config{BaseURL: "https://example.com", SiteDir: site},
71 		db:   db,
72 		mail: e.mail,
73 		log:  log.New(io.Discard, "", 0),
74 	}
75 	e.app.now = e.clock.now
76 	e.writeIndex(t, index)
77 	return e
78 }
79 
80 // writeIndex writes postdata/index.tsv and a body for each post, as
81 // build.sh does.
82 func (e *testEnv) writeIndex(t fataler, posts []Post) {
83 	t.Helper()
84 	var b strings.Builder
85 	for _, p := range posts {
86 		b.WriteString(p.Date + "\t" + p.Slug + "\t" + strings.Join(p.Tags, " ") + "\t" + p.Title + "\n")
87 		body := "<p>Body of " + p.Slug + " &amp; more.</p>\n"
88 		if err := os.WriteFile(filepath.Join(e.app.cfg.SiteDir, "postdata", p.Slug+".html"), []byte(body), 0o644); err != nil {
89 			t.Fatal(err)
90 		}
91 	}
92 	if err := os.WriteFile(filepath.Join(e.app.cfg.SiteDir, "postdata", "index.tsv"), []byte(b.String()), 0o644); err != nil {
93 		t.Fatal(err)
94 	}
95 }
96 
97 // signUp makes a verified user for email and returns it.
98 func (e *testEnv) signUp(t fataler, email string) user {
99 	t.Helper()
100 	tok, err := createLoginToken(e.app.db, email, e.clock.now())
101 	if err != nil || tok == "" {
102 		t.Fatalf("createLoginToken: %q, %v", tok, err)
103 	}
104 	u, err := consumeLoginToken(e.app.db, tok, e.clock.now())
105 	if err != nil {
106 		t.Fatal(err)
107 	}
108 	return u
109 }
110 
111 func (e *testEnv) advance(d time.Duration) { e.clock.t = e.clock.t.Add(d) }
112 
113 var oldPosts = []Post{
114 	{Date: "2025-09-11", Slug: "old-article", Tags: []string{"articles"}, Title: "Old article"},
115 	{Date: "2025-09-06", Slug: "old-project", Tags: []string{"projects"}, Title: "Old project"},
116 }