401 lines
10 KiB
Go
401 lines
10 KiB
Go
// Copyright 2014 beego Author. All Rights Reserved.
|
|
//
|
|
// Licensed 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 web
|
|
|
|
import (
|
|
"bytes"
|
|
"io"
|
|
"math"
|
|
"mime/multipart"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/beego/beego/v2/server/web/context"
|
|
)
|
|
|
|
var (
|
|
fileKey = "file1"
|
|
testFile = filepath.Join(currentWorkDir, "test/static/file.txt")
|
|
toFile = filepath.Join(currentWorkDir, "test/static/file2.txt")
|
|
)
|
|
|
|
func TestGetInt(t *testing.T) {
|
|
i := context.NewInput()
|
|
i.SetParam("age", "40")
|
|
ctx := &context.Context{Input: i}
|
|
ctrlr := Controller{Ctx: ctx}
|
|
val, _ := ctrlr.GetInt("age")
|
|
if val != 40 {
|
|
t.Errorf("TestGetInt expect 40,get %T,%v", val, val)
|
|
}
|
|
}
|
|
|
|
func TestGetInt8(t *testing.T) {
|
|
i := context.NewInput()
|
|
i.SetParam("age", "40")
|
|
ctx := &context.Context{Input: i}
|
|
ctrlr := Controller{Ctx: ctx}
|
|
val, _ := ctrlr.GetInt8("age")
|
|
if val != 40 {
|
|
t.Errorf("TestGetInt8 expect 40,get %T,%v", val, val)
|
|
}
|
|
// Output: int8
|
|
}
|
|
|
|
func TestGetInt16(t *testing.T) {
|
|
i := context.NewInput()
|
|
i.SetParam("age", "40")
|
|
ctx := &context.Context{Input: i}
|
|
ctrlr := Controller{Ctx: ctx}
|
|
val, _ := ctrlr.GetInt16("age")
|
|
if val != 40 {
|
|
t.Errorf("TestGetInt16 expect 40,get %T,%v", val, val)
|
|
}
|
|
}
|
|
|
|
func TestGetInt32(t *testing.T) {
|
|
i := context.NewInput()
|
|
i.SetParam("age", "40")
|
|
ctx := &context.Context{Input: i}
|
|
ctrlr := Controller{Ctx: ctx}
|
|
val, _ := ctrlr.GetInt32("age")
|
|
if val != 40 {
|
|
t.Errorf("TestGetInt32 expect 40,get %T,%v", val, val)
|
|
}
|
|
}
|
|
|
|
func TestGetInt64(t *testing.T) {
|
|
i := context.NewInput()
|
|
i.SetParam("age", "40")
|
|
ctx := &context.Context{Input: i}
|
|
ctrlr := Controller{Ctx: ctx}
|
|
val, _ := ctrlr.GetInt64("age")
|
|
if val != 40 {
|
|
t.Errorf("TestGeetInt64 expect 40,get %T,%v", val, val)
|
|
}
|
|
}
|
|
|
|
func TestGetUint8(t *testing.T) {
|
|
i := context.NewInput()
|
|
i.SetParam("age", strconv.FormatUint(math.MaxUint8, 10))
|
|
ctx := &context.Context{Input: i}
|
|
ctrlr := Controller{Ctx: ctx}
|
|
val, _ := ctrlr.GetUint8("age")
|
|
if val != math.MaxUint8 {
|
|
t.Errorf("TestGetUint8 expect %v,get %T,%v", math.MaxUint8, val, val)
|
|
}
|
|
}
|
|
|
|
func TestGetUint16(t *testing.T) {
|
|
i := context.NewInput()
|
|
i.SetParam("age", strconv.FormatUint(math.MaxUint16, 10))
|
|
ctx := &context.Context{Input: i}
|
|
ctrlr := Controller{Ctx: ctx}
|
|
val, _ := ctrlr.GetUint16("age")
|
|
if val != math.MaxUint16 {
|
|
t.Errorf("TestGetUint16 expect %v,get %T,%v", math.MaxUint16, val, val)
|
|
}
|
|
}
|
|
|
|
func TestGetUint32(t *testing.T) {
|
|
i := context.NewInput()
|
|
i.SetParam("age", strconv.FormatUint(math.MaxUint32, 10))
|
|
ctx := &context.Context{Input: i}
|
|
ctrlr := Controller{Ctx: ctx}
|
|
val, _ := ctrlr.GetUint32("age")
|
|
if val != math.MaxUint32 {
|
|
t.Errorf("TestGetUint32 expect %v,get %T,%v", math.MaxUint32, val, val)
|
|
}
|
|
}
|
|
|
|
func TestGetUint64(t *testing.T) {
|
|
i := context.NewInput()
|
|
i.SetParam("age", strconv.FormatUint(math.MaxUint64, 10))
|
|
ctx := &context.Context{Input: i}
|
|
ctrlr := Controller{Ctx: ctx}
|
|
val, _ := ctrlr.GetUint64("age")
|
|
if val != math.MaxUint64 {
|
|
t.Errorf("TestGetUint64 expect %v,get %T,%v", uint64(math.MaxUint64), val, val)
|
|
}
|
|
}
|
|
|
|
func TestAdditionalViewPaths(t *testing.T) {
|
|
tmpDir := os.TempDir()
|
|
dir1 := filepath.Join(tmpDir, "_beeTmp", "TestAdditionalViewPaths")
|
|
dir2 := filepath.Join(tmpDir, "_beeTmp2", "TestAdditionalViewPaths")
|
|
defer os.RemoveAll(dir1)
|
|
defer os.RemoveAll(dir2)
|
|
|
|
dir1file := "file1.tpl"
|
|
dir2file := "file2.tpl"
|
|
|
|
genFile := func(dir string, name string, content string) {
|
|
os.MkdirAll(filepath.Dir(filepath.Join(dir, name)), 0o777)
|
|
if f, err := os.Create(filepath.Join(dir, name)); err != nil {
|
|
t.Fatal(err)
|
|
} else {
|
|
defer f.Close()
|
|
f.WriteString(content)
|
|
f.Close()
|
|
}
|
|
}
|
|
genFile(dir1, dir1file, `<div>{{.Content}}</div>`)
|
|
genFile(dir2, dir2file, `<html>{{.Content}}</html>`)
|
|
|
|
AddViewPath(dir1)
|
|
AddViewPath(dir2)
|
|
|
|
ctrl := Controller{
|
|
TplName: "file1.tpl",
|
|
ViewPath: dir1,
|
|
}
|
|
ctrl.Data = map[interface{}]interface{}{
|
|
"Content": "value2",
|
|
}
|
|
if result, err := ctrl.RenderString(); err != nil {
|
|
t.Fatal(err)
|
|
} else {
|
|
if result != "<div>value2</div>" {
|
|
t.Fatalf("TestAdditionalViewPaths expect %s got %s", "<div>value2</div>", result)
|
|
}
|
|
}
|
|
|
|
func() {
|
|
ctrl.TplName = "file2.tpl"
|
|
defer func() {
|
|
if r := recover(); r == nil {
|
|
t.Fatal("TestAdditionalViewPaths expected error")
|
|
}
|
|
}()
|
|
ctrl.RenderString()
|
|
}()
|
|
|
|
ctrl.TplName = "file2.tpl"
|
|
ctrl.ViewPath = dir2
|
|
ctrl.RenderString()
|
|
}
|
|
|
|
func TestBindJson(t *testing.T) {
|
|
var s struct {
|
|
Foo string `json:"foo"`
|
|
}
|
|
header := map[string][]string{"Content-Type": {"application/json"}}
|
|
request := &http.Request{Header: header}
|
|
input := &context.BeegoInput{RequestBody: []byte(`{"foo": "FOO"}`)}
|
|
ctx := &context.Context{Request: request, Input: input}
|
|
ctrlr := Controller{Ctx: ctx}
|
|
err := ctrlr.Bind(&s)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "FOO", s.Foo)
|
|
}
|
|
|
|
func TestBindNoContentType(t *testing.T) {
|
|
var s struct {
|
|
Foo string `json:"foo"`
|
|
}
|
|
header := map[string][]string{}
|
|
request := &http.Request{Header: header}
|
|
input := &context.BeegoInput{RequestBody: []byte(`{"foo": "FOO"}`)}
|
|
ctx := &context.Context{Request: request, Input: input}
|
|
ctrlr := Controller{Ctx: ctx}
|
|
err := ctrlr.Bind(&s)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "FOO", s.Foo)
|
|
}
|
|
|
|
func TestBindXML(t *testing.T) {
|
|
var s struct {
|
|
Foo string `xml:"foo"`
|
|
}
|
|
xmlBody := `<?xml version="1.0" encoding="UTF-8"?>
|
|
<root>
|
|
<foo>FOO</foo>
|
|
</root>`
|
|
header := map[string][]string{"Content-Type": {"text/xml"}}
|
|
request := &http.Request{Header: header}
|
|
input := &context.BeegoInput{RequestBody: []byte(xmlBody)}
|
|
ctx := &context.Context{Request: request, Input: input}
|
|
ctrlr := Controller{Ctx: ctx}
|
|
err := ctrlr.Bind(&s)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "FOO", s.Foo)
|
|
}
|
|
|
|
func TestBindYAML(t *testing.T) {
|
|
var s struct {
|
|
Foo string `yaml:"foo"`
|
|
}
|
|
header := map[string][]string{"Content-Type": {"application/x-yaml"}}
|
|
request := &http.Request{Header: header}
|
|
input := &context.BeegoInput{RequestBody: []byte("foo: FOO")}
|
|
ctx := &context.Context{Request: request, Input: input}
|
|
ctrlr := Controller{Ctx: ctx}
|
|
err := ctrlr.Bind(&s)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "FOO", s.Foo)
|
|
}
|
|
|
|
type TestRespController struct {
|
|
Controller
|
|
}
|
|
|
|
func (t *TestRespController) TestResponse() {
|
|
type S struct {
|
|
Foo string `json:"foo" xml:"foo" yaml:"foo"`
|
|
}
|
|
|
|
bar := S{Foo: "bar"}
|
|
|
|
_ = t.Resp(bar)
|
|
}
|
|
|
|
func (t *TestRespController) TestSaveToFile() {
|
|
err := t.SaveToFile(fileKey, toFile)
|
|
if err != nil {
|
|
t.Ctx.WriteString("save file fail")
|
|
}
|
|
err = os.Remove(toFile)
|
|
if err != nil {
|
|
t.Ctx.WriteString("save file fail")
|
|
}
|
|
t.Ctx.WriteString("save file success")
|
|
}
|
|
|
|
type respTestCase struct {
|
|
Accept string
|
|
ExpectedContentLength int64
|
|
ExpectedResponse string
|
|
}
|
|
|
|
func TestControllerResp(t *testing.T) {
|
|
// test cases
|
|
tcs := []respTestCase{
|
|
{Accept: context.ApplicationJSON, ExpectedContentLength: 13, ExpectedResponse: `{"foo":"bar"}`},
|
|
{Accept: context.ApplicationXML, ExpectedContentLength: 21, ExpectedResponse: `<S><foo>bar</foo></S>`},
|
|
{Accept: context.ApplicationYAML, ExpectedContentLength: 9, ExpectedResponse: "foo: bar\n"},
|
|
{Accept: "OTHER", ExpectedContentLength: 13, ExpectedResponse: `{"foo":"bar"}`},
|
|
}
|
|
|
|
for _, tc := range tcs {
|
|
testControllerRespTestCases(t, tc)
|
|
}
|
|
}
|
|
|
|
func testControllerRespTestCases(t *testing.T, tc respTestCase) {
|
|
// create fake GET request
|
|
r, _ := http.NewRequest("GET", "/", nil)
|
|
r.Header.Set("Accept", tc.Accept)
|
|
w := httptest.NewRecorder()
|
|
|
|
// setup the handler
|
|
handler := NewControllerRegister()
|
|
handler.Add("/", &TestRespController{}, WithRouterMethods(&TestRespController{}, "get:TestResponse"))
|
|
handler.ServeHTTP(w, r)
|
|
|
|
response := w.Result()
|
|
if response.ContentLength != tc.ExpectedContentLength {
|
|
t.Errorf("TestResponse() unable to validate content length %d for %s", response.ContentLength, tc.Accept)
|
|
}
|
|
|
|
if response.StatusCode != http.StatusOK {
|
|
t.Errorf("TestResponse() failed to validate response code for %s", tc.Accept)
|
|
}
|
|
|
|
bodyBytes, err := io.ReadAll(response.Body)
|
|
if err != nil {
|
|
t.Errorf("TestResponse() failed to parse response body for %s", tc.Accept)
|
|
}
|
|
bodyString := string(bodyBytes)
|
|
if bodyString != tc.ExpectedResponse {
|
|
t.Errorf("TestResponse() failed to validate response body '%s' for %s", bodyString, tc.Accept)
|
|
}
|
|
}
|
|
|
|
func createReqBody(filePath string) (string, io.Reader, error) {
|
|
var err error
|
|
|
|
buf := new(bytes.Buffer)
|
|
bw := multipart.NewWriter(buf) // body writer
|
|
|
|
f, err := os.Open(filePath)
|
|
if err != nil {
|
|
return "", nil, err
|
|
}
|
|
defer func() {
|
|
_ = f.Close()
|
|
}()
|
|
|
|
// text part1
|
|
p1w, _ := bw.CreateFormField("name")
|
|
_, err = p1w.Write([]byte("Tony Bai"))
|
|
if err != nil {
|
|
return "", nil, err
|
|
}
|
|
|
|
// text part2
|
|
p2w, _ := bw.CreateFormField("age")
|
|
_, err = p2w.Write([]byte("15"))
|
|
if err != nil {
|
|
return "", nil, err
|
|
}
|
|
|
|
// file part1
|
|
_, fileName := filepath.Split(filePath)
|
|
fw1, _ := bw.CreateFormFile(fileKey, fileName)
|
|
_, err = io.Copy(fw1, f)
|
|
if err != nil {
|
|
return "", nil, err
|
|
}
|
|
|
|
_ = bw.Close() // write the tail boundary
|
|
return bw.FormDataContentType(), buf, nil
|
|
}
|
|
|
|
func TestControllerSaveFile(t *testing.T) {
|
|
// create body
|
|
contType, bodyReader, err := createReqBody(testFile)
|
|
assert.NoError(t, err)
|
|
|
|
// create fake POST request
|
|
r, _ := http.NewRequest("POST", "/upload_file", bodyReader)
|
|
r.Header.Set("Accept", context.ApplicationForm)
|
|
r.Header.Set("Content-Type", contType)
|
|
w := httptest.NewRecorder()
|
|
|
|
// setup the handler
|
|
handler := NewControllerRegister()
|
|
handler.Add("/upload_file", &TestRespController{},
|
|
WithRouterMethods(&TestRespController{}, "post:TestSaveToFile"))
|
|
handler.ServeHTTP(w, r)
|
|
|
|
response := w.Result()
|
|
bs := make([]byte, 100)
|
|
n, err := response.Body.Read(bs)
|
|
assert.NoError(t, err)
|
|
if string(bs[:n]) == "save file fail" {
|
|
t.Errorf("TestSaveToFile() failed to validate response")
|
|
}
|
|
if response.StatusCode != http.StatusOK {
|
|
t.Errorf("TestSaveToFile() failed to validate response code for %s", context.ApplicationJSON)
|
|
}
|
|
}
|