diff --git a/go.mod b/go.mod index cf78e7f..c0eb6c2 100644 --- a/go.mod +++ b/go.mod @@ -1,13 +1,14 @@ module github.com/linuxsuren/atest-ext-store-orm -go 1.22.2 +go 1.22.4 -toolchain go1.22.4 +toolchain go1.22.6 require ( github.com/linuxsuren/api-testing v0.0.18-0.20240712143814-6ce9363d5a07 github.com/spf13/cobra v1.8.0 github.com/stretchr/testify v1.9.0 + google.golang.org/protobuf v1.33.0 gorm.io/driver/mysql v1.5.2 gorm.io/driver/postgres v1.5.4 gorm.io/driver/sqlite v1.5.6 @@ -53,6 +54,7 @@ require ( github.com/jinzhu/now v1.1.5 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/linuxsuren/go-fake-runtime v0.0.4 // indirect + github.com/linuxsuren/oauth-hub v0.0.0-20240809060240-e78c21b5d8d4 // indirect github.com/linuxsuren/unstructured v0.0.1 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-sqlite3 v1.14.22 // indirect @@ -85,15 +87,17 @@ require ( go.uber.org/zap v1.27.0 // indirect golang.org/x/crypto v0.21.0 // indirect golang.org/x/net v0.23.0 // indirect - golang.org/x/oauth2 v0.18.0 // indirect + golang.org/x/oauth2 v0.22.0 // indirect golang.org/x/sync v0.6.0 // indirect golang.org/x/sys v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect - google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect - google.golang.org/protobuf v1.33.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) + +replace github.com/linuxsuren/api-testing => github.com/SamYSF/api-testing v0.0.0-20240913070731-63814573b192 + +//replace github.com/linuxsuren/api-testing => /home/ysf/project/api-testing diff --git a/go.sum b/go.sum index 4347643..c9b51d5 100644 --- a/go.sum +++ b/go.sum @@ -5,6 +5,8 @@ github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0 github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= +github.com/SamYSF/api-testing v0.0.0-20240913070731-63814573b192 h1:hHFszkA9lWWQ0UmyE8/C8uXRP4hAyQHAXilVSg87e/w= +github.com/SamYSF/api-testing v0.0.0-20240913070731-63814573b192/go.mod h1:uu0hyEYSgdSju31EugGwIGE2I/uv7VYNM/6ODPKKD7I= 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/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -47,11 +49,8 @@ github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+Gr github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -96,10 +95,10 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/linuxsuren/api-testing v0.0.18-0.20240712143814-6ce9363d5a07 h1:NmjqkiR+4KXjWs9H6wGmgQo7r7FJE+RED+E3EwAGsp4= -github.com/linuxsuren/api-testing v0.0.18-0.20240712143814-6ce9363d5a07/go.mod h1:8G3GZtQmSUHyqA/gZlSdFaDIveHQYbFInTibD7g3hMs= github.com/linuxsuren/go-fake-runtime v0.0.4 h1:y+tvBuw6MKTCav8Bo5HWwaXhBx1Z//VAvqI3gpOWqvw= github.com/linuxsuren/go-fake-runtime v0.0.4/go.mod h1:zmh6J78hSnWZo68faMA2eKOdaEp8eFbERHi3ZB9xHCQ= +github.com/linuxsuren/oauth-hub v0.0.0-20240809060240-e78c21b5d8d4 h1:muVmKxx+JneaVgUKHqLc+As5vpgKXZAfVu6h+iyb5LQ= +github.com/linuxsuren/oauth-hub v0.0.0-20240809060240-e78c21b5d8d4/go.mod h1:6K1L5ajpFTNO8iJSsNrxMWAigAqczI0UPfEV9NSE0nc= github.com/linuxsuren/unstructured v0.0.1 h1:ilUA8MUYbR6l9ebo/YPV2bKqlf62bzQursDSE+j00iU= github.com/linuxsuren/unstructured v0.0.1/go.mod h1:KH6aTj+FegzGBzc1vS6mzZx3/duhTUTEVyW5sO7p4as= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= @@ -205,8 +204,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI= -golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= +golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= +golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= @@ -225,7 +224,6 @@ golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= @@ -233,9 +231,6 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= -google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ= google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro= google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 h1:Lj5rbfG876hIAYFjqiJnPHfhXbv+nzTWfm04Fg/XSVU= @@ -244,8 +239,6 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1: google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk= google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/pkg/convert.go b/pkg/convert.go index 62a58cb..f291d2f 100644 --- a/pkg/convert.go +++ b/pkg/convert.go @@ -17,9 +17,12 @@ package pkg import ( "encoding/json" + "fmt" + "time" "github.com/linuxsuren/api-testing/pkg/server" "github.com/linuxsuren/api-testing/pkg/testing/remote" + "google.golang.org/protobuf/types/known/timestamppb" ) func ConverToDBTestCase(testcase *server.TestCase) (result *TestCase) { @@ -76,6 +79,33 @@ func ConvertToRemoteTestCase(testcase *TestCase) (result *server.TestCase) { return } +func ConvertHistoryToRemoteTestCase(historyTestcase *HistoryTestResult) (result *server.TestCase) { + result = &server.TestCase{ + Name: historyTestcase.CaseName, + SuiteName: historyTestcase.SuiteName, + + Request: &server.Request{ + Api: historyTestcase.CaseAPI, + Method: historyTestcase.Method, + Body: historyTestcase.Body, + Header: jsonToPair(historyTestcase.Header), + Cookie: jsonToPair(historyTestcase.Cookie), + Query: jsonToPair(historyTestcase.Query), + Form: jsonToPair(historyTestcase.Form), + }, + + Response: &server.Response{ + StatusCode: int32(historyTestcase.ExpectStatusCode), + Body: historyTestcase.ExpectBody, + Schema: historyTestcase.ExpectSchema, + Verify: jsonToSlice(historyTestcase.ExpectVerify), + BodyFieldsExpect: jsonToPair(historyTestcase.ExpectBodyFields), + Header: jsonToPair(historyTestcase.ExpectHeader), + }, + } + return +} + func ConvertToDBTestSuite(suite *remote.TestSuite) (result *TestSuite) { result = &TestSuite{ Name: suite.Name, @@ -91,6 +121,76 @@ func ConvertToDBTestSuite(suite *remote.TestSuite) (result *TestSuite) { return } +func ConvertToDBHistoryTestResult(historyTestResult *server.HistoryTestResult) (result *HistoryTestResult) { + result = &HistoryTestResult{ + Message: historyTestResult.Message, + Error: historyTestResult.Error, + } + if historyTestResult.CreateTime != nil { + id := fmt.Sprintf("%s_%s_%s", historyTestResult.CreateTime.AsTime().Local().Format("2006-01-02T15:04:05.999999999"), historyTestResult.Data.SuiteName, historyTestResult.Data.CaseName) + result.ID = id + result.CreateTime = historyTestResult.CreateTime.AsTime().Local().Format("2006-01-02T15:04:05.999999999") + result.HistorySuiteName = historyTestResult.CreateTime.AsTime().Local().Format("2006-1-2") + } + if historyTestResult.Data != nil { + result.Param = pairToJSON(historyTestResult.Data.SuiteParam) + result.CaseName = historyTestResult.Data.CaseName + result.SuiteName = historyTestResult.Data.SuiteName + result.SuiteAPI = historyTestResult.Data.SuiteApi + result.HistoryHeader = pairToJSON(historyTestResult.Data.HistoryHeader) + if historyTestResult.Data.Request != nil { + request := historyTestResult.Data.Request + result.CaseAPI = request.Api + result.Method = request.Method + result.Header = pairToJSON(request.Header) + result.Cookie = pairToJSON(request.Cookie) + result.Form = pairToJSON(request.Form) + result.Query = pairToJSON(request.Query) + } + if historyTestResult.Data.Response != nil { + resp := historyTestResult.Data.Response + result.ExpectBody = resp.Body + result.ExpectSchema = resp.Schema + result.ExpectStatusCode = int(resp.StatusCode) + result.ExpectHeader = pairToJSON(resp.Header) + result.ExpectBodyFields = pairToJSON(resp.BodyFieldsExpect) + result.ExpectVerify = SliceToJSON(resp.Verify) + } + if historyTestResult.Data.SuiteSpec != nil { + result.SpecKind = historyTestResult.Data.SuiteSpec.Kind + result.SpecURL = historyTestResult.Data.SuiteSpec.Url + } + } + for _, testCase := range historyTestResult.TestCaseResult { + result.StatusCode = int32(testCase.StatusCode) + result.Output = testCase.Output + result.Body = testCase.Body + } + return +} + +func ConvertToRemoteHistoryTestResult(historyTestResult *HistoryTestResult) (result *server.HistoryTestResult) { + createTime, err := time.Parse("2006-01-02T15:04:05.999999999", historyTestResult.CreateTime) + if err != nil { + fmt.Println("Error parsing time:", err) + } + result = &server.HistoryTestResult{ + Message: historyTestResult.Message, + Error: historyTestResult.Error, + CreateTime: timestamppb.New(createTime), + } + TestCaseResult := &server.TestCaseResult{ + StatusCode: historyTestResult.StatusCode, + Body: historyTestResult.Body, + Output: historyTestResult.Output, + Error: historyTestResult.Error, + Header: jsonToPair(historyTestResult.Header), + } + result.TestCaseResult = append(result.TestCaseResult, TestCaseResult) + result.Data = ConvertToGRPCHistoryTestCase(historyTestResult) + return +} + func ConvertToGRPCTestSuite(suite *TestSuite) (result *remote.TestSuite) { result = &remote.TestSuite{ Name: suite.Name, @@ -104,6 +204,58 @@ func ConvertToGRPCTestSuite(suite *TestSuite) (result *remote.TestSuite) { return } +func ConvertToGRPCHistoryTestSuite(historyTestResult *HistoryTestResult) (result *remote.HistoryTestSuite) { + result = &remote.HistoryTestSuite{ + HistorySuiteName: historyTestResult.HistorySuiteName, + } + + item := ConvertToGRPCHistoryTestCase(historyTestResult) + result.Items = append(result.Items, item) + return +} + +func ConvertToGRPCHistoryTestCase(historyTestResult *HistoryTestResult) (result *server.HistoryTestCase) { + createTime, err := time.Parse("2006-01-02T15:04:05.999999999", historyTestResult.CreateTime) + if err != nil { + fmt.Println("Error parsing time:", err) + } + result = &server.HistoryTestCase{ + ID: historyTestResult.ID, + SuiteName: historyTestResult.SuiteName, + CaseName: historyTestResult.CaseName, + SuiteApi: historyTestResult.SuiteAPI, + SuiteParam: jsonToPair(historyTestResult.Param), + HistorySuiteName: historyTestResult.HistorySuiteName, + CreateTime: timestamppb.New(createTime), + HistoryHeader: jsonToPair(historyTestResult.HistoryHeader), + + SuiteSpec: &server.APISpec{ + Kind: historyTestResult.SpecKind, + Url: historyTestResult.SpecURL, + }, + + Request: &server.Request{ + Api: historyTestResult.CaseAPI, + Method: historyTestResult.Method, + Body: historyTestResult.Body, + Header: jsonToPair(historyTestResult.Header), + Cookie: jsonToPair(historyTestResult.Cookie), + Query: jsonToPair(historyTestResult.Query), + Form: jsonToPair(historyTestResult.Form), + }, + + Response: &server.Response{ + StatusCode: int32(historyTestResult.ExpectStatusCode), + Body: historyTestResult.ExpectBody, + Schema: historyTestResult.ExpectSchema, + Verify: jsonToSlice(historyTestResult.ExpectVerify), + BodyFieldsExpect: jsonToPair(historyTestResult.ExpectBodyFields), + Header: jsonToPair(historyTestResult.ExpectHeader), + }, + } + return +} + func SliceToJSON(slice []string) (result string) { var data []byte var err error diff --git a/pkg/convert_test.go b/pkg/convert_test.go index 71764eb..be912a0 100644 --- a/pkg/convert_test.go +++ b/pkg/convert_test.go @@ -16,12 +16,13 @@ limitations under the License. package pkg_test import ( - "testing" - "github.com/linuxsuren/api-testing/pkg/server" "github.com/linuxsuren/api-testing/pkg/testing/remote" "github.com/linuxsuren/atest-ext-store-orm/pkg" "github.com/stretchr/testify/assert" + "google.golang.org/protobuf/types/known/timestamppb" + "testing" + "time" ) func TestConvertToRemoteTestCase(t *testing.T) { @@ -148,6 +149,113 @@ func TestConvertTestSuite(t *testing.T) { }) } +func TestConvertToDBHistoryTestResult(t *testing.T) { + t.Run("without testcaseResult and historyTestcase", func(t *testing.T) { + result := pkg.ConvertToDBHistoryTestResult(&server.HistoryTestResult{}) + assert.Equal(t, &pkg.HistoryTestResult{}, result) + }) + + t.Run("have testcaseResult", func(t *testing.T) { + result := pkg.ConvertToDBHistoryTestResult(&server.HistoryTestResult{ + TestCaseResult: []*server.TestCaseResult{ + { + StatusCode: 200, + Body: "Test body", + Output: "Test output", + }, + }, + }) + assert.Equal(t, &pkg.HistoryTestResult{ + StatusCode: 200, + Body: "Test body", + Output: "Test output", + }, result) + }) +} + +var now = time.Now().UTC() +var nowString = now.Format("2006-01-02T15:04:05.999999999") + +func TestConvertToRemoteHistoryTestResult(t *testing.T) { + assert.Equal(t, &server.HistoryTestResult{ + CreateTime: timestamppb.New(now), + TestCaseResult: []*server.TestCaseResult{ + { + Body: "body", + Output: "output", + Header: samplePairs, + }, + }, + Data: &server.HistoryTestCase{ + CreateTime: timestamppb.New(now), + SuiteSpec: &server.APISpec{ + Kind: "kind", + }, + Request: &server.Request{ + Api: "", + Method: "", + Header: []*server.Pair{ + {Key: "key", Value: "value"}, + }, + Body: "body", + }, + Response: &server.Response{ + StatusCode: 0, + Body: "", + Header: nil, + }, + }, + }, pkg.ConvertToRemoteHistoryTestResult(&pkg.HistoryTestResult{ + CreateTime: nowString, + Body: "body", + Output: "output", + Header: sampleJSONMap, + SpecKind: "kind", + })) +} + +func TestConvertToGRPCHistoryTestSuite(t *testing.T) { + assert.Equal(t, &remote.HistoryTestSuite{ + Items: []*server.HistoryTestCase{ + { + CreateTime: timestamppb.New(now), + SuiteName: "name", + SuiteSpec: &server.APISpec{ + Kind: "kind", + }, + Request: &server.Request{ + Body: "Test Body", + }, + Response: &server.Response{}, + }, + }, + }, pkg.ConvertToGRPCHistoryTestSuite(&pkg.HistoryTestResult{ + CreateTime: nowString, + SuiteName: "name", + Body: "Test Body", + SpecKind: "kind", + })) +} + +func TestConvertToGRPCHistoryTestCase(t *testing.T) { + assert.Equal(t, &server.HistoryTestCase{ + CreateTime: timestamppb.New(now), + SuiteName: "name", + SuiteSpec: &server.APISpec{ + Kind: "kind", + }, + Request: &server.Request{ + Body: "Test Body", + }, + Response: &server.Response{}, + }, pkg.ConvertToGRPCHistoryTestCase(&pkg.HistoryTestResult{ + CreateTime: nowString, + SuiteName: "name", + Body: "Test Body", + SpecKind: "kind", + })) +} + const sampleJSONMap = `{"key":"value"}` var samplePairs []*server.Pair = []*server.Pair{{ diff --git a/pkg/server.go b/pkg/server.go index 7496159..845be2a 100644 --- a/pkg/server.go +++ b/pkg/server.go @@ -19,9 +19,6 @@ import ( "context" "errors" "fmt" - "log" - "strings" - "github.com/linuxsuren/api-testing/pkg/extension" "github.com/linuxsuren/api-testing/pkg/server" "github.com/linuxsuren/api-testing/pkg/testing/remote" @@ -32,6 +29,11 @@ import ( "gorm.io/driver/sqlite" "gorm.io/gorm" "gorm.io/gorm/logger" + "log" + "os" + "path/filepath" + "strconv" + "strings" ) type dbserver struct { @@ -49,7 +51,7 @@ func createDB(user, password, address, database, driver string) (db *gorm.DB, er var dsn string switch driver { case "mysql", "": - dsn = fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4", user, password, address, database) + dsn = fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=true", user, password, address, database) dialector = mysql.Open(dsn) case "sqlite": dsn = fmt.Sprintf("%s.db", database) @@ -78,6 +80,7 @@ func createDB(user, password, address, database, driver string) (db *gorm.DB, er db.AutoMigrate(&TestCase{}) db.AutoMigrate(&TestSuite{}) + db.AutoMigrate(&HistoryTestResult{}) return } @@ -165,6 +168,19 @@ func (s *dbserver) GetTestSuite(ctx context.Context, suite *remote.TestSuite) (r return } +func (s *dbserver) GetHistoryTestSuite(ctx context.Context, suite *remote.HistoryTestSuite) (reply *remote.HistoryTestSuite, err error) { + query := &HistoryTestResult{} + var db *gorm.DB + if db, err = s.getClient(ctx); err != nil { + return + } + + db.Find(&query, nameQuery, suite.HistorySuiteName) + + reply = ConvertToGRPCHistoryTestSuite(query) + return +} + func (s *dbserver) UpdateTestSuite(ctx context.Context, suite *remote.TestSuite) (reply *remote.TestSuite, err error) { reply = &remote.TestSuite{} input := ConvertToDBTestSuite(suite) @@ -218,6 +234,72 @@ func (s *dbserver) CreateTestCase(ctx context.Context, testcase *server.TestCase return } +func (s *dbserver) CreateTestCaseHistory(ctx context.Context, historyTestResult *server.HistoryTestResult) (reply *server.Empty, err error) { + reply = &server.Empty{} + var db *gorm.DB + if db, err = s.getClient(ctx); err != nil { + return + } + + store := remote.GetStoreFromContext(ctx) + historyLimit := 10 + if v, ok := store.Properties["historyLimit"]; ok { + if parsedHistoryLimit, parseErr := strconv.Atoi(v); parseErr == nil { + historyLimit = parsedHistoryLimit + } + } + + var count int64 + db.Model(&HistoryTestResult{}).Count(&count) + + if count >= int64(historyLimit) { + var oldestRecord HistoryTestResult + if err = db.Order("create_time").First(&oldestRecord).Error; err != nil { + fmt.Printf("Error find oldest record: %v\n", err) + return + } + + if err = db.Delete(&oldestRecord).Error; err != nil { + fmt.Printf("Error delete oldest record: %v\n", err) + return + } + fmt.Printf("Existing count: %d, limit: %d\nmaximum number of entries reached.\n", count, historyLimit) + } + + db.Create(ConvertToDBHistoryTestResult(historyTestResult)) + return +} + +func (s *dbserver) ListHistoryTestSuite(ctx context.Context, _ *server.Empty) (suites *remote.HistoryTestSuites, err error) { + items := make([]*HistoryTestResult, 0) + + var db *gorm.DB + if db, err = s.getClient(ctx); err != nil { + return + } + + db.Find(&items) + + groupedItems := make(map[string][]*HistoryTestResult) + for _, item := range items { + groupedItems[item.HistorySuiteName] = append(groupedItems[item.HistorySuiteName], item) + } + + suites = &remote.HistoryTestSuites{} + + for historySuiteName, group := range groupedItems { + suite := &remote.HistoryTestSuite{ + HistorySuiteName: historySuiteName, + } + for _, item := range group { + converted := ConvertToGRPCHistoryTestSuite(item) + suite.Items = append(suite.Items, converted.Items[0]) + } + suites.Data = append(suites.Data, suite) + } + return +} + func (s *dbserver) GetTestCase(ctx context.Context, testcase *server.TestCase) (result *server.TestCase, err error) { item := &TestCase{} var db *gorm.DB @@ -230,6 +312,45 @@ func (s *dbserver) GetTestCase(ctx context.Context, testcase *server.TestCase) ( return } +func (s *dbserver) GetHistoryTestCaseWithResult(ctx context.Context, testcase *server.HistoryTestCase) (result *server.HistoryTestResult, err error) { + item := &HistoryTestResult{} + var db *gorm.DB + if db, err = s.getClient(ctx); err != nil { + return + } + db.Find(&item, "id = ? ", testcase.ID) + + result = ConvertToRemoteHistoryTestResult(item) + return +} + +func (s *dbserver) GetHistoryTestCase(ctx context.Context, testcase *server.HistoryTestCase) (result *server.HistoryTestCase, err error) { + item := &HistoryTestResult{} + var db *gorm.DB + if db, err = s.getClient(ctx); err != nil { + return + } + db.Find(&item, "id = ? ", testcase.ID) + + result = ConvertToGRPCHistoryTestCase(item) + return +} + +func (s *dbserver) GetTestCaseAllHistory(ctx context.Context, testcase *server.TestCase) (result *server.HistoryTestCases, err error) { + items := make([]*HistoryTestResult, 0) + var db *gorm.DB + if db, err = s.getClient(ctx); err != nil { + return + } + db.Find(&items, "suite_name = ? AND case_name = ? ", testcase.SuiteName, testcase.Name) + + result = &server.HistoryTestCases{} + for i := range items { + result.Data = append(result.Data, ConvertToGRPCHistoryTestCase(items[i])) + } + return +} + func (s *dbserver) UpdateTestCase(ctx context.Context, testcase *server.TestCase) (reply *server.TestCase, err error) { reply = &server.TestCase{} input := ConverToDBTestCase(testcase) @@ -237,7 +358,7 @@ func (s *dbserver) UpdateTestCase(ctx context.Context, testcase *server.TestCase if db, err = s.getClient(ctx); err != nil { return } - testCaseIdentiy(db, input).Updates(input) + testCaseIdentity(db, input).Updates(input) data := make(map[string]interface{}) if input.ExpectBody == "" { @@ -248,7 +369,7 @@ func (s *dbserver) UpdateTestCase(ctx context.Context, testcase *server.TestCase } if len(data) > 0 { - testCaseIdentiy(db, input).Updates(data) + testCaseIdentity(db, input).Updates(data) } return } @@ -260,7 +381,64 @@ func (s *dbserver) DeleteTestCase(ctx context.Context, testcase *server.TestCase if db, err = s.getClient(ctx); err != nil { return } - testCaseIdentiy(db, input).Delete(input) + testCaseIdentity(db, input).Delete(input) + return +} + +func (s *dbserver) DeleteHistoryTestCase(ctx context.Context, historyTestCase *server.HistoryTestCase) (reply *server.Empty, err error) { + reply = &server.Empty{} + input := &HistoryTestResult{ + ID: historyTestCase.ID, + } + var db *gorm.DB + if db, err = s.getClient(ctx); err != nil { + return + } + var historyTestResult HistoryTestResult + if err = historyTestCaseIdentity(db, input).Find(&historyTestResult).Error; err != nil { + return nil, err + } + fileName := historyTestResult.Body + if strings.HasPrefix(fileName, "isFilePath-") { + tempDir := os.TempDir() + fullFilePath := filepath.Join(tempDir, fileName) + + if err = os.Remove(fullFilePath); err != nil { + log.Printf("Failed to delete file: %s, error: %v\n", fullFilePath, err) + } + } + + db.Delete(&historyTestResult) + return +} + +func (s *dbserver) DeleteAllHistoryTestCase(ctx context.Context, historyTestCase *server.HistoryTestCase) (reply *server.Empty, err error) { + reply = &server.Empty{} + input := &HistoryTestResult{ + SuiteName: historyTestCase.SuiteName, + CaseName: historyTestCase.CaseName, + } + var db *gorm.DB + if db, err = s.getClient(ctx); err != nil { + return + } + + var historyTestResults []HistoryTestResult + if err = allHistoryTestCaseIdentity(db, input).Find(&historyTestResults).Error; err != nil { + return nil, err + } + for _, historyTestResult := range historyTestResults { + fileName := historyTestResult.Body + if strings.HasPrefix(fileName, "isFilePath-") { + tempDir := os.TempDir() + fullFilePath := filepath.Join(tempDir, fileName) + + if err = os.Remove(fullFilePath); err != nil { + log.Printf("Failed to delete file: %s, error: %v\n", fullFilePath, err) + } + } + db.Delete(&historyTestResult) + } return } @@ -292,6 +470,14 @@ func (s *dbserver) PProf(ctx context.Context, in *server.PProfRequest) (data *se return } -func testCaseIdentiy(db *gorm.DB, testcase *TestCase) *gorm.DB { +func testCaseIdentity(db *gorm.DB, testcase *TestCase) *gorm.DB { return db.Model(testcase).Where(fmt.Sprintf("suite_name = '%s' AND name = '%s'", testcase.SuiteName, testcase.Name)) } + +func historyTestCaseIdentity(db *gorm.DB, historyTestResult *HistoryTestResult) *gorm.DB { + return db.Model(historyTestResult).Where(fmt.Sprintf("id = '%s'", historyTestResult.ID)) +} + +func allHistoryTestCaseIdentity(db *gorm.DB, historyTestResult *HistoryTestResult) *gorm.DB { + return db.Model(historyTestResult).Where(fmt.Sprintf("suite_name = '%s' AND case_name = '%s'", historyTestResult.SuiteName, historyTestResult.CaseName)) +} diff --git a/pkg/server_test.go b/pkg/server_test.go index 742a95b..2978578 100644 --- a/pkg/server_test.go +++ b/pkg/server_test.go @@ -17,13 +17,15 @@ package pkg import ( "context" - "os" - "testing" - + "fmt" "github.com/linuxsuren/api-testing/pkg/server" atest "github.com/linuxsuren/api-testing/pkg/testing" "github.com/linuxsuren/api-testing/pkg/testing/remote" "github.com/stretchr/testify/assert" + "google.golang.org/protobuf/types/known/timestamppb" + "os" + "testing" + "time" ) func TestNewRemoteServer(t *testing.T) { @@ -87,6 +89,36 @@ func TestNewRemoteServer(t *testing.T) { assert.False(t, reply.Ready) }) + t.Run("CreateTestCaseHistory", func(t *testing.T) { + _, err := remoteServer.CreateTestCaseHistory(defaultCtx, nil) + assert.Error(t, err) + }) + + t.Run("ListHistoryTestSuite", func(t *testing.T) { + _, err := remoteServer.ListHistoryTestSuite(defaultCtx, nil) + assert.Error(t, err) + }) + + t.Run("GetHistoryTestCase", func(t *testing.T) { + _, err := remoteServer.GetHistoryTestCase(defaultCtx, nil) + assert.Error(t, err) + }) + + t.Run("GetTestCaseAllHistory", func(t *testing.T) { + _, err := remoteServer.GetTestCaseAllHistory(defaultCtx, nil) + assert.Error(t, err) + }) + + t.Run("DeleteHistoryTestCase", func(t *testing.T) { + _, err := remoteServer.DeleteHistoryTestCase(defaultCtx, &server.HistoryTestCase{}) + assert.Error(t, err) + }) + + t.Run("DeleteAllHistoryTestCase", func(t *testing.T) { + _, err := remoteServer.DeleteAllHistoryTestCase(defaultCtx, &server.HistoryTestCase{}) + assert.Error(t, err) + }) + t.Run("invalid orm driver", func(t *testing.T) { remoteServer := NewRemoteServer() assert.NotNil(t, remoteServer) @@ -225,4 +257,68 @@ func TestSQLite(t *testing.T) { _, err := remoteServer.GetVersion(defaultCtx, &server.Empty{}) assert.NoError(t, err) }) + + now := time.Now() + t.Run("CreateTestCaseHistory", func(t *testing.T) { + _, err := remoteServer.CreateTestCaseHistory(defaultCtx, &server.HistoryTestResult{ + CreateTime: timestamppb.New(now), + Data: &server.HistoryTestCase{ + CaseName: "test", + SuiteName: "test", + }, + TestCaseResult: []*server.TestCaseResult{ + {Output: "test output"}, + }, + }) + assert.NoError(t, err) + }) + id := fmt.Sprintf("%s_test_test", now.Local().Format("2006-01-02T15:04:05.999999999")) + + t.Run("ListHistoryTestSuite", func(t *testing.T) { + result, err := remoteServer.ListHistoryTestSuite(defaultCtx, nil) + assert.NoError(t, err) + assert.Equal(t, 1, len(result.Data)) + assert.Equal(t, "test", result.Data[0].Items[0].SuiteName) + }) + + t.Run("GetHistoryTestCase", func(t *testing.T) { + result, err := remoteServer.GetHistoryTestCase(defaultCtx, &server.HistoryTestCase{ + ID: id, + }) + assert.NoError(t, err) + assert.Equal(t, "test", result.CaseName) + }) + + t.Run("GetHistoryTestCaseWithResult", func(t *testing.T) { + result, err := remoteServer.GetHistoryTestCaseWithResult(defaultCtx, &server.HistoryTestCase{ + ID: id, + }) + assert.NoError(t, err) + assert.Equal(t, "test", result.Data.CaseName) + assert.Equal(t, "test output", result.TestCaseResult[0].Output) + }) + + t.Run("GetTestCaseAllHistory", func(t *testing.T) { + result, err := remoteServer.GetTestCaseAllHistory(defaultCtx, &server.TestCase{ + Name: "test", + SuiteName: "test", + }) + assert.NoError(t, err) + assert.Equal(t, 1, len(result.Data)) + }) + + t.Run("DeleteHistoryTestCase", func(t *testing.T) { + _, err := remoteServer.DeleteHistoryTestCase(defaultCtx, &server.HistoryTestCase{ + ID: id, + }) + assert.NoError(t, err) + }) + + t.Run("DeleteAllHistoryTestCase", func(t *testing.T) { + _, err := remoteServer.DeleteAllHistoryTestCase(defaultCtx, &server.HistoryTestCase{ + CaseName: "test", + SuiteName: "test", + }) + assert.NoError(t, err) + }) } diff --git a/pkg/types.go b/pkg/types.go index e50db11..0804d0f 100644 --- a/pkg/types.go +++ b/pkg/types.go @@ -26,3 +26,44 @@ type TestSuite struct { SpecURL string Param string } + +type HistoryTestSuite struct { + Name string +} + +type HistoryTestResult struct { + ID string `gorm:"primaryKey"` + HistorySuiteName string + CreateTime string + + //suite information + SuiteName string `json:"suiteName"` + SuiteAPI string + SpecKind string + SpecURL string + Param string + + //case information + CaseName string `json:"caseName"` + CaseAPI string + Method string + Body string + Header string + HistoryHeader string + Cookie string + Query string + Form string + + ExpectStatusCode int + ExpectBody string + ExpectSchema string + ExpectHeader string + ExpectBodyFields string + ExpectVerify string + + //result information + Message string `json:"message"` + Error string `json:"error"` + StatusCode int32 `json:"statusCode"` + Output string `json:"output"` +}