Skip to content

Commit

Permalink
add html page
Browse files Browse the repository at this point in the history
  • Loading branch information
vearne committed Oct 22, 2024
1 parent 5e67df6 commit a23b62e
Show file tree
Hide file tree
Showing 7 changed files with 250 additions and 4 deletions.
60 changes: 57 additions & 3 deletions internal/command/http_automate.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package command

import (
"context"
"embed"
"github.com/lianggaoqiang/progress"
"github.com/vearne/autotest/internal/config"
"github.com/vearne/autotest/internal/model"
Expand All @@ -11,13 +12,18 @@ import (
slog "github.com/vearne/simplelog"
"github.com/vearne/zaplog"
"go.uber.org/zap"
"html/template"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
"time"
)

//go:embed template/*.tpl
var mytpl embed.FS

type ResultInfo struct {
Total int
SuccessCount int
Expand Down Expand Up @@ -54,12 +60,12 @@ func HttpAutomateTest(httpTestCases map[string][]*config.TestCaseHttp) {
slog.Info("HttpTestCases, total:%v, finishCount:%v, successCount:%v, failedCount:%v",
total, finishCount, successCount, failedCount)
// generate report file
GenReportFileHttp(filePath, tcResultList)
GenReportFileHttp(filePath, tcResultList, info)
}
slog.Info("[end]HttpTestCases, total:%v, cost:%v", total, time.Since(begin))
}

func GenReportFileHttp(testCasefilePath string, tcResultList []HttpTestCaseResult) {
func GenReportFileHttp(testCasefilePath string, tcResultList []HttpTestCaseResult, info *ResultInfo) {
filename := filepath.Base(testCasefilePath)
name := strings.TrimSuffix(filename, filepath.Ext(filename))
filename = name + ".csv"
Expand All @@ -69,8 +75,9 @@ func GenReportFileHttp(testCasefilePath string, tcResultList []HttpTestCaseResul
sort.Slice(tcResultList, func(i, j int) bool {
return tcResultList[i].ID < tcResultList[j].ID
})
// 1. csv file
var records [][]string
records = append(records, []string{"id", "desc", "state", "reason"})
records = append(records, []string{"id", "description", "state", "reason"})
for _, item := range tcResultList {
reasonStr := item.Reason.String()
if item.Reason == model.ReasonSuccess {
Expand All @@ -80,6 +87,53 @@ func GenReportFileHttp(testCasefilePath string, tcResultList []HttpTestCaseResul
item.Desc, item.State.String(), reasonStr})
}
util.WriterCSV(reportPath, records)
// 2. html file
dirName := util.MD5(reportDirPath + name)
obj := map[string]any{
"info": info,
"tcResultList": tcResultList,
"dirName": dirName,
}
// index file
err := RenderTpl(mytpl, "template/index.tpl", obj, filepath.Join(reportDirPath, name+".html"))
if err != nil {
slog.Error("RenderTpl, %v", err)
return
}

for _, item := range tcResultList {
data := map[string]any{
"Error": item.Error,
"reqDetail": item.ReqDetail(),
"respDetail": item.RespDetail(),
}
err := RenderTpl(mytpl, "template/case.tpl", data,
filepath.Join(reportDirPath, dirName, strconv.Itoa(int(item.ID))+".html"))
if err != nil {
slog.Error("RenderTpl, %v", err)
return
}
}
}

func RenderTpl(fs embed.FS, key string, obj map[string]any, targetPath string) error {
data, err := fs.ReadFile(key)
if err != nil {
slog.Error("mytpl.ReadFile, %v", err)
return err
}
t, err := template.New("index").Parse(string(data))
if err != nil {
slog.Error("template Parse, %v", err)
return err
}
file, err := os.Create(targetPath)
if err != nil {
slog.Error("Create file, %v", err)
return err
}
defer file.Close()
return t.Execute(file, obj)
}

func HandleSingleFileHttp(workerNum int, filePath string) (*ResultInfo, []HttpTestCaseResult) {
Expand Down
68 changes: 68 additions & 0 deletions internal/command/http_call.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ package command

import (
"context"
"fmt"
"github.com/go-resty/resty/v2"
"github.com/vearne/autotest/internal/config"
"github.com/vearne/autotest/internal/model"
"github.com/vearne/autotest/internal/resource"
"github.com/vearne/executor"
"github.com/vearne/zaplog"
"go.uber.org/zap"
"net/url"
"strings"
"time"
)
Expand All @@ -22,6 +24,66 @@ type HttpTestCaseResult struct {
Request config.RequestHttp
TestCase *config.TestCaseHttp
KeyValues map[string]any
Error error
Response *resty.Response
}

/*
POST /api/order/subscribe HTTP/1.1
HOST: localhost:8080
HEADERS:
Content-Type: application/json
User-Agent: go-resty/2.12.0 (https://github.com/go-resty/resty)
BODY:
{
"title": "book3_title",
"author": "book3_author"
}
*/

func (t *HttpTestCaseResult) ReqDetail() string {
var builder strings.Builder
builder.WriteString(fmt.Sprintf("%v %v\n", t.Request.Method, t.Request.URL))
u, _ := url.Parse(t.Request.URL)
builder.WriteString(fmt.Sprintf("HOST: %v\n", u.Host))
builder.WriteString("HEADERS:\n")
for _, item := range t.Request.Headers {
builder.WriteString(fmt.Sprintf("%v\n", item))
}
builder.WriteString("BODY:\n")
builder.WriteString(fmt.Sprintf("%v\n", t.Request.Body))
return builder.String()
}

/*
STATUS : 200 OK
PROTO : HTTP/1.1
RECEIVED AT : 2024-10-17T17:05:05.156315+08:00
TIME DURATION: 38.354958ms
HEADERS :
Content-Length: 17
Content-Type: application/json; charset=utf-8
Date: Thu, 17 Oct 2024 09:05:05 GMT
BODY :
{
"code": "E000"
}
*/

func (t *HttpTestCaseResult) RespDetail() string {
if t.Response == nil {
return ""
}

var builder strings.Builder
builder.WriteString(fmt.Sprintf("STATUS: %v %v\n", t.Response.StatusCode, t.Response.Status))
builder.WriteString("HEADERS:\n")
for key, values := range t.Response.Header() {
builder.WriteString(fmt.Sprintf("%v: %v\n", key, strings.Join(values, ",")))
}
builder.WriteString("BODY:\n")
builder.WriteString(fmt.Sprintf("%v\n", t.Response.String()))
return builder.String()
}

type HttpTestCallable struct {
Expand All @@ -40,6 +102,8 @@ func (m *HttpTestCallable) Call(ctx context.Context) *executor.GPResult {
Reason: model.ReasonSuccess,
TestCase: m.testcase,
KeyValues: map[string]any{},
Error: nil,
Response: nil,
}

// 1. Check other test cases of dependencies
Expand Down Expand Up @@ -75,6 +139,7 @@ func (m *HttpTestCallable) Call(ctx context.Context) *executor.GPResult {
if err != nil {
tcResult.State = model.StateFailed
tcResult.Reason = model.ReasonTemplateRenderError
tcResult.Error = err
r.Value = tcResult
r.Err = err
return &r
Expand Down Expand Up @@ -119,11 +184,14 @@ func (m *HttpTestCallable) Call(ctx context.Context) *executor.GPResult {
)
tcResult.State = model.StateFailed
tcResult.Reason = model.ReasonRequestFailed
tcResult.Error = err
r.Value = tcResult
r.Err = err
return &r
}

tcResult.Response = out

// 6. export
if m.testcase.Export != nil {
exportConfig := m.testcase.Export
Expand Down
36 changes: 36 additions & 0 deletions internal/command/template/case.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>HTTP Request and Response Display</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
h2 { color: #333; }
.container { margin-bottom: 20px; }
.section-title { font-weight: bold; margin-top: 20px; }
pre { background-color: #f4f4f4; padding: 10px; border: 1px solid #ddd; overflow: auto; white-space: pre-wrap; }
</style>
</head>
<body>
<h2>HTTP Request and Response Display</h2>

<div class="container">
<div class="section-title">~~~ REQUEST ~~~</div>
<pre>
{{ .reqDetail }}
</pre>
</div>

<div class="container">
<div class="section-title">~~~ RESPONSE ~~~</div>
{{if .Error}}

{{else}}
<pre>
{{ .respDetail }}
</pre>
{{end}}
</div>
</body>
</html>
72 changes: 72 additions & 0 deletions internal/command/template/index.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Styled Table Example</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
padding: 20px;
}
table {
width: 80%;
margin: 0 auto;
border-collapse: collapse;
background-color: white;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
th, td {
padding: 12px 15px;
text-align: left;
}
th {
background-color: #007BFF;
color: white;
}
tr:nth-child(even) {
background-color: #f2f2f2;
}
a {
color: #007BFF;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
caption {
font-size: 1.5em;
margin: 10px;
font-weight: bold;
}
</style>
</head>
<body>

<table>
<caption>FinishCount: {{.info.Total}}, SuccessCount: {{.info.SuccessCount}}, FailedCount: {{.info.FailedCount}}</caption>
<thead>
<tr>
<th>id</th>
<th>description</th>
<th>state</th>
<th>reason</th>
<th>content</th>
</tr>
</thead>
<tbody>
{{range .tcResultList}}
<tr>
<td>{{ .ID }}</td>
<td>{{ .Desc }}</td>
<td>{{ .State.String() }}</td>
<td>{{ .Reason.String() }}</td>
<td><a href="{{.dirName}}/{{ .ID }}.html">View Details</a></td>
</tr>
{{end}}
</tbody>
</table>

</body>
</html>
2 changes: 1 addition & 1 deletion internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ type AutoTestConfig struct {
} `yaml:"logger"`
Report struct {
DirPath string `yaml:"dir_path"`
}
} `yaml:"report"`
} `yaml:"global"`

HttpRuleFiles []string `yaml:"http_rule_files"`
Expand Down
10 changes: 10 additions & 0 deletions internal/resource/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
"sync"
"sync/atomic"
Expand Down Expand Up @@ -223,6 +224,15 @@ func ParseConfigFile(filePath string) error {
GlobalConfig.GrpcRuleFiles[idx] = absolutePath
}

// 3) modify report path
newPath := filepath.Join(GlobalConfig.Global.Report.DirPath, "autotest_"+strconv.Itoa(int(time.Now().Unix())))
// 创建单个文件夹
err = os.Mkdir("newPath", 0755)
if err != nil {
return err
}

GlobalConfig.Global.Report.DirPath = newPath
slog.Info("[end]ParseConfigFile")
return nil
}
Expand Down
6 changes: 6 additions & 0 deletions internal/util/util.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package util

import (
"crypto/md5"
"encoding/csv"
"fmt"
slog "github.com/vearne/simplelog"
"os"
)
Expand All @@ -24,3 +26,7 @@ func WriterCSV(path string, records [][]string) {
writer.Flush()
slog.Info("write report file:%v", path)
}

func MD5(data string) string {
return fmt.Sprintf("%x", md5.Sum([]byte(data)))
}

0 comments on commit a23b62e

Please sign in to comment.