feat: add conditional verify support (#209)
This commit is contained in:
parent
51bfd94849
commit
80ceae3988
|
@ -133,12 +133,36 @@
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"conditionalVerify": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/ConditionalVerify"
|
||||||
|
}
|
||||||
|
},
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"title": "Expect"
|
"title": "Expect"
|
||||||
},
|
},
|
||||||
|
"ConditionalVerify": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"condition": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"verify":{
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Request": {
|
"Request": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -5,7 +5,7 @@ go 1.18
|
||||||
require (
|
require (
|
||||||
github.com/Masterminds/sprig/v3 v3.2.3
|
github.com/Masterminds/sprig/v3 v3.2.3
|
||||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883
|
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883
|
||||||
github.com/antonmedv/expr v1.14.0
|
github.com/antonmedv/expr v1.15.0
|
||||||
github.com/bufbuild/protocompile v0.6.0
|
github.com/bufbuild/protocompile v0.6.0
|
||||||
github.com/cucumber/godog v0.12.6
|
github.com/cucumber/godog v0.12.6
|
||||||
github.com/flopp/go-findfont v0.1.0
|
github.com/flopp/go-findfont v0.1.0
|
||||||
|
|
6
go.sum
6
go.sum
|
@ -6,8 +6,8 @@ github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj
|
||||||
github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
|
github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
|
||||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
|
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
|
||||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
||||||
github.com/antonmedv/expr v1.14.0 h1:C4BHw+0cVyKy/ndU3uqYo6TV5rCtq/SY2Wdlwanvo/Q=
|
github.com/antonmedv/expr v1.15.0 h1:sBHNMx1i+b1lZfkBFGhicvSLW6RLnca3R0B7jWrk8iM=
|
||||||
github.com/antonmedv/expr v1.14.0/go.mod h1:FPC8iWArxls7axbVLsW+kpg1mz29A1b2M6jt+hZfDkU=
|
github.com/antonmedv/expr v1.15.0/go.mod h1:0E/6TxnOlRNp81GMzX9QfDPAmHo2Phg00y4JUv1ihsE=
|
||||||
github.com/bufbuild/protocompile v0.6.0 h1:Uu7WiSQ6Yj9DbkdnOe7U4mNKp58y9WDMKDn28/ZlunY=
|
github.com/bufbuild/protocompile v0.6.0 h1:Uu7WiSQ6Yj9DbkdnOe7U4mNKp58y9WDMKDn28/ZlunY=
|
||||||
github.com/bufbuild/protocompile v0.6.0/go.mod h1:YNP35qEYoYGme7QMtz5SBCoN4kL4g12jTtjuzRNdjpE=
|
github.com/bufbuild/protocompile v0.6.0/go.mod h1:YNP35qEYoYGme7QMtz5SBCoN4kL4g12jTtjuzRNdjpE=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
|
@ -115,7 +115,6 @@ github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUq
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.3.1-0.20190311161405-34c6fa2dc709/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.1-0.20190311161405-34c6fa2dc709/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
@ -123,7 +122,6 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
|
||||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
|
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
|
||||||
|
|
|
@ -31,24 +31,49 @@ import (
|
||||||
|
|
||||||
// Verify if the data satisfies the expression.
|
// Verify if the data satisfies the expression.
|
||||||
func Verify(expect testing.Response, data map[string]any) (err error) {
|
func Verify(expect testing.Response, data map[string]any) (err error) {
|
||||||
for _, verify := range expect.Verify {
|
for _, verifyExpr := range expect.Verify {
|
||||||
var program *vm.Program
|
var ok bool
|
||||||
if program, err = expr.Compile(verify, expr.Env(data),
|
if ok, err = verify(verifyExpr, data); !ok {
|
||||||
expr.AsBool(), kubernetes.PodValidatorFunc(),
|
err = fmt.Errorf("failed to verify: %q, %v", verifyExpr, err)
|
||||||
kubernetes.KubernetesValidatorFunc()); err != nil {
|
return
|
||||||
return err
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, verifyCon := range expect.ConditionalVerify {
|
||||||
|
pass := true
|
||||||
|
for _, con := range verifyCon.Condition {
|
||||||
|
if ok, _ := verify(con, data); !ok {
|
||||||
|
pass = false
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var result interface{}
|
if pass {
|
||||||
if result, err = expr.Run(program, data); err != nil {
|
for _, verifyExpr := range verifyCon.Verify {
|
||||||
return err
|
var ok bool
|
||||||
}
|
if ok, err = verify(verifyExpr, data); !ok {
|
||||||
|
err = fmt.Errorf("failed to verify: %q, %v", verifyExpr, err)
|
||||||
if !result.(bool) {
|
return
|
||||||
err = fmt.Errorf("failed to verify: %s", verify)
|
}
|
||||||
fmt.Println(err)
|
}
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func verify(verify string, data map[string]any) (ok bool, err error) {
|
||||||
|
var program *vm.Program
|
||||||
|
if program, err = expr.Compile(verify, expr.Env(data),
|
||||||
|
expr.AsBool(), kubernetes.PodValidatorFunc(),
|
||||||
|
kubernetes.KubernetesValidatorFunc()); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var result interface{}
|
||||||
|
if result, err = expr.Run(program, data); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = result.(bool)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2023 API Testing Authors.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package runner_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/linuxsuren/api-testing/pkg/runner"
|
||||||
|
atest "github.com/linuxsuren/api-testing/pkg/testing"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestVerify(t *testing.T) {
|
||||||
|
t.Run("conditionalVerify", func(t *testing.T) {
|
||||||
|
err := runner.Verify(atest.Response{
|
||||||
|
ConditionalVerify: []atest.ConditionalVerify{{
|
||||||
|
Condition: []string{
|
||||||
|
"1 == 1",
|
||||||
|
"2 == 2",
|
||||||
|
},
|
||||||
|
Verify: []string{"1 == 2"},
|
||||||
|
}},
|
||||||
|
}, nil)
|
||||||
|
assert.Error(t, err)
|
||||||
|
|
||||||
|
err = runner.Verify(atest.Response{
|
||||||
|
ConditionalVerify: []atest.ConditionalVerify{{
|
||||||
|
Condition: []string{"1 != 1"},
|
||||||
|
Verify: []string{"1 == 2"},
|
||||||
|
}},
|
||||||
|
}, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
})
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
/**
|
/**
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2023 Rick
|
Copyright (c) 2023 API Testing Authors.
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/**
|
/**
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2023 Rick
|
Copyright (c) 2023 API Testing Authors.
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -529,12 +529,27 @@ func convertToTestingTestCase(in *TestCase) (result testing.TestCase) {
|
||||||
result.Expect.Schema = resp.Schema
|
result.Expect.Schema = resp.Schema
|
||||||
result.Expect.StatusCode = int(resp.StatusCode)
|
result.Expect.StatusCode = int(resp.StatusCode)
|
||||||
result.Expect.Verify = resp.Verify
|
result.Expect.Verify = resp.Verify
|
||||||
|
result.Expect.ConditionalVerify = convertConditionalVerify(resp.ConditionalVerify)
|
||||||
result.Expect.BodyFieldsExpect = pairToInterMap(resp.BodyFieldsExpect)
|
result.Expect.BodyFieldsExpect = pairToInterMap(resp.BodyFieldsExpect)
|
||||||
result.Expect.Header = pairToMap(resp.Header)
|
result.Expect.Header = pairToMap(resp.Header)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func convertConditionalVerify(verify []*ConditionalVerify) (result []testing.ConditionalVerify) {
|
||||||
|
if verify != nil {
|
||||||
|
result = make([]testing.ConditionalVerify, 0)
|
||||||
|
|
||||||
|
for _, item := range verify {
|
||||||
|
result = append(result, testing.ConditionalVerify{
|
||||||
|
Condition: item.Condition,
|
||||||
|
Verify: item.Verify,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (s *server) CreateTestCase(ctx context.Context, in *TestCaseWithSuite) (reply *HelloReply, err error) {
|
func (s *server) CreateTestCase(ctx context.Context, in *TestCaseWithSuite) (reply *HelloReply, err error) {
|
||||||
reply = &HelloReply{}
|
reply = &HelloReply{}
|
||||||
loader := s.getLoader(ctx)
|
loader := s.getLoader(ctx)
|
||||||
|
|
|
@ -38,6 +38,7 @@ import (
|
||||||
_ "embed"
|
_ "embed"
|
||||||
|
|
||||||
"github.com/h2non/gock"
|
"github.com/h2non/gock"
|
||||||
|
atest "github.com/linuxsuren/api-testing/pkg/testing"
|
||||||
atesting "github.com/linuxsuren/api-testing/pkg/testing"
|
atesting "github.com/linuxsuren/api-testing/pkg/testing"
|
||||||
"github.com/linuxsuren/api-testing/sample"
|
"github.com/linuxsuren/api-testing/sample"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
@ -428,6 +429,16 @@ func TestListTestCase(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotNil(t, reply)
|
assert.NotNil(t, reply)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("convertConditionalVerify", func(t *testing.T) {
|
||||||
|
assert.Equal(t, []atest.ConditionalVerify{{
|
||||||
|
Condition: []string{"1 == 1"},
|
||||||
|
Verify: []string{"1 == 1"},
|
||||||
|
}}, convertConditionalVerify([]*ConditionalVerify{{
|
||||||
|
Condition: []string{"1 == 1"},
|
||||||
|
Verify: []string{"1 == 1"},
|
||||||
|
}}))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRemoteServerSuite(t *testing.T) {
|
func TestRemoteServerSuite(t *testing.T) {
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -146,7 +146,13 @@ message Response {
|
||||||
repeated Pair header = 3;
|
repeated Pair header = 3;
|
||||||
repeated Pair bodyFieldsExpect = 4;
|
repeated Pair bodyFieldsExpect = 4;
|
||||||
repeated string verify = 5;
|
repeated string verify = 5;
|
||||||
string schema = 6;
|
repeated ConditionalVerify ConditionalVerify = 6;
|
||||||
|
string schema = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ConditionalVerify {
|
||||||
|
repeated string condition = 1;
|
||||||
|
repeated string verify = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message TestCaseResult {
|
message TestCaseResult {
|
||||||
|
|
|
@ -64,10 +64,16 @@ type Request struct {
|
||||||
|
|
||||||
// Response is the expected response
|
// Response is the expected response
|
||||||
type Response struct {
|
type Response struct {
|
||||||
StatusCode int `yaml:"statusCode,omitempty" json:"statusCode,omitempty"`
|
StatusCode int `yaml:"statusCode,omitempty" json:"statusCode,omitempty"`
|
||||||
Body string `yaml:"body,omitempty" json:"body,omitempty"`
|
Body string `yaml:"body,omitempty" json:"body,omitempty"`
|
||||||
Header map[string]string `yaml:"header,omitempty" json:"header,omitempty"`
|
Header map[string]string `yaml:"header,omitempty" json:"header,omitempty"`
|
||||||
BodyFieldsExpect map[string]interface{} `yaml:"bodyFieldsExpect,omitempty" json:"bodyFieldsExpect,omitempty"`
|
BodyFieldsExpect map[string]interface{} `yaml:"bodyFieldsExpect,omitempty" json:"bodyFieldsExpect,omitempty"`
|
||||||
Verify []string `yaml:"verify,omitempty" json:"verify,omitempty"`
|
Verify []string `yaml:"verify,omitempty" json:"verify,omitempty"`
|
||||||
Schema string `yaml:"schema,omitempty" json:"schema,omitempty"`
|
ConditionalVerify []ConditionalVerify `yaml:"conditionalVerify,omitempty" json:"conditionalVerify,omitempty"`
|
||||||
|
Schema string `yaml:"schema,omitempty" json:"schema,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConditionalVerify struct {
|
||||||
|
Condition []string `yaml:"condition,omitempty" json:"condition,omitempty"`
|
||||||
|
Verify []string `yaml:"verify,omitempty" json:"verify,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue