272 lines
7.4 KiB
Go
272 lines
7.4 KiB
Go
package test
|
|
|
|
import (
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/gorilla/schema"
|
|
"github.com/labstack/echo/v4"
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/thomiceli/opengist/internal/config"
|
|
"github.com/thomiceli/opengist/internal/db"
|
|
"github.com/thomiceli/opengist/internal/git"
|
|
"github.com/thomiceli/opengist/internal/index"
|
|
"github.com/thomiceli/opengist/internal/web/context"
|
|
"github.com/thomiceli/opengist/internal/web/handlers/metrics"
|
|
"github.com/thomiceli/opengist/internal/web/server"
|
|
)
|
|
|
|
var databaseType string
|
|
var formEncoder *schema.Encoder
|
|
|
|
func init() {
|
|
formEncoder = schema.NewEncoder()
|
|
formEncoder.SetAliasTag("form")
|
|
}
|
|
|
|
type Server struct {
|
|
server *server.Server
|
|
SessionCookie string
|
|
contextData echo.Map
|
|
}
|
|
|
|
func (s *Server) Request(t *testing.T, method, uri string, data interface{}, expectedCode int) *http.Response {
|
|
return s.RequestWithHeaders(t, method, uri, data, expectedCode, nil)
|
|
}
|
|
|
|
func (s *Server) RequestWithHeaders(t *testing.T, method, uri string, data interface{}, expectedCode int, headers map[string]string) *http.Response {
|
|
var bodyReader io.Reader
|
|
if method == http.MethodPost || method == http.MethodPut || method == http.MethodDelete {
|
|
if values, ok := data.(url.Values); ok {
|
|
bodyReader = strings.NewReader(values.Encode())
|
|
} else if data != nil {
|
|
values := url.Values{}
|
|
_ = formEncoder.Encode(data, values)
|
|
bodyReader = strings.NewReader(values.Encode())
|
|
}
|
|
}
|
|
|
|
req := httptest.NewRequest(method, uri, bodyReader)
|
|
w := httptest.NewRecorder()
|
|
|
|
if method == http.MethodPost || method == http.MethodPut {
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
}
|
|
|
|
req.Header.Set("Sec-Fetch-Site", "same-origin")
|
|
|
|
for key, value := range headers {
|
|
req.Header.Set(key, value)
|
|
}
|
|
|
|
if s.SessionCookie != "" {
|
|
req.AddCookie(&http.Cookie{Name: "session", Value: s.SessionCookie})
|
|
}
|
|
|
|
s.server.ServeHTTP(w, req)
|
|
if expectedCode != 0 {
|
|
require.Equalf(t, expectedCode, w.Code, "Unexpected status code for %s %s: got %d, expected %d", method, uri, w.Code, expectedCode)
|
|
}
|
|
if method == http.MethodPost {
|
|
if strings.Contains(uri, "/login") {
|
|
cookie := ""
|
|
h := w.Header().Get("Set-Cookie")
|
|
parts := strings.Split(h, "; ")
|
|
for _, p := range parts {
|
|
if strings.HasPrefix(p, "session=") {
|
|
cookie = p
|
|
break
|
|
}
|
|
}
|
|
s.SessionCookie = strings.TrimPrefix(cookie, "session=")
|
|
} else if strings.Contains(uri, "/logout") {
|
|
s.SessionCookie = ""
|
|
}
|
|
}
|
|
|
|
return w.Result()
|
|
}
|
|
|
|
func (s *Server) RawRequest(t *testing.T, req *http.Request, expectedCode int) *http.Response {
|
|
w := httptest.NewRecorder()
|
|
|
|
req.Header.Set("Sec-Fetch-Site", "same-origin")
|
|
|
|
if s.SessionCookie != "" {
|
|
req.AddCookie(&http.Cookie{Name: "session", Value: s.SessionCookie})
|
|
}
|
|
|
|
s.server.ServeHTTP(w, req)
|
|
|
|
require.Equal(t, expectedCode, w.Code, "unexpected status code for %s %s", req.Method, req.URL.Path)
|
|
|
|
return w.Result()
|
|
}
|
|
|
|
func (s *Server) StartHttpServer(t *testing.T) string {
|
|
hs := httptest.NewServer(s.server)
|
|
t.Cleanup(hs.Close)
|
|
return hs.URL
|
|
}
|
|
|
|
func (s *Server) User() *db.User {
|
|
s.Request(nil, "GET", "/", nil, 0)
|
|
if user, ok := s.contextData["userLogged"].(*db.User); ok {
|
|
return user
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *Server) TestCtxData(t *testing.T, expected echo.Map) {
|
|
for key, expectedValue := range expected {
|
|
actualValue, exists := s.contextData[key]
|
|
require.True(t, exists, "Key %q not found in context data", key)
|
|
require.Equal(t, expectedValue, actualValue, "Context data mismatch for key %q", key)
|
|
}
|
|
}
|
|
|
|
func (s *Server) Register(t *testing.T, user string) {
|
|
s.Request(t, "POST", "/register", db.UserDTO{Username: user, Password: user}, 302)
|
|
}
|
|
|
|
func (s *Server) Login(t *testing.T, user string) {
|
|
s.Request(t, "POST", "/login", db.UserDTO{Username: user, Password: user}, 302)
|
|
}
|
|
|
|
func (s *Server) Logout() {
|
|
s.SessionCookie = ""
|
|
}
|
|
|
|
func (s *Server) CreateGist(t *testing.T, visibility string) (gistPath string, gist *db.Gist, username, identifier string) {
|
|
s.Request(t, "POST", "/register", db.UserDTO{Username: "thomas", Password: "thomas"}, 0)
|
|
s.Login(t, "thomas")
|
|
|
|
resp := s.Request(t, "POST", "/", url.Values{
|
|
"title": {"Test"},
|
|
"name": {"file.txt", "otherfile.txt"},
|
|
"content": {"hello world", "other content"},
|
|
"topics": {"hello opengist"},
|
|
"private": {visibility},
|
|
}, 302)
|
|
|
|
// Extract gist identifier from redirect
|
|
location := resp.Header.Get("Location")
|
|
parts := strings.Split(strings.TrimPrefix(location, "/"), "/")
|
|
require.Len(t, parts, 2, "Expected redirect format: /{username}/{identifier}")
|
|
|
|
gistUsername := parts[0]
|
|
gistIdentifier := parts[1]
|
|
|
|
gist, err := db.GetGist(gistUsername, gistIdentifier)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, gist)
|
|
|
|
gistPath = filepath.Join(config.GetHomeDir(), git.ReposDirectory, "thomas", gist.Uuid)
|
|
|
|
// Verify gist exists on filesystem
|
|
_, err = os.Stat(gistPath)
|
|
require.NoError(t, err, "Gist repository should exist at %s", gistPath)
|
|
|
|
username = gist.User.Username
|
|
identifier = gist.Identifier()
|
|
|
|
s.Logout()
|
|
return gistPath, gist, username, identifier
|
|
}
|
|
|
|
func Setup(t *testing.T) *Server {
|
|
tmpDir := t.TempDir()
|
|
t.Setenv("OPENGIST_SKIP_GIT_HOOKS", "1")
|
|
|
|
err := config.InitConfig("", io.Discard)
|
|
require.NoError(t, err, "Could not init config")
|
|
|
|
config.C.LogLevel = "warn"
|
|
config.C.LogOutput = "stdout"
|
|
config.C.GitDefaultBranch = "master"
|
|
config.C.OpengistHome = tmpDir
|
|
|
|
config.SetupSecretKey()
|
|
config.InitLog()
|
|
|
|
tmpGitConfig := filepath.Join(tmpDir, "gitconfig")
|
|
t.Setenv("GIT_CONFIG_GLOBAL", tmpGitConfig)
|
|
|
|
err = exec.Command("git", "config", "--global", "--type", "bool", "push.autoSetupRemote", "true").Run()
|
|
require.NoError(t, err)
|
|
err = exec.Command("git", "config", "--global", "user.email", "test@opengist.io").Run()
|
|
require.NoError(t, err)
|
|
err = exec.Command("git", "config", "--global", "user.name", "test").Run()
|
|
require.NoError(t, err)
|
|
homePath := config.GetHomeDir()
|
|
|
|
var databaseDsn string
|
|
databaseType = os.Getenv("OPENGIST_TEST_DB")
|
|
switch databaseType {
|
|
case "postgres":
|
|
databaseDsn = "postgres://postgres:opengist@localhost:5432/opengist_test"
|
|
case "mysql":
|
|
databaseDsn = "mysql://root:opengist@localhost:3306/opengist_test"
|
|
default:
|
|
databaseDsn = config.C.DBUri
|
|
}
|
|
|
|
err = os.MkdirAll(filepath.Join(homePath, "sessions"), 0755)
|
|
require.NoError(t, err, "Could not create sessions directory")
|
|
|
|
err = os.MkdirAll(filepath.Join(homePath, "repos"), 0755)
|
|
require.NoError(t, err, "Could not create repos directory")
|
|
|
|
err = os.MkdirAll(filepath.Join(homePath, "tmp", "repos"), 0755)
|
|
require.NoError(t, err, "Could not create tmp repos directory")
|
|
|
|
err = os.MkdirAll(filepath.Join(homePath, "custom"), 0755)
|
|
require.NoError(t, err, "Could not create custom directory")
|
|
|
|
err = db.Setup(databaseDsn)
|
|
require.NoError(t, err, "Could not initialize database")
|
|
|
|
t.Cleanup(func() {
|
|
db.Close()
|
|
})
|
|
|
|
if index.IndexEnabled() {
|
|
go index.NewIndexer(index.IndexType())
|
|
}
|
|
|
|
s := &Server{
|
|
server: server.NewServer(true),
|
|
}
|
|
|
|
s.server.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
|
|
return func(c echo.Context) error {
|
|
err := next(c)
|
|
if data, ok := c.Request().Context().Value(context.DataKeyStr).(echo.Map); ok {
|
|
s.contextData = data
|
|
}
|
|
return err
|
|
}
|
|
})
|
|
|
|
return s
|
|
}
|
|
|
|
func Teardown(t *testing.T) {
|
|
switch databaseType {
|
|
case "postgres", "mysql":
|
|
err := db.TruncateDatabase()
|
|
require.NoError(t, err, "Could not truncate database")
|
|
}
|
|
}
|
|
|
|
func NewTestMetricsServer() *metrics.Server {
|
|
return metrics.NewServer()
|
|
}
|