goc/pkg/cover/server_test.go

364 lines
9.0 KiB
Go

package cover
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"net/url"
"os"
"reflect"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"golang.org/x/tools/cover"
)
// MockStore is mock store mainly for unittest
type MockStore struct {
mock.Mock
}
func (m *MockStore) Add(s ServiceUnderTest) error {
args := m.Called(s)
return args.Error(0)
}
func (m *MockStore) Get(name string) []string {
args := m.Called(name)
return args.Get(0).([]string)
}
func (m *MockStore) GetAll() map[string][]string {
args := m.Called()
return args.Get(0).(map[string][]string)
}
func (m *MockStore) Init() error {
args := m.Called()
return args.Error(0)
}
func (m *MockStore) Set(services map[string][]string) {
}
func TestContains(t *testing.T) {
assert.Equal(t, contains([]string{"a", "b"}, "a"), true)
assert.Equal(t, contains([]string{"a", "b"}, "c"), false)
}
func TestFilterAddrs(t *testing.T) {
svrAll := map[string][]string{
"service1": {"http://127.0.0.1:7777", "http://127.0.0.1:8888"},
"service2": {"http://127.0.0.1:9999"},
}
addrAll := []string{}
for _, addr := range svrAll {
addrAll = append(addrAll, addr...)
}
items := []struct {
svrList []string
addrList []string
force bool
err string
addrRes []string
}{
{
svrList: []string{"service1"},
addrList: []string{"http://127.0.0.1:7777"},
err: "use 'service' flag and 'address' flag at the same time may cause ambiguity, please use them separately",
},
{
addrRes: addrAll,
},
{
svrList: []string{"service1", "unknown"},
err: "service [unknown] not found",
},
{
svrList: []string{"service1", "service2", "unknown"},
force: true,
addrRes: addrAll,
},
{
svrList: []string{"unknown"},
force: true,
},
{
addrList: []string{"http://127.0.0.1:7777", "http://127.0.0.2:7777"},
err: "address [http://127.0.0.2:7777] not found",
},
{
addrList: []string{"http://127.0.0.1:7777", "http://127.0.0.1:9999", "http://127.0.0.2:7777"},
force: true,
addrRes: []string{"http://127.0.0.1:7777", "http://127.0.0.1:9999"},
},
}
for _, item := range items {
addrs, err := filterAddrs(item.svrList, item.addrList, item.force, svrAll)
if err != nil {
assert.Equal(t, err.Error(), item.err)
} else {
if len(addrs) == 0 {
assert.Equal(t, addrs, item.addrRes)
}
for _, a := range addrs {
assert.Contains(t, item.addrRes, a)
}
}
}
}
func TestRegisterService(t *testing.T) {
server := &Server{
Store: NewFileStore("_svrs_address.txt"),
}
router := server.Route(os.Stdout)
// register with empty service struct
w := httptest.NewRecorder()
req, _ := http.NewRequest("POST", "/v1/cover/register", nil)
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusBadRequest, w.Code)
// register with invalid service.Address
data := url.Values{}
data.Set("name", "aaa")
data.Set("address", "&%%")
w = httptest.NewRecorder()
req, _ = http.NewRequest("POST", "/v1/cover/register", strings.NewReader(data.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusBadRequest, w.Code)
assert.Contains(t, w.Body.String(), "invalid URL escape")
// register with host but no port
data = url.Values{}
data.Set("name", "aaa")
data.Set("address", "http://127.0.0.1")
w = httptest.NewRecorder()
req, _ = http.NewRequest("POST", "/v1/cover/register", strings.NewReader(data.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusBadRequest, w.Code)
assert.Contains(t, w.Body.String(), "missing port in address")
// register with store failure
expectedS := ServiceUnderTest{
Name: "foo",
Address: "http://:64444", // the real IP is empty in unittest, so server will get a empty one
}
testObj := new(MockStore)
testObj.On("Get", "foo").Return([]string{"http://127.0.0.1:66666"})
testObj.On("Add", expectedS).Return(fmt.Errorf("lala error"))
server.Store = testObj
w = httptest.NewRecorder()
data.Set("name", expectedS.Name)
data.Set("address", expectedS.Address)
req, _ = http.NewRequest("POST", "/v1/cover/register", strings.NewReader(data.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusInternalServerError, w.Code)
assert.Contains(t, w.Body.String(), "lala error")
}
func TestProfileService(t *testing.T) {
server := &Server{
Store: NewFileStore("_svrs_address.txt"),
}
router := server.Route(os.Stdout)
// get profile with invalid force parameter
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/v1/cover/profile?force=11", nil)
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusExpectationFailed, w.Code)
assert.Contains(t, w.Body.String(), "invalid syntax")
}
func TestClearService(t *testing.T) {
testObj := new(MockStore)
testObj.On("GetAll").Return(map[string][]string{"foo": {"http://127.0.0.1:66666"}})
server := &Server{
Store: testObj,
}
router := server.Route(os.Stdout)
// clear profile with non-exist port
w := httptest.NewRecorder()
req, _ := http.NewRequest("POST", "/v1/cover/clear", bytes.NewBuffer([]byte(`{}`)))
req.Header.Set("Content-Type", "application/json")
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusExpectationFailed, w.Code)
assert.Contains(t, w.Body.String(), "invalid port")
// clear profile with invalid service
w = httptest.NewRecorder()
req, _ = http.NewRequest("POST", "/v1/cover/clear", nil)
req.Header.Set("Content-Type", "application/json")
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusExpectationFailed, w.Code)
assert.Contains(t, w.Body.String(), "invalid request")
// clear profile with service and address set at at the same time
p := ProfileParam{
Service: []string{"goc"},
Address: []string{"http://127.0.0.1:3333"},
}
encoded, err := json.Marshal(p)
assert.NoError(t, err)
w = httptest.NewRecorder()
req, _ = http.NewRequest("POST", "/v1/cover/clear", bytes.NewBuffer(encoded))
req.Header.Set("Content-Type", "application/json")
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusExpectationFailed, w.Code)
assert.Contains(t, w.Body.String(), "use 'service' flag and 'address' flag at the same time may cause ambiguity, please use them separately")
}
func TestInitService(t *testing.T) {
testObj := new(MockStore)
testObj.On("Init").Return(fmt.Errorf("lala error"))
server := &Server{
Store: testObj,
}
router := server.Route(os.Stdout)
// get profile with invalid force parameter
w := httptest.NewRecorder()
req, _ := http.NewRequest("POST", "/v1/cover/init", nil)
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusInternalServerError, w.Code)
assert.Contains(t, w.Body.String(), "lala error")
}
func TestFilterProfile(t *testing.T) {
var tcs = []struct {
name string
pattern []string
input []*cover.Profile
output []*cover.Profile
expectErr bool
}{
{
name: "normal path",
pattern: []string{"some/fancy/gopath", "a/fancy/gopath"},
input: []*cover.Profile{
{
FileName: "some/fancy/gopath/a.go",
},
{
FileName: "some/fancy/gopath/b/a.go",
},
{
FileName: "a/fancy/gopath/a.go",
},
{
FileName: "b/fancy/gopath/a.go",
},
{
FileName: "b/a/fancy/gopath/a.go",
},
},
output: []*cover.Profile{
{
FileName: "some/fancy/gopath/a.go",
},
{
FileName: "some/fancy/gopath/b/a.go",
},
{
FileName: "a/fancy/gopath/a.go",
},
{
FileName: "b/a/fancy/gopath/a.go",
},
},
},
{
name: "with regular expression",
pattern: []string{"fancy/gopath/a.go$", "^b/a/"},
input: []*cover.Profile{
{
FileName: "some/fancy/gopath/a.go",
},
{
FileName: "some/fancy/gopath/b/a.go",
},
{
FileName: "a/fancy/gopath/a.go",
},
{
FileName: "b/fancy/gopath/c/a.go",
},
{
FileName: "b/a/fancy/gopath/a.go",
},
},
output: []*cover.Profile{
{
FileName: "some/fancy/gopath/a.go",
},
{
FileName: "a/fancy/gopath/a.go",
},
{
FileName: "b/a/fancy/gopath/a.go",
},
},
},
{
name: "with invalid regular expression",
pattern: []string{"(?!a)"},
input: []*cover.Profile{
{
FileName: "some/fancy/gopath/a.go",
},
},
expectErr: true,
},
}
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
out, err := filterProfile(tc.pattern, tc.input)
if err != nil {
if !tc.expectErr {
t.Errorf("Unexpected error: %v", err)
}
return
}
if tc.expectErr {
t.Errorf("Expected an error, but got value %s", stringifyCoverProfile(out))
}
if !reflect.DeepEqual(out, tc.output) {
t.Errorf("Mismatched results. \nExpected: %s\nActual:%s", stringifyCoverProfile(tc.output), stringifyCoverProfile(out))
}
})
}
}
func stringifyCoverProfile(profiles []*cover.Profile) string {
res := make([]cover.Profile, 0, len(profiles))
for _, p := range profiles {
res = append(res, *p)
}
return fmt.Sprintf("%#v", res)
}