feat: add webhook bearer auth support (#619)
* feat: add webhook bearer auth support * add tpl func randFloat * refactor run webhook * add template func uptimeSeconds * add random enum with weight feature --------- Co-authored-by: rick <LinuxSuRen@users.noreply.github.com>
This commit is contained in:
parent
430d9127c6
commit
4b4ee3a60c
|
@ -7,16 +7,20 @@ items:
|
||||||
response:
|
response:
|
||||||
header:
|
header:
|
||||||
server: mock
|
server: mock
|
||||||
|
content-type: application/json
|
||||||
body: |
|
body: |
|
||||||
{
|
{
|
||||||
"count": 1,
|
"count": 1,
|
||||||
"items": [{
|
"items": [{
|
||||||
"title": "fix: there is a bug on page {{ randEnum "one" }}",
|
"title": "fix: there is a bug on page {{ randEnum "one" "two" "three" "four" }}",
|
||||||
"number": 123,
|
"number": {{randInt 100 199}},
|
||||||
|
"float": {{randFloat 0.0 1.0}},
|
||||||
|
"status": "{{randWeightEnum (weightObject 4 "open") (weightObject 1 "closed")}}",
|
||||||
"message": "{{.Response.Header.server}}",
|
"message": "{{.Response.Header.server}}",
|
||||||
"author": "someone",
|
"author": "{{env "USER"}}",
|
||||||
"status": "success"
|
"created": "{{ now.Format "2006-01-02T15:04:05Z07:00" }}"
|
||||||
}]
|
}],
|
||||||
|
"uptime": "{{uptime}}"
|
||||||
}
|
}
|
||||||
- name: base64
|
- name: base64
|
||||||
request:
|
request:
|
||||||
|
|
|
@ -10,4 +10,28 @@ title = "用例模板"
|
||||||
|
|
||||||
```
|
```
|
||||||
182{{shuffle "09876543"}}
|
182{{shuffle "09876543"}}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 带权重的随机枚举
|
||||||
|
|
||||||
|
下面的代码以 80% 的概率返回 `open`,以 20% 的概率返回 `closed`:
|
||||||
|
|
||||||
|
```
|
||||||
|
{{randWeightEnum (weightObject 4 "open") (weightObject 1 "closed")}}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 时间
|
||||||
|
|
||||||
|
下面的代码可以生成当前时间,并制定时间格式:
|
||||||
|
|
||||||
|
```
|
||||||
|
{{ now.Format "2006-01-02T15:04:05Z07:00" }}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 环境变量
|
||||||
|
|
||||||
|
下面的代码可以获取环境变量 `SHELL` 的值,在需要使用一个全局变量的时候,可以使用这个模板函数:
|
||||||
|
|
||||||
|
```
|
||||||
|
{{ env "SHELL" }}
|
||||||
|
```
|
||||||
|
|
|
@ -16,470 +16,529 @@ limitations under the License.
|
||||||
package mock
|
package mock
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"bytes"
|
||||||
"encoding/base64"
|
"context"
|
||||||
"encoding/json"
|
"encoding/base64"
|
||||||
"fmt"
|
"encoding/json"
|
||||||
"io"
|
"fmt"
|
||||||
"net"
|
"io"
|
||||||
"net/http"
|
"net"
|
||||||
"strings"
|
"net/http"
|
||||||
"sync"
|
"strings"
|
||||||
"time"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/swaggest/openapi-go/openapi3"
|
"github.com/swaggest/openapi-go/openapi3"
|
||||||
"github.com/swaggest/rest/gorillamux"
|
"github.com/swaggest/rest/gorillamux"
|
||||||
|
|
||||||
"github.com/linuxsuren/api-testing/pkg/version"
|
"github.com/linuxsuren/api-testing/pkg/version"
|
||||||
|
|
||||||
"github.com/linuxsuren/api-testing/pkg/logging"
|
"github.com/linuxsuren/api-testing/pkg/logging"
|
||||||
"github.com/linuxsuren/api-testing/pkg/render"
|
"github.com/linuxsuren/api-testing/pkg/render"
|
||||||
"github.com/linuxsuren/api-testing/pkg/util"
|
"github.com/linuxsuren/api-testing/pkg/util"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
memLogger = logging.DefaultLogger(logging.LogLevelInfo).WithName("memory")
|
memLogger = logging.DefaultLogger(logging.LogLevelInfo).WithName("memory")
|
||||||
)
|
)
|
||||||
|
|
||||||
type inMemoryServer struct {
|
type inMemoryServer struct {
|
||||||
data map[string][]map[string]interface{}
|
data map[string][]map[string]interface{}
|
||||||
mux *mux.Router
|
mux *mux.Router
|
||||||
listener net.Listener
|
listener net.Listener
|
||||||
port int
|
port int
|
||||||
prefix string
|
prefix string
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
cancelFunc context.CancelFunc
|
cancelFunc context.CancelFunc
|
||||||
reader Reader
|
reader Reader
|
||||||
metrics RequestMetrics
|
metrics RequestMetrics
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewInMemoryServer(ctx context.Context, port int) DynamicServer {
|
func NewInMemoryServer(ctx context.Context, port int) DynamicServer {
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
return &inMemoryServer{
|
return &inMemoryServer{
|
||||||
port: port,
|
port: port,
|
||||||
wg: sync.WaitGroup{},
|
wg: sync.WaitGroup{},
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
cancelFunc: cancel,
|
cancelFunc: cancel,
|
||||||
metrics: NewNoopMetrics(),
|
metrics: NewNoopMetrics(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *inMemoryServer) SetupHandler(reader Reader, prefix string) (handler http.Handler, err error) {
|
func (s *inMemoryServer) SetupHandler(reader Reader, prefix string) (handler http.Handler, err error) {
|
||||||
s.reader = reader
|
s.reader = reader
|
||||||
// init the data
|
// init the data
|
||||||
s.data = make(map[string][]map[string]interface{})
|
s.data = make(map[string][]map[string]interface{})
|
||||||
s.mux = mux.NewRouter().PathPrefix(prefix).Subrouter()
|
s.mux = mux.NewRouter().PathPrefix(prefix).Subrouter()
|
||||||
s.prefix = prefix
|
s.prefix = prefix
|
||||||
handler = s.mux
|
handler = s.mux
|
||||||
s.metrics.AddMetricsHandler(s.mux)
|
s.metrics.AddMetricsHandler(s.mux)
|
||||||
err = s.Load()
|
err = s.Load()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *inMemoryServer) Load() (err error) {
|
func (s *inMemoryServer) Load() (err error) {
|
||||||
var server *Server
|
var server *Server
|
||||||
if server, err = s.reader.Parse(); err != nil {
|
if server, err = s.reader.Parse(); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
memLogger.Info("start to run all the APIs from objects", "count", len(server.Objects))
|
memLogger.Info("start to run all the APIs from objects", "count", len(server.Objects))
|
||||||
for _, obj := range server.Objects {
|
for _, obj := range server.Objects {
|
||||||
memLogger.Info("start mock server from object", "name", obj.Name)
|
memLogger.Info("start mock server from object", "name", obj.Name)
|
||||||
s.startObject(obj)
|
s.startObject(obj)
|
||||||
s.initObjectData(obj)
|
s.initObjectData(obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
memLogger.Info("start to run all the APIs from items", "count", len(server.Items))
|
memLogger.Info("start to run all the APIs from items", "count", len(server.Items))
|
||||||
for _, item := range server.Items {
|
for _, item := range server.Items {
|
||||||
s.startItem(item)
|
s.startItem(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
memLogger.Info("start webhook servers", "count", len(server.Webhooks))
|
memLogger.Info("start webhook servers", "count", len(server.Webhooks))
|
||||||
for _, item := range server.Webhooks {
|
for _, item := range server.Webhooks {
|
||||||
if err = s.startWebhook(&item); err != nil {
|
if err = s.startWebhook(&item); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s.handleOpenAPI()
|
s.handleOpenAPI()
|
||||||
|
|
||||||
for _, proxy := range server.Proxies {
|
for _, proxy := range server.Proxies {
|
||||||
memLogger.Info("start to proxy", "target", proxy.Target)
|
memLogger.Info("start to proxy", "target", proxy.Target)
|
||||||
s.mux.HandleFunc(proxy.Path, func(w http.ResponseWriter, req *http.Request) {
|
s.mux.HandleFunc(proxy.Path, func(w http.ResponseWriter, req *http.Request) {
|
||||||
api := fmt.Sprintf("%s/%s", proxy.Target, strings.TrimPrefix(req.URL.Path, s.prefix))
|
api := fmt.Sprintf("%s/%s", proxy.Target, strings.TrimPrefix(req.URL.Path, s.prefix))
|
||||||
api, err = render.Render("proxy api", api, s)
|
api, err = render.Render("proxy api", api, s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
memLogger.Error(err, "failed to render proxy api")
|
memLogger.Error(err, "failed to render proxy api")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
memLogger.Info("redirect to", "target", api)
|
memLogger.Info("redirect to", "target", api)
|
||||||
|
|
||||||
targetReq, err := http.NewRequestWithContext(req.Context(), req.Method, api, req.Body)
|
targetReq, err := http.NewRequestWithContext(req.Context(), req.Method, api, req.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
memLogger.Error(err, "failed to create proxy request")
|
memLogger.Error(err, "failed to create proxy request")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := http.DefaultClient.Do(targetReq)
|
resp, err := http.DefaultClient.Do(targetReq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
memLogger.Error(err, "failed to do proxy request")
|
memLogger.Error(err, "failed to do proxy request")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := io.ReadAll(resp.Body)
|
data, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
memLogger.Error(err, "failed to read response body")
|
memLogger.Error(err, "failed to read response body")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, v := range resp.Header {
|
for k, v := range resp.Header {
|
||||||
w.Header().Add(k, v[0])
|
w.Header().Add(k, v[0])
|
||||||
}
|
}
|
||||||
w.Write(data)
|
w.Write(data)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *inMemoryServer) Start(reader Reader, prefix string) (err error) {
|
func (s *inMemoryServer) Start(reader Reader, prefix string) (err error) {
|
||||||
var handler http.Handler
|
var handler http.Handler
|
||||||
if handler, err = s.SetupHandler(reader, prefix); err == nil {
|
if handler, err = s.SetupHandler(reader, prefix); err == nil {
|
||||||
if s.listener, err = net.Listen("tcp", fmt.Sprintf(":%d", s.port)); err == nil {
|
if s.listener, err = net.Listen("tcp", fmt.Sprintf(":%d", s.port)); err == nil {
|
||||||
go func() {
|
go func() {
|
||||||
err = http.Serve(s.listener, handler)
|
err = http.Serve(s.listener, handler)
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *inMemoryServer) EnableMetrics() {
|
func (s *inMemoryServer) EnableMetrics() {
|
||||||
s.metrics = NewInMemoryMetrics()
|
s.metrics = NewInMemoryMetrics()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *inMemoryServer) startObject(obj Object) {
|
func (s *inMemoryServer) startObject(obj Object) {
|
||||||
// create a simple CRUD server
|
// create a simple CRUD server
|
||||||
s.mux.HandleFunc("/"+obj.Name, func(w http.ResponseWriter, req *http.Request) {
|
s.mux.HandleFunc("/"+obj.Name, func(w http.ResponseWriter, req *http.Request) {
|
||||||
fmt.Println("mock server received request", req.URL.Path)
|
fmt.Println("mock server received request", req.URL.Path)
|
||||||
s.metrics.RecordRequest(req.URL.Path)
|
s.metrics.RecordRequest(req.URL.Path)
|
||||||
method := req.Method
|
method := req.Method
|
||||||
w.Header().Set(util.ContentType, util.JSON)
|
w.Header().Set(util.ContentType, util.JSON)
|
||||||
|
|
||||||
switch method {
|
switch method {
|
||||||
case http.MethodGet:
|
case http.MethodGet:
|
||||||
// list all items
|
// list all items
|
||||||
allItems := s.data[obj.Name]
|
allItems := s.data[obj.Name]
|
||||||
filteredItems := make([]map[string]interface{}, 0)
|
filteredItems := make([]map[string]interface{}, 0)
|
||||||
|
|
||||||
for i, item := range allItems {
|
for i, item := range allItems {
|
||||||
exclude := false
|
exclude := false
|
||||||
|
|
||||||
for k, v := range req.URL.Query() {
|
for k, v := range req.URL.Query() {
|
||||||
if len(v) == 0 {
|
if len(v) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if val, ok := item[k]; ok && val != v[0] {
|
if val, ok := item[k]; ok && val != v[0] {
|
||||||
exclude = true
|
exclude = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !exclude {
|
if !exclude {
|
||||||
filteredItems = append(filteredItems, allItems[i])
|
filteredItems = append(filteredItems, allItems[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(filteredItems) != len(allItems) {
|
if len(filteredItems) != len(allItems) {
|
||||||
allItems = filteredItems
|
allItems = filteredItems
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := json.Marshal(allItems)
|
data, err := json.Marshal(allItems)
|
||||||
writeResponse(w, data, err)
|
writeResponse(w, data, err)
|
||||||
case http.MethodPost:
|
case http.MethodPost:
|
||||||
// create an item
|
// create an item
|
||||||
if data, err := io.ReadAll(req.Body); err == nil {
|
if data, err := io.ReadAll(req.Body); err == nil {
|
||||||
objData := map[string]interface{}{}
|
objData := map[string]interface{}{}
|
||||||
|
|
||||||
jsonErr := json.Unmarshal(data, &objData)
|
jsonErr := json.Unmarshal(data, &objData)
|
||||||
if jsonErr != nil {
|
if jsonErr != nil {
|
||||||
memLogger.Info(jsonErr.Error())
|
memLogger.Info(jsonErr.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
s.data[obj.Name] = append(s.data[obj.Name], objData)
|
s.data[obj.Name] = append(s.data[obj.Name], objData)
|
||||||
|
|
||||||
_, _ = w.Write(data)
|
_, _ = w.Write(data)
|
||||||
} else {
|
} else {
|
||||||
memLogger.Info("failed to read from body", "error", err)
|
memLogger.Info("failed to read from body", "error", err)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// handle a single object
|
// handle a single object
|
||||||
s.mux.HandleFunc(fmt.Sprintf("/%s/{name}", obj.Name), func(w http.ResponseWriter, req *http.Request) {
|
s.mux.HandleFunc(fmt.Sprintf("/%s/{name}", obj.Name), func(w http.ResponseWriter, req *http.Request) {
|
||||||
s.metrics.RecordRequest(req.URL.Path)
|
s.metrics.RecordRequest(req.URL.Path)
|
||||||
w.Header().Set(util.ContentType, util.JSON)
|
w.Header().Set(util.ContentType, util.JSON)
|
||||||
objects := s.data[obj.Name]
|
objects := s.data[obj.Name]
|
||||||
if objects != nil {
|
if objects != nil {
|
||||||
name := mux.Vars(req)["name"]
|
name := mux.Vars(req)["name"]
|
||||||
var data []byte
|
var data []byte
|
||||||
for _, obj := range objects {
|
for _, obj := range objects {
|
||||||
if obj["name"] == name {
|
if obj["name"] == name {
|
||||||
|
|
||||||
data, _ = json.Marshal(obj)
|
data, _ = json.Marshal(obj)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(data) == 0 {
|
if len(data) == 0 {
|
||||||
w.WriteHeader(http.StatusNotFound)
|
w.WriteHeader(http.StatusNotFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
method := req.Method
|
method := req.Method
|
||||||
switch method {
|
switch method {
|
||||||
case http.MethodGet:
|
case http.MethodGet:
|
||||||
writeResponse(w, data, nil)
|
writeResponse(w, data, nil)
|
||||||
case http.MethodPut:
|
case http.MethodPut:
|
||||||
objData := map[string]interface{}{}
|
objData := map[string]interface{}{}
|
||||||
if data, err := io.ReadAll(req.Body); err == nil {
|
if data, err := io.ReadAll(req.Body); err == nil {
|
||||||
|
|
||||||
jsonErr := json.Unmarshal(data, &objData)
|
jsonErr := json.Unmarshal(data, &objData)
|
||||||
if jsonErr != nil {
|
if jsonErr != nil {
|
||||||
memLogger.Info(jsonErr.Error())
|
memLogger.Info(jsonErr.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for i, item := range s.data[obj.Name] {
|
for i, item := range s.data[obj.Name] {
|
||||||
if item["name"] == name {
|
if item["name"] == name {
|
||||||
s.data[obj.Name][i] = objData
|
s.data[obj.Name][i] = objData
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_, _ = w.Write(data)
|
_, _ = w.Write(data)
|
||||||
}
|
}
|
||||||
case http.MethodDelete:
|
case http.MethodDelete:
|
||||||
for i, item := range s.data[obj.Name] {
|
for i, item := range s.data[obj.Name] {
|
||||||
if item["name"] == name {
|
if item["name"] == name {
|
||||||
if len(s.data[obj.Name]) == i+1 {
|
if len(s.data[obj.Name]) == i+1 {
|
||||||
s.data[obj.Name] = s.data[obj.Name][:i]
|
s.data[obj.Name] = s.data[obj.Name][:i]
|
||||||
} else {
|
} else {
|
||||||
s.data[obj.Name] = append(s.data[obj.Name][:i], s.data[obj.Name][i+1])
|
s.data[obj.Name] = append(s.data[obj.Name][:i], s.data[obj.Name][i+1])
|
||||||
}
|
}
|
||||||
|
|
||||||
writeResponse(w, []byte(`{"msg": "deleted"}`), nil)
|
writeResponse(w, []byte(`{"msg": "deleted"}`), nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *inMemoryServer) startItem(item Item) {
|
func (s *inMemoryServer) startItem(item Item) {
|
||||||
method := util.EmptyThenDefault(item.Request.Method, http.MethodGet)
|
method := util.EmptyThenDefault(item.Request.Method, http.MethodGet)
|
||||||
memLogger.Info("register mock service", "method", method, "path", item.Request.Path, "encoder", item.Response.Encoder)
|
memLogger.Info("register mock service", "method", method, "path", item.Request.Path, "encoder", item.Response.Encoder)
|
||||||
|
|
||||||
var headerSlices []string
|
var headerSlices []string
|
||||||
for k, v := range item.Request.Header {
|
for k, v := range item.Request.Header {
|
||||||
headerSlices = append(headerSlices, k, v)
|
headerSlices = append(headerSlices, k, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
adHandler := &advanceHandler{item: &item, metrics: s.metrics}
|
adHandler := &advanceHandler{item: &item, metrics: s.metrics}
|
||||||
s.mux.HandleFunc(item.Request.Path, adHandler.handle).Methods(strings.Split(method, ",")...).Headers(headerSlices...)
|
s.mux.HandleFunc(item.Request.Path, adHandler.handle).Methods(strings.Split(method, ",")...).Headers(headerSlices...)
|
||||||
}
|
}
|
||||||
|
|
||||||
type advanceHandler struct {
|
type advanceHandler struct {
|
||||||
item *Item
|
item *Item
|
||||||
metrics RequestMetrics
|
metrics RequestMetrics
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *advanceHandler) handle(w http.ResponseWriter, req *http.Request) {
|
func (h *advanceHandler) handle(w http.ResponseWriter, req *http.Request) {
|
||||||
h.metrics.RecordRequest(req.URL.Path)
|
h.metrics.RecordRequest(req.URL.Path)
|
||||||
memLogger.Info("receiving mock request", "name", h.item.Name, "method", req.Method, "path", req.URL.Path,
|
memLogger.Info("receiving mock request", "name", h.item.Name, "method", req.Method, "path", req.URL.Path,
|
||||||
"encoder", h.item.Response.Encoder)
|
"encoder", h.item.Response.Encoder)
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
if h.item.Response.Encoder == "base64" {
|
if h.item.Response.Encoder == "base64" {
|
||||||
h.item.Response.BodyData, err = base64.StdEncoding.DecodeString(h.item.Response.Body)
|
h.item.Response.BodyData, err = base64.StdEncoding.DecodeString(h.item.Response.Body)
|
||||||
} else if h.item.Response.Encoder == "url" {
|
} else if h.item.Response.Encoder == "url" {
|
||||||
var resp *http.Response
|
var resp *http.Response
|
||||||
if resp, err = http.Get(h.item.Response.Body); err == nil {
|
if resp, err = http.Get(h.item.Response.Body); err == nil {
|
||||||
h.item.Response.BodyData, err = io.ReadAll(resp.Body)
|
h.item.Response.BodyData, err = io.ReadAll(resp.Body)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
h.item.Response.BodyData, err = render.RenderAsBytes("start-item", h.item.Response.Body, h.item)
|
h.item.Response.BodyData, err = render.RenderAsBytes("start-item", h.item.Response.Body, h.item)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
h.item.Param = mux.Vars(req)
|
h.item.Param = mux.Vars(req)
|
||||||
if h.item.Param == nil {
|
if h.item.Param == nil {
|
||||||
h.item.Param = make(map[string]string)
|
h.item.Param = make(map[string]string)
|
||||||
}
|
}
|
||||||
h.item.Param["Host"] = req.Host
|
h.item.Param["Host"] = req.Host
|
||||||
if h.item.Response.Header == nil {
|
if h.item.Response.Header == nil {
|
||||||
h.item.Response.Header = make(map[string]string)
|
h.item.Response.Header = make(map[string]string)
|
||||||
}
|
}
|
||||||
h.item.Response.Header[headerMockServer] = fmt.Sprintf("api-testing: %s", version.GetVersion())
|
h.item.Response.Header[headerMockServer] = fmt.Sprintf("api-testing: %s", version.GetVersion())
|
||||||
h.item.Response.Header[util.ContentLength] = fmt.Sprintf("%d", len(h.item.Response.BodyData))
|
h.item.Response.Header[util.ContentLength] = fmt.Sprintf("%d", len(h.item.Response.BodyData))
|
||||||
for k, v := range h.item.Response.Header {
|
for k, v := range h.item.Response.Header {
|
||||||
hv, hErr := render.Render("mock-server-header", v, &h.item)
|
hv, hErr := render.Render("mock-server-header", v, &h.item)
|
||||||
if hErr != nil {
|
if hErr != nil {
|
||||||
hv = v
|
hv = v
|
||||||
memLogger.Error(hErr, "failed render mock-server-header", "value", v)
|
memLogger.Error(hErr, "failed render mock-server-header", "value", v)
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set(k, hv)
|
w.Header().Set(k, hv)
|
||||||
}
|
}
|
||||||
w.WriteHeader(util.ZeroThenDefault(h.item.Response.StatusCode, http.StatusOK))
|
w.WriteHeader(util.ZeroThenDefault(h.item.Response.StatusCode, http.StatusOK))
|
||||||
}
|
}
|
||||||
|
|
||||||
writeResponse(w, h.item.Response.BodyData, err)
|
writeResponse(w, h.item.Response.BodyData, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeResponse(w http.ResponseWriter, data []byte, err error) {
|
func writeResponse(w http.ResponseWriter, data []byte, err error) {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
w.Write(data)
|
w.Write(data)
|
||||||
} else {
|
} else {
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *inMemoryServer) initObjectData(obj Object) {
|
func (s *inMemoryServer) initObjectData(obj Object) {
|
||||||
if obj.Sample == "" {
|
if obj.Sample == "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultCount := 1
|
defaultCount := 1
|
||||||
if obj.InitCount == nil {
|
if obj.InitCount == nil {
|
||||||
obj.InitCount = &defaultCount
|
obj.InitCount = &defaultCount
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < *obj.InitCount; i++ {
|
for i := 0; i < *obj.InitCount; i++ {
|
||||||
objData, jsonErr := jsonStrToInterface(obj.Sample)
|
objData, jsonErr := jsonStrToInterface(obj.Sample)
|
||||||
if jsonErr == nil {
|
if jsonErr == nil {
|
||||||
s.data[obj.Name] = append(s.data[obj.Name], objData)
|
s.data[obj.Name] = append(s.data[obj.Name], objData)
|
||||||
} else {
|
} else {
|
||||||
memLogger.Info(jsonErr.Error())
|
memLogger.Info(jsonErr.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *inMemoryServer) startWebhook(webhook *Webhook) (err error) {
|
func (s *inMemoryServer) startWebhook(webhook *Webhook) (err error) {
|
||||||
if webhook.Timer == "" || webhook.Name == "" {
|
if webhook.Timer == "" || webhook.Name == "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var duration time.Duration
|
var duration time.Duration
|
||||||
duration, err = time.ParseDuration(webhook.Timer)
|
duration, err = time.ParseDuration(webhook.Timer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
memLogger.Error(err, "Error parsing webhook timer")
|
memLogger.Error(err, "Error parsing webhook timer")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
s.wg.Add(1)
|
s.wg.Add(1)
|
||||||
go func(wh *Webhook) {
|
go func(wh *Webhook) {
|
||||||
defer s.wg.Done()
|
defer s.wg.Done()
|
||||||
|
|
||||||
memLogger.Info("start webhook server", "name", wh.Name)
|
memLogger.Info("start webhook server", "name", wh.Name)
|
||||||
timer := time.NewTimer(duration)
|
timer := time.NewTimer(duration)
|
||||||
for {
|
for {
|
||||||
timer.Reset(duration)
|
timer.Reset(duration)
|
||||||
select {
|
select {
|
||||||
case <-s.ctx.Done():
|
case <-s.ctx.Done():
|
||||||
memLogger.Info("stop webhook server", "name", wh.Name)
|
memLogger.Info("stop webhook server", "name", wh.Name)
|
||||||
return
|
return
|
||||||
case <-timer.C:
|
case <-timer.C:
|
||||||
client := http.DefaultClient
|
if err = runWebhook(s.ctx, s, wh); err != nil {
|
||||||
|
memLogger.Error(err, "Error when run webhook")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}(webhook)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
payload, err := render.RenderAsReader("mock webhook server payload", wh.Request.Body, wh)
|
func runWebhook(ctx context.Context, objCtx interface{}, wh *Webhook) (err error) {
|
||||||
if err != nil {
|
client := http.DefaultClient
|
||||||
memLogger.Error(err, "Error when render payload")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
method := util.EmptyThenDefault(wh.Request.Method, http.MethodPost)
|
var payload io.Reader
|
||||||
api, err := render.Render("webhook request api", wh.Request.Path, s)
|
payload, err = render.RenderAsReader("mock webhook server payload", wh.Request.Body, wh)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
memLogger.Error(err, "Error when render api", "raw", wh.Request.Path)
|
err = fmt.Errorf("error when render payload: %w", err)
|
||||||
continue
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(s.ctx, method, api, payload)
|
method := util.EmptyThenDefault(wh.Request.Method, http.MethodPost)
|
||||||
if err != nil {
|
var api string
|
||||||
memLogger.Error(err, "Error when create request")
|
api, err = render.Render("webhook request api", wh.Request.Path, objCtx)
|
||||||
continue
|
if err != nil {
|
||||||
}
|
err = fmt.Errorf("error when render api: %w", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
resp, err := client.Do(req)
|
var bearerToken string
|
||||||
if err != nil {
|
bearerToken, err = getBearerToken(ctx, wh.Request)
|
||||||
memLogger.Error(err, "Error when sending webhook")
|
if err != nil {
|
||||||
} else {
|
memLogger.Error(err, "Error when render bearer token")
|
||||||
memLogger.Info("received from webhook", "code", resp.StatusCode)
|
return
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
var req *http.Request
|
||||||
}(webhook)
|
req, err = http.NewRequestWithContext(ctx, method, api, payload)
|
||||||
return
|
if err != nil {
|
||||||
|
memLogger.Error(err, "Error when create request")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if bearerToken != "" {
|
||||||
|
memLogger.V(7).Info("set bearer token", "token", bearerToken)
|
||||||
|
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", bearerToken))
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range wh.Request.Header {
|
||||||
|
req.Header.Set(k, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("error when sending webhook")
|
||||||
|
} else {
|
||||||
|
data, _ := io.ReadAll(resp.Body)
|
||||||
|
memLogger.V(7).Info("received from webhook", "code", resp.StatusCode, "response", string(data))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type bearerToken struct {
|
||||||
|
Token string `json:"token"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBearerToken(ctx context.Context, request RequestWithAuth) (token string, err error) {
|
||||||
|
if request.BearerAPI == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var data []byte
|
||||||
|
if data, err = json.Marshal(&request); err == nil {
|
||||||
|
client := http.DefaultClient
|
||||||
|
var req *http.Request
|
||||||
|
if req, err = http.NewRequestWithContext(ctx, http.MethodPost, request.BearerAPI, bytes.NewBuffer(data)); err == nil {
|
||||||
|
req.Header.Set(util.ContentType, util.JSON)
|
||||||
|
|
||||||
|
var resp *http.Response
|
||||||
|
if resp, err = client.Do(req); err == nil && resp.StatusCode == http.StatusOK {
|
||||||
|
if data, err = io.ReadAll(resp.Body); err == nil {
|
||||||
|
var tokenObj bearerToken
|
||||||
|
if err = json.Unmarshal(data, &tokenObj); err == nil {
|
||||||
|
token = tokenObj.Token
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *inMemoryServer) handleOpenAPI() {
|
func (s *inMemoryServer) handleOpenAPI() {
|
||||||
s.mux.HandleFunc("/api.json", func(w http.ResponseWriter, req *http.Request) {
|
s.mux.HandleFunc("/api.json", func(w http.ResponseWriter, req *http.Request) {
|
||||||
// Setup OpenAPI schema
|
// Setup OpenAPI schema
|
||||||
reflector := openapi3.NewReflector()
|
reflector := openapi3.NewReflector()
|
||||||
reflector.SpecSchema().SetTitle("Mock Server API")
|
reflector.SpecSchema().SetTitle("Mock Server API")
|
||||||
reflector.SpecSchema().SetVersion(version.GetVersion())
|
reflector.SpecSchema().SetVersion(version.GetVersion())
|
||||||
reflector.SpecSchema().SetDescription("Powered by https://github.com/linuxsuren/api-testing")
|
reflector.SpecSchema().SetDescription("Powered by https://github.com/linuxsuren/api-testing")
|
||||||
|
|
||||||
// Walk the router with OpenAPI collector
|
// Walk the router with OpenAPI collector
|
||||||
c := gorillamux.NewOpenAPICollector(reflector)
|
c := gorillamux.NewOpenAPICollector(reflector)
|
||||||
|
|
||||||
_ = s.mux.Walk(c.Walker)
|
_ = s.mux.Walk(c.Walker)
|
||||||
|
|
||||||
// Get the resulting schema
|
// Get the resulting schema
|
||||||
if jsonData, err := reflector.Spec.MarshalJSON(); err == nil {
|
if jsonData, err := reflector.Spec.MarshalJSON(); err == nil {
|
||||||
w.Header().Set(util.ContentType, util.JSON)
|
w.Header().Set(util.ContentType, util.JSON)
|
||||||
_, _ = w.Write(jsonData)
|
_, _ = w.Write(jsonData)
|
||||||
} else {
|
} else {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
_, _ = w.Write([]byte(err.Error()))
|
_, _ = w.Write([]byte(err.Error()))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func jsonStrToInterface(jsonStr string) (objData map[string]interface{}, err error) {
|
func jsonStrToInterface(jsonStr string) (objData map[string]interface{}, err error) {
|
||||||
if jsonStr, err = render.Render("init object", jsonStr, nil); err == nil {
|
if jsonStr, err = render.Render("init object", jsonStr, nil); err == nil {
|
||||||
objData = map[string]interface{}{}
|
objData = map[string]interface{}{}
|
||||||
err = json.Unmarshal([]byte(jsonStr), &objData)
|
err = json.Unmarshal([]byte(jsonStr), &objData)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *inMemoryServer) GetPort() string {
|
func (s *inMemoryServer) GetPort() string {
|
||||||
return util.GetPort(s.listener)
|
return util.GetPort(s.listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *inMemoryServer) Stop() (err error) {
|
func (s *inMemoryServer) Stop() (err error) {
|
||||||
if s.listener != nil {
|
if s.listener != nil {
|
||||||
err = s.listener.Close()
|
err = s.listener.Close()
|
||||||
} else {
|
} else {
|
||||||
memLogger.Info("listener is nil")
|
memLogger.Info("listener is nil")
|
||||||
}
|
}
|
||||||
if s.cancelFunc != nil {
|
if s.cancelFunc != nil {
|
||||||
s.cancelFunc()
|
s.cancelFunc()
|
||||||
}
|
}
|
||||||
s.wg.Wait()
|
s.wg.Wait()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,7 +55,7 @@ proxies:
|
||||||
- path: /v1/invalid-template
|
- path: /v1/invalid-template
|
||||||
target: http://localhost:{{.GetPort}
|
target: http://localhost:{{.GetPort}
|
||||||
webhooks:
|
webhooks:
|
||||||
- timer: 1ms
|
- timer: 1m
|
||||||
name: baidu
|
name: baidu
|
||||||
request:
|
request:
|
||||||
method: GET
|
method: GET
|
||||||
|
|
|
@ -16,47 +16,54 @@ limitations under the License.
|
||||||
package mock
|
package mock
|
||||||
|
|
||||||
type Object struct {
|
type Object struct {
|
||||||
Name string `yaml:"name" json:"name"`
|
Name string `yaml:"name" json:"name"`
|
||||||
InitCount *int `yaml:"initCount" json:"initCount"`
|
InitCount *int `yaml:"initCount" json:"initCount"`
|
||||||
Sample string `yaml:"sample" json:"sample"`
|
Sample string `yaml:"sample" json:"sample"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Item struct {
|
type Item struct {
|
||||||
Name string `yaml:"name" json:"name"`
|
Name string `yaml:"name" json:"name"`
|
||||||
Request Request `yaml:"request" json:"request"`
|
Request Request `yaml:"request" json:"request"`
|
||||||
Response Response `yaml:"response" json:"response"`
|
Response Response `yaml:"response" json:"response"`
|
||||||
Param map[string]string
|
Param map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Request struct {
|
type Request struct {
|
||||||
Path string `yaml:"path" json:"path"`
|
Path string `yaml:"path" json:"path"`
|
||||||
Method string `yaml:"method" json:"method"`
|
Method string `yaml:"method" json:"method"`
|
||||||
Header map[string]string `yaml:"header" json:"header"`
|
Header map[string]string `yaml:"header" json:"header"`
|
||||||
Body string `yaml:"body" json:"body"`
|
Body string `yaml:"body" json:"body"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RequestWithAuth struct {
|
||||||
|
Request `yaml:",inline"`
|
||||||
|
BearerAPI string `yaml:"bearerAPI" json:"bearerAPI"`
|
||||||
|
Username string `yaml:"username" json:"username"`
|
||||||
|
Password string `yaml:"password" json:"password"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Response struct {
|
type Response struct {
|
||||||
Encoder string `yaml:"encoder" json:"encoder"`
|
Encoder string `yaml:"encoder" json:"encoder"`
|
||||||
Body string `yaml:"body" json:"body"`
|
Body string `yaml:"body" json:"body"`
|
||||||
Header map[string]string `yaml:"header" json:"header"`
|
Header map[string]string `yaml:"header" json:"header"`
|
||||||
StatusCode int `yaml:"statusCode" json:"statusCode"`
|
StatusCode int `yaml:"statusCode" json:"statusCode"`
|
||||||
BodyData []byte
|
BodyData []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
type Webhook struct {
|
type Webhook struct {
|
||||||
Name string `yaml:"name" json:"name"`
|
Name string `yaml:"name" json:"name"`
|
||||||
Timer string `yaml:"timer" json:"timer"`
|
Timer string `yaml:"timer" json:"timer"`
|
||||||
Request Request `yaml:"request" json:"request"`
|
Request RequestWithAuth `yaml:"request" json:"request"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Proxy struct {
|
type Proxy struct {
|
||||||
Path string `yaml:"path" json:"path"`
|
Path string `yaml:"path" json:"path"`
|
||||||
Target string `yaml:"target" json:"target"`
|
Target string `yaml:"target" json:"target"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
Objects []Object `yaml:"objects" json:"objects"`
|
Objects []Object `yaml:"objects" json:"objects"`
|
||||||
Items []Item `yaml:"items" json:"items"`
|
Items []Item `yaml:"items" json:"items"`
|
||||||
Proxies []Proxy `yaml:"proxies" json:"proxies"`
|
Proxies []Proxy `yaml:"proxies" json:"proxies"`
|
||||||
Webhooks []Webhook `yaml:"webhooks" json:"webhooks"`
|
Webhooks []Webhook `yaml:"webhooks" json:"webhooks"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2023-2024 API Testing Authors.
|
Copyright 2023-2025 API Testing Authors.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -30,6 +30,7 @@ import (
|
||||||
mathrand "math/rand"
|
mathrand "math/rand"
|
||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/linuxsuren/api-testing/pkg/version"
|
"github.com/linuxsuren/api-testing/pkg/version"
|
||||||
|
|
||||||
|
@ -179,18 +180,60 @@ var advancedFuncs = []AdvancedFunc{{
|
||||||
h.Write(data)
|
h.Write(data)
|
||||||
return hex.EncodeToString(h.Sum(nil))
|
return hex.EncodeToString(h.Sum(nil))
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
|
FuncName: "randFloat",
|
||||||
|
Func: func(from float64, to float64) float64 {
|
||||||
|
return mathrand.Float64()*(to-from) + from
|
||||||
|
},
|
||||||
}, {
|
}, {
|
||||||
FuncName: "randEnum",
|
FuncName: "randEnum",
|
||||||
Func: func(items ...string) string {
|
Func: func(items ...string) string {
|
||||||
return items[mathrand.Intn(len(items))]
|
return items[mathrand.Intn(len(items))]
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
|
FuncName: "weightObject",
|
||||||
|
Func: func(weight int, object interface{}) WeightEnum {
|
||||||
|
return WeightEnum{
|
||||||
|
Weight: weight,
|
||||||
|
Object: object,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
FuncName: "randWeightEnum",
|
||||||
|
Func: func(items ...WeightEnum) interface{} {
|
||||||
|
var newItems []interface{}
|
||||||
|
for _, item := range items {
|
||||||
|
for j := 0; j < item.Weight; j++ {
|
||||||
|
newItems = append(newItems, item.Object)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newItems[mathrand.Intn(len(newItems))]
|
||||||
|
},
|
||||||
}, {
|
}, {
|
||||||
FuncName: "randEmail",
|
FuncName: "randEmail",
|
||||||
Func: func() string {
|
Func: func() string {
|
||||||
return fmt.Sprintf("%s@%s.com", util.String(3), util.String(3))
|
return fmt.Sprintf("%s@%s.com", util.String(3), util.String(3))
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
|
FuncName: "uptime",
|
||||||
|
Func: func() string {
|
||||||
|
return time.Since(uptime).String()
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
FuncName: "uptimeSeconds",
|
||||||
|
Func: func() float64 {
|
||||||
|
return time.Since(uptime).Seconds()
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
// WeightEnum is a weight enum
|
||||||
|
type WeightEnum struct {
|
||||||
|
Weight int
|
||||||
|
Object interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var uptime = time.Now()
|
||||||
|
|
||||||
// GetAdvancedFuncs returns all the advanced functions
|
// GetAdvancedFuncs returns all the advanced functions
|
||||||
func GetAdvancedFuncs() []AdvancedFunc {
|
func GetAdvancedFuncs() []AdvancedFunc {
|
||||||
return advancedFuncs
|
return advancedFuncs
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2023-2024 API Testing Authors.
|
Copyright 2023-2025 API Testing Authors.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -81,12 +81,24 @@ func TestRender(t *testing.T) {
|
||||||
verify: func(t *testing.T, s string) {
|
verify: func(t *testing.T, s string) {
|
||||||
assert.Equal(t, 20, len(s), s)
|
assert.Equal(t, 20, len(s), s)
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
|
name: "randFloat",
|
||||||
|
text: `{{randFloat 1 2}}`,
|
||||||
|
verify: func(t *testing.T, s string) {
|
||||||
|
assert.NotEmpty(t, s)
|
||||||
|
},
|
||||||
}, {
|
}, {
|
||||||
name: "randEnum",
|
name: "randEnum",
|
||||||
text: `{{randEnum "a" "b" "c"}}`,
|
text: `{{randEnum "a" "b" "c"}}`,
|
||||||
verify: func(t *testing.T, s string) {
|
verify: func(t *testing.T, s string) {
|
||||||
assert.Contains(t, []string{"a", "b", "c"}, s)
|
assert.Contains(t, []string{"a", "b", "c"}, s)
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
|
name: "randWeightEnum",
|
||||||
|
text: `{{randWeightEnum (weightObject 1 "a") (weightObject 2 "b") (weightObject 3 "c")}}`,
|
||||||
|
verify: func(t *testing.T, s string) {
|
||||||
|
assert.Contains(t, []string{"a", "b", "c"}, s)
|
||||||
|
},
|
||||||
}, {
|
}, {
|
||||||
name: "randEmail",
|
name: "randEmail",
|
||||||
text: `{{randEmail}}`,
|
text: `{{randEmail}}`,
|
||||||
|
@ -103,6 +115,18 @@ func TestRender(t *testing.T) {
|
||||||
verify: func(t *testing.T, s string) {
|
verify: func(t *testing.T, s string) {
|
||||||
assert.Equal(t, "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824", s)
|
assert.Equal(t, "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824", s)
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
|
name: "uptime",
|
||||||
|
text: `{{uptime}}`,
|
||||||
|
verify: func(t *testing.T, s string) {
|
||||||
|
assert.NotEmpty(t, s)
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
name: "uptimeSeconds",
|
||||||
|
text: `{{uptimeSeconds}}`,
|
||||||
|
verify: func(t *testing.T, s string) {
|
||||||
|
assert.NotEmpty(t, s)
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
|
Loading…
Reference in New Issue