@@ -20,6 +20,7 @@ import ( |
| 20 | 20 | "time" |
| 21 | 21 | |
| 22 | 22 | "github.com/go-chi/chi/v5" |
| 23 | + "github.com/jackc/pgx/v5/pgxpool" |
| 23 | 24 | "github.com/justinas/nosurf" |
| 24 | 25 | |
| 25 | 26 | "github.com/tenseleyFlow/shithub/internal/auth/email" |
@@ -67,6 +68,16 @@ func (c *captureSender) reset() { |
| 67 | 68 | var fastArgon = password.Params{Memory: 16 * 1024, Time: 1, Threads: 1, SaltLen: 16, KeyLen: 32} |
| 68 | 69 | |
| 69 | 70 | func newTestServer(t *testing.T, requireVerify bool) (*httptest.Server, *captureSender) { |
| 71 | + srv, _, captor := newTestServerWithPool(t, requireVerify) |
| 72 | + return srv, captor |
| 73 | +} |
| 74 | + |
| 75 | +// newTestServerWithPool is identical to newTestServer but also exposes |
| 76 | +// the underlying pool so tests that need to manipulate DB state (e.g. |
| 77 | +// backdating timestamps) can do so against the SAME database the server |
| 78 | +// is reading from. Use the simpler newTestServer when no DB poking is |
| 79 | +// needed. |
| 80 | +func newTestServerWithPool(t *testing.T, requireVerify bool) (*httptest.Server, *pgxpool.Pool, *captureSender) { |
| 70 | 81 | t.Helper() |
| 71 | 82 | pool := dbtest.NewTestDB(t) |
| 72 | 83 | |
@@ -151,7 +162,7 @@ func newTestServer(t *testing.T, requireVerify bool) (*httptest.Server, *capture |
| 151 | 162 | |
| 152 | 163 | srv := httptest.NewServer(r) |
| 153 | 164 | t.Cleanup(srv.Close) |
| 154 | | - return srv, cap |
| 165 | + return srv, pool, cap |
| 155 | 166 | } |
| 156 | 167 | |
| 157 | 168 | // authTemplatesFS returns a minimal templates FS sufficient for the auth |
@@ -180,6 +191,7 @@ func authTemplatesFS() fs.FS { |
| 180 | 191 | emailsTpl := `{{ define "page" }}<h1>Emails</h1>{{ with .Error }}<p class=error>{{.}}</p>{{ end }}{{ with .Success }}<p class=notice>{{.}}</p>{{ end }}<form action="/settings/emails" method=POST><input name=csrf_token value="{{.CSRFToken}}"></form>EMAILS={{ range .Emails }}{{.ID}}:{{.Email}}:p={{.IsPrimary}}:v={{.Verified}};{{ end }}{{ end }}` |
| 181 | 192 | notifTpl := `{{ define "page" }}<h1>Notifications</h1>{{ with .Success }}<p class=notice>{{.}}</p>{{ end }}<form action="/settings/notifications" method=POST><input name=csrf_token value="{{.CSRFToken}}">CHANNELS={{ range .Channels }}{{.Key}}:e={{.Enabled}}:r={{.Required}};{{ end }}</form>{{ end }}` |
| 182 | 193 | sessTpl := `{{ define "page" }}<h1>Sessions</h1>{{ with .Success }}<p class=notice>{{.}}</p>{{ end }}<form action="/settings/sessions/logout-everywhere" method=POST><input name=csrf_token value="{{.CSRFToken}}">UA={{.UserAgent}};</form>{{ end }}` |
| 194 | + dangerTpl := `{{ define "page" }}<h1>Delete</h1>{{ with .Error }}<p class=error>{{.}}</p>{{ end }}<form action="/settings/danger" method=POST><input name=csrf_token value="{{.CSRFToken}}">USER={{.Username}};GRACE={{.GraceWindowDays}};</form>{{ end }}` |
| 183 | 195 | errorPage := `{{ define "page" }}<h1>{{.Status}} {{.StatusText}}</h1><p>{{.Message}}</p>{{ end }}` |
| 184 | 196 | return fstest.MapFS{ |
| 185 | 197 | "_layout.html": {Data: []byte(layout)}, |
@@ -202,6 +214,7 @@ func authTemplatesFS() fs.FS { |
| 202 | 214 | "settings/emails.html": {Data: []byte(emailsTpl)}, |
| 203 | 215 | "settings/notifications.html": {Data: []byte(notifTpl)}, |
| 204 | 216 | "settings/sessions.html": {Data: []byte(sessTpl)}, |
| 217 | + "settings/danger.html": {Data: []byte(dangerTpl)}, |
| 205 | 218 | "errors/404.html": {Data: []byte(errorPage)}, |
| 206 | 219 | "errors/403.html": {Data: []byte(errorPage)}, |
| 207 | 220 | "errors/429.html": {Data: []byte(errorPage)}, |