Skip to content

Commit 6dc057a

Browse files
committed
add httpResultWriter
1 parent ab38bef commit 6dc057a

File tree

4 files changed

+279
-1
lines changed

4 files changed

+279
-1
lines changed

cmd/run.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"fmt"
2323
"io"
2424
"log"
25+
"net/http"
2526
"os"
2627
"os/exec"
2728
"path/filepath"
@@ -58,6 +59,8 @@ type runOption struct {
5859
reportWriter runner.ReportResultWriter
5960
report string
6061
reportIgnore bool
62+
reportTemplate string
63+
reportDest string
6164
swaggerURL string
6265
level string
6366
caseItems []string
@@ -106,9 +109,11 @@ See also https://github.com/LinuxSuRen/api-testing/tree/master/sample`,
106109
flags.DurationVarP(&opt.duration, "duration", "", 0, "Running duration")
107110
flags.DurationVarP(&opt.requestTimeout, "request-timeout", "", time.Minute, "Timeout for per request")
108111
flags.BoolVarP(&opt.requestIgnoreError, "request-ignore-error", "", false, "Indicate if ignore the request error")
109-
flags.StringVarP(&opt.report, "report", "", "", "The type of target report. Supported: markdown, md, html, json, discard, std, prometheus")
112+
flags.StringVarP(&opt.report, "report", "", "", "The type of target report. Supported: markdown, md, html, json, discard, std, prometheus, http")
110113
flags.StringVarP(&opt.reportFile, "report-file", "", "", "The file path of the report")
111114
flags.BoolVarP(&opt.reportIgnore, "report-ignore", "", false, "Indicate if ignore the report output")
115+
flags.StringVarP(&opt.reportTemplate, "report-template", "", "", "The template used to render the report")
116+
flags.StringVarP(&opt.reportDest, "report-dest", "", "", "The server url where you want to send the report")
112117
flags.StringVarP(&opt.swaggerURL, "swagger-url", "", "", "The URL of swagger")
113118
flags.Int64VarP(&opt.thread, "thread", "", 1, "Threads of the execution")
114119
flags.Int32VarP(&opt.qps, "qps", "", 5, "QPS")
@@ -153,6 +158,9 @@ func (o *runOption) preRunE(cmd *cobra.Command, args []string) (err error) {
153158
case "github":
154159
o.githubReportOption.ReportFile = o.reportFile
155160
o.reportWriter, err = runner.NewGithubPRCommentWriter(o.githubReportOption)
161+
case "http":
162+
templateOption := runner.NewTemplateOption(o.reportTemplate, "json")
163+
o.reportWriter = runner.NewHTTPResultWriter(http.MethodPost, o.reportDest, nil, templateOption)
156164
default:
157165
err = fmt.Errorf("not supported report type: '%s'", o.report)
158166
}

pkg/runner/writer_http.go

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
/*
2+
Copyright 2024 API Testing Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package runner
18+
19+
import (
20+
"bytes"
21+
_ "embed"
22+
"fmt"
23+
"html/template"
24+
"io"
25+
"log"
26+
"net/http"
27+
"os"
28+
29+
"github.com/linuxsuren/api-testing/pkg/apispec"
30+
)
31+
32+
type httpResultWriter struct {
33+
requestMethod string
34+
targetUrl string
35+
parameters map[string]string
36+
templateFile *TemplateOption
37+
}
38+
39+
type TemplateOption struct {
40+
filename string
41+
fileType string
42+
}
43+
44+
// NewHTTPResultWriter creates a new httpResultWriter
45+
func NewHTTPResultWriter(requestType string, url string, parameters map[string]string, templateFile *TemplateOption) ReportResultWriter {
46+
return &httpResultWriter{
47+
requestMethod: requestType,
48+
targetUrl: url,
49+
parameters: parameters,
50+
templateFile: templateFile,
51+
}
52+
}
53+
54+
func NewTemplateOption(filename string, fileType string) *TemplateOption {
55+
return &TemplateOption{
56+
filename: filename,
57+
fileType: fileType,
58+
}
59+
}
60+
61+
// Output writes the JSON base report to target writer
62+
func (w *httpResultWriter) Output(result []ReportResult) (err error) {
63+
url := w.targetUrl
64+
for key, value := range w.parameters {
65+
if url == w.targetUrl {
66+
url = fmt.Sprintf("%s?%s=%s", url, key, value)
67+
} else {
68+
url = fmt.Sprintf("%s&%s=%s", url, key, value)
69+
}
70+
}
71+
log.Println("will send report to:" + url)
72+
73+
var tmpl *template.Template
74+
if w.templateFile == nil {
75+
// use the default template file to serialize the data to JSON format
76+
tmpl, err = template.New("HTTP report template").Parse(defaultTemplate)
77+
if err != nil {
78+
log.Fatalf("Failed to parse template: %v", err)
79+
}
80+
} else {
81+
content, err := os.ReadFile(w.templateFile.filename)
82+
if err != nil {
83+
log.Println("Error reading file:", err)
84+
return err
85+
}
86+
87+
tmpl, err = template.New("HTTP report template").Parse(string(content))
88+
if err != nil {
89+
log.Println("Error parsing template:", err)
90+
return err
91+
}
92+
}
93+
94+
buf := new(bytes.Buffer)
95+
err = tmpl.Execute(buf, result)
96+
if err != nil {
97+
log.Printf("Failed to render template: %v", err)
98+
return
99+
}
100+
req, err := http.NewRequest(w.requestMethod, url, buf)
101+
if err != nil {
102+
log.Println("Error creating request:", err)
103+
return
104+
}
105+
106+
var contentType string
107+
if w.templateFile != nil {
108+
switch w.templateFile.fileType {
109+
case "html":
110+
contentType = "text/html"
111+
case "yaml":
112+
contentType = "application/yaml"
113+
case "xml":
114+
contentType = "application/xml"
115+
default:
116+
contentType = "application/json"
117+
}
118+
} else {
119+
contentType = "application/json"
120+
}
121+
req.Header.Set("Content-Type", contentType)
122+
123+
var resp *http.Response
124+
if resp, err = http.DefaultClient.Do(req); err != nil {
125+
log.Println("error when client do", err)
126+
return
127+
}
128+
if resp.StatusCode == http.StatusOK {
129+
var data []byte
130+
if data, err = io.ReadAll(resp.Body); err != nil {
131+
log.Println("error when ReadAll", err)
132+
return
133+
}
134+
log.Println("getting response back", data)
135+
}
136+
return
137+
}
138+
139+
//go:embed writer_templates/example.tpl
140+
var defaultTemplate string
141+
142+
// WithAPIConverage sets the api coverage
143+
func (w *httpResultWriter) WithAPIConverage(apiConverage apispec.APIConverage) ReportResultWriter {
144+
return w
145+
}
146+
147+
func (w *httpResultWriter) WithResourceUsage([]ResourceUsage) ReportResultWriter {
148+
return w
149+
}

pkg/runner/writer_http_test.go

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
Copyright 2024 API Testing Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package runner
18+
19+
import (
20+
"net/http"
21+
"testing"
22+
23+
"github.com/h2non/gock"
24+
"github.com/stretchr/testify/assert"
25+
)
26+
27+
func TestHTTPResultWriter(t *testing.T) {
28+
t.Run("test get request", func(t *testing.T) {
29+
defer gock.Off()
30+
gock.New("https://test.com").Get("/result/get").Reply(http.StatusOK).JSON([]comment{})
31+
32+
writer := NewHTTPResultWriter("GET", "https://test.com/result/get", nil, nil)
33+
34+
err := writer.Output([]ReportResult{{
35+
API: "/api",
36+
}})
37+
assert.NoError(t, err)
38+
})
39+
40+
t.Run("test post request", func(t *testing.T) {
41+
defer gock.Off()
42+
gock.New("https://test.com").Post("/result/post").Reply(http.StatusOK).JSON([]comment{})
43+
44+
writer := NewHTTPResultWriter("POST", "https://test.com/result/post", nil, nil)
45+
46+
err := writer.Output([]ReportResult{{
47+
API: "/api",
48+
}})
49+
assert.NoError(t, err)
50+
})
51+
52+
t.Run("test parameters", func(t *testing.T) {
53+
defer gock.Off()
54+
gock.New("https://test.com/result/post?username=1&pwd=2").Post("").Reply(http.StatusOK).JSON([]comment{})
55+
56+
parameters := map[string]string{
57+
"username": "1",
58+
"pwd": "2",
59+
}
60+
61+
writer := NewHTTPResultWriter("POST", "https://test.com/result/post", parameters, nil)
62+
63+
err := writer.Output([]ReportResult{{
64+
API: "/api",
65+
}})
66+
assert.NoError(t, err)
67+
})
68+
69+
t.Run("test user does not send template file", func(t *testing.T) {
70+
defer gock.Off()
71+
gock.New("https://test.com/result/post?username=1&pwd=2").Post("").Reply(http.StatusOK).JSON([]comment{})
72+
73+
parameters := map[string]string{
74+
"username": "1",
75+
"pwd": "2",
76+
}
77+
78+
writer := NewHTTPResultWriter("POST", "https://test.com/result/post", parameters, nil)
79+
80+
err := writer.Output([]ReportResult{{
81+
Name: "test",
82+
API: "/api",
83+
Count: 1,
84+
}})
85+
assert.NoError(t, err)
86+
})
87+
88+
t.Run("test user send template file", func(t *testing.T) {
89+
defer gock.Off()
90+
gock.New("https://test.com/result/post?username=1&pwd=2").Post("").Reply(http.StatusOK).JSON([]comment{})
91+
92+
parameters := map[string]string{
93+
"username": "1",
94+
"pwd": "2",
95+
}
96+
templateOption := NewTemplateOption("./writer_templates/example.tpl", "json")
97+
writer := NewHTTPResultWriter("POST", "https://test.com/result/post", parameters, templateOption)
98+
99+
err := writer.Output([]ReportResult{{
100+
Name: "test",
101+
API: "/api",
102+
Count: 1,
103+
}})
104+
assert.NoError(t, err)
105+
})
106+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[
2+
{{range $index, $result := .}}
3+
{
4+
"Name": "{{$result.Name}}",
5+
"API": "{{$result.API}}",
6+
"Count": {{$result.Count}},
7+
"Average": "{{$result.Average}}",
8+
"Max": "{{$result.Max}}",
9+
"Min": "{{$result.Min}}",
10+
"QPS": {{$result.QPS}},
11+
"Error": {{$result.Error}},
12+
"LastErrorMessage": "{{$result.LastErrorMessage}}"
13+
}
14+
{{end}}
15+
]

0 commit comments

Comments
 (0)