-
Notifications
You must be signed in to change notification settings - Fork 11
/
testing.go
163 lines (141 loc) · 4.17 KB
/
testing.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
// Package test provides some utilities for integration testing at endpoint levels.
package test
import (
"crypto/rand"
_ "embed"
"fmt"
"log"
"net/http"
"net/http/httptest"
"net/url"
"os"
"path/filepath"
"strings"
"testing"
"github.com/labstack/echo/v4"
"github.com/natsukagami/kjudge/db"
"github.com/natsukagami/kjudge/server"
"github.com/natsukagami/kjudge/server/auth"
"github.com/pkg/errors"
)
// the embedded test data.
//
//go:embed data.sql
var testData string
// Content of the kjudge.db containing the full test database.
var databaseContent []byte
func init() {
tmpDir, err := os.MkdirTemp(os.TempDir(), "kjudge_test")
if err != nil {
log.Panic("cannot create temp dir:", err)
}
defer os.RemoveAll(tmpDir)
dbFile := filepath.Join(tmpDir, "kjudge.db")
tmpDb, err := db.New(dbFile)
if err != nil {
log.Panic("cannot create temp database:", errors.WithStack(err))
}
if _, err := tmpDb.Exec(testData); err != nil {
log.Panic("Cannot import test data:", err)
}
if err := tmpDb.Close(); err != nil {
log.Panic("cannot create temp database:", errors.WithStack(err))
}
dbFileContent, err := os.ReadFile(dbFile)
if err != nil {
log.Panic("cannot read temp database:", errors.WithStack(err))
}
databaseContent = dbFileContent
}
// TestServer wraps Server and adds some fancy stuff.
type TestServer struct {
*server.Server
DB *db.DB
}
// NewDB creates and populates a test database.
func NewDB(t *testing.T) *db.DB {
tmpDbFile := filepath.Join(t.TempDir(), "kjudge.db")
if err := os.WriteFile(tmpDbFile, databaseContent, 0644); err != nil {
t.Fatal("cannot create temp database:", errors.WithStack(err))
}
tmpDb, err := db.New(tmpDbFile)
if err != nil {
t.Fatal(errors.WithStack(err))
}
return tmpDb
}
// NewServer creates a new kjudge server running on a test database.
func NewServer(t *testing.T) *TestServer {
// generate an admin key
adminKey := make([]byte, 32)
if _, err := rand.Read(adminKey); err != nil {
t.Fatal("generating admin key:", err)
}
t.Setenv(auth.AdminKeyEnv, fmt.Sprintf("%x", adminKey))
db := NewDB(t)
s, err := server.New(db)
if err != nil {
t.Fatal(err)
}
return &TestServer{Server: s, DB: db}
}
// PostForm fires a new POST request with a form body.
func (ts *TestServer) PostForm(t *testing.T, path string, body url.Values) *http.Request {
req := httptest.NewRequest(http.MethodPost, path, strings.NewReader(body.Encode()))
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationForm)
return req
}
// Get fires a new GET request with URL queries.
func (ts *TestServer) Get(t *testing.T, path string, queries url.Values) *http.Request {
if len(queries) > 0 {
path = path + "?" + queries.Encode()
}
req := httptest.NewRequest(http.MethodGet, path, nil)
return req
}
// Serve serves a HTTP request and returns its response.
func (ts *TestServer) Serve(req *http.Request, opts ...ReqOpt) *http.Response {
for _, opt := range opts {
opt(req)
}
rec := httptest.NewRecorder()
ts.ServeHTTP(rec, req)
return rec.Result()
}
// WithMisaka logs in as Misaka for the next request.
func (ts *TestServer) WithMisaka(t *testing.T) ReqOpt {
// Perform the log in.
form := url.Values{}
form.Set("id", "misaka")
form.Set("password", "misaka")
resp := ts.Serve(ts.PostForm(t, "/user/login", form))
if resp.StatusCode >= 400 {
t.Fatalf("Cannot login as misaka: got code %d", resp.StatusCode)
}
cookies := resp.Cookies()
if len(cookies) != 1 {
t.Fatalf("Cannot login as misaka: expect one cookie, got %#v", cookies)
}
return func(req *http.Request) {
req.AddCookie(cookies[0])
}
}
// WithAdmin logs in with the admin panel cookie for the next request.
func (ts *TestServer) WithAdmin(t *testing.T) ReqOpt {
// Perform the log in.
form := url.Values{}
form.Set("key", os.Getenv(auth.AdminKeyEnv))
resp := ts.Serve(ts.PostForm(t, "/admin/login", form))
if resp.StatusCode >= 400 {
t.Fatalf("Cannot login to admin panel: got code %d", resp.StatusCode)
}
cookies := resp.Cookies()
if len(cookies) != 1 {
t.Fatalf("Cannot login to admin panel: expect one cookie, got %#v", cookies)
}
return func(req *http.Request) {
req.AddCookie(cookies[0])
}
}
// ReqOpt is an option for sending requests.
type ReqOpt func(*http.Request)