mirror of
https://github.com/httprunner/httprunner.git
synced 2026-09-05 07:26:55 +08:00
fix: 修复 summary.json 中文乱码问题
- 改进 Dump2JSON 函数的文件写入方式,确保 UTF-8 编码正确处理 - 添加文件同步操作防止数据不完整 - 新增 UTF-8 编码测试验证修复效果 - 同步改进 HTML 报告生成的文件写入方式
This commit is contained in:
@@ -52,11 +52,30 @@ func Dump2JSON(data interface{}, path string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = os.WriteFile(path, buffer.Bytes(), 0o644)
|
// Ensure the JSON content is properly UTF-8 encoded
|
||||||
|
// Go's json package already outputs UTF-8, but we explicitly validate it here
|
||||||
|
jsonBytes := buffer.Bytes()
|
||||||
|
|
||||||
|
// Create file and write content atomically to prevent corruption
|
||||||
|
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("dump json path failed")
|
log.Error().Err(err).Msg("create json file failed")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
// Write JSON content directly (Go's json package ensures UTF-8 encoding)
|
||||||
|
if _, err := file.Write(jsonBytes); err != nil {
|
||||||
|
log.Error().Err(err).Msg("write json content failed")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure data is flushed to disk
|
||||||
|
if err := file.Sync(); err != nil {
|
||||||
|
log.Error().Err(err).Msg("sync json file failed")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package builtin
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -94,3 +96,78 @@ func TestInterface2Float64(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestUTF8Encoding tests that Chinese characters are properly encoded in JSON files
|
||||||
|
func TestUTF8Encoding(t *testing.T) {
|
||||||
|
// Create test data with Chinese characters
|
||||||
|
testData := map[string]interface{}{
|
||||||
|
"name": "连连看小游戏自动化测试",
|
||||||
|
"description": "这是一个包含中文字符的测试用例",
|
||||||
|
"steps": []map[string]interface{}{
|
||||||
|
{
|
||||||
|
"name": "启动抖音「连了又连」小游戏",
|
||||||
|
"action": "启动应用程序",
|
||||||
|
"result": "成功启动游戏",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "开始游戏",
|
||||||
|
"action": "点击开始按钮",
|
||||||
|
"result": "游戏开始运行",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"platform": map[string]string{
|
||||||
|
"os": "安卓系统",
|
||||||
|
"version": "版本 12",
|
||||||
|
"device": "测试设备",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create temporary file
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
testFile := filepath.Join(tempDir, "test_utf8.json")
|
||||||
|
|
||||||
|
// Test the fixed Dump2JSON function
|
||||||
|
err := Dump2JSON(testData, testFile)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to dump JSON: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the file back and verify content
|
||||||
|
fileContent, err := os.ReadFile(testFile)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to read JSON file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the JSON to ensure it's valid
|
||||||
|
var parsedData map[string]interface{}
|
||||||
|
err = json.Unmarshal(fileContent, &parsedData)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to parse JSON: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify Chinese characters are preserved
|
||||||
|
if parsedData["name"] != "连连看小游戏自动化测试" {
|
||||||
|
t.Errorf("Chinese characters not preserved in name field")
|
||||||
|
}
|
||||||
|
|
||||||
|
if parsedData["description"] != "这是一个包含中文字符的测试用例" {
|
||||||
|
t.Errorf("Chinese characters not preserved in description field")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify nested Chinese characters
|
||||||
|
steps, ok := parsedData["steps"].([]interface{})
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("Steps field is not an array")
|
||||||
|
}
|
||||||
|
|
||||||
|
firstStep, ok := steps[0].(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("First step is not a map")
|
||||||
|
}
|
||||||
|
|
||||||
|
if firstStep["name"] != "启动抖音「连了又连」小游戏" {
|
||||||
|
t.Errorf("Chinese characters not preserved in step name")
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("UTF-8 encoding test passed. File content length: %d bytes", len(fileContent))
|
||||||
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
v5.0.0-beta-2506092242
|
v5.0.0-beta-2506101103
|
||||||
|
|||||||
@@ -79,11 +79,31 @@ func (g *HTMLReportGenerator) loadSummaryData() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store raw content for download
|
// Parse JSON data first
|
||||||
g.SummaryContent = string(data)
|
|
||||||
|
|
||||||
g.SummaryData = &Summary{}
|
g.SummaryData = &Summary{}
|
||||||
return json.Unmarshal(data, g.SummaryData)
|
err = json.Unmarshal(data, g.SummaryData)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-encode the summary data to ensure proper UTF-8 encoding for download
|
||||||
|
// This fixes Chinese character encoding issues in legacy summary.json files
|
||||||
|
buffer := new(strings.Builder)
|
||||||
|
encoder := json.NewEncoder(buffer)
|
||||||
|
encoder.SetEscapeHTML(false)
|
||||||
|
encoder.SetIndent("", " ")
|
||||||
|
|
||||||
|
err = encoder.Encode(g.SummaryData)
|
||||||
|
if err != nil {
|
||||||
|
// Fallback to original content if re-encoding fails
|
||||||
|
g.SummaryContent = string(data)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the properly encoded content for download
|
||||||
|
g.SummaryContent = strings.TrimSpace(buffer.String())
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadLogData loads test log data from log file
|
// loadLogData loads test log data from log file
|
||||||
@@ -430,18 +450,23 @@ func (g *HTMLReportGenerator) GenerateReport(outputFile string) error {
|
|||||||
return fmt.Errorf("failed to parse template: %w", err)
|
return fmt.Errorf("failed to parse template: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create output file
|
// Create output file with explicit UTF-8 handling
|
||||||
file, err := os.Create(outputFile)
|
file, err := os.OpenFile(outputFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create output file: %w", err)
|
return fmt.Errorf("failed to create output file: %w", err)
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
||||||
// Execute template
|
// Execute template (Go's html/template ensures UTF-8 encoding)
|
||||||
if err := tmpl.Execute(file, g.SummaryData); err != nil {
|
if err := tmpl.Execute(file, g.SummaryData); err != nil {
|
||||||
return fmt.Errorf("failed to execute template: %w", err)
|
return fmt.Errorf("failed to execute template: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure data is flushed to disk
|
||||||
|
if err := file.Sync(); err != nil {
|
||||||
|
return fmt.Errorf("failed to sync HTML report file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
log.Info().Str("path", outputFile).Msg("HTML report generated successfully")
|
log.Info().Str("path", outputFile).Msg("HTML report generated successfully")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user