From ecee5d73dbe4620f3ec6b4ee7646bb951412e575 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 14 Oct 2022 12:25:03 +0800 Subject: [PATCH] [add] studio server unit-tests --- studio/router.go | 71 +++++++++++++++++---- studio/studio.go | 4 +- studio/studio_test.go | 141 +++++++++++++++++++++++++++++++++++++++--- tests/studio/table.js | 12 ++++ 4 files changed, 205 insertions(+), 23 deletions(-) diff --git a/studio/router.go b/studio/router.go index caccfcb7..6bb90e54 100644 --- a/studio/router.go +++ b/studio/router.go @@ -4,6 +4,8 @@ import ( "fmt" "io" "os" + "regexp" + "strconv" "strings" "github.com/gin-gonic/gin" @@ -11,6 +13,8 @@ import ( "github.com/yaoapp/gou" ) +var regExcp = regexp.MustCompile("^Exception\\|([0-9]+):(.+)$") + // Serve start the api server func setRouter(router *gin.Engine) { @@ -42,7 +46,7 @@ func setRouter(router *gin.Engine) { } c.JSON(200, res) c.Done() - break + return case "readdir": name := c.Query("name") @@ -62,11 +66,13 @@ func setRouter(router *gin.Engine) { } c.JSON(200, data) c.Done() - break + return } + + throw(c, 404, fmt.Sprintf("%s method does not found", c.Param("method"))) }) - // DSL WriteFile, Mkdir, MkdirAll ... + // DSL WriteFile, Mkdir, MkdirAll, Remove, RemoveAll ... router.POST("/dsl/:method", func(c *gin.Context) { method := strings.ToLower(c.Param("method")) @@ -97,7 +103,7 @@ func setRouter(router *gin.Engine) { c.JSON(200, length) c.Done() - break + return case "mkdir": name := c.Query("name") @@ -113,7 +119,7 @@ func setRouter(router *gin.Engine) { } c.Status(200) c.Done() - break + return case "mkdirall": name := c.Query("name") @@ -129,9 +135,42 @@ func setRouter(router *gin.Engine) { } c.Status(200) c.Done() - break + return + + case "remove": + name := c.Query("name") + if name == "" { + throw(c, 400, "name is required") + return + } + + err := dfs.Remove(name) + if err != nil { + throw(c, 500, err.Error()) + return + } + c.Status(200) + c.Done() + return + + case "removeall": + name := c.Query("name") + if name == "" { + throw(c, 400, "name is required") + return + } + + err := dfs.RemoveAll(name) + if err != nil { + throw(c, 500, err.Error()) + return + } + c.Status(200) + c.Done() + return } + throw(c, 404, fmt.Sprintf("%s method does not found", c.Param("method"))) }) // Cloud Functions @@ -151,21 +190,31 @@ func setRouter(router *gin.Engine) { return } - if payload == nil || len(payload) > 0 { + if payload == nil || len(payload) == 0 { throw(c, 400, "file content is required") return } - var call cloudCall - err = jsoniter.Unmarshal(payload, &call) + var fun cfunc + err = jsoniter.Unmarshal(payload, &fun) if err != nil { throw(c, 500, err.Error()) return } - res, err := gou.Yao.Engine.Call(map[string]interface{}{}, service, call.Method, call.Args...) + res, err := gou.Yao.Engine.Call(map[string]interface{}{}, service, fun.Method, fun.Args...) if err != nil { - throw(c, 500, err.Error()) + // parse Exception + code := 500 + message := err.Error() + match := regExcp.FindStringSubmatch(message) + if len(match) > 0 { + code, err = strconv.Atoi(match[1]) + if err == nil { + message = strings.TrimSpace(match[2]) + } + } + throw(c, code, message) return } diff --git a/studio/studio.go b/studio/studio.go index 513111c0..b704e0ef 100644 --- a/studio/studio.go +++ b/studio/studio.go @@ -23,7 +23,7 @@ var shutdownSignal = make(chan bool, 1) var dfs fs.FileSystem var scripts = map[string][]byte{} -type cloudCall struct { +type cfunc struct { Method string `json:"method"` Args []interface{} `json:"args,omitempty"` } @@ -38,7 +38,7 @@ func Start(cfg config.Config) (err error) { errCh := make(chan error, 1) // Set router - router := gin.Default() + router := gin.New() setRouter(router) // Server setting diff --git a/studio/studio_test.go b/studio/studio_test.go index 4c9c7a95..97548c6d 100644 --- a/studio/studio_test.go +++ b/studio/studio_test.go @@ -1,16 +1,22 @@ package studio import ( + "bytes" "fmt" + "io" + "net/http" "testing" "time" + jsoniter "github.com/json-iterator/go" "github.com/stretchr/testify/assert" "github.com/yaoapp/gou" "github.com/yaoapp/yao/config" - "github.com/yaoapp/yao/network" ) +type kv map[string]interface{} +type arr []interface{} + func TestLoad(t *testing.T) { err := Load(config.Conf) if err != nil { @@ -63,16 +69,131 @@ func TestGetAPI(t *testing.T) { defer Stop() time.Sleep(500 * time.Millisecond) - url := fmt.Sprintf("http://127.0.0.1:%d/dsl/readfile?name=/models/user.json", config.Conf.Studio.Port) - res := network.RequestGet(url, nil, nil) - assert.Equal(t, "用户", res.Data.(map[string]interface{})["name"]) + code, row := httpGet[kv]("/dsl/ReadFile?name=/models/user.json", t) + assert.Equal(t, 200, code) + assert.Equal(t, "用户", row["name"]) - url = fmt.Sprintf("http://127.0.0.1:%d/dsl/readdir?name=/models", config.Conf.Studio.Port) - res = network.RequestGet(url, nil, nil) - assert.Equal(t, 11, len(res.Data.([]interface{}))) + code, rows := httpGet[arr]("/dsl/ReadDir?name=/models", t) + assert.Equal(t, 200, code) + assert.Equal(t, 11, len(rows)) - url = fmt.Sprintf("http://127.0.0.1:%d/dsl/readdir?name=/models&recursive=1", config.Conf.Studio.Port) - res = network.RequestGet(url, nil, nil) - assert.Equal(t, 12, len(res.Data.([]interface{}))) + code, rows = httpGet[arr]("/dsl/ReadDir?name=/models&recursive=1", t) + assert.Equal(t, 200, code) + assert.Equal(t, 12, len(rows)) + code, length := httpPost[int]("/dsl/WriteFile?name=/models/foo.mod.json", []byte(`{"name":"foo"}`), t) + assert.Equal(t, 200, code) + assert.Equal(t, 19, length) + + code, _ = httpPost[kv]("/dsl/Remove?name=/models/foo.mod.json", nil, t) + assert.Equal(t, 200, code) + + code, _ = httpPost[kv]("/dsl/Mkdir?name=/models/bar", nil, t) + assert.Equal(t, 200, code) + + code, _ = httpPost[kv]("/dsl/Remove?name=/models/bar", nil, t) + assert.Equal(t, 200, code) + + code, _ = httpPost[kv]("/dsl/MkdirAll?name=/models/bar/hi", nil, t) + assert.Equal(t, 200, code) + + code, _ = httpPost[kv]("/dsl/RemoveAll?name=/models/bar", nil, t) + assert.Equal(t, 200, code) + + code, res := httpPostJSON[arr]( + "/service/table", + kv{ + "method": "UnitTest", + "args": []interface{}{ + "foo", 1, 0.618, + kv{"string": "world", "int": 1, "float": 0.618}, + arr{"foo", 1, 0.618}, + }, + }, t) + + assert.Equal(t, 200, code) + assert.Equal(t, "foo", res[0]) + assert.Equal(t, float64(1), res[1]) + assert.Equal(t, 0.618, res[2]) + assert.Equal(t, "world", res[3].(map[string]interface{})["string"]) + assert.Equal(t, float64(1), res[3].(map[string]interface{})["int"]) + assert.Equal(t, 0.618, res[3].(map[string]interface{})["float"]) + assert.Equal(t, "foo", res[4].([]interface{})[0]) + assert.Equal(t, float64(1), res[4].([]interface{})[1]) + assert.Equal(t, 0.618, res[4].([]interface{})[2]) + + code, excp := httpPostJSON[kv]("/service/table", kv{"method": "UnitTest", "args": []interface{}{"throw-test"}}, t) + assert.Equal(t, 418, code) + assert.Equal(t, float64(418), excp["code"]) + assert.Equal(t, "I'm a teapot", excp["message"]) +} + +func httpGet[T kv | arr | interface{} | map[string]interface{} | int | []interface{}](url string, t *testing.T) (int, T) { + + var data T + url = fmt.Sprintf("http://127.0.0.1:%d%s", config.Conf.Studio.Port, url) + res, err := http.Get(url) + if err != nil { + t.Fatal(err) + } + + if res.Body != nil { + body, err := io.ReadAll(res.Body) + if err != nil { + t.Fatal(err) + } + + if body != nil { + err = jsoniter.Unmarshal(body, &data) + if err != nil { + t.Fatal(err) + } + } + } + + return res.StatusCode, data +} + +func httpPost[T kv | arr | interface{} | map[string]interface{} | int | []interface{}](url string, payload []byte, t *testing.T) (int, T) { + + var data T + var buff *bytes.Buffer = bytes.NewBuffer([]byte{}) + + if payload != nil { + buff = bytes.NewBuffer(payload) + } + + url = fmt.Sprintf("http://127.0.0.1:%d%s", config.Conf.Studio.Port, url) + res, err := http.Post(url, "application/json", buff) + if err != nil { + t.Fatal(err) + } + + if res.Body != nil { + body, err := io.ReadAll(res.Body) + if err != nil { + t.Fatal(err) + } + + if body != nil && string(body) != "" { + err = jsoniter.Unmarshal(body, &data) + if err != nil { + t.Fatal(err) + } + } + } + + return res.StatusCode, data +} + +func httpPostJSON[T kv | arr | interface{} | map[string]interface{} | int | []interface{}](url string, payload interface{}, t *testing.T) (int, T) { + var data []byte + var err error + if payload != nil { + data, err = jsoniter.Marshal(payload) + if err != nil { + t.Fatal(err) + } + } + return httpPost[T](url, data, t) } diff --git a/tests/studio/table.js b/tests/studio/table.js index 2cda5ccc..925de552 100644 --- a/tests/studio/table.js +++ b/tests/studio/table.js @@ -31,3 +31,15 @@ function Model(name) { function Ping() { return "PONG"; } + +/** + * for unit tests + * @param {...any} args + * @returns + */ +function UnitTest(...args) { + if (args.length > 0 && args[0] == "throw-test") { + throw new Exception("I'm a teapot", 418); + } + return args; +}