From f1f58cc99f7ae624dc81456a27852add36fbc342 Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 25 Mar 2023 17:46:57 +0800 Subject: [PATCH] [add] migrate widgets.table api --- Makefile | 2 +- test/request.go | 225 +++++++++++++++++++++++++ test/{test.go => utils.go} | 70 +++++++- widgets/table/api_test.go | 325 +++++++++++++++++++------------------ 4 files changed, 460 insertions(+), 162 deletions(-) create mode 100644 test/request.go rename test/{test.go => utils.go} (67%) diff --git a/Makefile b/Makefile index 51bda110..d0151efc 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ COMMIT := $(shell git log | head -n 1 | awk '{print substr($$2, 0, 12)}') NOW := $(shell date +"%FT%T%z") # ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) -TESTFOLDER := $(shell $(GO) list ./... | grep -E 'api|model|flow|script|fs|i18n|connector|query|plugin|cert|crypto|task|schedule|runtime|helper|utils|widget|importer|store|widgets/app|widgets/login|widgets/table' | grep -vE 'examples|tests*|config|widgets') +TESTFOLDER := $(shell $(GO) list ./... | grep -E 'api|model|flow|script|fs|i18n|connector|query|plugin|cert|crypto|task|schedule|runtime|helper|utils|widget|importer|store' | grep -vE 'examples|tests*|config|widgets') TESTTAGS ?= "" # TESTWIDGETS := $(shell $(GO) list ./widgets/...) diff --git a/test/request.go b/test/request.go new file mode 100644 index 00000000..a1494ddc --- /dev/null +++ b/test/request.go @@ -0,0 +1,225 @@ +package test + +import ( + "bytes" + "context" + "fmt" + "io" + "net/http" + "time" + + jsoniter "github.com/json-iterator/go" + "github.com/yaoapp/gou/model" + "github.com/yaoapp/gou/process" + "github.com/yaoapp/gou/session" + "github.com/yaoapp/yao/helper" +) + +// Request request +type Request struct { + host string + port int + route string + method string + data map[string]interface{} + params map[string]string + headers map[string]string +} + +// Response response +type Response struct { + status int + body []byte +} + +// NewRequest create a new request +func NewRequest(port int) *Request { + return &Request{ + host: "127.0.0.1", + port: port, + data: map[string]interface{}{}, + params: map[string]string{}, + headers: map[string]string{}, + } +} + +// Token set token +func (r *Request) Token(token string) *Request { + r.headers["Authorization"] = fmt.Sprintf("Bearer %s", token) + return r +} + +// Header set header +func (r *Request) Header(key string, value string) *Request { + r.headers[key] = value + return r +} + +// Param set saram +func (r *Request) Param(key string, value string) *Request { + r.params[key] = value + return r +} + +// Data set data +func (r *Request) Data(data map[string]interface{}) *Request { + r.data = data + return r +} + +// Route set the route +func (r *Request) Route(route string) *Request { + r.route = route + return r +} + +// Get request +func (r *Request) Get() (*Response, error) { + r.method = "GET" + return r.Send() +} + +// Post request +func (r *Request) Post() (*Response, error) { + r.method = "POST" + return r.Send() +} + +// Send request +func (r *Request) Send() (*Response, error) { + + client := http.Client{} + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + // set body + var data io.Reader = nil + if len(r.data) > 0 { + content, err := jsoniter.Marshal(r.data) + if err != nil { + return nil, err + } + data = bytes.NewBuffer(content) + } + + url := fmt.Sprintf("http://%s:%d%s", r.host, r.port, r.route) + req, err := http.NewRequestWithContext(ctx, r.method, url, data) + if err != nil { + return nil, err + } + + // Set header + for key, value := range r.headers { + req.Header.Add(key, value) + } + + if _, has := r.headers["Content-Type"]; !has { + req.Header.Add("Content-Type", "application/json") + } + + // Set Parms + if len(r.params) > 0 { + q := req.URL.Query() + for key, value := range r.params { + q.Add(key, value) + } + req.URL.RawQuery = q.Encode() + } + + // Send Request + res, err := client.Do(req) + if err != nil { + return nil, err + } + defer res.Body.Close() + + // response body + body, err := io.ReadAll(res.Body) + if err != nil { + return nil, err + } + + p := &Response{ + status: res.StatusCode, + body: body, + } + + return p, nil +} + +// Map to map +func (p *Response) Map() (map[string]interface{}, error) { + v := map[string]interface{}{} + err := jsoniter.Unmarshal(p.body, &v) + if err != nil { + return nil, err + } + return v, nil +} + +// Int to int +func (p *Response) Int() (int, error) { + v := 0 + err := jsoniter.Unmarshal(p.body, &v) + if err != nil { + return 0, err + } + return v, nil +} + +// Status get the reaponse status +func (p *Response) Status() int { + return p.status +} + +// Body get the reaponse body +func (p *Response) Body() string { + return string(p.body) +} + +// To cast to custom sturct +func (p *Response) To(v interface{}) error { + err := jsoniter.Unmarshal(p.body, v) + if err != nil { + return err + } + return nil +} + +// AutoLogin auto login +func AutoLogin(id int) (map[string]interface{}, error) { + + user := model.Select("admin.user") + row, err := user.Find(id, model.QueryParam(model.QueryParam{Select: []interface{}{"id", "name", "type", "email", "mobile", "extra", "status"}})) + if err != nil { + return nil, err + } + expiresAt := time.Now().Unix() + 3600 + sid := session.ID() + token := helper.JwtMake(id, map[string]interface{}{}, map[string]interface{}{ + "expires_at": expiresAt, + "sid": sid, + "issuer": "admin", + }) + session.Global().Expire(time.Duration(token.ExpiresAt)*time.Second).ID(sid).Set("user_id", id) + session.Global().ID(sid).Set("user", row) + session.Global().ID(sid).Set("issuer", "admin") + + p, err := process.Of("yao.app.menu") + if err != nil { + return nil, err + } + + menus, err := p.Exec() + if err != nil { + return nil, err + } + + return map[string]interface{}{ + "expires_at": token.ExpiresAt, + "token": token.Token, + "user": row, + "menus": menus, + }, nil +} diff --git a/test/test.go b/test/utils.go similarity index 67% rename from test/test.go rename to test/utils.go index e31acb20..925f9677 100644 --- a/test/test.go +++ b/test/utils.go @@ -3,21 +3,29 @@ package test import ( "fmt" "os" + "strings" "testing" + "time" + "github.com/gin-gonic/gin" + "github.com/yaoapp/gou/api" "github.com/yaoapp/gou/application" "github.com/yaoapp/gou/model" "github.com/yaoapp/gou/query" "github.com/yaoapp/gou/query/gou" v8 "github.com/yaoapp/gou/runtime/v8" + "github.com/yaoapp/gou/server/http" "github.com/yaoapp/kun/exception" "github.com/yaoapp/xun/capsule" "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/fs" + "github.com/yaoapp/yao/helper" "github.com/yaoapp/yao/runtime" "github.com/yaoapp/yao/share" ) +var testServer *http.Server = nil + // Prepare test environment func Prepare(t *testing.T, cfg config.Config) { root := os.Getenv("YAO_TEST_APPLICATION") @@ -42,7 +50,7 @@ func Prepare(t *testing.T, cfg config.Config) { dbconnect(t, cfg) load(t, cfg) - start(t, cfg) + startRuntime(t, cfg) } // Clean the test environment @@ -51,6 +59,49 @@ func Clean() { runtime.Stop() } +// Start the test server +func Start(t *testing.T, guards map[string]gin.HandlerFunc, cfg config.Config) { + + var err error + option := http.Option{Port: 0, Root: "/", Timeout: 2 * time.Second} + gin.SetMode(gin.ReleaseMode) + + router := gin.New() + api.SetGuards(guards) + api.SetRoutes(router, "api") + + testServer = http.New(router, option) + go func() { err = testServer.Start() }() + + <-testServer.Event() + if err != nil { + t.Fatal(err) + } +} + +// Stop the test server +func Stop() { + if testServer != nil { + testServer.Stop() + <-testServer.Event() + } + + dbclose() + runtime.Stop() +} + +// Port Get the test server port +func Port(t *testing.T) int { + if testServer == nil { + t.Fatal(fmt.Errorf("server not started")) + } + port, err := testServer.Port() + if err != nil { + t.Fatal(err) + } + return port +} + func dbclose() { if capsule.Global != nil { capsule.Global.Connections.Range(func(key, value any) bool { @@ -76,7 +127,7 @@ func dbconnect(t *testing.T, cfg config.Config) { } -func start(t *testing.T, cfg config.Config) { +func startRuntime(t *testing.T, cfg config.Config) { err := runtime.Start(cfg) if err != nil { t.Fatal(err) @@ -145,3 +196,18 @@ func loadQuery(t *testing.T, cfg config.Config) { AESKey: cfg.DB.AESKey, }) } + +// GuardBearerJWT test guard +func GuardBearerJWT(c *gin.Context) { + tokenString := c.Request.Header.Get("Authorization") + tokenString = strings.TrimSpace(strings.TrimPrefix(tokenString, "Bearer ")) + + if tokenString == "" { + c.JSON(403, gin.H{"code": 403, "message": "No permission"}) + c.Abort() + return + } + + claims := helper.JwtValidate(tokenString) + c.Set("__sid", claims.SID) +} diff --git a/widgets/table/api_test.go b/widgets/table/api_test.go index 5472844e..29e75001 100644 --- a/widgets/table/api_test.go +++ b/widgets/table/api_test.go @@ -1,8 +1,15 @@ package table import ( + "fmt" + "net/url" + "testing" + "github.com/gin-gonic/gin" - "github.com/yaoapp/yao/widgets/test" + "github.com/stretchr/testify/assert" + "github.com/yaoapp/kun/any" + "github.com/yaoapp/yao/config" + "github.com/yaoapp/yao/test" ) var guards = map[string]gin.HandlerFunc{ @@ -10,194 +17,194 @@ var guards = map[string]gin.HandlerFunc{ "widget-table": Guard, } -// func TestAPISetting(t *testing.T) { +func TestAPISetting(t *testing.T) { -// gtest.Prepare(t, config.Conf) -// defer gtest.Clean() + port := start(t) + defer stop() -// prepare(t) -// clear(t) -// testData(t) + req := test.NewRequest(port).Route("/api/__yao/table/pet/setting") + res, err := req.Get() + if err != nil { + t.Fatal(err) + } + assert.Equal(t, 403, res.Status()) -// port := start(t) -// defer test.Stop(func() {}) + req = test.NewRequest(port).Route("/api/__yao/table/pet/setting").Token(token(t)) + res, err = req.Get() + if err != nil { + t.Fatal(err) + } + assert.Equal(t, 200, res.Status()) -// req := test.NewRequest(port).Route("/api/__yao/table/pet/setting") -// res, err := req.Get() -// if err != nil { -// t.Fatal(err) -// } -// assert.Equal(t, 403, res.Status()) + v, err := res.Map() + if err != nil { + t.Fatal(err) + } -// req = test.NewRequest(port).Route("/api/__yao/table/pet/setting").Token(token(t)) -// res, err = req.Get() -// if err != nil { -// t.Fatal(err) -// } -// assert.Equal(t, 200, res.Status()) + data := any.Of(v).MapStr().Dot() + assert.Equal(t, "/api/xiang/import/pet", data.Get("header.preset.import.api.import")) + // assert.Equal(t, "跳转", data.Get("header.preset.import.operation.0.title")) + assert.Equal(t, "/api/__yao/table/pet/component/fields.table."+url.QueryEscape("入院状态")+".view.props.xProps/remote", data.Get("fields.table.入院状态.view.props.xProps.remote.api")) + assert.Equal(t, "/api/__yao/table/pet/component/fields.table."+url.QueryEscape("入院状态")+".edit.props.xProps/remote", data.Get("fields.table.入院状态.edit.props.xProps.remote.api")) +} -// v, err := res.Map() -// if err != nil { -// t.Fatal(err) -// } -// data := any.Of(v).MapStr().Dot() -// assert.Equal(t, "/api/xiang/import/pet", data.Get("header.preset.import.api.import")) -// // assert.Equal(t, "跳转", data.Get("header.preset.import.operation.0.title")) -// assert.Equal(t, "/api/__yao/table/pet/component/fields.table."+url.QueryEscape("入院状态")+".view.props.xProps/remote", data.Get("fields.table.入院状态.view.props.xProps.remote.api")) -// assert.Equal(t, "/api/__yao/table/pet/component/fields.table."+url.QueryEscape("入院状态")+".edit.props.xProps/remote", data.Get("fields.table.入院状态.edit.props.xProps.remote.api")) -// } +func TestAPISearch(t *testing.T) { + port := start(t) + defer test.Stop() -// func TestAPISearch(t *testing.T) { -// port := start(t) -// defer test.Stop(func() {}) + req := test.NewRequest(port).Route("/api/__yao/table/session/search") + res, err := req.Get() + if err != nil { + t.Fatal(err) + } + assert.Equal(t, 403, res.Status()) -// req := test.NewRequest(port).Route("/api/__yao/table/session/search") -// res, err := req.Get() -// if err != nil { -// t.Fatal(err) -// } -// assert.Equal(t, 403, res.Status()) + req = test.NewRequest(port).Route("/api/__yao/table/session/search").Token(token(t)) + res, err = req.Get() + if err != nil { + t.Fatal(err) + } + assert.Equal(t, 200, res.Status()) + resp, err := res.Map() + if err != nil { + t.Fatal(err) + } -// req = test.NewRequest(port).Route("/api/__yao/table/session/search").Token(token(t)) -// res, err = req.Get() -// if err != nil { -// t.Fatal(err) -// } -// assert.Equal(t, 200, res.Status()) -// resp, err := res.Map() -// if err != nil { -// t.Fatal(err) -// } + data := any.Of(resp).MapStr().Dot() + assert.Equal(t, "1", fmt.Sprintf("%v", data.Get("pagesize"))) + assert.Equal(t, "3", fmt.Sprintf("%v", data.Get("total"))) + assert.Equal(t, "checked", data.Get("data.0.status")) + assert.Equal(t, "enabled", data.Get("data.0.mode")) + assert.Equal(t, "1", fmt.Sprintf("%v", data.Get("data.0.doctor_id"))) -// data := any.Of(resp).MapStr().Dot() -// assert.Equal(t, "1", fmt.Sprintf("%v", data.Get("pagesize"))) -// assert.Equal(t, "3", fmt.Sprintf("%v", data.Get("total"))) -// assert.Equal(t, "checked", data.Get("data.0.status")) -// assert.Equal(t, "enabled", data.Get("data.0.mode")) -// assert.Equal(t, "1", fmt.Sprintf("%v", data.Get("data.0.doctor_id"))) +} -// } +func TestAPISave(t *testing.T) { + port := start(t) + defer test.Stop() -// func TestAPISave(t *testing.T) { -// port := start(t) -// defer test.Stop(func() {}) + payload := map[string]interface{}{ + "name": "New Pet", + "type": "cat", + "status": "checked", + "mode": "enabled", + "stay": 66, + "cost": 24, + "doctor_id": 1, + } -// payload := map[string]interface{}{ -// "name": "New Pet", -// "type": "cat", -// "status": "checked", -// "mode": "enabled", -// "stay": 66, -// "cost": 24, -// "doctor_id": 1, -// } + req := test.NewRequest(port).Route("/api/__yao/table/pet/save").Data(payload) + res, err := req.Post() + if err != nil { + t.Fatal(err) + } + assert.Equal(t, 403, res.Status()) -// req := test.NewRequest(port).Route("/api/__yao/table/pet/save").Data(payload) -// res, err := req.Post() -// if err != nil { -// t.Fatal(err) -// } -// assert.Equal(t, 403, res.Status()) + req = test.NewRequest(port).Route("/api/__yao/table/pet/save").Data(payload).Token(token(t)) + res, err = req.Post() + if err != nil { + t.Fatal(err) + } + assert.Equal(t, 200, res.Status()) -// req = test.NewRequest(port).Route("/api/__yao/table/pet/save").Data(payload).Token(token(t)) -// res, err = req.Post() -// if err != nil { -// t.Fatal(err) -// } -// assert.Equal(t, 200, res.Status()) + v, err := res.Int() + if err != nil { + t.Fatal(err) + } -// v, err := res.Int() -// if err != nil { -// t.Fatal(err) -// } + assert.Equal(t, 4, v) +} -// assert.Equal(t, 4, v) -// } +func TestAPICustomGuard(t *testing.T) { -// func TestAPICustomGuard(t *testing.T) { + port := start(t) + defer test.Stop() -// port := start(t) -// defer test.Stop(func() {}) + req := test.NewRequest(port).Route("/api/__yao/table/pet/find/1") + res, err := req.Get() + if err != nil { + t.Fatal(err) + } -// req := test.NewRequest(port).Route("/api/__yao/table/pet/find/1") -// res, err := req.Get() -// if err != nil { -// t.Fatal(err) -// } + req = test.NewRequest(port).Route("/api/__yao/table/pet/get") + res, err = req.Get() + if err != nil { + t.Fatal(err) + } + assert.Equal(t, 403, res.Status()) -// req = test.NewRequest(port).Route("/api/__yao/table/pet/get") -// res, err = req.Get() -// if err != nil { -// t.Fatal(err) -// } -// assert.Equal(t, 403, res.Status()) + req = test.NewRequest(port).Route("/api/__yao/table/pet/get").Token(token(t)).Header("Unit-Test", "yes") + res, err = req.Get() + if err != nil { + t.Fatal(err) + } + assert.Equal(t, 418, res.Status()) -// req = test.NewRequest(port).Route("/api/__yao/table/pet/get").Token(token(t)).Header("Unit-Test", "yes") -// res, err = req.Get() -// if err != nil { -// t.Fatal(err) -// } -// assert.Equal(t, 418, res.Status()) + req = test.NewRequest(port).Route("/api/__yao/table/pet/get").Token(token(t)) + res, err = req.Get() + if err != nil { + t.Fatal(err) + } + assert.Equal(t, 200, res.Status()) +} -// req = test.NewRequest(port).Route("/api/__yao/table/pet/get").Token(token(t)) -// res, err = req.Get() -// if err != nil { -// t.Fatal(err) -// } -// assert.Equal(t, 200, res.Status()) -// } +func TestAPIGlobalCustomGuard(t *testing.T) { -// func TestAPIGlobalCustomGuard(t *testing.T) { + port := start(t) + defer test.Stop() -// port := start(t) -// defer test.Stop(func() {}) + req := test.NewRequest(port).Route("/api/__yao/table/guard/find/1") + res, err := req.Get() + if err != nil { + t.Fatal(err) + } + assert.Equal(t, 403, res.Status()) -// req := test.NewRequest(port).Route("/api/__yao/table/guard/find/1") -// res, err := req.Get() -// if err != nil { -// t.Fatal(err) -// } -// assert.Equal(t, 403, res.Status()) + req = test.NewRequest(port).Route("/api/__yao/table/guard/get") + res, err = req.Get() + if err != nil { + t.Fatal(err) + } + assert.Equal(t, 403, res.Status()) -// req = test.NewRequest(port).Route("/api/__yao/table/guard/get") -// res, err = req.Get() -// if err != nil { -// t.Fatal(err) -// } -// assert.Equal(t, 403, res.Status()) + req = test.NewRequest(port).Route("/api/__yao/table/guard/get").Token(token(t)).Header("Unit-Test", "yes") + res, err = req.Get() + if err != nil { + t.Fatal(err) + } + assert.Equal(t, 418, res.Status()) -// req = test.NewRequest(port).Route("/api/__yao/table/guard/get").Token(token(t)).Header("Unit-Test", "yes") -// res, err = req.Get() -// if err != nil { -// t.Fatal(err) -// } -// assert.Equal(t, 418, res.Status()) + req = test.NewRequest(port).Route("/api/__yao/table/guard/get").Token(token(t)) + res, err = req.Get() + if err != nil { + t.Fatal(err) + } + assert.Equal(t, 200, res.Status()) +} -// req = test.NewRequest(port).Route("/api/__yao/table/guard/get").Token(token(t)) -// res, err = req.Get() -// if err != nil { -// t.Fatal(err) -// } -// assert.Equal(t, 200, res.Status()) -// } +func start(t *testing.T) int { + test.Prepare(t, config.Conf) + prepare(t) + clear(t) + testData(t) + test.Start(t, guards, config.Conf) -// func start(t *testing.T) int { -// port := 0 + return test.Port(t) +} -// go test.Start(t, guards, port) -// time.Sleep(200 * time.Millisecond) -// return port -// } +func stop() { + test.Stop() +} -// func token(t *testing.T) string { -// res, err := test.AutoLogin(1) -// if err != nil { -// t.Fatal(err) -// } +func token(t *testing.T) string { + res, err := test.AutoLogin(1) + if err != nil { + t.Fatal(err) + } -// token, ok := res["token"].(string) -// if !ok { -// t.Fatal(fmt.Errorf("get token error %v", res)) -// } -// return token -// } + token, ok := res["token"].(string) + if !ok { + t.Fatal(fmt.Errorf("get token error %v", res)) + } + return token +}