2017-04-25 10:24:51 +03:00
// Copyright 2017 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package integrations
import (
"bytes"
2017-04-30 09:30:12 +03:00
"database/sql"
2017-06-17 07:49:45 +03:00
"encoding/json"
2017-04-25 10:24:51 +03:00
"fmt"
"io"
2017-04-30 09:30:12 +03:00
"log"
2017-04-25 10:24:51 +03:00
"net/http"
2017-05-02 03:49:55 +03:00
"net/http/cookiejar"
2017-12-04 01:46:01 +03:00
"net/http/httptest"
2017-05-02 03:49:55 +03:00
"net/url"
2017-04-25 10:24:51 +03:00
"os"
2017-06-09 21:13:46 +03:00
"path"
2017-11-02 20:51:03 +03:00
"path/filepath"
2017-05-02 03:49:55 +03:00
"strings"
2017-04-25 10:24:51 +03:00
"testing"
"code.gitea.io/gitea/models"
2019-04-17 19:06:35 +03:00
"code.gitea.io/gitea/modules/base"
2017-04-25 10:24:51 +03:00
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/routers"
"code.gitea.io/gitea/routers/routes"
2017-12-11 05:15:27 +03:00
"github.com/PuerkitoBio/goquery"
2017-04-28 16:23:28 +03:00
"github.com/Unknwon/com"
"github.com/stretchr/testify/assert"
2017-04-25 10:24:51 +03:00
"gopkg.in/macaron.v1"
"gopkg.in/testfixtures.v2"
)
var mac * macaron . Macaron
2019-02-12 18:09:43 +03:00
type NilResponseRecorder struct {
httptest . ResponseRecorder
Length int
}
func ( n * NilResponseRecorder ) Write ( b [ ] byte ) ( int , error ) {
2019-06-12 22:41:28 +03:00
n . Length += len ( b )
2019-02-12 18:09:43 +03:00
return len ( b ) , nil
}
// NewRecorder returns an initialized ResponseRecorder.
func NewNilResponseRecorder ( ) * NilResponseRecorder {
return & NilResponseRecorder {
ResponseRecorder : * httptest . NewRecorder ( ) ,
}
}
2017-04-25 10:24:51 +03:00
func TestMain ( m * testing . M ) {
2017-04-30 09:30:12 +03:00
initIntegrationTest ( )
2017-04-25 10:24:51 +03:00
mac = routes . NewMacaron ( )
routes . RegisterRoutes ( mac )
var helper testfixtures . Helper
if setting . UseMySQL {
helper = & testfixtures . MySQL { }
} else if setting . UsePostgreSQL {
helper = & testfixtures . PostgreSQL { }
} else if setting . UseSQLite3 {
helper = & testfixtures . SQLite { }
2018-12-12 04:01:41 +03:00
} else if setting . UseMSSQL {
helper = & testfixtures . SQLServer { }
2017-04-25 10:24:51 +03:00
} else {
fmt . Println ( "Unsupported RDBMS for integration tests" )
os . Exit ( 1 )
}
err := models . InitFixtures (
helper ,
2017-11-02 20:51:03 +03:00
path . Join ( filepath . Dir ( setting . AppPath ) , "models/fixtures/" ) ,
2017-04-25 10:24:51 +03:00
)
if err != nil {
fmt . Printf ( "Error initializing test database: %v\n" , err )
os . Exit ( 1 )
}
2017-09-16 23:16:21 +03:00
exitCode := m . Run ( )
2019-04-11 14:49:49 +03:00
writerCloser . t = nil
2017-09-16 23:16:21 +03:00
if err = os . RemoveAll ( setting . Indexer . IssuePath ) ; err != nil {
fmt . Printf ( "os.RemoveAll: %v\n" , err )
os . Exit ( 1 )
}
2017-10-27 09:10:54 +03:00
if err = os . RemoveAll ( setting . Indexer . RepoPath ) ; err != nil {
fmt . Printf ( "Unable to remove repo indexer: %v\n" , err )
os . Exit ( 1 )
}
2017-09-16 23:16:21 +03:00
os . Exit ( exitCode )
2017-04-25 10:24:51 +03:00
}
2017-04-30 09:30:12 +03:00
func initIntegrationTest ( ) {
2019-04-17 19:06:35 +03:00
giteaRoot := base . SetupGiteaRoot ( )
2017-06-10 05:27:13 +03:00
if giteaRoot == "" {
fmt . Println ( "Environment variable $GITEA_ROOT not set" )
2017-04-30 09:30:12 +03:00
os . Exit ( 1 )
}
2017-06-10 05:27:13 +03:00
setting . AppPath = path . Join ( giteaRoot , "gitea" )
2017-09-18 11:35:42 +03:00
if _ , err := os . Stat ( setting . AppPath ) ; err != nil {
fmt . Printf ( "Could not find gitea binary at %s\n" , setting . AppPath )
os . Exit ( 1 )
}
2017-06-10 05:27:13 +03:00
giteaConf := os . Getenv ( "GITEA_CONF" )
if giteaConf == "" {
fmt . Println ( "Environment variable $GITEA_CONF not set" )
2017-05-02 03:49:55 +03:00
os . Exit ( 1 )
2017-06-10 05:27:13 +03:00
} else if ! path . IsAbs ( giteaConf ) {
setting . CustomConf = path . Join ( giteaRoot , giteaConf )
2017-06-09 21:13:46 +03:00
} else {
2017-06-10 05:27:13 +03:00
setting . CustomConf = giteaConf
2017-05-02 03:49:55 +03:00
}
2017-04-30 09:30:12 +03:00
2019-05-14 18:20:35 +03:00
setting . SetCustomPathAndConf ( "" , "" , "" )
2017-04-30 09:30:12 +03:00
setting . NewContext ( )
2018-12-19 04:17:43 +03:00
setting . CheckLFSVersion ( )
2017-04-30 09:30:12 +03:00
models . LoadConfigs ( )
switch {
case setting . UseMySQL :
db , err := sql . Open ( "mysql" , fmt . Sprintf ( "%s:%s@tcp(%s)/" ,
models . DbCfg . User , models . DbCfg . Passwd , models . DbCfg . Host ) )
defer db . Close ( )
if err != nil {
log . Fatalf ( "sql.Open: %v" , err )
}
if _ , err = db . Exec ( "CREATE DATABASE IF NOT EXISTS testgitea" ) ; err != nil {
log . Fatalf ( "db.Exec: %v" , err )
}
case setting . UsePostgreSQL :
db , err := sql . Open ( "postgres" , fmt . Sprintf ( "postgres://%s:%s@%s/?sslmode=%s" ,
models . DbCfg . User , models . DbCfg . Passwd , models . DbCfg . Host , models . DbCfg . SSLMode ) )
defer db . Close ( )
if err != nil {
log . Fatalf ( "sql.Open: %v" , err )
}
2019-06-12 22:41:28 +03:00
rows , err := db . Query ( fmt . Sprintf ( "SELECT 1 FROM pg_database WHERE datname = '%s'" , models . DbCfg . Name ) )
2017-04-30 09:30:12 +03:00
if err != nil {
log . Fatalf ( "db.Query: %v" , err )
}
2017-05-08 05:55:27 +03:00
defer rows . Close ( )
2017-05-09 16:42:55 +03:00
if rows . Next ( ) {
2017-05-11 18:32:43 +03:00
break
2017-04-30 09:30:12 +03:00
}
2017-05-09 16:42:55 +03:00
if _ , err = db . Exec ( "CREATE DATABASE testgitea" ) ; err != nil {
log . Fatalf ( "db.Exec: %v" , err )
}
2018-12-12 04:01:41 +03:00
case setting . UseMSSQL :
host , port := models . ParseMSSQLHostPort ( models . DbCfg . Host )
db , err := sql . Open ( "mssql" , fmt . Sprintf ( "server=%s; port=%s; database=%s; user id=%s; password=%s;" ,
host , port , "master" , models . DbCfg . User , models . DbCfg . Passwd ) )
if err != nil {
log . Fatalf ( "sql.Open: %v" , err )
}
if _ , err := db . Exec ( "If(db_id(N'gitea') IS NULL) BEGIN CREATE DATABASE gitea; END;" ) ; err != nil {
log . Fatalf ( "db.Exec: %v" , err )
}
defer db . Close ( )
2017-04-30 09:30:12 +03:00
}
routers . GlobalInit ( )
}
2019-04-07 03:25:14 +03:00
func prepareTestEnv ( t testing . TB , skip ... int ) {
ourSkip := 2
if len ( skip ) > 0 {
ourSkip += skip [ 0 ]
}
PrintCurrentTest ( t , ourSkip )
2017-04-28 16:23:28 +03:00
assert . NoError ( t , models . LoadFixtures ( ) )
2017-09-12 08:51:12 +03:00
assert . NoError ( t , os . RemoveAll ( setting . RepoRootPath ) )
2017-12-11 03:52:05 +03:00
assert . NoError ( t , os . RemoveAll ( models . LocalCopyPath ( ) ) )
2017-11-02 20:51:03 +03:00
assert . NoError ( t , com . CopyDir ( path . Join ( filepath . Dir ( setting . AppPath ) , "integrations/gitea-repositories-meta" ) ,
setting . RepoRootPath ) )
2017-04-28 16:23:28 +03:00
}
2017-05-02 03:49:55 +03:00
type TestSession struct {
jar http . CookieJar
}
func ( s * TestSession ) GetCookie ( name string ) * http . Cookie {
baseURL , err := url . Parse ( setting . AppURL )
if err != nil {
return nil
}
for _ , c := range s . jar . Cookies ( baseURL ) {
if c . Name == name {
return c
}
}
return nil
}
2017-12-04 01:46:01 +03:00
func ( s * TestSession ) MakeRequest ( t testing . TB , req * http . Request , expectedStatus int ) * httptest . ResponseRecorder {
2017-05-02 03:49:55 +03:00
baseURL , err := url . Parse ( setting . AppURL )
assert . NoError ( t , err )
for _ , c := range s . jar . Cookies ( baseURL ) {
req . AddCookie ( c )
}
2017-07-07 22:36:47 +03:00
resp := MakeRequest ( t , req , expectedStatus )
2017-05-02 03:49:55 +03:00
ch := http . Header { }
2019-06-12 22:41:28 +03:00
ch . Add ( "Cookie" , strings . Join ( resp . Header ( ) [ "Set-Cookie" ] , ";" ) )
2017-05-02 03:49:55 +03:00
cr := http . Request { Header : ch }
s . jar . SetCookies ( baseURL , cr . Cookies ( ) )
return resp
}
2019-02-12 18:09:43 +03:00
func ( s * TestSession ) MakeRequestNilResponseRecorder ( t testing . TB , req * http . Request , expectedStatus int ) * NilResponseRecorder {
baseURL , err := url . Parse ( setting . AppURL )
assert . NoError ( t , err )
for _ , c := range s . jar . Cookies ( baseURL ) {
req . AddCookie ( c )
}
resp := MakeRequestNilResponseRecorder ( t , req , expectedStatus )
ch := http . Header { }
2019-06-12 22:41:28 +03:00
ch . Add ( "Cookie" , strings . Join ( resp . Header ( ) [ "Set-Cookie" ] , ";" ) )
2019-02-12 18:09:43 +03:00
cr := http . Request { Header : ch }
s . jar . SetCookies ( baseURL , cr . Cookies ( ) )
return resp
}
2017-06-17 07:49:45 +03:00
const userPassword = "password"
2017-07-09 05:07:29 +03:00
var loginSessionCache = make ( map [ string ] * TestSession , 10 )
2017-08-23 10:30:33 +03:00
func emptyTestSession ( t testing . TB ) * TestSession {
jar , err := cookiejar . New ( nil )
assert . NoError ( t , err )
return & TestSession { jar : jar }
}
2017-06-17 18:01:03 +03:00
func loginUser ( t testing . TB , userName string ) * TestSession {
2017-07-09 05:07:29 +03:00
if session , ok := loginSessionCache [ userName ] ; ok {
return session
}
session := loginUserWithPassword ( t , userName , userPassword )
loginSessionCache [ userName ] = session
return session
2017-06-17 07:49:45 +03:00
}
2017-06-17 18:01:03 +03:00
func loginUserWithPassword ( t testing . TB , userName , password string ) * TestSession {
2017-06-10 03:41:36 +03:00
req := NewRequest ( t , "GET" , "/user/login" )
2017-07-07 22:36:47 +03:00
resp := MakeRequest ( t , req , http . StatusOK )
2017-05-02 03:49:55 +03:00
2017-06-17 19:29:59 +03:00
doc := NewHTMLParser ( t , resp . Body )
2017-06-17 07:49:45 +03:00
req = NewRequestWithValues ( t , "POST" , "/user/login" , map [ string ] string {
"_csrf" : doc . GetCSRF ( ) ,
"user_name" : userName ,
"password" : password ,
} )
2017-07-07 22:36:47 +03:00
resp = MakeRequest ( t , req , http . StatusFound )
2017-05-02 03:49:55 +03:00
ch := http . Header { }
2019-06-12 22:41:28 +03:00
ch . Add ( "Cookie" , strings . Join ( resp . Header ( ) [ "Set-Cookie" ] , ";" ) )
2017-05-02 03:49:55 +03:00
cr := http . Request { Header : ch }
2017-08-23 10:30:33 +03:00
session := emptyTestSession ( t )
2017-05-02 03:49:55 +03:00
baseURL , err := url . Parse ( setting . AppURL )
assert . NoError ( t , err )
2017-08-23 10:30:33 +03:00
session . jar . SetCookies ( baseURL , cr . Cookies ( ) )
2017-05-02 03:49:55 +03:00
2017-08-23 10:30:33 +03:00
return session
2017-05-02 03:49:55 +03:00
}
2018-09-10 19:15:52 +03:00
func getTokenForLoggedInUser ( t testing . TB , session * TestSession ) string {
req := NewRequest ( t , "GET" , "/user/settings/applications" )
resp := session . MakeRequest ( t , req , http . StatusOK )
doc := NewHTMLParser ( t , resp . Body )
req = NewRequestWithValues ( t , "POST" , "/user/settings/applications" , map [ string ] string {
"_csrf" : doc . GetCSRF ( ) ,
"name" : "api-testing-token" ,
} )
resp = session . MakeRequest ( t , req , http . StatusFound )
req = NewRequest ( t , "GET" , "/user/settings/applications" )
resp = session . MakeRequest ( t , req , http . StatusOK )
htmlDoc := NewHTMLParser ( t , resp . Body )
token := htmlDoc . doc . Find ( ".ui.info p" ) . Text ( )
return token
}
2017-06-17 18:01:03 +03:00
func NewRequest ( t testing . TB , method , urlStr string ) * http . Request {
2017-06-17 07:49:45 +03:00
return NewRequestWithBody ( t , method , urlStr , nil )
}
2017-06-25 03:15:42 +03:00
func NewRequestf ( t testing . TB , method , urlFormat string , args ... interface { } ) * http . Request {
return NewRequest ( t , method , fmt . Sprintf ( urlFormat , args ... ) )
}
2017-06-17 18:01:03 +03:00
func NewRequestWithValues ( t testing . TB , method , urlStr string , values map [ string ] string ) * http . Request {
2017-06-17 07:49:45 +03:00
urlValues := url . Values { }
for key , value := range values {
urlValues [ key ] = [ ] string { value }
}
2017-06-25 03:15:42 +03:00
req := NewRequestWithBody ( t , method , urlStr , bytes . NewBufferString ( urlValues . Encode ( ) ) )
req . Header . Add ( "Content-Type" , "application/x-www-form-urlencoded" )
return req
2017-06-17 07:49:45 +03:00
}
2017-06-17 18:01:03 +03:00
func NewRequestWithJSON ( t testing . TB , method , urlStr string , v interface { } ) * http . Request {
2017-06-17 07:49:45 +03:00
jsonBytes , err := json . Marshal ( v )
assert . NoError ( t , err )
2017-06-25 02:52:51 +03:00
req := NewRequestWithBody ( t , method , urlStr , bytes . NewBuffer ( jsonBytes ) )
req . Header . Add ( "Content-Type" , "application/json" )
return req
2017-06-10 03:41:36 +03:00
}
2017-06-17 18:01:03 +03:00
func NewRequestWithBody ( t testing . TB , method , urlStr string , body io . Reader ) * http . Request {
2017-06-17 07:49:45 +03:00
request , err := http . NewRequest ( method , urlStr , body )
2017-06-10 03:41:36 +03:00
assert . NoError ( t , err )
2017-06-17 07:49:45 +03:00
request . RequestURI = urlStr
2017-06-10 03:41:36 +03:00
return request
}
2018-07-07 04:54:30 +03:00
func AddBasicAuthHeader ( request * http . Request , username string ) * http . Request {
request . SetBasicAuth ( username , userPassword )
return request
}
2017-07-07 22:36:47 +03:00
const NoExpectedStatus = - 1
2017-12-04 01:46:01 +03:00
func MakeRequest ( t testing . TB , req * http . Request , expectedStatus int ) * httptest . ResponseRecorder {
recorder := httptest . NewRecorder ( )
mac . ServeHTTP ( recorder , req )
2017-07-07 22:36:47 +03:00
if expectedStatus != NoExpectedStatus {
2017-12-11 05:15:27 +03:00
if ! assert . EqualValues ( t , expectedStatus , recorder . Code ,
"Request: %s %s" , req . Method , req . URL . String ( ) ) {
logUnexpectedResponse ( t , recorder )
}
2017-07-07 22:36:47 +03:00
}
2017-12-04 01:46:01 +03:00
return recorder
2017-04-25 10:24:51 +03:00
}
2017-06-18 12:06:17 +03:00
2019-02-12 18:09:43 +03:00
func MakeRequestNilResponseRecorder ( t testing . TB , req * http . Request , expectedStatus int ) * NilResponseRecorder {
recorder := NewNilResponseRecorder ( )
mac . ServeHTTP ( recorder , req )
if expectedStatus != NoExpectedStatus {
if ! assert . EqualValues ( t , expectedStatus , recorder . Code ,
"Request: %s %s" , req . Method , req . URL . String ( ) ) {
logUnexpectedResponse ( t , & recorder . ResponseRecorder )
}
}
return recorder
}
2017-12-11 05:15:27 +03:00
// logUnexpectedResponse logs the contents of an unexpected response.
func logUnexpectedResponse ( t testing . TB , recorder * httptest . ResponseRecorder ) {
respBytes := recorder . Body . Bytes ( )
if len ( respBytes ) == 0 {
return
} else if len ( respBytes ) < 500 {
// if body is short, just log the whole thing
t . Log ( "Response:" , string ( respBytes ) )
return
}
// log the "flash" error message, if one exists
// we must create a new buffer, so that we don't "use up" resp.Body
htmlDoc , err := goquery . NewDocumentFromReader ( bytes . NewBuffer ( respBytes ) )
if err != nil {
return // probably a non-HTML response
}
errMsg := htmlDoc . Find ( ".ui.negative.message" ) . Text ( )
if len ( errMsg ) > 0 {
t . Log ( "A flash error message was found:" , errMsg )
}
}
2017-12-04 01:46:01 +03:00
func DecodeJSON ( t testing . TB , resp * httptest . ResponseRecorder , v interface { } ) {
decoder := json . NewDecoder ( resp . Body )
2017-06-18 12:06:17 +03:00
assert . NoError ( t , decoder . Decode ( v ) )
}
2017-07-07 22:36:47 +03:00
func GetCSRF ( t testing . TB , session * TestSession , urlStr string ) string {
req := NewRequest ( t , "GET" , urlStr )
resp := session . MakeRequest ( t , req , http . StatusOK )
doc := NewHTMLParser ( t , resp . Body )
return doc . GetCSRF ( )
}