mirror of
https://github.com/airlabspl/uptimemonitor.git
synced 2025-08-14 20:29:16 +02:00
initial commit
This commit is contained in:
349
test/test_case.go
Normal file
349
test/test_case.go
Normal file
@@ -0,0 +1,349 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
"uptimemonitor"
|
||||
"uptimemonitor/handler"
|
||||
"uptimemonitor/pkg/testutil"
|
||||
"uptimemonitor/router"
|
||||
"uptimemonitor/service"
|
||||
"uptimemonitor/store"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
var TestWebhookCalledCount int64
|
||||
var TestWebhookBody string
|
||||
var ExpectedWebhookBody string
|
||||
var ExpectedWebhookHeaderKey string
|
||||
var ExpectedWebhookHeaderValue string
|
||||
|
||||
type TestCase struct {
|
||||
T *testing.T
|
||||
Server *httptest.Server
|
||||
Client *http.Client
|
||||
Store *store.Store
|
||||
User *uptimemonitor.User
|
||||
Headers map[string]string
|
||||
Cookies []*http.Cookie
|
||||
}
|
||||
|
||||
func NewTestCase(t *testing.T) *TestCase {
|
||||
store := store.New(":memory:")
|
||||
service := service.New(store)
|
||||
handler := handler.New(store, service, false)
|
||||
router := router.New(handler, registerRoutes)
|
||||
server := httptest.NewServer(router)
|
||||
|
||||
TestWebhookBody = ""
|
||||
TestWebhookCalledCount = 0
|
||||
ExpectedWebhookBody = ""
|
||||
ExpectedWebhookHeaderKey = ""
|
||||
ExpectedWebhookHeaderValue = ""
|
||||
|
||||
return &TestCase{
|
||||
T: t,
|
||||
Server: server,
|
||||
Client: server.Client(),
|
||||
Store: store,
|
||||
Headers: map[string]string{},
|
||||
Cookies: []*http.Cookie{},
|
||||
}
|
||||
}
|
||||
|
||||
func registerRoutes(router *http.ServeMux) {
|
||||
router.HandleFunc("GET /test/200", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
})
|
||||
|
||||
router.HandleFunc("GET /test/404", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
})
|
||||
|
||||
router.HandleFunc("GET /test/500", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
})
|
||||
|
||||
router.HandleFunc("GET /test/timeout", func(w http.ResponseWriter, r *http.Request) {
|
||||
time.Sleep(30 * time.Second)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
})
|
||||
|
||||
router.HandleFunc("GET /test/panic", func(w http.ResponseWriter, r *http.Request) {
|
||||
panic("test")
|
||||
})
|
||||
|
||||
i := 0
|
||||
router.HandleFunc("GET /test/even", func(w http.ResponseWriter, r *http.Request) {
|
||||
defer func() {
|
||||
i++
|
||||
}()
|
||||
|
||||
if i%2 == 0 {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
})
|
||||
|
||||
router.HandleFunc("POST /test/post", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
})
|
||||
router.HandleFunc("PATCH /test/patch", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
})
|
||||
router.HandleFunc("PUT /test/put", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
})
|
||||
router.HandleFunc("DELETE /test/delete", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
})
|
||||
|
||||
router.HandleFunc("POST /test/body", func(w http.ResponseWriter, r *http.Request) {
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
if string(body) != `{"test":123}` {
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
})
|
||||
|
||||
router.HandleFunc("POST /test/headers", func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Header.Get("test") != "abc" {
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
})
|
||||
|
||||
router.HandleFunc("POST /test/webhook", func(w http.ResponseWriter, r *http.Request) {
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
if ExpectedWebhookBody != "" && string(body) != ExpectedWebhookBody {
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if ExpectedWebhookHeaderKey != "" && r.Header.Get(ExpectedWebhookHeaderKey) != ExpectedWebhookHeaderValue {
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
TestWebhookCalledCount++
|
||||
TestWebhookBody = string(body)
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
})
|
||||
}
|
||||
|
||||
func (tc *TestCase) Close() {
|
||||
tc.Server.Close()
|
||||
}
|
||||
|
||||
func (tc *TestCase) WithHeader(key, value string) *TestCase {
|
||||
tc.Headers[key] = value
|
||||
|
||||
return tc
|
||||
}
|
||||
|
||||
func (tc *TestCase) WithCookie(c *http.Cookie) *TestCase {
|
||||
tc.Cookies = append(tc.Cookies, c)
|
||||
|
||||
return tc
|
||||
}
|
||||
|
||||
func (tc *TestCase) Get(url string) *testutil.AssertableResponse {
|
||||
req, err := http.NewRequest(http.MethodGet, tc.Server.URL+url, nil)
|
||||
if err != nil {
|
||||
tc.T.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if len(tc.Cookies) > 0 {
|
||||
for _, c := range tc.Cookies {
|
||||
req.AddCookie(c)
|
||||
}
|
||||
}
|
||||
|
||||
if len(tc.Headers) > 0 {
|
||||
for k, v := range tc.Headers {
|
||||
req.Header.Set(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
res, err := tc.Client.Do(req)
|
||||
if err != nil {
|
||||
tc.T.Fatalf("failed to get %s: %v", url, err)
|
||||
}
|
||||
|
||||
return testutil.NewAssertableResponse(tc.T, res)
|
||||
}
|
||||
|
||||
func (tc *TestCase) Post(url string, data url.Values) *testutil.AssertableResponse {
|
||||
req, err := http.NewRequest(http.MethodPost, tc.Server.URL+url, strings.NewReader(data.Encode()))
|
||||
if err != nil {
|
||||
tc.T.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
|
||||
if len(tc.Cookies) > 0 {
|
||||
for _, c := range tc.Cookies {
|
||||
req.AddCookie(c)
|
||||
}
|
||||
}
|
||||
|
||||
res, err := tc.Client.Do(req)
|
||||
if err != nil {
|
||||
tc.T.Fatalf("failed to post %s: %v", url, err)
|
||||
}
|
||||
|
||||
for _, c := range res.Cookies() {
|
||||
if c.Name == "session" {
|
||||
tc.Cookies = append(tc.Cookies, c)
|
||||
}
|
||||
}
|
||||
|
||||
return testutil.NewAssertableResponse(tc.T, res)
|
||||
}
|
||||
|
||||
func (tc *TestCase) Patch(url string, data url.Values) *testutil.AssertableResponse {
|
||||
req, err := http.NewRequest(http.MethodPatch, tc.Server.URL+url, strings.NewReader(data.Encode()))
|
||||
if err != nil {
|
||||
tc.T.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
|
||||
if len(tc.Cookies) > 0 {
|
||||
for _, c := range tc.Cookies {
|
||||
req.AddCookie(c)
|
||||
}
|
||||
}
|
||||
|
||||
res, err := tc.Client.Do(req)
|
||||
if err != nil {
|
||||
tc.T.Fatalf("failed to post %s: %v", url, err)
|
||||
}
|
||||
|
||||
return testutil.NewAssertableResponse(tc.T, res)
|
||||
}
|
||||
|
||||
func (tc *TestCase) Delete(url string) *testutil.AssertableResponse {
|
||||
req, err := http.NewRequest(http.MethodDelete, tc.Server.URL+url, nil)
|
||||
if err != nil {
|
||||
tc.T.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if len(tc.Cookies) > 0 {
|
||||
for _, c := range tc.Cookies {
|
||||
req.AddCookie(c)
|
||||
}
|
||||
}
|
||||
|
||||
res, err := tc.Client.Do(req)
|
||||
if err != nil {
|
||||
tc.T.Fatalf("failed to delete %s: %v", url, err)
|
||||
}
|
||||
|
||||
return testutil.NewAssertableResponse(tc.T, res)
|
||||
}
|
||||
|
||||
func (tc *TestCase) AssertDatabaseCount(table string, expected int) *TestCase {
|
||||
tc.T.Helper()
|
||||
|
||||
stmt := fmt.Sprintf(`SELECT COUNT(*) FROM %s`, table)
|
||||
var count int
|
||||
|
||||
err := tc.Store.DB().QueryRow(stmt).Scan(&count)
|
||||
if err != nil {
|
||||
tc.T.Fatalf("failed to count rows from table '%s', error: %v", table, err)
|
||||
}
|
||||
|
||||
if count != expected {
|
||||
tc.T.Fatalf("expected to find %d number of rows in a table '%s, but found %d", expected, table, count)
|
||||
}
|
||||
|
||||
return tc
|
||||
}
|
||||
|
||||
func (tc *TestCase) CreateTestUser(email, password string) *TestCase {
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
tc.T.Fatalf("unexpected bcrypt error: %v", err)
|
||||
}
|
||||
|
||||
user, err := tc.Store.CreateUser(tc.T.Context(), uptimemonitor.User{
|
||||
Name: "Test User",
|
||||
Email: email,
|
||||
PasswordHash: string(hash),
|
||||
})
|
||||
if err != nil {
|
||||
tc.T.Fatalf("unable to create test user: %v", err)
|
||||
}
|
||||
|
||||
tc.User = &user
|
||||
|
||||
return tc
|
||||
}
|
||||
|
||||
func (tc *TestCase) LogIn() *TestCase {
|
||||
tc.CreateTestUser("test@example.com", "password")
|
||||
|
||||
session, err := tc.Store.CreateSession(tc.T.Context(), uptimemonitor.Session{
|
||||
User: *tc.User,
|
||||
UserID: tc.User.ID,
|
||||
ExpiresAt: time.Now().Add(time.Hour),
|
||||
})
|
||||
if err != nil {
|
||||
tc.T.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
c := &http.Cookie{
|
||||
Name: "session",
|
||||
Value: session.Uuid,
|
||||
HttpOnly: true,
|
||||
SameSite: http.SameSiteLaxMode,
|
||||
Secure: false,
|
||||
Expires: session.ExpiresAt,
|
||||
}
|
||||
|
||||
return tc.WithCookie(c)
|
||||
}
|
||||
|
||||
func (tc *TestCase) AssertEqual(a, b any) *TestCase {
|
||||
tc.T.Helper()
|
||||
|
||||
if !reflect.DeepEqual(a, b) {
|
||||
tc.T.Fatalf(`expected "%v" to be equal to "%v"`, a, b)
|
||||
}
|
||||
return tc
|
||||
}
|
||||
|
||||
func (tc *TestCase) AssertNoError(err error) *TestCase {
|
||||
tc.T.Helper()
|
||||
|
||||
if err != nil {
|
||||
tc.T.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
return tc
|
||||
}
|
Reference in New Issue
Block a user