// Copyright 2020 beego // // 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 mock import ( "context" "encoding/json" "net/textproto" "regexp" "github.com/beego/beego/v2/client/httplib" ) type RequestCondition interface { Match(ctx context.Context, req *httplib.BeegoHTTPRequest) bool } // reqCondition create condition // - path: same path // - pathReg: request path match pathReg // - method: same method // - Query parameters (key, value) // - header (key, value) // - Body json format, contains specific (key, value). type SimpleCondition struct { pathReg string path string method string query map[string]string header map[string]string body map[string]interface{} } func NewSimpleCondition(path string, opts ...simpleConditionOption) *SimpleCondition { sc := &SimpleCondition{ path: path, query: make(map[string]string), header: make(map[string]string), body: map[string]interface{}{}, } for _, o := range opts { o(sc) } return sc } func (sc *SimpleCondition) Match(ctx context.Context, req *httplib.BeegoHTTPRequest) bool { var res bool if len(sc.path) > 0 { res = sc.matchPath(ctx, req) } else if len(sc.pathReg) > 0 { res = sc.matchPathReg(ctx, req) } else { return false } return res && sc.matchMethod(ctx, req) && sc.matchQuery(ctx, req) && sc.matchHeader(ctx, req) && sc.matchBodyFields(ctx, req) } func (sc *SimpleCondition) matchPath(ctx context.Context, req *httplib.BeegoHTTPRequest) bool { path := req.GetRequest().URL.Path return path == sc.path } func (sc *SimpleCondition) matchPathReg(ctx context.Context, req *httplib.BeegoHTTPRequest) bool { path := req.GetRequest().URL.Path if b, err := regexp.Match(sc.pathReg, []byte(path)); err == nil { return b } return false } func (sc *SimpleCondition) matchQuery(ctx context.Context, req *httplib.BeegoHTTPRequest) bool { qs := req.GetRequest().URL.Query() for k, v := range sc.query { if uv, ok := qs[k]; !ok || uv[0] != v { return false } } return true } func (sc *SimpleCondition) matchHeader(ctx context.Context, req *httplib.BeegoHTTPRequest) bool { headers := req.GetRequest().Header for k, v := range sc.header { if uv, ok := headers[k]; !ok || uv[0] != v { return false } } return true } func (sc *SimpleCondition) matchBodyFields(ctx context.Context, req *httplib.BeegoHTTPRequest) bool { if len(sc.body) == 0 { return true } getBody := req.GetRequest().GetBody body, err := getBody() if err != nil { return false } bytes := make([]byte, req.GetRequest().ContentLength) _, err = body.Read(bytes) if err != nil { return false } m := make(map[string]interface{}) err = json.Unmarshal(bytes, &m) if err != nil { return false } for k, v := range sc.body { if uv, ok := m[k]; !ok || uv != v { return false } } return true } func (sc *SimpleCondition) matchMethod(ctx context.Context, req *httplib.BeegoHTTPRequest) bool { if len(sc.method) > 0 { return sc.method == req.GetRequest().Method } return true } type simpleConditionOption func(sc *SimpleCondition) func WithPathReg(pathReg string) simpleConditionOption { return func(sc *SimpleCondition) { sc.pathReg = pathReg } } func WithQuery(key, value string) simpleConditionOption { return func(sc *SimpleCondition) { sc.query[key] = value } } func WithHeader(key, value string) simpleConditionOption { return func(sc *SimpleCondition) { sc.header[textproto.CanonicalMIMEHeaderKey(key)] = value } } func WithJsonBodyFields(field string, value interface{}) simpleConditionOption { return func(sc *SimpleCondition) { sc.body[field] = value } } func WithMethod(method string) simpleConditionOption { return func(sc *SimpleCondition) { sc.method = method } }