mirror of https://github.com/go-gitea/gitea
Integration test framework (#1290)
* Integration test framework * udpate drone sign * Formatting fixes and move router.go to routers/ * update sign for dronepull/1298/merge
parent
3012971e92
commit
c58708d3ee
@ -1 +1 @@ |
||||
eyJhbGciOiJIUzI1NiJ9.d29ya3NwYWNlOgogIGJhc2U6IC9zcnYvYXBwCiAgcGF0aDogc3JjL2NvZGUuZ2l0ZWEuaW8vZ2l0ZWEKCnBpcGVsaW5lOgogIGNsb25lOgogICAgaW1hZ2U6IHBsdWdpbnMvZ2l0CiAgICB0YWdzOiB0cnVlCgogIHRlc3Q6CiAgICBpbWFnZTogd2ViaGlwcGllL2dvbGFuZzplZGdlCiAgICBwdWxsOiB0cnVlCiAgICBlbnZpcm9ubWVudDoKICAgICAgVEFHUzogYmluZGF0YSBzcWxpdGUKICAgICAgR09QQVRIOiAvc3J2L2FwcAogICAgY29tbWFuZHM6CiAgICAgIC0gYXBrIC1VIGFkZCBvcGVuc3NoLWNsaWVudAogICAgICAtIG1ha2UgY2xlYW4KICAgICAgLSBtYWtlIGdlbmVyYXRlCiAgICAgIC0gbWFrZSB2ZXQKICAgICAgLSBtYWtlIGxpbnQKICAgICAgLSBtYWtlIHRlc3QtdmVuZG9yCiAgICAgIC0gbWFrZSB0ZXN0CiAgICAgIC0gbWFrZSBidWlsZAogICAgd2hlbjoKICAgICAgZXZlbnQ6IFsgcHVzaCwgdGFnLCBwdWxsX3JlcXVlc3QgXQoKICB0ZXN0LW15c3FsOgogICAgaW1hZ2U6IHdlYmhpcHBpZS9nb2xhbmc6ZWRnZQogICAgcHVsbDogdHJ1ZQogICAgZW52aXJvbm1lbnQ6CiAgICAgIFRBR1M6IGJpbmRhdGEKICAgICAgR09QQVRIOiAvc3J2L2FwcAogICAgY29tbWFuZHM6CiAgICAgIC0gbWFrZSB0ZXN0LW15c3FsCiAgICB3aGVuOgogICAgICBldmVudDogWyBwdXNoLCB0YWcsIHB1bGxfcmVxdWVzdCBdCgogIHRlc3QtcGdzcWw6CiAgICBpbWFnZTogd2ViaGlwcGllL2dvbGFuZzplZGdlCiAgICBwdWxsOiB0cnVlCiAgICBlbnZpcm9ubWVudDoKICAgICAgVEFHUzogYmluZGF0YQogICAgICBHT1BBVEg6IC9zcnYvYXBwCiAgICBjb21tYW5kczoKICAgICAgLSBtYWtlIHRlc3QtcGdzcWwKICAgIHdoZW46CiAgICAgIGV2ZW50OiBbIHB1c2gsIHRhZywgcHVsbF9yZXF1ZXN0IF0KCiAgc3RhdGljOgogICAgaW1hZ2U6IGthcmFsYWJlL3hnby1sYXRlc3Q6bGF0ZXN0CiAgICBwdWxsOiB0cnVlCiAgICBlbnZpcm9ubWVudDoKICAgICAgVEFHUzogYmluZGF0YSBzcWxpdGUKICAgICAgR09QQVRIOiAvc3J2L2FwcAogICAgY29tbWFuZHM6CiAgICAgIC0gbWFrZSByZWxlYXNlCiAgICB3aGVuOgogICAgICBldmVudDogWyBwdXNoLCB0YWcsIHB1bGxfcmVxdWVzdCBdCgogIGNvdmVyYWdlOgogICAgaW1hZ2U6IHBsdWdpbnMvY292ZXJhZ2UKICAgIHNlcnZlcjogaHR0cHM6Ly9jb3ZlcmFnZS5naXRlYS5pbwogICAgd2hlbjoKICAgICAgZXZlbnQ6IFsgcHVzaCwgdGFnLCBwdWxsX3JlcXVlc3QgXQoKICBkb2NrZXI6CiAgICBpbWFnZTogcGx1Z2lucy9kb2NrZXIKICAgIHJlcG86IGdpdGVhL2dpdGVhCiAgICB0YWdzOiBbICcke0RST05FX1RBRyMjdn0nIF0KICAgIHdoZW46CiAgICAgIGV2ZW50OiBbIHRhZyBdCiAgICAgIGJyYW5jaDogWyByZWZzL3RhZ3MvKiBdCgogIGRvY2tlcjoKICAgIGltYWdlOiBwbHVnaW5zL2RvY2tlcgogICAgcmVwbzogZ2l0ZWEvZ2l0ZWEKICAgIHRhZ3M6IFsgJyR7RFJPTkVfQlJBTkNIIyNyZWxlYXNlL3Z9JyBdCiAgICB3aGVuOgogICAgICBldmVudDogWyBwdXNoIF0KICAgICAgYnJhbmNoOiBbIHJlbGVhc2UvKiBdCgogIGRvY2tlcjoKICAgIGltYWdlOiBwbHVnaW5zL2RvY2tlcgogICAgcmVwbzogZ2l0ZWEvZ2l0ZWEKICAgIHRhZ3M6IFsgJ2xhdGVzdCcgXQogICAgd2hlbjoKICAgICAgZXZlbnQ6IFsgcHVzaCBdCiAgICAgIGJyYW5jaDogWyBtYXN0ZXIgXQoKICByZWxlYXNlOgogICAgaW1hZ2U6IHBsdWdpbnMvczMKICAgIHBhdGhfc3R5bGU6IHRydWUKICAgIHN0cmlwX3ByZWZpeDogZGlzdC9yZWxlYXNlLwogICAgc291cmNlOiBkaXN0L3JlbGVhc2UvKgogICAgdGFyZ2V0OiAvZ2l0ZWEvJHtEUk9ORV9UQUcjI3Z9CiAgICB3aGVuOgogICAgICBldmVudDogWyB0YWcgXQogICAgICBicmFuY2g6IFsgcmVmcy90YWdzLyogXQoKICByZWxlYXNlOgogICAgaW1hZ2U6IHBsdWdpbnMvczMKICAgIHBhdGhfc3R5bGU6IHRydWUKICAgIHN0cmlwX3ByZWZpeDogZGlzdC9yZWxlYXNlLwogICAgc291cmNlOiBkaXN0L3JlbGVhc2UvKgogICAgdGFyZ2V0OiAvZ2l0ZWEvJHtEUk9ORV9CUkFOQ0gjI3JlbGVhc2Uvdn0KICAgIHdoZW46CiAgICAgIGV2ZW50OiBbIHB1c2ggXQogICAgICBicmFuY2g6IFsgcmVsZWFzZS8qIF0KCiAgcmVsZWFzZToKICAgIGltYWdlOiBwbHVnaW5zL3MzCiAgICBwYXRoX3N0eWxlOiB0cnVlCiAgICBzdHJpcF9wcmVmaXg6IGRpc3QvcmVsZWFzZS8KICAgIHNvdXJjZTogZGlzdC9yZWxlYXNlLyoKICAgIHRhcmdldDogL2dpdGVhL21hc3RlcgogICAgd2hlbjoKICAgICAgZXZlbnQ6IFsgcHVzaCBdCiAgICAgIGJyYW5jaDogWyBtYXN0ZXIgXQoKICBnaXRodWI6CiAgICBpbWFnZTogcGx1Z2lucy9naXRodWItcmVsZWFzZQogICAgZmlsZXM6CiAgICAgIC0gZGlzdC9yZWxlYXNlLyoKICAgIHdoZW46CiAgICAgIGV2ZW50OiBbIHRhZyBdCiAgICAgIGJyYW5jaDogWyByZWZzL3RhZ3MvKiBdCgogIGdpdHRlcjoKICAgIGltYWdlOiBwbHVnaW5zL2dpdHRlcgoKc2VydmljZXM6CiAgbXlzcWw6CiAgICBpbWFnZTogbXlzcWw6NS43CiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBNWVNRTF9EQVRBQkFTRT10ZXN0CiAgICAgIC0gTVlTUUxfQUxMT1dfRU1QVFlfUEFTU1dPUkQ9eWVzCiAgICB3aGVuOgogICAgICBldmVudDogWyBwdXNoLCB0YWcsIHB1bGxfcmVxdWVzdCBdCgogIHBnc3FsOgogICAgaW1hZ2U6IHBvc3RncmVzOjkuNQogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gUE9TVEdSRVNfREI9dGVzdAogICAgd2hlbjoKICAgICAgZXZlbnQ6IFsgcHVzaCwgdGFnLCBwdWxsX3JlcXVlc3QgXQo.C4Zwhff5mceN64UpDsVF263WmpDTnMMY0pcbB6KKdd0 |
||||
eyJhbGciOiJIUzI1NiJ9.d29ya3NwYWNlOgogIGJhc2U6IC9zcnYvYXBwCiAgcGF0aDogc3JjL2NvZGUuZ2l0ZWEuaW8vZ2l0ZWEKCnBpcGVsaW5lOgogIGNsb25lOgogICAgaW1hZ2U6IHBsdWdpbnMvZ2l0CiAgICB0YWdzOiB0cnVlCgogIHRlc3Q6CiAgICBpbWFnZTogd2ViaGlwcGllL2dvbGFuZzplZGdlCiAgICBwdWxsOiB0cnVlCiAgICBlbnZpcm9ubWVudDoKICAgICAgVEFHUzogYmluZGF0YSBzcWxpdGUKICAgICAgR09QQVRIOiAvc3J2L2FwcAogICAgY29tbWFuZHM6CiAgICAgIC0gYXBrIC1VIGFkZCBvcGVuc3NoLWNsaWVudAogICAgICAtIG1ha2UgY2xlYW4KICAgICAgLSBtYWtlIGdlbmVyYXRlCiAgICAgIC0gbWFrZSB2ZXQKICAgICAgLSBtYWtlIGxpbnQKICAgICAgLSBtYWtlIHRlc3QtdmVuZG9yCiAgICAgIC0gbWFrZSB0ZXN0CiAgICAgIC0gbWFrZSBidWlsZAogICAgd2hlbjoKICAgICAgZXZlbnQ6IFsgcHVzaCwgdGFnLCBwdWxsX3JlcXVlc3QgXQoKICB0ZXN0LXNxbGl0ZToKICAgIGltYWdlOiB3ZWJoaXBwaWUvZ29sYW5nOmVkZ2UKICAgIHB1bGw6IHRydWUKICAgIGVudmlyb25tZW50OgogICAgICBUQUdTOiBiaW5kYXRhCiAgICAgIEdPUEFUSDogL3Nydi9hcHAKICAgIGNvbW1hbmRzOgogICAgICAtIG1ha2UgdGVzdC1zcWxpdGUKICAgIHdoZW46CiAgICAgIGV2ZW50OiBbIHB1c2gsIHRhZywgcHVsbF9yZXF1ZXN0IF0KCiAgdGVzdC1teXNxbDoKICAgIGltYWdlOiB3ZWJoaXBwaWUvZ29sYW5nOmVkZ2UKICAgIHB1bGw6IHRydWUKICAgIGVudmlyb25tZW50OgogICAgICBUQUdTOiBiaW5kYXRhCiAgICAgIEdPUEFUSDogL3Nydi9hcHAKICAgIGNvbW1hbmRzOgogICAgICAtIGVjaG8gbWFrZSB0ZXN0LW15c3FsICMgTm90IHJlYWR5IHlldAogICAgd2hlbjoKICAgICAgZXZlbnQ6IFsgcHVzaCwgdGFnLCBwdWxsX3JlcXVlc3QgXQoKICB0ZXN0LXBnc3FsOgogICAgaW1hZ2U6IHdlYmhpcHBpZS9nb2xhbmc6ZWRnZQogICAgcHVsbDogdHJ1ZQogICAgZW52aXJvbm1lbnQ6CiAgICAgIFRBR1M6IGJpbmRhdGEKICAgICAgR09QQVRIOiAvc3J2L2FwcAogICAgY29tbWFuZHM6CiAgICAgIC0gZWNobyBtYWtlIHRlc3QtcHFzcWwgIyBOb3QgcmVhZHkgeWV0CiAgICB3aGVuOgogICAgICBldmVudDogWyBwdXNoLCB0YWcsIHB1bGxfcmVxdWVzdCBdCgogIHN0YXRpYzoKICAgIGltYWdlOiBrYXJhbGFiZS94Z28tbGF0ZXN0OmxhdGVzdAogICAgcHVsbDogdHJ1ZQogICAgZW52aXJvbm1lbnQ6CiAgICAgIFRBR1M6IGJpbmRhdGEgc3FsaXRlCiAgICAgIEdPUEFUSDogL3Nydi9hcHAKICAgIGNvbW1hbmRzOgogICAgICAtIG1ha2UgcmVsZWFzZQogICAgd2hlbjoKICAgICAgZXZlbnQ6IFsgcHVzaCwgdGFnLCBwdWxsX3JlcXVlc3QgXQoKICBjb3ZlcmFnZToKICAgIGltYWdlOiBwbHVnaW5zL2NvdmVyYWdlCiAgICBzZXJ2ZXI6IGh0dHBzOi8vY292ZXJhZ2UuZ2l0ZWEuaW8KICAgIHdoZW46CiAgICAgIGV2ZW50OiBbIHB1c2gsIHRhZywgcHVsbF9yZXF1ZXN0IF0KCiAgZG9ja2VyOgogICAgaW1hZ2U6IHBsdWdpbnMvZG9ja2VyCiAgICByZXBvOiBnaXRlYS9naXRlYQogICAgdGFnczogWyAnJHtEUk9ORV9UQUcjI3Z9JyBdCiAgICB3aGVuOgogICAgICBldmVudDogWyB0YWcgXQogICAgICBicmFuY2g6IFsgcmVmcy90YWdzLyogXQoKICBkb2NrZXI6CiAgICBpbWFnZTogcGx1Z2lucy9kb2NrZXIKICAgIHJlcG86IGdpdGVhL2dpdGVhCiAgICB0YWdzOiBbICcke0RST05FX0JSQU5DSCMjcmVsZWFzZS92fScgXQogICAgd2hlbjoKICAgICAgZXZlbnQ6IFsgcHVzaCBdCiAgICAgIGJyYW5jaDogWyByZWxlYXNlLyogXQoKICBkb2NrZXI6CiAgICBpbWFnZTogcGx1Z2lucy9kb2NrZXIKICAgIHJlcG86IGdpdGVhL2dpdGVhCiAgICB0YWdzOiBbICdsYXRlc3QnIF0KICAgIHdoZW46CiAgICAgIGV2ZW50OiBbIHB1c2ggXQogICAgICBicmFuY2g6IFsgbWFzdGVyIF0KCiAgcmVsZWFzZToKICAgIGltYWdlOiBwbHVnaW5zL3MzCiAgICBwYXRoX3N0eWxlOiB0cnVlCiAgICBzdHJpcF9wcmVmaXg6IGRpc3QvcmVsZWFzZS8KICAgIHNvdXJjZTogZGlzdC9yZWxlYXNlLyoKICAgIHRhcmdldDogL2dpdGVhLyR7RFJPTkVfVEFHIyN2fQogICAgd2hlbjoKICAgICAgZXZlbnQ6IFsgdGFnIF0KICAgICAgYnJhbmNoOiBbIHJlZnMvdGFncy8qIF0KCiAgcmVsZWFzZToKICAgIGltYWdlOiBwbHVnaW5zL3MzCiAgICBwYXRoX3N0eWxlOiB0cnVlCiAgICBzdHJpcF9wcmVmaXg6IGRpc3QvcmVsZWFzZS8KICAgIHNvdXJjZTogZGlzdC9yZWxlYXNlLyoKICAgIHRhcmdldDogL2dpdGVhLyR7RFJPTkVfQlJBTkNIIyNyZWxlYXNlL3Z9CiAgICB3aGVuOgogICAgICBldmVudDogWyBwdXNoIF0KICAgICAgYnJhbmNoOiBbIHJlbGVhc2UvKiBdCgogIHJlbGVhc2U6CiAgICBpbWFnZTogcGx1Z2lucy9zMwogICAgcGF0aF9zdHlsZTogdHJ1ZQogICAgc3RyaXBfcHJlZml4OiBkaXN0L3JlbGVhc2UvCiAgICBzb3VyY2U6IGRpc3QvcmVsZWFzZS8qCiAgICB0YXJnZXQ6IC9naXRlYS9tYXN0ZXIKICAgIHdoZW46CiAgICAgIGV2ZW50OiBbIHB1c2ggXQogICAgICBicmFuY2g6IFsgbWFzdGVyIF0KCiAgZ2l0aHViOgogICAgaW1hZ2U6IHBsdWdpbnMvZ2l0aHViLXJlbGVhc2UKICAgIGZpbGVzOgogICAgICAtIGRpc3QvcmVsZWFzZS8qCiAgICB3aGVuOgogICAgICBldmVudDogWyB0YWcgXQogICAgICBicmFuY2g6IFsgcmVmcy90YWdzLyogXQoKICBnaXR0ZXI6CiAgICBpbWFnZTogcGx1Z2lucy9naXR0ZXIKCnNlcnZpY2VzOgogIG15c3FsOgogICAgaW1hZ2U6IG15c3FsOjUuNwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gTVlTUUxfREFUQUJBU0U9dGVzdAogICAgICAtIE1ZU1FMX0FMTE9XX0VNUFRZX1BBU1NXT1JEPXllcwogICAgd2hlbjoKICAgICAgZXZlbnQ6IFsgcHVzaCwgdGFnLCBwdWxsX3JlcXVlc3QgXQoKICBwZ3NxbDoKICAgIGltYWdlOiBwb3N0Z3Jlczo5LjUKICAgIGVudmlyb25tZW50OgogICAgICAtIFBPU1RHUkVTX0RCPXRlc3QKICAgIHdoZW46CiAgICAgIGV2ZW50OiBbIHB1c2gsIHRhZywgcHVsbF9yZXF1ZXN0IF0K.dA2VK6LdoPXvBTYAUywWervhOZmgOjU32uiiPrBbVdQ |
@ -1,91 +0,0 @@ |
||||
// 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 integration |
||||
|
||||
import ( |
||||
"fmt" |
||||
"net/http" |
||||
"os" |
||||
"os/user" |
||||
"testing" |
||||
"time" |
||||
|
||||
"code.gitea.io/gitea/integrations/internal/utils" |
||||
) |
||||
|
||||
// The HTTP port listened by the Gitea server.
|
||||
const ServerHTTPPort = "3001" |
||||
|
||||
const _RetryLimit = 10 |
||||
|
||||
func makeSimpleSettings(user, port string) map[string][]string { |
||||
return map[string][]string{ |
||||
"db_type": {"SQLite3"}, |
||||
"db_host": {"localhost"}, |
||||
"db_path": {"data/gitea.db"}, |
||||
"app_name": {"Gitea: Git with a cup of tea"}, |
||||
"repo_root_path": {"repositories"}, |
||||
"run_user": {user}, |
||||
"domain": {"localhost"}, |
||||
"ssh_port": {"22"}, |
||||
"http_port": {port}, |
||||
"app_url": {"http://localhost:" + port}, |
||||
"log_root_path": {"log"}, |
||||
} |
||||
} |
||||
|
||||
func install(t *utils.T) error { |
||||
var r *http.Response |
||||
var err error |
||||
|
||||
for i := 1; i <= _RetryLimit; i++ { |
||||
|
||||
r, err = http.Get("http://:" + ServerHTTPPort + "/") |
||||
if err == nil { |
||||
fmt.Fprintln(os.Stderr) |
||||
break |
||||
} |
||||
|
||||
// Give the server some amount of time to warm up.
|
||||
time.Sleep(100 * time.Millisecond) |
||||
fmt.Fprint(os.Stderr, ".") |
||||
} |
||||
|
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
defer r.Body.Close() |
||||
|
||||
_user, err := user.Current() |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
settings := makeSimpleSettings(_user.Username, ServerHTTPPort) |
||||
r, err = http.PostForm("http://:"+ServerHTTPPort+"/install", settings) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
defer r.Body.Close() |
||||
|
||||
if r.StatusCode != http.StatusOK { |
||||
return fmt.Errorf("'/install': %s", r.Status) |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
func TestInstall(t *testing.T) { |
||||
conf := utils.Config{ |
||||
Program: "../gitea", |
||||
WorkDir: "", |
||||
Args: []string{"web", "--port", ServerHTTPPort}, |
||||
LogFile: os.Stderr, |
||||
} |
||||
|
||||
if err := utils.New(t, &conf).RunTest(install); err != nil { |
||||
t.Fatal(err) |
||||
} |
||||
} |
@ -0,0 +1,92 @@ |
||||
// 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" |
||||
"fmt" |
||||
"io" |
||||
"net/http" |
||||
"os" |
||||
"testing" |
||||
|
||||
"code.gitea.io/gitea/models" |
||||
"code.gitea.io/gitea/modules/setting" |
||||
"code.gitea.io/gitea/routers" |
||||
"code.gitea.io/gitea/routers/routes" |
||||
|
||||
"gopkg.in/macaron.v1" |
||||
"gopkg.in/testfixtures.v2" |
||||
) |
||||
|
||||
var mac *macaron.Macaron |
||||
|
||||
func TestMain(m *testing.M) { |
||||
appIniPath := os.Getenv("GITEA_CONF") |
||||
if appIniPath == "" { |
||||
fmt.Println("Environment variable $GITEA_CONF not set") |
||||
os.Exit(1) |
||||
} |
||||
setting.CustomConf = appIniPath |
||||
routers.GlobalInit() |
||||
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{} |
||||
} else { |
||||
fmt.Println("Unsupported RDBMS for integration tests") |
||||
os.Exit(1) |
||||
} |
||||
|
||||
err := models.InitFixtures( |
||||
helper, |
||||
"integrations/gitea-integration/fixtures/", |
||||
) |
||||
if err != nil { |
||||
fmt.Printf("Error initializing test database: %v\n", err) |
||||
os.Exit(1) |
||||
} |
||||
os.Exit(m.Run()) |
||||
} |
||||
|
||||
type TestResponseWriter struct { |
||||
HeaderCode int |
||||
Writer io.Writer |
||||
} |
||||
|
||||
func (w *TestResponseWriter) Header() http.Header { |
||||
return make(map[string][]string) |
||||
} |
||||
|
||||
func (w *TestResponseWriter) Write(b []byte) (int, error) { |
||||
return w.Writer.Write(b) |
||||
} |
||||
|
||||
func (w *TestResponseWriter) WriteHeader(n int) { |
||||
w.HeaderCode = n |
||||
} |
||||
|
||||
type TestResponse struct { |
||||
HeaderCode int |
||||
Body []byte |
||||
} |
||||
|
||||
func MakeRequest(req *http.Request) *TestResponse { |
||||
buffer := bytes.NewBuffer(nil) |
||||
respWriter := &TestResponseWriter{ |
||||
Writer: buffer, |
||||
} |
||||
mac.ServeHTTP(respWriter, req) |
||||
return &TestResponse{ |
||||
HeaderCode: respWriter.HeaderCode, |
||||
Body: buffer.Bytes(), |
||||
} |
||||
} |
@ -1,156 +0,0 @@ |
||||
// 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 utils |
||||
|
||||
import ( |
||||
"errors" |
||||
"fmt" |
||||
"io" |
||||
"io/ioutil" |
||||
"log" |
||||
"net/http" |
||||
"os" |
||||
"os/exec" |
||||
"path/filepath" |
||||
"syscall" |
||||
"testing" |
||||
) |
||||
|
||||
// T wraps testing.T and the configurations of the testing instance.
|
||||
type T struct { |
||||
*testing.T |
||||
Config *Config |
||||
} |
||||
|
||||
// New create an instance of T
|
||||
func New(t *testing.T, c *Config) *T { |
||||
return &T{T: t, Config: c} |
||||
} |
||||
|
||||
// Config Settings of the testing program
|
||||
type Config struct { |
||||
// The executable path of the tested program.
|
||||
Program string |
||||
// Working directory prepared for the tested program.
|
||||
// If empty, a directory named with random suffixes is picked, and created under the platform-dependent default temporary directory.
|
||||
// The directory will be removed when the test finishes.
|
||||
WorkDir string |
||||
// Command-line arguments passed to the tested program.
|
||||
Args []string |
||||
|
||||
// Where to redirect the stdout/stderr to. For debugging purposes.
|
||||
LogFile *os.File |
||||
} |
||||
|
||||
func redirect(cmd *exec.Cmd, f *os.File) error { |
||||
stdout, err := cmd.StdoutPipe() |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
stderr, err := cmd.StderrPipe() |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
go io.Copy(f, stdout) |
||||
go io.Copy(f, stderr) |
||||
return nil |
||||
} |
||||
|
||||
// RunTest Helper function for setting up a running Gitea server for functional testing and then gracefully terminating it.
|
||||
func (t *T) RunTest(tests ...func(*T) error) (err error) { |
||||
if t.Config.Program == "" { |
||||
return errors.New("Need input file") |
||||
} |
||||
|
||||
path, err := filepath.Abs(t.Config.Program) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
workdir := t.Config.WorkDir |
||||
if workdir == "" { |
||||
workdir, err = ioutil.TempDir(os.TempDir(), "gitea_tests-") |
||||
if err != nil { |
||||
return err |
||||
} |
||||
defer os.RemoveAll(workdir) |
||||
} |
||||
|
||||
newpath := filepath.Join(workdir, filepath.Base(path)) |
||||
if err := os.Symlink(path, newpath); err != nil { |
||||
return err |
||||
} |
||||
|
||||
log.Printf("Starting the server: %s args:%s workdir:%s", newpath, t.Config.Args, workdir) |
||||
|
||||
cmd := exec.Command(newpath, t.Config.Args...) |
||||
cmd.Dir = workdir |
||||
|
||||
if t.Config.LogFile != nil && testing.Verbose() { |
||||
if err := redirect(cmd, t.Config.LogFile); err != nil { |
||||
return err |
||||
} |
||||
} |
||||
|
||||
if err := cmd.Start(); err != nil { |
||||
return err |
||||
} |
||||
|
||||
log.Println("Server started.") |
||||
|
||||
defer func() { |
||||
// Do not early return. We have to call Wait anyway.
|
||||
_ = cmd.Process.Signal(syscall.SIGTERM) |
||||
|
||||
if _err := cmd.Wait(); _err != nil { |
||||
if _err.Error() != "signal: terminated" { |
||||
err = _err |
||||
return |
||||
} |
||||
} |
||||
|
||||
log.Println("Server exited") |
||||
}() |
||||
|
||||
for _, fn := range tests { |
||||
if err := fn(t); err != nil { |
||||
return err |
||||
} |
||||
} |
||||
|
||||
// Note that the return value 'err' may be updated by the 'defer' statement before despite it's returning nil here.
|
||||
return nil |
||||
} |
||||
|
||||
// GetAndPost provides a convenient helper function for testing an HTTP endpoint with GET and POST method.
|
||||
// The function sends GET first and then POST with the given form.
|
||||
func GetAndPost(url string, form map[string][]string) error { |
||||
var err error |
||||
var r *http.Response |
||||
|
||||
r, err = http.Get(url) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
defer r.Body.Close() |
||||
|
||||
if r.StatusCode != http.StatusOK { |
||||
return fmt.Errorf("GET '%s': %s", url, r.Status) |
||||
} |
||||
|
||||
r, err = http.PostForm(url, form) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
defer r.Body.Close() |
||||
|
||||
if r.StatusCode != http.StatusOK { |
||||
return fmt.Errorf("POST '%s': %s", url, r.Status) |
||||
} |
||||
|
||||
return nil |
||||
} |
@ -0,0 +1,56 @@ |
||||
APP_NAME = Gitea: Git with a cup of tea |
||||
RUN_MODE = prod |
||||
|
||||
[database] |
||||
DB_TYPE = mysql |
||||
HOST = 127.0.0.1:3306 |
||||
NAME = testgitea |
||||
USER = root |
||||
PASSWD = |
||||
SSL_MODE = disable |
||||
PATH = data/gitea.db |
||||
|
||||
[repository] |
||||
ROOT = integrations/gitea-integration/gitea-repositories |
||||
|
||||
[server] |
||||
SSH_DOMAIN = localhost |
||||
HTTP_PORT = 3000 |
||||
ROOT_URL = http://localhost:3000/ |
||||
DISABLE_SSH = false |
||||
SSH_PORT = 22 |
||||
LFS_START_SERVER = false |
||||
OFFLINE_MODE = false |
||||
|
||||
[mailer] |
||||
ENABLED = false |
||||
|
||||
[service] |
||||
REGISTER_EMAIL_CONFIRM = false |
||||
ENABLE_NOTIFY_MAIL = false |
||||
DISABLE_REGISTRATION = false |
||||
ENABLE_CAPTCHA = false |
||||
REQUIRE_SIGNIN_VIEW = false |
||||
DEFAULT_KEEP_EMAIL_PRIVATE = false |
||||
NO_REPLY_ADDRESS = noreply.example.org |
||||
|
||||
[picture] |
||||
DISABLE_GRAVATAR = false |
||||
ENABLE_FEDERATED_AVATAR = false |
||||
|
||||
[session] |
||||
PROVIDER = file |
||||
|
||||
[log] |
||||
MODE = console,file |
||||
|
||||
[log.console] |
||||
LEVEL = Warn |
||||
|
||||
[log.file] |
||||
LEVEL = Info |
||||
ROOT_PATH = log |
||||
|
||||
[security] |
||||
INSTALL_LOCK = true |
||||
SECRET_KEY = 9pCviYTWSb |
@ -0,0 +1,56 @@ |
||||
APP_NAME = Gitea: Git with a cup of tea |
||||
RUN_MODE = prod |
||||
|
||||
[database] |
||||
DB_TYPE = postgres |
||||
HOST = 127.0.0.1:5432 |
||||
NAME = testgitea |
||||
USER = postgres |
||||
PASSWD = postgres |
||||
SSL_MODE = disable |
||||
PATH = data/gitea.db |
||||
|
||||
[repository] |
||||
ROOT = integrations/gitea-integration/gitea-repositories |
||||
|
||||
[server] |
||||
SSH_DOMAIN = localhost |
||||
HTTP_PORT = 3000 |
||||
ROOT_URL = http://localhost:3000/ |
||||
DISABLE_SSH = false |
||||
SSH_PORT = 22 |
||||
LFS_START_SERVER = false |
||||
OFFLINE_MODE = false |
||||
|
||||
[mailer] |
||||
ENABLED = false |
||||
|
||||
[service] |
||||
REGISTER_EMAIL_CONFIRM = false |
||||
ENABLE_NOTIFY_MAIL = false |
||||
DISABLE_REGISTRATION = false |
||||
ENABLE_CAPTCHA = false |
||||
REQUIRE_SIGNIN_VIEW = false |
||||
DEFAULT_KEEP_EMAIL_PRIVATE = false |
||||
NO_REPLY_ADDRESS = noreply.example.org |
||||
|
||||
[picture] |
||||
DISABLE_GRAVATAR = false |
||||
ENABLE_FEDERATED_AVATAR = false |
||||
|
||||
[session] |
||||
PROVIDER = file |
||||
|
||||
[log] |
||||
MODE = console,file |
||||
|
||||
[log.console] |
||||
LEVEL = Warn |
||||
|
||||
[log.file] |
||||
LEVEL = Info |
||||
ROOT_PATH = log |
||||
|
||||
[security] |
||||
INSTALL_LOCK = true |
||||
SECRET_KEY = 9pCviYTWSb |
@ -0,0 +1,58 @@ |
||||
APP_NAME = Gitea: Git with a cup of tea |
||||
RUN_MODE = prod |
||||
|
||||
[database] |
||||
DB_TYPE = sqlite3 |
||||
HOST = 127.0.0.1:3306 |
||||
NAME = testgitea |
||||
USER = gitea |
||||
PASSWD = |
||||
SSL_MODE = disable |
||||
PATH = :memory: |
||||
|
||||
[repository] |
||||
ROOT = integrations/gitea-integration/gitea-repositories |
||||
|
||||
[server] |
||||
SSH_DOMAIN = localhost |
||||
HTTP_PORT = 3000 |
||||
ROOT_URL = http://localhost:3000/ |
||||
DISABLE_SSH = false |
||||
SSH_PORT = 22 |
||||
LFS_START_SERVER = false |
||||
OFFLINE_MODE = false |
||||
|
||||
[mailer] |
||||
ENABLED = false |
||||
|
||||
[service] |
||||
REGISTER_EMAIL_CONFIRM = false |
||||
ENABLE_NOTIFY_MAIL = false |
||||
DISABLE_REGISTRATION = false |
||||
ENABLE_CAPTCHA = false |
||||
REQUIRE_SIGNIN_VIEW = false |
||||
DEFAULT_KEEP_EMAIL_PRIVATE = false |
||||
NO_REPLY_ADDRESS = noreply.example.org |
||||
|
||||
[picture] |
||||
DISABLE_GRAVATAR = false |
||||
ENABLE_FEDERATED_AVATAR = false |
||||
|
||||
[session] |
||||
PROVIDER = file |
||||
|
||||
[log] |
||||
MODE = console,file |
||||
|
||||
[log.console] |
||||
LEVEL = Warn |
||||
|
||||
[log.file] |
||||
LEVEL = Info |
||||
ROOT_PATH = log |
||||
|
||||
[security] |
||||
INSTALL_LOCK = true |
||||
SECRET_KEY = 9pCviYTWSb |
||||
INTERNAL_TOKEN = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE0OTI3OTU5ODN9.OQkH5UmzID2XBdwQ9TAI6Jj2t1X-wElVTjbE7aoN4I8 |
||||
|
@ -0,0 +1,32 @@ |
||||
// 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 ( |
||||
"net/http" |
||||
"testing" |
||||
|
||||
"code.gitea.io/gitea/models" |
||||
|
||||
"github.com/stretchr/testify/assert" |
||||
) |
||||
|
||||
func TestViewRepo(t *testing.T) { |
||||
assert.NoError(t, models.LoadFixtures()) |
||||
|
||||
req, err := http.NewRequest("GET", "/user1/repo1", nil) |
||||
assert.NoError(t, err) |
||||
resp := MakeRequest(req) |
||||
assert.EqualValues(t, http.StatusOK, resp.HeaderCode) |
||||
} |
||||
|
||||
func TestViewUser(t *testing.T) { |
||||
assert.NoError(t, models.LoadFixtures()) |
||||
|
||||
req, err := http.NewRequest("GET", "/user1", nil) |
||||
assert.NoError(t, err) |
||||
resp := MakeRequest(req) |
||||
assert.EqualValues(t, http.StatusOK, resp.HeaderCode) |
||||
} |
@ -0,0 +1,23 @@ |
||||
// 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 models |
||||
|
||||
import ( |
||||
"gopkg.in/testfixtures.v2" |
||||
) |
||||
|
||||
var fixtures *testfixtures.Context |
||||
|
||||
// InitFixtures initialize test fixtures for a test database
|
||||
func InitFixtures(helper testfixtures.Helper, dir string) (err error) { |
||||
testfixtures.SkipDatabaseNameCheck(true) |
||||
fixtures, err = testfixtures.NewFolder(x.DB().DB, helper, dir) |
||||
return err |
||||
} |
||||
|
||||
// LoadFixtures load fixtures for a test database
|
||||
func LoadFixtures() error { |
||||
return fixtures.Load() |
||||
} |
@ -0,0 +1,645 @@ |
||||
// 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 routes |
||||
|
||||
import ( |
||||
"os" |
||||
"path" |
||||
|
||||
"code.gitea.io/gitea/models" |
||||
"code.gitea.io/gitea/modules/auth" |
||||
"code.gitea.io/gitea/modules/context" |
||||
"code.gitea.io/gitea/modules/lfs" |
||||
"code.gitea.io/gitea/modules/log" |
||||
"code.gitea.io/gitea/modules/options" |
||||
"code.gitea.io/gitea/modules/public" |
||||
"code.gitea.io/gitea/modules/setting" |
||||
"code.gitea.io/gitea/modules/templates" |
||||
"code.gitea.io/gitea/modules/validation" |
||||
"code.gitea.io/gitea/routers" |
||||
"code.gitea.io/gitea/routers/admin" |
||||
apiv1 "code.gitea.io/gitea/routers/api/v1" |
||||
"code.gitea.io/gitea/routers/dev" |
||||
"code.gitea.io/gitea/routers/org" |
||||
"code.gitea.io/gitea/routers/private" |
||||
"code.gitea.io/gitea/routers/repo" |
||||
"code.gitea.io/gitea/routers/user" |
||||
|
||||
"github.com/go-macaron/binding" |
||||
"github.com/go-macaron/cache" |
||||
"github.com/go-macaron/captcha" |
||||
"github.com/go-macaron/csrf" |
||||
"github.com/go-macaron/gzip" |
||||
"github.com/go-macaron/i18n" |
||||
"github.com/go-macaron/session" |
||||
"github.com/go-macaron/toolbox" |
||||
"gopkg.in/macaron.v1" |
||||
) |
||||
|
||||
// NewMacaron initializes Macaron instance.
|
||||
func NewMacaron() *macaron.Macaron { |
||||
m := macaron.New() |
||||
if !setting.DisableRouterLog { |
||||
m.Use(macaron.Logger()) |
||||
} |
||||
m.Use(macaron.Recovery()) |
||||
if setting.EnableGzip { |
||||
m.Use(gzip.Gziper()) |
||||
} |
||||
if setting.Protocol == setting.FCGI { |
||||
m.SetURLPrefix(setting.AppSubURL) |
||||
} |
||||
m.Use(public.Custom( |
||||
&public.Options{ |
||||
SkipLogging: setting.DisableRouterLog, |
||||
}, |
||||
)) |
||||
m.Use(public.Static( |
||||
&public.Options{ |
||||
Directory: path.Join(setting.StaticRootPath, "public"), |
||||
SkipLogging: setting.DisableRouterLog, |
||||
}, |
||||
)) |
||||
m.Use(macaron.Static( |
||||
setting.AvatarUploadPath, |
||||
macaron.StaticOptions{ |
||||
Prefix: "avatars", |
||||
SkipLogging: setting.DisableRouterLog, |
||||
ETag: true, |
||||
}, |
||||
)) |
||||
|
||||
m.Use(templates.Renderer()) |
||||
models.InitMailRender(templates.Mailer()) |
||||
|
||||
localeNames, err := options.Dir("locale") |
||||
|
||||
if err != nil { |
||||
log.Fatal(4, "Failed to list locale files: %v", err) |
||||
} |
||||
|
||||
localFiles := make(map[string][]byte) |
||||
|
||||
for _, name := range localeNames { |
||||
localFiles[name], err = options.Locale(name) |
||||
|
||||
if err != nil { |
||||
log.Fatal(4, "Failed to load %s locale file. %v", name, err) |
||||
} |
||||
} |
||||
|
||||
m.Use(i18n.I18n(i18n.Options{ |
||||
SubURL: setting.AppSubURL, |
||||
Files: localFiles, |
||||
Langs: setting.Langs, |
||||
Names: setting.Names, |
||||
DefaultLang: "en-US", |
||||
Redirect: true, |
||||
})) |
||||
m.Use(cache.Cacher(cache.Options{ |
||||
Adapter: setting.CacheAdapter, |
||||
AdapterConfig: setting.CacheConn, |
||||
Interval: setting.CacheInterval, |
||||
})) |
||||
m.Use(captcha.Captchaer(captcha.Options{ |
||||
SubURL: setting.AppSubURL, |
||||
})) |
||||
m.Use(session.Sessioner(setting.SessionConfig)) |
||||
m.Use(csrf.Csrfer(csrf.Options{ |
||||
Secret: setting.SecretKey, |
||||
Cookie: setting.CSRFCookieName, |
||||
SetCookie: true, |
||||
Header: "X-Csrf-Token", |
||||
CookiePath: setting.AppSubURL, |
||||
})) |
||||
m.Use(toolbox.Toolboxer(m, toolbox.Options{ |
||||
HealthCheckFuncs: []*toolbox.HealthCheckFuncDesc{ |
||||
{ |
||||
Desc: "Database connection", |
||||
Func: models.Ping, |
||||
}, |
||||
}, |
||||
})) |
||||
m.Use(context.Contexter()) |
||||
return m |
||||
} |
||||
|
||||
// RegisterRoutes routes routes to Macaron
|
||||
func RegisterRoutes(m *macaron.Macaron) { |
||||
reqSignIn := context.Toggle(&context.ToggleOptions{SignInRequired: true}) |
||||
ignSignIn := context.Toggle(&context.ToggleOptions{SignInRequired: setting.Service.RequireSignInView}) |
||||
ignSignInAndCsrf := context.Toggle(&context.ToggleOptions{DisableCSRF: true}) |
||||
reqSignOut := context.Toggle(&context.ToggleOptions{SignOutRequired: true}) |
||||
|
||||
bindIgnErr := binding.BindIgnErr |
||||
validation.AddBindingRules() |
||||
|
||||
m.Use(user.GetNotificationCount) |
||||
|
||||
// FIXME: not all routes need go through same middlewares.
|
||||
// Especially some AJAX requests, we can reduce middleware number to improve performance.
|
||||
// Routers.
|
||||
// for health check
|
||||
m.Head("/", func() string { |
||||
return "" |
||||
}) |
||||
m.Get("/", ignSignIn, routers.Home) |
||||
m.Group("/explore", func() { |
||||
m.Get("", func(ctx *context.Context) { |
||||
ctx.Redirect(setting.AppSubURL + "/explore/repos") |
||||
}) |
||||
m.Get("/repos", routers.ExploreRepos) |
||||
m.Get("/users", routers.ExploreUsers) |
||||
m.Get("/organizations", routers.ExploreOrganizations) |
||||
}, ignSignIn) |
||||
m.Combo("/install", routers.InstallInit).Get(routers.Install). |
||||
Post(bindIgnErr(auth.InstallForm{}), routers.InstallPost) |
||||
m.Get("/^:type(issues|pulls)$", reqSignIn, user.Issues) |
||||
|
||||
// ***** START: User *****
|
||||
m.Group("/user", func() { |
||||
m.Get("/login", user.SignIn) |
||||
m.Post("/login", bindIgnErr(auth.SignInForm{}), user.SignInPost) |
||||
if setting.Service.EnableOpenIDSignIn { |
||||
m.Combo("/login/openid"). |
||||
Get(user.SignInOpenID). |
||||
Post(bindIgnErr(auth.SignInOpenIDForm{}), user.SignInOpenIDPost) |
||||
m.Group("/openid", func() { |
||||
m.Combo("/connect"). |
||||
Get(user.ConnectOpenID). |
||||
Post(bindIgnErr(auth.ConnectOpenIDForm{}), user.ConnectOpenIDPost) |
||||
m.Combo("/routes"). |
||||
Get(user.RegisterOpenID). |
||||
Post(bindIgnErr(auth.SignUpOpenIDForm{}), user.RegisterOpenIDPost) |
||||
}) |
||||
} |
||||
m.Get("/sign_up", user.SignUp) |
||||
m.Post("/sign_up", bindIgnErr(auth.RegisterForm{}), user.SignUpPost) |
||||
m.Get("/reset_password", user.ResetPasswd) |
||||
m.Post("/reset_password", user.ResetPasswdPost) |
||||
m.Group("/oauth2", func() { |
||||
m.Get("/:provider", user.SignInOAuth) |
||||
m.Get("/:provider/callback", user.SignInOAuthCallback) |
||||
}) |
||||
m.Get("/link_account", user.LinkAccount) |
||||
m.Post("/link_account_signin", bindIgnErr(auth.SignInForm{}), user.LinkAccountPostSignIn) |
||||
m.Post("/link_account_signup", bindIgnErr(auth.RegisterForm{}), user.LinkAccountPostRegister) |
||||
m.Group("/two_factor", func() { |
||||
m.Get("", user.TwoFactor) |
||||
m.Post("", bindIgnErr(auth.TwoFactorAuthForm{}), user.TwoFactorPost) |
||||
m.Get("/scratch", user.TwoFactorScratch) |
||||
m.Post("/scratch", bindIgnErr(auth.TwoFactorScratchAuthForm{}), user.TwoFactorScratchPost) |
||||
}) |
||||
}, reqSignOut) |
||||
|
||||
m.Group("/user/settings", func() { |
||||
m.Get("", user.Settings) |
||||
m.Post("", bindIgnErr(auth.UpdateProfileForm{}), user.SettingsPost) |
||||
m.Combo("/avatar").Get(user.SettingsAvatar). |
||||
Post(binding.MultipartForm(auth.AvatarForm{}), user.SettingsAvatarPost) |
||||
m.Post("/avatar/delete", user.SettingsDeleteAvatar) |
||||
m.Combo("/email").Get(user.SettingsEmails). |
||||
Post(bindIgnErr(auth.AddEmailForm{}), user.SettingsEmailPost) |
||||
m.Post("/email/delete", user.DeleteEmail) |
||||
m.Get("/password", user.SettingsPassword) |
||||
m.Post("/password", bindIgnErr(auth.ChangePasswordForm{}), user.SettingsPasswordPost) |
||||
if setting.Service.EnableOpenIDSignIn { |
||||
m.Group("/openid", func() { |
||||
m.Combo("").Get(user.SettingsOpenID). |
||||
Post(bindIgnErr(auth.AddOpenIDForm{}), user.SettingsOpenIDPost) |
||||
m.Post("/delete", user.DeleteOpenID) |
||||
m.Post("/toggle_visibility", user.ToggleOpenIDVisibility) |
||||
}) |
||||
} |
||||
|
||||
m.Combo("/ssh").Get(user.SettingsSSHKeys). |
||||
Post(bindIgnErr(auth.AddSSHKeyForm{}), user.SettingsSSHKeysPost) |
||||
m.Post("/ssh/delete", user.DeleteSSHKey) |
||||
m.Combo("/applications").Get(user.SettingsApplications). |
||||
Post(bindIgnErr(auth.NewAccessTokenForm{}), user.SettingsApplicationsPost) |
||||
m.Post("/applications/delete", user.SettingsDeleteApplication) |
||||
m.Route("/delete", "GET,POST", user.SettingsDelete) |
||||
m.Combo("/account_link").Get(user.SettingsAccountLinks).Post(user.SettingsDeleteAccountLink) |
||||
m.Group("/two_factor", func() { |
||||
m.Get("", user.SettingsTwoFactor) |
||||
m.Post("/regenerate_scratch", user.SettingsTwoFactorRegenerateScratch) |
||||
m.Post("/disable", user.SettingsTwoFactorDisable) |
||||
m.Get("/enroll", user.SettingsTwoFactorEnroll) |
||||
m.Post("/enroll", bindIgnErr(auth.TwoFactorAuthForm{}), user.SettingsTwoFactorEnrollPost) |
||||
}) |
||||
}, reqSignIn, func(ctx *context.Context) { |
||||
ctx.Data["PageIsUserSettings"] = true |
||||
}) |
||||
|
||||
m.Group("/user", func() { |
||||
// r.Get("/feeds", binding.Bind(auth.FeedsForm{}), user.Feeds)
|
||||
m.Any("/activate", user.Activate) |
||||
m.Any("/activate_email", user.ActivateEmail) |
||||
m.Get("/email2user", user.Email2User) |
||||
m.Get("/forgot_password", user.ForgotPasswd) |
||||
m.Post("/forgot_password", user.ForgotPasswdPost) |
||||
m.Get("/logout", user.SignOut) |
||||
}) |
||||
// ***** END: User *****
|
||||
|
||||
adminReq := context.Toggle(&context.ToggleOptions{SignInRequired: true, AdminRequired: true}) |
||||
|
||||
// ***** START: Admin *****
|
||||
m.Group("/admin", func() { |
||||
m.Get("", adminReq, admin.Dashboard) |
||||
m.Get("/config", admin.Config) |
||||
m.Post("/config/test_mail", admin.SendTestMail) |
||||
m.Get("/monitor", admin.Monitor) |
||||
|
||||
m.Group("/users", func() { |
||||
m.Get("", admin.Users) |
||||
m.Combo("/new").Get(admin.NewUser).Post(bindIgnErr(auth.AdminCreateUserForm{}), admin.NewUserPost) |
||||
m.Combo("/:userid").Get(admin.EditUser).Post(bindIgnErr(auth.AdminEditUserForm{}), admin.EditUserPost) |
||||
m.Post("/:userid/delete", admin.DeleteUser) |
||||
}) |
||||
|
||||
m.Group("/orgs", func() { |
||||
m.Get("", admin.Organizations) |
||||
}) |
||||
|
||||
m.Group("/repos", func() { |
||||
m.Get("", admin.Repos) |
||||
m.Post("/delete", admin.DeleteRepo) |
||||
}) |
||||
|
||||
m.Group("/auths", func() { |
||||
m.Get("", admin.Authentications) |
||||
m.Combo("/new").Get(admin.NewAuthSource).Post(bindIgnErr(auth.AuthenticationForm{}), admin.NewAuthSourcePost) |
||||
m.Combo("/:authid").Get(admin.EditAuthSource). |
||||
Post(bindIgnErr(auth.AuthenticationForm{}), admin.EditAuthSourcePost) |
||||
m.Post("/:authid/delete", admin.DeleteAuthSource) |
||||
}) |
||||
|
||||
m.Group("/notices", func() { |
||||
m.Get("", admin.Notices) |
||||
m.Post("/delete", admin.DeleteNotices) |
||||
m.Get("/empty", admin.EmptyNotices) |
||||
}) |
||||
}, adminReq) |
||||
// ***** END: Admin *****
|
||||
|
||||
m.Group("", func() { |
||||
m.Group("/:username", func() { |
||||
m.Get("", user.Profile) |
||||
m.Get("/followers", user.Followers) |
||||
m.Get("/following", user.Following) |
||||
}) |
||||
|
||||
m.Get("/attachments/:uuid", func(ctx *context.Context) { |
||||
attach, err := models.GetAttachmentByUUID(ctx.Params(":uuid")) |
||||
if err != nil { |
||||
if models.IsErrAttachmentNotExist(err) { |
||||
ctx.Error(404) |
||||
} else { |
||||
ctx.Handle(500, "GetAttachmentByUUID", err) |
||||
} |
||||
return |
||||
} |
||||
|
||||
fr, err := os.Open(attach.LocalPath()) |
||||
if err != nil { |
||||
ctx.Handle(500, "Open", err) |
||||
return |
||||
} |
||||
defer fr.Close() |
||||
|
||||
if err := attach.IncreaseDownloadCount(); err != nil { |
||||
ctx.Handle(500, "Update", err) |
||||
return |
||||
} |
||||
|
||||
if err = repo.ServeData(ctx, attach.Name, fr); err != nil { |
||||
ctx.Handle(500, "ServeData", err) |
||||
return |
||||
} |
||||
}) |
||||
m.Post("/attachments", repo.UploadAttachment) |
||||
}, ignSignIn) |
||||
|
||||
m.Group("/:username", func() { |
||||
m.Get("/action/:action", user.Action) |
||||
}, reqSignIn) |
||||
|
||||
if macaron.Env == macaron.DEV { |
||||
m.Get("/template/*", dev.TemplatePreview) |
||||
} |
||||
|
||||
reqRepoAdmin := context.RequireRepoAdmin() |
||||
reqRepoWriter := context.RequireRepoWriter() |
||||
|
||||
// ***** START: Organization *****
|
||||
m.Group("/org", func() { |
||||
m.Group("", func() { |
||||
m.Get("/create", org.Create) |
||||
m.Post("/create", bindIgnErr(auth.CreateOrgForm{}), org.CreatePost) |
||||
}, func(ctx *context.Context) { |
||||
if !ctx.User.CanCreateOrganization() { |
||||
ctx.NotFound() |
||||
} |
||||
}) |
||||
|
||||
m.Group("/:org", func() { |
||||
m.Get("/dashboard", user.Dashboard) |
||||
m.Get("/^:type(issues|pulls)$", user.Issues) |
||||
m.Get("/members", org.Members) |
||||
m.Get("/members/action/:action", org.MembersAction) |
||||
|
||||
m.Get("/teams", org.Teams) |
||||
}, context.OrgAssignment(true)) |
||||
|
||||
m.Group("/:org", func() { |
||||
m.Get("/teams/:team", org.TeamMembers) |
||||
m.Get("/teams/:team/repositories", org.TeamRepositories) |
||||
m.Route("/teams/:team/action/:action", "GET,POST", org.TeamsAction) |
||||
m.Route("/teams/:team/action/repo/:action", "GET,POST", org.TeamsRepoAction) |
||||
}, context.OrgAssignment(true, false, true)) |
||||
|
||||
m.Group("/:org", func() { |
||||
m.Get("/teams/new", org.NewTeam) |
||||
m.Post("/teams/new", bindIgnErr(auth.CreateTeamForm{}), org.NewTeamPost) |
||||
m.Get("/teams/:team/edit", org.EditTeam) |
||||
m.Post("/teams/:team/edit", bindIgnErr(auth.CreateTeamForm{}), org.EditTeamPost) |
||||
m.Post("/teams/:team/delete", org.DeleteTeam) |
||||
|
||||
m.Group("/settings", func() { |
||||
m.Combo("").Get(org.Settings). |
||||
Post(bindIgnErr(auth.UpdateOrgSettingForm{}), org.SettingsPost) |
||||
m.Post("/avatar", binding.MultipartForm(auth.AvatarForm{}), org.SettingsAvatar) |
||||
m.Post("/avatar/delete", org.SettingsDeleteAvatar) |
||||
|
||||
m.Group("/hooks", func() { |
||||
m.Get("", org.Webhooks) |
||||
m.Post("/delete", org.DeleteWebhook) |
||||
m.Get("/:type/new", repo.WebhooksNew) |
||||
m.Post("/gogs/new", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksNewPost) |
||||
m.Post("/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost) |
||||
m.Get("/:id", repo.WebHooksEdit) |
||||
m.Post("/gogs/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost) |
||||
m.Post("/slack/:id", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksEditPost) |
||||
}) |
||||
|
||||
m.Route("/delete", "GET,POST", org.SettingsDelete) |
||||
}) |
||||
|
||||
m.Route("/invitations/new", "GET,POST", org.Invitation) |
||||
}, context.OrgAssignment(true, true)) |
||||
}, reqSignIn) |
||||
// ***** END: Organization *****
|
||||
|
||||
// ***** START: Repository *****
|
||||
m.Group("/repo", func() { |
||||
m.Get("/create", repo.Create) |
||||
m.Post("/create", bindIgnErr(auth.CreateRepoForm{}), repo.CreatePost) |
||||
m.Get("/migrate", repo.Migrate) |
||||
m.Post("/migrate", bindIgnErr(auth.MigrateRepoForm{}), repo.MigratePost) |
||||
m.Combo("/fork/:repoid").Get(repo.Fork). |
||||
Post(bindIgnErr(auth.CreateRepoForm{}), repo.ForkPost) |
||||
}, reqSignIn) |
||||
|
||||
m.Group("/:username/:reponame", func() { |
||||
m.Group("/settings", func() { |
||||
m.Combo("").Get(repo.Settings). |
||||
Post(bindIgnErr(auth.RepoSettingForm{}), repo.SettingsPost) |
||||
m.Group("/collaboration", func() { |
||||
m.Combo("").Get(repo.Collaboration).Post(repo.CollaborationPost) |
||||
m.Post("/access_mode", repo.ChangeCollaborationAccessMode) |
||||
m.Post("/delete", repo.DeleteCollaboration) |
||||
}) |
||||
m.Group("/branches", func() { |
||||
m.Combo("").Get(repo.ProtectedBranch).Post(repo.ProtectedBranchPost) |
||||
m.Post("/can_push", repo.ChangeProtectedBranch) |
||||
m.Post("/delete", repo.DeleteProtectedBranch) |
||||
}, repo.MustBeNotBare) |
||||
|
||||
m.Group("/hooks", func() { |
||||
m.Get("", repo.Webhooks) |
||||
m.Post("/delete", repo.DeleteWebhook) |
||||
m.Get("/:type/new", repo.WebhooksNew) |
||||
m.Post("/gogs/new", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksNewPost) |
||||
m.Post("/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost) |
||||
m.Get("/:id", repo.WebHooksEdit) |
||||
m.Post("/:id/test", repo.TestWebhook) |
||||
m.Post("/gogs/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost) |
||||
m.Post("/slack/:id", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksEditPost) |
||||
|
||||
m.Group("/git", func() { |
||||
m.Get("", repo.GitHooks) |
||||
m.Combo("/:name").Get(repo.GitHooksEdit). |
||||
Post(repo.GitHooksEditPost) |
||||
}, context.GitHookService()) |
||||
}) |
||||
|
||||
m.Group("/keys", func() { |
||||
m.Combo("").Get(repo.DeployKeys). |
||||
Post(bindIgnErr(auth.AddSSHKeyForm{}), repo.DeployKeysPost) |
||||
m.Post("/delete", repo.DeleteDeployKey) |
||||
}) |
||||
|
||||
}, func(ctx *context.Context) { |
||||
ctx.Data["PageIsSettings"] = true |
||||
}, context.UnitTypes()) |
||||
}, reqSignIn, context.RepoAssignment(), reqRepoAdmin, context.RepoRef()) |
||||
|
||||
m.Get("/:username/:reponame/action/:action", reqSignIn, context.RepoAssignment(), repo.Action) |
||||
m.Group("/:username/:reponame", func() { |
||||
// FIXME: should use different URLs but mostly same logic for comments of issue and pull reuqest.
|
||||
// So they can apply their own enable/disable logic on routers.
|
||||
m.Group("/issues", func() { |
||||
m.Combo("/new", repo.MustEnableIssues).Get(context.RepoRef(), repo.NewIssue). |
||||
Post(bindIgnErr(auth.CreateIssueForm{}), repo.NewIssuePost) |
||||
|
||||
m.Group("/:index", func() { |
||||
m.Post("/title", repo.UpdateIssueTitle) |
||||
m.Post("/content", repo.UpdateIssueContent) |
||||
m.Post("/watch", repo.IssueWatch) |
||||
m.Combo("/comments").Post(bindIgnErr(auth.CreateCommentForm{}), repo.NewComment) |
||||
}) |
||||
|
||||
m.Post("/labels", repo.UpdateIssueLabel, reqRepoWriter) |
||||
m.Post("/milestone", repo.UpdateIssueMilestone, reqRepoWriter) |
||||
m.Post("/assignee", repo.UpdateIssueAssignee, reqRepoWriter) |
||||
m.Post("/status", repo.UpdateIssueStatus, reqRepoWriter) |
||||
}) |
||||
m.Group("/comments/:id", func() { |
||||
m.Post("", repo.UpdateCommentContent) |
||||
m.Post("/delete", repo.DeleteComment) |
||||
}) |
||||
m.Group("/labels", func() { |
||||
m.Post("/new", bindIgnErr(auth.CreateLabelForm{}), repo.NewLabel) |
||||
m.Post("/edit", bindIgnErr(auth.CreateLabelForm{}), repo.UpdateLabel) |
||||
m.Post("/delete", repo.DeleteLabel) |
||||
m.Post("/initialize", bindIgnErr(auth.InitializeLabelsForm{}), repo.InitializeLabels) |
||||
}, reqRepoWriter, context.RepoRef()) |
||||
m.Group("/milestones", func() { |
||||
m.Combo("/new").Get(repo.NewMilestone). |
||||
Post(bindIgnErr(auth.CreateMilestoneForm{}), repo.NewMilestonePost) |
||||
m.Get("/:id/edit", repo.EditMilestone) |
||||
m.Post("/:id/edit", bindIgnErr(auth.CreateMilestoneForm{}), repo.EditMilestonePost) |
||||
m.Get("/:id/:action", repo.ChangeMilestonStatus) |
||||
m.Post("/delete", repo.DeleteMilestone) |
||||
}, reqRepoWriter, context.RepoRef()) |
||||
m.Group("/releases", func() { |
||||
m.Get("/new", repo.NewRelease) |
||||
m.Post("/new", bindIgnErr(auth.NewReleaseForm{}), repo.NewReleasePost) |
||||
m.Post("/delete", repo.DeleteRelease) |
||||
}, repo.MustBeNotBare, reqRepoWriter, context.RepoRef()) |
||||
m.Group("/releases", func() { |
||||
m.Get("/edit/*", repo.EditRelease) |
||||
m.Post("/edit/*", bindIgnErr(auth.EditReleaseForm{}), repo.EditReleasePost) |
||||
}, repo.MustBeNotBare, reqRepoWriter, func(ctx *context.Context) { |
||||
var err error |
||||
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch) |
||||
if err != nil { |
||||
ctx.Handle(500, "GetBranchCommit", err) |
||||
return |
||||
} |
||||
ctx.Repo.CommitsCount, err = ctx.Repo.Commit.CommitsCount() |
||||
if err != nil { |
||||
ctx.Handle(500, "CommitsCount", err) |
||||
return |
||||
} |
||||
ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount |
||||
}) |
||||
|
||||
m.Combo("/compare/*", repo.MustAllowPulls, repo.SetEditorconfigIfExists). |
||||
Get(repo.CompareAndPullRequest). |
||||
Post(bindIgnErr(auth.CreateIssueForm{}), repo.CompareAndPullRequestPost) |
||||
|
||||
m.Group("", func() { |
||||
m.Combo("/_edit/*").Get(repo.EditFile). |
||||
Post(bindIgnErr(auth.EditRepoFileForm{}), repo.EditFilePost) |
||||
m.Combo("/_new/*").Get(repo.NewFile). |
||||
Post(bindIgnErr(auth.EditRepoFileForm{}), repo.NewFilePost) |
||||
m.Post("/_preview/*", bindIgnErr(auth.EditPreviewDiffForm{}), repo.DiffPreviewPost) |
||||
m.Combo("/_delete/*").Get(repo.DeleteFile). |
||||
Post(bindIgnErr(auth.DeleteRepoFileForm{}), repo.DeleteFilePost) |
||||
|
||||
m.Group("", func() { |
||||
m.Combo("/_upload/*").Get(repo.UploadFile). |
||||
Post(bindIgnErr(auth.UploadRepoFileForm{}), repo.UploadFilePost) |
||||
m.Post("/upload-file", repo.UploadFileToServer) |
||||
m.Post("/upload-remove", bindIgnErr(auth.RemoveUploadFileForm{}), repo.RemoveUploadFileFromServer) |
||||
}, func(ctx *context.Context) { |
||||
if !setting.Repository.Upload.Enabled { |
||||
ctx.Handle(404, "", nil) |
||||
return |
||||
} |
||||
}) |
||||
}, repo.MustBeNotBare, reqRepoWriter, context.RepoRef(), func(ctx *context.Context) { |
||||
if !ctx.Repo.Repository.CanEnableEditor() || ctx.Repo.IsViewCommit { |
||||
ctx.Handle(404, "", nil) |
||||
return |
||||
} |
||||
}) |
||||
}, reqSignIn, context.RepoAssignment(), context.UnitTypes()) |
||||
|
||||
m.Group("/:username/:reponame", func() { |
||||
m.Group("", func() { |
||||
m.Get("/releases", repo.MustBeNotBare, repo.Releases) |
||||
m.Get("/^:type(issues|pulls)$", repo.RetrieveLabels, repo.Issues) |
||||
m.Get("/^:type(issues|pulls)$/:index", repo.ViewIssue) |
||||
m.Get("/labels/", repo.RetrieveLabels, repo.Labels) |
||||
m.Get("/milestones", repo.Milestones) |
||||
}, context.RepoRef()) |
||||
|
||||
// m.Get("/branches", repo.Branches)
|
||||
m.Post("/branches/:name/delete", reqSignIn, reqRepoWriter, repo.MustBeNotBare, repo.DeleteBranchPost) |
||||
|
||||
m.Group("/wiki", func() { |
||||
m.Get("/?:page", repo.Wiki) |
||||
m.Get("/_pages", repo.WikiPages) |
||||
|
||||
m.Group("", func() { |
||||
m.Combo("/_new").Get(repo.NewWiki). |
||||
Post(bindIgnErr(auth.NewWikiForm{}), repo.NewWikiPost) |
||||
m.Combo("/:page/_edit").Get(repo.EditWiki). |
||||
Post(bindIgnErr(auth.NewWikiForm{}), repo.EditWikiPost) |
||||
m.Post("/:page/delete", repo.DeleteWikiPagePost) |
||||
}, reqSignIn, reqRepoWriter) |
||||
}, repo.MustEnableWiki, context.RepoRef()) |
||||
|
||||
m.Group("/wiki", func() { |
||||
m.Get("/raw/*", repo.WikiRaw) |
||||
m.Get("/*", repo.WikiRaw) |
||||
}, repo.MustEnableWiki) |
||||
|
||||
m.Get("/archive/*", repo.MustBeNotBare, repo.Download) |
||||
|
||||
m.Group("/pulls/:index", func() { |
||||
m.Get("/commits", context.RepoRef(), repo.ViewPullCommits) |
||||
m.Get("/files", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.ViewPullFiles) |
||||
m.Post("/merge", reqRepoWriter, repo.MergePullRequest) |
||||
}, repo.MustAllowPulls) |
||||
|
||||
m.Group("", func() { |
||||
m.Get("/src/*", repo.SetEditorconfigIfExists, repo.Home) |
||||
m.Get("/raw/*", repo.SingleDownload) |
||||
m.Get("/commits/*", repo.RefCommits) |
||||
m.Get("/graph", repo.Graph) |
||||
m.Get("/commit/:sha([a-f0-9]{7,40})$", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.Diff) |
||||
m.Get("/forks", repo.Forks) |
||||
}, context.RepoRef()) |
||||
m.Get("/commit/:sha([a-f0-9]{7,40})\\.:ext(patch|diff)", repo.MustBeNotBare, repo.RawDiff) |
||||
|
||||
m.Get("/compare/:before([a-z0-9]{40})\\.\\.\\.:after([a-z0-9]{40})", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.MustBeNotBare, repo.CompareDiff) |
||||
}, ignSignIn, context.RepoAssignment(), context.UnitTypes()) |
||||
m.Group("/:username/:reponame", func() { |
||||
m.Get("/stars", repo.Stars) |
||||
m.Get("/watchers", repo.Watchers) |
||||
}, ignSignIn, context.RepoAssignment(), context.RepoRef(), context.UnitTypes()) |
||||
|
||||
m.Group("/:username", func() { |
||||
m.Group("/:reponame", func() { |
||||
m.Get("", repo.SetEditorconfigIfExists, repo.Home) |
||||
m.Get("\\.git$", repo.SetEditorconfigIfExists, repo.Home) |
||||
}, ignSignIn, context.RepoAssignment(), context.RepoRef(), context.UnitTypes()) |
||||
|
||||
m.Group("/:reponame", func() { |
||||
m.Group("/info/lfs", func() { |
||||
m.Post("/objects/batch", lfs.BatchHandler) |
||||
m.Get("/objects/:oid/:filename", lfs.ObjectOidHandler) |
||||
m.Any("/objects/:oid", lfs.ObjectOidHandler) |
||||
m.Post("/objects", lfs.PostHandler) |
||||
m.Any("/*", func(ctx *context.Context) { |
||||
ctx.Handle(404, "", nil) |
||||
}) |
||||
}, ignSignInAndCsrf) |
||||
m.Any("/*", ignSignInAndCsrf, repo.HTTP) |
||||
m.Head("/tasks/trigger", repo.TriggerTask) |
||||
}) |
||||
}) |
||||
// ***** END: Repository *****
|
||||
|
||||
m.Group("/notifications", func() { |
||||
m.Get("", user.Notifications) |
||||
m.Post("/status", user.NotificationStatusPost) |
||||
}, reqSignIn) |
||||
|
||||
m.Group("/api", func() { |
||||
apiv1.RegisterRoutes(m) |
||||
}, ignSignIn) |
||||
|
||||
m.Group("/api/internal", func() { |
||||
// package name internal is ideal but Golang is not allowed, so we use private as package name.
|
||||
private.RegisterRoutes(m) |
||||
}) |
||||
|
||||
// robots.txt
|
||||
m.Get("/robots.txt", func(ctx *context.Context) { |
||||
if setting.HasRobotsTxt { |
||||
ctx.ServeFileContent(path.Join(setting.CustomPath, "robots.txt")) |
||||
} else { |
||||
ctx.Error(404) |
||||
} |
||||
}) |
||||
|
||||
// Not found handler.
|
||||
m.NotFound(routers.NotFound) |
||||
} |
Loading…
Reference in new issue