Skip to content

Commit 8482936

Browse files
typelesslunny
authored andcommitted
Add basic integration test infrastructure (and new endpoint /api/v1/version for testing it) (#741)
* Implement '/api/v1/version' * Cleanup and various fixes * Enhance run.sh * Add install_test.go * Add parameter utils.Config for testing handlers * Re-organize TestVersion.go * Rename functions * handling process cleanup properly * Fix missing function renaming * Cleanup the 'retry' logic * Cleanup * Remove unneeded logging code * Logging messages tweaking * Logging message tweaking * Fix logging messages * Use 'const' instead of hardwired numbers * We don't really need retries anymore * Move constant ServerHttpPort to install_test.go * Restore mistakenly removed constant * Add required comments to make the linter happy. * Fix comments and naming to address linter's complaints * Detect Gitea executale version automatically * Remove tests/run.sh, `go test` suffices. * Make `make build` a prerequisite of `make test` * Do not sleep before trying * Speedup the server pinging loop * Use defined const instead of hardwired numbers * Remove redundant error handling * Use a dedicated target for running code.gitea.io/tests * Do not make 'test' depend on 'build' target * Rectify the excluded package list * Remove redundant 'exit 1' * Change the API to allow passing test.T to test handlers * Make testing.T an embedded field * Use assert.Equal to comparing results * Add copyright info * Parametrized logging output * Use tmpdir instead * Eliminate redundant casting * Remove unneeded variable * Fix last commit * Add missing copyright info * Replace fmt.Fprintf with fmt.Fprint * rename the xtest to integration-test * Use Symlink instead of hard-link for cross-device linking * Turn debugging logs on * Follow the existing framework for APIs * Output logs only if test.v is true * Re-order import statements * Enhance the error message * Fix comment which breaks the linter's rule * Rename 'integration-test' to 'e2e-test' for saving keystrokes * Add comment to avoid possible confusion * Rename tests -> integration-tests Also change back the Makefile to use `make integration-test`. * Use tests/integration for now * tests/integration -> integrations Slightly flattened directory hierarchy is better. * Update Makefile accordingly * Fix a missing change in Makefile * govendor update code.gitea.io/sdk/gitea * Fix comment of struct fields * Fix conditional nonsense * Fix missing updates regarding version string changes * Make variable naming more consistent * Check http status code * Rectify error messages
1 parent 2215840 commit 8482936

File tree

8 files changed

+341
-4
lines changed

8 files changed

+341
-4
lines changed

Makefile

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ JAVASCRIPTS :=
1414
LDFLAGS := -X "main.Version=$(shell git describe --tags --always | sed 's/-/+/' | sed 's/^v//')" -X "main.Tags=$(TAGS)"
1515

1616
TARGETS ?= linux/*,darwin/*,windows/*
17-
PACKAGES ?= $(shell go list ./... | grep -v /vendor/)
17+
PACKAGES ?= $(filter-out code.gitea.io/gitea/integrations,$(shell go list ./... | grep -v /vendor/))
1818
SOURCES ?= $(shell find . -name "*.go" -type f)
1919

2020
TAGS ?=
@@ -66,6 +66,11 @@ lint:
6666
fi
6767
for PKG in $(PACKAGES); do golint -set_exit_status $$PKG || exit 1; done;
6868

69+
.PHONY: integrations
70+
integrations: TAGS=bindata sqlite
71+
integrations: build
72+
go test code.gitea.io/gitea/integrations
73+
6974
.PHONY: test
7075
test:
7176
for PKG in $(PACKAGES); do go test -cover -coverprofile $$GOPATH/src/$$PKG/coverage.out $$PKG || exit 1; done;

integrations/install_test.go

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// Copyright 2017 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package integration
6+
7+
import (
8+
"fmt"
9+
"net/http"
10+
"os"
11+
"os/user"
12+
"path/filepath"
13+
"testing"
14+
"time"
15+
16+
"code.gitea.io/gitea/integrations/internal/utils"
17+
)
18+
19+
// The HTTP port listened by the Gitea server.
20+
const ServerHTTPPort = "3001"
21+
22+
const _RetryLimit = 10
23+
24+
func makeSimpleSettings(user, workdir, port string) map[string][]string {
25+
return map[string][]string{
26+
"db_type": {"SQLite3"},
27+
"db_host": {"localhost"},
28+
"db_path": {workdir + "data/gitea.db"},
29+
"app_name": {"Gitea: Git with a cup of tea"},
30+
"repo_root_path": {workdir + "repositories"},
31+
"run_user": {user},
32+
"domain": {"localhost"},
33+
"ssh_port": {"22"},
34+
"http_port": {port},
35+
"app_url": {"http://localhost:" + port},
36+
"log_root_path": {workdir + "log"},
37+
}
38+
}
39+
40+
func install(t *utils.T) error {
41+
var r *http.Response
42+
var err error
43+
44+
for i := 1; i <= _RetryLimit; i++ {
45+
46+
r, err = http.Get("http://:" + ServerHTTPPort + "/")
47+
if err == nil {
48+
fmt.Fprintln(os.Stderr)
49+
break
50+
}
51+
52+
// Give the server some amount of time to warm up.
53+
time.Sleep(100 * time.Millisecond)
54+
fmt.Fprint(os.Stderr, ".")
55+
}
56+
57+
if err != nil {
58+
return err
59+
}
60+
61+
defer r.Body.Close()
62+
63+
_user, err := user.Current()
64+
if err != nil {
65+
return err
66+
}
67+
68+
path, err := filepath.Abs(t.Config.WorkDir)
69+
if err != nil {
70+
return err
71+
}
72+
73+
settings := makeSimpleSettings(_user.Username, path, ServerHTTPPort)
74+
r, err = http.PostForm("http://:"+ServerHTTPPort+"/install", settings)
75+
if err != nil {
76+
return err
77+
}
78+
defer r.Body.Close()
79+
80+
if r.StatusCode != http.StatusOK {
81+
return fmt.Errorf("'/install': %s", r.Status)
82+
}
83+
return nil
84+
}
85+
86+
func TestInstall(t *testing.T) {
87+
conf := utils.Config{
88+
Program: "../gitea",
89+
WorkDir: "",
90+
Args: []string{"web", "--port", ServerHTTPPort},
91+
LogFile: os.Stderr,
92+
}
93+
94+
if err := utils.New(t, &conf).RunTest(install); err != nil {
95+
t.Fatal(err)
96+
}
97+
}

integrations/internal/utils/utils.go

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
// Copyright 2017 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package utils
6+
7+
import (
8+
"errors"
9+
"io"
10+
"io/ioutil"
11+
"log"
12+
"os"
13+
"os/exec"
14+
"path/filepath"
15+
"syscall"
16+
"testing"
17+
)
18+
19+
// T wraps testing.T and the configurations of the testing instance.
20+
type T struct {
21+
*testing.T
22+
Config *Config
23+
}
24+
25+
// New create an instance of T
26+
func New(t *testing.T, c *Config) *T {
27+
return &T{T: t, Config: c}
28+
}
29+
30+
// Config Settings of the testing program
31+
type Config struct {
32+
// The executable path of the tested program.
33+
Program string
34+
// Working directory prepared for the tested program.
35+
// If empty, a directory named with random suffixes is picked, and created under the platform-dependent default temporary directory.
36+
// The directory will be removed when the test finishes.
37+
WorkDir string
38+
// Command-line arguments passed to the tested program.
39+
Args []string
40+
41+
// Where to redirect the stdout/stderr to. For debugging purposes.
42+
LogFile *os.File
43+
}
44+
45+
func redirect(cmd *exec.Cmd, f *os.File) error {
46+
stdout, err := cmd.StdoutPipe()
47+
if err != nil {
48+
return err
49+
}
50+
51+
stderr, err := cmd.StderrPipe()
52+
if err != nil {
53+
return err
54+
}
55+
56+
go io.Copy(f, stdout)
57+
go io.Copy(f, stderr)
58+
return nil
59+
}
60+
61+
// RunTest Helper function for setting up a running Gitea server for functional testing and then gracefully terminating it.
62+
func (t *T) RunTest(tests ...func(*T) error) (err error) {
63+
if t.Config.Program == "" {
64+
return errors.New("Need input file")
65+
}
66+
67+
path, err := filepath.Abs(t.Config.Program)
68+
if err != nil {
69+
return err
70+
}
71+
72+
workdir := t.Config.WorkDir
73+
if workdir == "" {
74+
workdir, err = ioutil.TempDir(os.TempDir(), "gitea_tests-")
75+
if err != nil {
76+
return err
77+
}
78+
defer os.RemoveAll(workdir)
79+
}
80+
81+
newpath := filepath.Join(workdir, filepath.Base(path))
82+
if err := os.Symlink(path, newpath); err != nil {
83+
return err
84+
}
85+
86+
log.Printf("Starting the server: %s args:%s workdir:%s", newpath, t.Config.Args, workdir)
87+
88+
cmd := exec.Command(newpath, t.Config.Args...)
89+
cmd.Dir = workdir
90+
91+
if t.Config.LogFile != nil && testing.Verbose() {
92+
if err := redirect(cmd, t.Config.LogFile); err != nil {
93+
return err
94+
}
95+
}
96+
97+
if err := cmd.Start(); err != nil {
98+
return err
99+
}
100+
101+
log.Println("Server started.")
102+
103+
defer func() {
104+
// Do not early return. We have to call Wait anyway.
105+
_ = cmd.Process.Signal(syscall.SIGTERM)
106+
107+
if _err := cmd.Wait(); _err != nil {
108+
if _err.Error() != "signal: terminated" {
109+
err = _err
110+
return
111+
}
112+
}
113+
114+
log.Println("Server exited")
115+
}()
116+
117+
for _, fn := range tests {
118+
if err := fn(t); err != nil {
119+
return err
120+
}
121+
}
122+
123+
// Note that the return value 'err' may be updated by the 'defer' statement before despite it's returning nil here.
124+
return nil
125+
}

integrations/version_test.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Copyright 2017 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package integration
6+
7+
import (
8+
"encoding/json"
9+
"fmt"
10+
"log"
11+
"net/http"
12+
"os"
13+
"os/exec"
14+
"path/filepath"
15+
"strings"
16+
"testing"
17+
18+
"code.gitea.io/gitea/integrations/internal/utils"
19+
"code.gitea.io/sdk/gitea"
20+
21+
"github.com/stretchr/testify/assert"
22+
)
23+
24+
func version(t *utils.T) error {
25+
var err error
26+
27+
path, err := filepath.Abs(t.Config.Program)
28+
if err != nil {
29+
return err
30+
}
31+
32+
cmd := exec.Command(path, "--version")
33+
out, err := cmd.Output()
34+
if err != nil {
35+
return err
36+
}
37+
38+
fields := strings.Fields(string(out))
39+
if !strings.HasPrefix(string(out), "Gitea version") {
40+
return fmt.Errorf("unexpected version string '%s' of the gitea executable", out)
41+
}
42+
43+
expected := fields[2]
44+
45+
var r *http.Response
46+
r, err = http.Get("http://:" + ServerHTTPPort + "/api/v1/version")
47+
if err != nil {
48+
return err
49+
}
50+
defer r.Body.Close()
51+
52+
if r.StatusCode != http.StatusOK {
53+
return fmt.Errorf("'/api/v1/version': %s\n", r.Status)
54+
}
55+
56+
var v gitea.ServerVersion
57+
58+
dec := json.NewDecoder(r.Body)
59+
if err := dec.Decode(&v); err != nil {
60+
return err
61+
}
62+
63+
actual := v.Version
64+
65+
log.Printf("Actual: \"%s\" Expected: \"%s\"\n", actual, expected)
66+
assert.Equal(t, expected, actual)
67+
68+
return nil
69+
}
70+
71+
func TestVersion(t *testing.T) {
72+
conf := utils.Config{
73+
Program: "../gitea",
74+
WorkDir: "",
75+
Args: []string{"web", "--port", ServerHTTPPort},
76+
LogFile: os.Stderr,
77+
}
78+
79+
if err := utils.New(t, &conf).RunTest(install, version); err != nil {
80+
t.Fatal(err)
81+
}
82+
}

routers/api/v1/api.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ func RegisterRoutes(m *macaron.Macaron) {
232232

233233
m.Group("/v1", func() {
234234
// Miscellaneous
235+
m.Get("/version", misc.Version)
235236
m.Post("/markdown", bind(api.MarkdownOption{}), misc.Markdown)
236237
m.Post("/markdown/raw", misc.MarkdownRaw)
237238

routers/api/v1/misc/version.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2017 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package misc
6+
7+
import (
8+
"code.gitea.io/gitea/modules/context"
9+
"code.gitea.io/gitea/modules/setting"
10+
"code.gitea.io/sdk/gitea"
11+
)
12+
13+
// Version shows the version of the Gitea server
14+
func Version(ctx *context.APIContext) {
15+
ctx.JSON(200, &gitea.ServerVersion{Version: setting.AppVer})
16+
}

vendor/code.gitea.io/sdk/gitea/miscellaneous.go

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/vendor.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@
99
"revisionTime": "2017-02-22T02:52:05Z"
1010
},
1111
{
12-
"checksumSHA1": "K0VWBaa3ZUE598zVFGavdLB7vW4=",
12+
"checksumSHA1": "qXD1HI8bTn7qNJZJOeZqQgxo354=",
1313
"path": "code.gitea.io/sdk/gitea",
14-
"revision": "06902fe19508c7ede2be38b71287c665efa1f10d",
15-
"revisionTime": "2017-02-19T11:17:32Z"
14+
"revision": "8807a1d2ced513880b288a5e2add39df6bf72144",
15+
"revisionTime": "2017-03-04T10:22:44Z"
1616
},
1717
{
1818
"checksumSHA1": "IyfS7Rbl6OgR83QR7TOfKdDCq+M=",

0 commit comments

Comments
 (0)