base.go 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. package base
  18. import (
  19. "context"
  20. "crypto/tls"
  21. "fmt"
  22. "io/ioutil"
  23. "net"
  24. "net/http"
  25. "os/exec"
  26. "strconv"
  27. "strings"
  28. "time"
  29. "github.com/gavv/httpexpect/v2"
  30. . "github.com/onsi/ginkgo/v2"
  31. "github.com/stretchr/testify/assert"
  32. "github.com/tidwall/gjson"
  33. )
  34. var (
  35. token string
  36. UpstreamIp = "upstream"
  37. UpstreamGrpcIp = "upstream_grpc"
  38. UpstreamHTTPBinIp = "upstream_httpbin"
  39. APISIXHost = "http://127.0.0.1:9080"
  40. APISIXAdminAPIHost = "http://127.0.0.1:9180"
  41. APISIXInternalUrl = "http://apisix:9080"
  42. APISIXSingleWorkerHost = "http://127.0.0.1:9081"
  43. ManagerAPIHost = "http://127.0.0.1:9000"
  44. PrometheusExporter = "http://127.0.0.1:9091"
  45. )
  46. func GetToken() string {
  47. if token != "" {
  48. return token
  49. }
  50. requestBody := `{
  51. "username": "admin",
  52. "password": "admin"
  53. }`
  54. url := ManagerAPIHost + "/apisix/admin/user/login"
  55. body, _, err := HttpPost(url, nil, requestBody)
  56. if err != nil {
  57. panic(err)
  58. }
  59. respond := gjson.ParseBytes(body)
  60. token = respond.Get("data.token").String()
  61. return token
  62. }
  63. func getTestingHandle() httpexpect.LoggerReporter {
  64. return GinkgoT()
  65. }
  66. func ManagerApiExpect() *httpexpect.Expect {
  67. return httpexpect.New(GinkgoT(), ManagerAPIHost)
  68. }
  69. func APISIXExpect() *httpexpect.Expect {
  70. return httpexpect.New(GinkgoT(), APISIXHost)
  71. }
  72. func APISIXAdminAPIExpect() *httpexpect.Expect {
  73. return httpexpect.New(GinkgoT(), APISIXAdminAPIHost)
  74. }
  75. func APISIXStreamProxyExpect(port uint16, sni string) *httpexpect.Expect {
  76. if port == 0 {
  77. port = 10090
  78. }
  79. if sni != "" {
  80. addr := net.JoinHostPort(sni, strconv.Itoa(int(port)))
  81. return httpexpect.WithConfig(httpexpect.Config{
  82. BaseURL: "https://" + addr,
  83. Reporter: httpexpect.NewAssertReporter(GinkgoT()),
  84. Client: &http.Client{
  85. Transport: &http.Transport{
  86. TLSClientConfig: &tls.Config{
  87. // accept any certificate; for testing only!
  88. InsecureSkipVerify: true,
  89. },
  90. DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
  91. addr = net.JoinHostPort("127.0.0.1", strconv.Itoa(int(port)))
  92. dialer := &net.Dialer{}
  93. return dialer.DialContext(ctx, network, addr)
  94. },
  95. },
  96. },
  97. })
  98. } else {
  99. return httpexpect.New(GinkgoT(), "http://"+net.JoinHostPort("127.0.0.1", strconv.Itoa(int(port))))
  100. }
  101. }
  102. func PrometheusExporterExpect() *httpexpect.Expect {
  103. return httpexpect.New(GinkgoT(), PrometheusExporter)
  104. }
  105. func APISIXHTTPSExpect() *httpexpect.Expect {
  106. e := httpexpect.WithConfig(httpexpect.Config{
  107. BaseURL: "https://www.test2.com:9443",
  108. Reporter: httpexpect.NewAssertReporter(GinkgoT()),
  109. Client: &http.Client{
  110. Transport: &http.Transport{
  111. TLSClientConfig: &tls.Config{
  112. // accept any certificate; for testing only!
  113. InsecureSkipVerify: true,
  114. },
  115. DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
  116. if addr == "www.test2.com:9443" {
  117. addr = "127.0.0.1:9443"
  118. }
  119. dialer := &net.Dialer{}
  120. return dialer.DialContext(ctx, network, addr)
  121. },
  122. },
  123. },
  124. })
  125. return e
  126. }
  127. var SleepTime = 300 * time.Millisecond
  128. type HttpTestCase struct {
  129. Desc string
  130. Object *httpexpect.Expect
  131. Method string
  132. Path string
  133. Query string
  134. Body string
  135. Headers map[string]string
  136. Headers_test map[string]interface{}
  137. ExpectStatus int
  138. ExpectCode int
  139. ExpectMessage string
  140. ExpectBody interface{}
  141. UnexpectBody interface{}
  142. ExpectHeaders map[string]string
  143. Sleep time.Duration //ms
  144. }
  145. func RunTestCase(tc HttpTestCase) {
  146. //init
  147. expectObj := tc.Object
  148. var req *httpexpect.Request
  149. switch tc.Method {
  150. case http.MethodGet:
  151. req = expectObj.GET(tc.Path)
  152. case http.MethodPut:
  153. req = expectObj.PUT(tc.Path)
  154. case http.MethodPost:
  155. req = expectObj.POST(tc.Path)
  156. case http.MethodDelete:
  157. req = expectObj.DELETE(tc.Path)
  158. case http.MethodPatch:
  159. req = expectObj.PATCH(tc.Path)
  160. case http.MethodOptions:
  161. req = expectObj.OPTIONS(tc.Path)
  162. default:
  163. }
  164. if req == nil {
  165. panic("fail to init request")
  166. }
  167. if tc.Sleep != 0 {
  168. time.Sleep(tc.Sleep)
  169. } else {
  170. time.Sleep(time.Duration(50) * time.Millisecond)
  171. }
  172. if tc.Query != "" {
  173. req.WithQueryString(tc.Query)
  174. }
  175. // set header
  176. setContentType := false
  177. for key, val := range tc.Headers {
  178. req.WithHeader(key, val)
  179. if strings.ToLower(key) == "content-type" {
  180. setContentType = true
  181. }
  182. }
  183. // set default content-type
  184. if !setContentType {
  185. req.WithHeader("Content-Type", "application/json")
  186. }
  187. // set body
  188. if tc.Body != "" {
  189. req.WithText(tc.Body)
  190. }
  191. // respond check
  192. resp := req.Expect()
  193. // match http status
  194. if tc.ExpectStatus != 0 {
  195. resp.Status(tc.ExpectStatus)
  196. }
  197. // match headers
  198. if tc.ExpectHeaders != nil {
  199. for key, val := range tc.ExpectHeaders {
  200. resp.Header(key).Equal(val)
  201. }
  202. }
  203. // match body
  204. if tc.ExpectBody != nil {
  205. //assert.Contains(t, []string{"string", "[]string"}, reflect.TypeOf(tc.ExpectBody).String())
  206. if body, ok := tc.ExpectBody.(string); ok {
  207. if body == "" {
  208. // "" indicates the body is expected to be empty
  209. resp.Body().Empty()
  210. } else {
  211. resp.Body().Contains(body)
  212. }
  213. } else if bodies, ok := tc.ExpectBody.([]string); ok && len(bodies) != 0 {
  214. for _, b := range bodies {
  215. resp.Body().Contains(b)
  216. }
  217. }
  218. }
  219. // match UnexpectBody
  220. if tc.UnexpectBody != nil {
  221. //assert.Contains(t, []string{"string", "[]string"}, reflect.TypeOf(tc.UnexpectBody).String())
  222. if body, ok := tc.UnexpectBody.(string); ok {
  223. // "" indicates the body is expected to be non empty
  224. if body == "" {
  225. resp.Body().NotEmpty()
  226. } else {
  227. resp.Body().NotContains(body)
  228. }
  229. } else if bodies, ok := tc.UnexpectBody.([]string); ok && len(bodies) != 0 {
  230. for _, b := range bodies {
  231. resp.Body().NotContains(b)
  232. }
  233. }
  234. }
  235. }
  236. func ReadAPISIXErrorLog() string {
  237. cmd := exec.Command("pwd")
  238. pwdByte, err := cmd.CombinedOutput()
  239. pwd := string(pwdByte)
  240. pwd = strings.Replace(pwd, "\n", "", 1)
  241. pwd = pwd[:strings.Index(pwd, "/e2e")]
  242. bytes, err := ioutil.ReadFile(pwd + "/docker/apisix_logs/error.log")
  243. assert.Nil(GinkgoT(), err)
  244. logContent := string(bytes)
  245. return logContent
  246. }
  247. func CleanAPISIXErrorLog() {
  248. cmd := exec.Command("pwd")
  249. pwdByte, err := cmd.CombinedOutput()
  250. pwd := string(pwdByte)
  251. pwd = strings.Replace(pwd, "\n", "", 1)
  252. pwd = pwd[:strings.Index(pwd, "/e2e")]
  253. cmdStr := "echo | sudo tee " + pwd + "/docker/apisix_logs/error.log"
  254. cmd = exec.Command("bash", "-c", cmdStr)
  255. _, err = cmd.Output()
  256. if err != nil {
  257. fmt.Println("cmd error:", err.Error())
  258. }
  259. assert.Nil(GinkgoT(), err)
  260. }
  261. func GetResourceList(resource string) string {
  262. body, _, err := HttpGet(ManagerAPIHost+"/apisix/admin/"+resource, map[string]string{"Authorization": GetToken()})
  263. assert.Nil(GinkgoT(), err)
  264. return string(body)
  265. }
  266. func CleanResource(resource string) {
  267. resources := GetResourceList(resource)
  268. list := gjson.Get(resources, "data.rows").Value().([]interface{})
  269. for _, item := range list {
  270. resourceObj := item.(map[string]interface{})
  271. tc := HttpTestCase{
  272. Desc: "delete " + resource + "/" + resourceObj["id"].(string),
  273. Object: ManagerApiExpect(),
  274. Method: http.MethodDelete,
  275. Path: "/apisix/admin/" + resource + "/" + resourceObj["id"].(string),
  276. Headers: map[string]string{"Authorization": GetToken()},
  277. }
  278. RunTestCase(tc)
  279. }
  280. time.Sleep(SleepTime)
  281. }
  282. func CleanAllResource() {
  283. CleanResource("routes")
  284. CleanResource("upstreams")
  285. CleanResource("consumers")
  286. CleanResource("services")
  287. CleanResource("global_rules")
  288. CleanResource("plugin_configs")
  289. CleanResource("proto")
  290. CleanResource("ssl")
  291. CleanResource("stream_routes")
  292. }
  293. func RestartManagerAPI() {
  294. e := exec.Command("docker", "restart", "docker_managerapi")
  295. e.Run()
  296. }
  297. var jwtToken string
  298. func GetJwtToken(userKey string) string {
  299. if jwtToken != "" {
  300. return jwtToken
  301. }
  302. time.Sleep(SleepTime)
  303. body, status, err := HttpGet(APISIXHost+"/apisix/plugin/jwt/sign?key="+userKey, nil)
  304. assert.Nil(GinkgoT(), err)
  305. assert.Equal(GinkgoT(), http.StatusOK, status)
  306. jwtToken = string(body)
  307. return jwtToken
  308. }