blob: dd9d9015b39d25b5a6c65d58664906a1feac9ce7 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package e2e
import (
"bytes"
"context"
"crypto/tls"
"fmt"
"io/ioutil"
"net"
"net/http"
"os/exec"
"reflect"
"strings"
"testing"
"time"
"github.com/gavv/httpexpect/v2"
"github.com/stretchr/testify/assert"
"github.com/tidwall/gjson"
)
var (
token string
Token string
APISIXHost = "http://127.0.0.1:9080"
APISIXInternalUrl = "http://172.16.238.30:9080"
APISIXSingleWorkerHost = "http://127.0.0.1:9081"
ManagerAPIHost = "http://127.0.0.1:9000"
)
func init() {
//login to get auth token
requestBody := []byte(`{
"username": "admin",
"password": "admin"
}`)
url := ManagerAPIHost + "/apisix/admin/user/login"
req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(requestBody))
if err != nil {
panic(err)
}
req.Header.Add("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
respond := gjson.ParseBytes(body)
token = respond.Get("data.token").String()
Token = token
}
func httpGet(url string, headers map[string]string) ([]byte, int, error) {
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
return nil, 0, err
}
for key, val := range headers {
req.Header.Add(key, val)
}
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, 0, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, 0, err
}
return body, resp.StatusCode, nil
}
func ManagerApiExpect(t *testing.T) *httpexpect.Expect {
return httpexpect.New(t, ManagerAPIHost)
}
func APISIXExpect(t *testing.T) *httpexpect.Expect {
return httpexpect.New(t, APISIXHost)
}
func APISIXHTTPSExpect(t *testing.T) *httpexpect.Expect {
e := httpexpect.WithConfig(httpexpect.Config{
BaseURL: "https://www.test2.com:9443",
Reporter: httpexpect.NewAssertReporter(t),
Client: &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
// accept any certificate; for testing only!
InsecureSkipVerify: true,
},
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
if addr == "www.test2.com:9443" {
addr = "127.0.0.1:9443"
}
dialer := &net.Dialer{}
return dialer.DialContext(ctx, network, addr)
},
},
},
})
return e
}
func BatchTestServerPort(t *testing.T, times int) map[string]int {
url := APISIXSingleWorkerHost + "/server_port"
req, err := http.NewRequest(http.MethodGet, url, nil)
assert.Nil(t, err)
res := map[string]int{}
var resp *http.Response
var client *http.Client
var body string
var bodyByte []byte
for i := 0; i < times; i++ {
client = &http.Client{}
resp, err = client.Do(req)
assert.Nil(t, err)
bodyByte, err = ioutil.ReadAll(resp.Body)
assert.Nil(t, err)
body = string(bodyByte)
if _, ok := res[body]; !ok {
res[body] = 1
} else {
res[body] += 1
}
}
defer resp.Body.Close()
return res
}
var sleepTime = time.Duration(300) * time.Millisecond
type HttpTestCase struct {
Desc string
Object *httpexpect.Expect
Method string
Path string
Query string
Body string
Headers map[string]string
Headers_test map[string]interface{}
ExpectStatus int
ExpectCode int
ExpectMessage string
ExpectBody interface{}
UnexpectBody interface{}
ExpectHeaders map[string]string
Sleep time.Duration //ms
}
func testCaseCheck(tc HttpTestCase, t *testing.T) {
t.Run(tc.Desc, func(t *testing.T) {
//init
expectObj := tc.Object
var req *httpexpect.Request
switch tc.Method {
case http.MethodGet:
req = expectObj.GET(tc.Path)
case http.MethodPut:
req = expectObj.PUT(tc.Path)
case http.MethodPost:
req = expectObj.POST(tc.Path)
case http.MethodDelete:
req = expectObj.DELETE(tc.Path)
case http.MethodPatch:
req = expectObj.PATCH(tc.Path)
case http.MethodOptions:
req = expectObj.OPTIONS(tc.Path)
default:
}
if req == nil {
panic("fail to init request")
}
if tc.Sleep != 0 {
time.Sleep(tc.Sleep)
} else {
time.Sleep(time.Duration(50) * time.Millisecond)
}
if tc.Query != "" {
req.WithQueryString(tc.Query)
}
// set header
setContentType := false
for key, val := range tc.Headers {
req.WithHeader(key, val)
if strings.ToLower(key) == "content-type" {
setContentType = true
}
}
// set default content-type
if !setContentType {
req.WithHeader("Content-Type", "application/json")
}
// set body
if tc.Body != "" {
req.WithText(tc.Body)
}
// respond check
resp := req.Expect()
// match http status
if tc.ExpectStatus != 0 {
resp.Status(tc.ExpectStatus)
}
// match headers
if tc.ExpectHeaders != nil {
for key, val := range tc.ExpectHeaders {
resp.Header(key).Equal(val)
}
}
// match body
if tc.ExpectBody != nil {
assert.Contains(t, []string{"string", "[]string"}, reflect.TypeOf(tc.ExpectBody).String())
if body, ok := tc.ExpectBody.(string); ok {
if body == "" {
// "" indicates the body is expected to be empty
resp.Body().Empty()
} else {
resp.Body().Contains(body)
}
} else if bodies, ok := tc.ExpectBody.([]string); ok && len(bodies) != 0 {
for _, b := range bodies {
resp.Body().Contains(b)
}
}
}
// match UnexpectBody
if tc.UnexpectBody != nil {
assert.Contains(t, []string{"string", "[]string"}, reflect.TypeOf(tc.UnexpectBody).String())
if body, ok := tc.UnexpectBody.(string); ok {
// "" indicates the body is expected to be non empty
if body == "" {
resp.Body().NotEmpty()
} else {
resp.Body().NotContains(body)
}
} else if bodies, ok := tc.UnexpectBody.([]string); ok && len(bodies) != 0 {
for _, b := range bodies {
resp.Body().NotContains(b)
}
}
}
})
}
func RunTestCases(tc HttpTestCase, t *testing.T) {
testCaseCheck(tc, t)
}
func ReadAPISIXErrorLog(t *testing.T) string {
cmd := exec.Command("pwd")
pwdByte, err := cmd.CombinedOutput()
pwd := string(pwdByte)
pwd = strings.Replace(pwd, "\n", "", 1)
pwd = pwd[:strings.Index(pwd, "/e2e")]
bytes, err := ioutil.ReadFile(pwd + "/docker/apisix_logs/error.log")
assert.Nil(t, err)
logContent := string(bytes)
return logContent
}
func CleanAPISIXErrorLog(t *testing.T) {
cmd := exec.Command("pwd")
pwdByte, err := cmd.CombinedOutput()
pwd := string(pwdByte)
pwd = strings.Replace(pwd, "\n", "", 1)
pwd = pwd[:strings.Index(pwd, "/e2e")]
cmd = exec.Command("sudo", "echo", " > ", pwd+"/docker/apisix_logs/error.log")
_, err = cmd.CombinedOutput()
if err != nil {
fmt.Println("cmd error:", err.Error())
}
assert.Nil(t, err)
}
// ReadAPISIXAccessLog reads the access log of APISIX.
func ReadAPISIXAccessLog(t *testing.T) string {
cmd := exec.Command("pwd")
pwdByte, err := cmd.CombinedOutput()
pwd := string(pwdByte)
pwd = strings.Replace(pwd, "\n", "", 1)
pwd = pwd[:strings.Index(pwd, "/e2e")]
bytes, err := ioutil.ReadFile(pwd + "/docker/apisix_logs/access.log")
assert.Nil(t, err)
logContent := string(bytes)
return logContent
}
// CleanAPISIXAccessLog cleans the access log of APISIX.
// It's always recommended to call this function before checking
// its content.
func CleanAPISIXAccessLog(t *testing.T) {
cmd := exec.Command("pwd")
pwdByte, err := cmd.CombinedOutput()
pwd := string(pwdByte)
pwd = strings.Replace(pwd, "\n", "", 1)
pwd = pwd[:strings.Index(pwd, "/e2e")]
cmd = exec.Command("sudo", "echo", " > ", pwd+"/docker/apisix_logs/access.log")
_, err = cmd.CombinedOutput()
if err != nil {
fmt.Println("cmd error:", err.Error())
}
assert.Nil(t, err)
}