mirror of
https://github.com/black-ant/Ant-Browser.git
synced 2026-07-14 18:48:55 +08:00
channel: master version: 1.0.0 source-ref: master published-at-utc: 2026-03-13T15:19:28Z
281 lines
7.0 KiB
Go
281 lines
7.0 KiB
Go
package logger
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/leanovate/gopter"
|
|
"github.com/leanovate/gopter/gen"
|
|
"github.com/leanovate/gopter/prop"
|
|
)
|
|
|
|
// genLevel 生成随机日志级别
|
|
func genLevel() gopter.Gen {
|
|
return gen.IntRange(0, 3).Map(func(i int) Level {
|
|
return Level(i)
|
|
})
|
|
}
|
|
|
|
// genLogEntry 生成随机 LogEntry
|
|
func genLogEntry() gopter.Gen {
|
|
return gopter.CombineGens(
|
|
genLevel(),
|
|
gen.AlphaString(),
|
|
gen.AlphaString(),
|
|
gen.AlphaString(),
|
|
gen.AlphaString(),
|
|
gen.Int64Range(0, 10000),
|
|
).Map(func(values []interface{}) *LogEntry {
|
|
level := values[0].(Level)
|
|
component := values[1].(string)
|
|
message := values[2].(string)
|
|
requestID := values[3].(string)
|
|
method := values[4].(string)
|
|
duration := values[5].(int64)
|
|
|
|
entry := &LogEntry{
|
|
Timestamp: time.Now(),
|
|
Level: level,
|
|
Component: component,
|
|
Message: message,
|
|
RequestID: requestID,
|
|
Method: method,
|
|
Duration: duration,
|
|
}
|
|
return entry
|
|
})
|
|
}
|
|
|
|
// TestProperty13_JSONFormatValidity 属性测试:JSON 格式有效性
|
|
// **Property 13: JSON Format Validity**
|
|
// **Validates: Requirements 6.2**
|
|
// *For any* log entry when JSON format is configured, the output SHALL be valid JSON
|
|
// that can be parsed without error.
|
|
func TestProperty13_JSONFormatValidity(t *testing.T) {
|
|
parameters := gopter.DefaultTestParameters()
|
|
parameters.MinSuccessfulTests = 100
|
|
properties := gopter.NewProperties(parameters)
|
|
|
|
formatter := NewJSONFormatter()
|
|
|
|
properties.Property("JSON output is always valid JSON", prop.ForAll(
|
|
func(entry *LogEntry) bool {
|
|
// Format the entry
|
|
data, err := formatter.Format(entry)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
// Verify it's valid JSON by attempting to unmarshal
|
|
var result map[string]interface{}
|
|
if err := json.Unmarshal(data, &result); err != nil {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
},
|
|
genLogEntry(),
|
|
))
|
|
|
|
properties.TestingRun(t)
|
|
}
|
|
|
|
// TestProperty12_StructuredLogFieldCompleteness 属性测试:结构化日志字段完整性
|
|
// **Property 12: Structured Log Field Completeness**
|
|
// **Validates: Requirements 6.1**
|
|
// *For any* log entry, the output SHALL contain: timestamp (ISO 8601), level
|
|
// (DEBUG/INFO/WARN/ERROR), component name, and message.
|
|
func TestProperty12_StructuredLogFieldCompleteness(t *testing.T) {
|
|
parameters := gopter.DefaultTestParameters()
|
|
parameters.MinSuccessfulTests = 100
|
|
properties := gopter.NewProperties(parameters)
|
|
|
|
textFormatter := NewTextFormatter()
|
|
jsonFormatter := NewJSONFormatter()
|
|
|
|
// Test TextFormatter field completeness
|
|
properties.Property("TextFormatter output contains all required fields", prop.ForAll(
|
|
func(entry *LogEntry) bool {
|
|
data, err := textFormatter.Format(entry)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
output := string(data)
|
|
|
|
// Check timestamp format (YYYY-MM-DD HH:MM:SS.mmm)
|
|
if !strings.Contains(output, "[") || !strings.Contains(output, "]") {
|
|
return false
|
|
}
|
|
|
|
// Check level is present (DEBUG/INFO/WARN/ERROR)
|
|
levelStr := entry.Level.String()
|
|
if !strings.Contains(output, "["+levelStr+"]") {
|
|
return false
|
|
}
|
|
|
|
// Check component is present (or "-" if empty)
|
|
if entry.Component != "" {
|
|
if !strings.Contains(output, "["+entry.Component+"]") {
|
|
return false
|
|
}
|
|
} else {
|
|
if !strings.Contains(output, "[-]") {
|
|
return false
|
|
}
|
|
}
|
|
|
|
// Check message is present
|
|
if !strings.Contains(output, entry.Message) {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
},
|
|
genLogEntry(),
|
|
))
|
|
|
|
// Test JSONFormatter field completeness
|
|
properties.Property("JSONFormatter output contains all required fields", prop.ForAll(
|
|
func(entry *LogEntry) bool {
|
|
data, err := jsonFormatter.Format(entry)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
var result map[string]interface{}
|
|
if err := json.Unmarshal(data, &result); err != nil {
|
|
return false
|
|
}
|
|
|
|
// Check timestamp exists and is in ISO 8601 format
|
|
timestamp, ok := result["timestamp"].(string)
|
|
if !ok || timestamp == "" {
|
|
return false
|
|
}
|
|
// Verify timestamp can be parsed as ISO 8601
|
|
_, err = time.Parse("2006-01-02T15:04:05.000Z07:00", timestamp)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
// Check level exists and is valid
|
|
level, ok := result["level"].(string)
|
|
if !ok {
|
|
return false
|
|
}
|
|
validLevels := map[string]bool{"DEBUG": true, "INFO": true, "WARN": true, "ERROR": true}
|
|
if !validLevels[level] {
|
|
return false
|
|
}
|
|
|
|
// Check component exists
|
|
if _, ok := result["component"]; !ok {
|
|
return false
|
|
}
|
|
|
|
// Check message exists
|
|
if _, ok := result["message"]; !ok {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
},
|
|
genLogEntry(),
|
|
))
|
|
|
|
properties.TestingRun(t)
|
|
}
|
|
|
|
// TestTextFormatterBasic 基础单元测试:TextFormatter
|
|
func TestTextFormatterBasic(t *testing.T) {
|
|
formatter := NewTextFormatter()
|
|
testTime := time.Date(2024, 1, 15, 10, 30, 0, 0, time.UTC)
|
|
|
|
entry := &LogEntry{
|
|
Timestamp: testTime,
|
|
Level: INFO,
|
|
Component: "TestComponent",
|
|
Message: "Test message",
|
|
}
|
|
|
|
data, err := formatter.Format(entry)
|
|
if err != nil {
|
|
t.Fatalf("Format failed: %v", err)
|
|
}
|
|
|
|
output := string(data)
|
|
|
|
// Verify basic structure
|
|
if !strings.Contains(output, "[2024-01-15 10:30:00.000]") {
|
|
t.Errorf("Timestamp not found in output: %s", output)
|
|
}
|
|
if !strings.Contains(output, "[INFO]") {
|
|
t.Errorf("Level not found in output: %s", output)
|
|
}
|
|
if !strings.Contains(output, "[TestComponent]") {
|
|
t.Errorf("Component not found in output: %s", output)
|
|
}
|
|
if !strings.Contains(output, "Test message") {
|
|
t.Errorf("Message not found in output: %s", output)
|
|
}
|
|
}
|
|
|
|
// TestJSONFormatterBasic 基础单元测试:JSONFormatter
|
|
func TestJSONFormatterBasic(t *testing.T) {
|
|
formatter := NewJSONFormatter()
|
|
testTime := time.Date(2024, 1, 15, 10, 30, 0, 0, time.UTC)
|
|
|
|
entry := &LogEntry{
|
|
Timestamp: testTime,
|
|
Level: INFO,
|
|
Component: "TestComponent",
|
|
Message: "Test message",
|
|
RequestID: "req-123",
|
|
Method: "TestMethod",
|
|
Duration: 150,
|
|
}
|
|
|
|
data, err := formatter.Format(entry)
|
|
if err != nil {
|
|
t.Fatalf("Format failed: %v", err)
|
|
}
|
|
|
|
var result map[string]interface{}
|
|
if err := json.Unmarshal(data, &result); err != nil {
|
|
t.Fatalf("JSON unmarshal failed: %v", err)
|
|
}
|
|
|
|
// Verify fields
|
|
if result["level"] != "INFO" {
|
|
t.Errorf("Level should be 'INFO', got %v", result["level"])
|
|
}
|
|
if result["component"] != "TestComponent" {
|
|
t.Errorf("Component should be 'TestComponent', got %v", result["component"])
|
|
}
|
|
if result["message"] != "Test message" {
|
|
t.Errorf("Message should be 'Test message', got %v", result["message"])
|
|
}
|
|
if result["request_id"] != "req-123" {
|
|
t.Errorf("RequestID should be 'req-123', got %v", result["request_id"])
|
|
}
|
|
}
|
|
|
|
// TestFormatterNilEntry 测试 nil entry 处理
|
|
func TestFormatterNilEntry(t *testing.T) {
|
|
textFormatter := NewTextFormatter()
|
|
jsonFormatter := NewJSONFormatter()
|
|
|
|
_, err := textFormatter.Format(nil)
|
|
if err == nil {
|
|
t.Error("TextFormatter should return error for nil entry")
|
|
}
|
|
|
|
_, err = jsonFormatter.Format(nil)
|
|
if err == nil {
|
|
t.Error("JSONFormatter should return error for nil entry")
|
|
}
|
|
}
|