Files
v-hono/tests/test_usockets_integration.go
T
meiseayoung 05bb2d6c95 feat: cross-platform uSockets support and route cache optimization
1. Cross-platform uSockets configuration
   - Use @VMODROOT for portable library paths
   - Support macOS (arm64/x86_64), Windows, and Linux
   - Auto-detect Homebrew paths for libuv on macOS

2. Route cache optimization (3x performance boost)
   - Replace LRU cache with FastRouteCache for dynamic routes
   - Zero-overhead cache lookup (no TTL check, no LRU reordering)
   - Dynamic route performance: 30k -> 97k RPS

3. New test files
   - Go-based integration tests for picoev and uSockets
   - FastCache optimization validation tests
   - Benchmark comparison tools
2026-01-02 21:44:07 +08:00

269 lines
7.0 KiB
Go

// uSockets 集成测试 (Go 版本)
//
// 使用方法:
// 1. 先启动测试服务器: v -enable-globals run v-hono/tests/test_usockets_server.v
// 2. 运行测试: go run v-hono/tests/test_usockets_integration.go
package main
import (
"fmt"
"io"
"net"
"net/http"
"strings"
"time"
)
const baseURL = "http://127.0.0.1:9998"
type TestStats struct {
total int
passed int
failed int
errors []string
}
func (s *TestStats) run(name string, testFn func() bool) {
s.total++
fmt.Printf(" 🧪 %s... ", name)
if testFn() {
s.passed++
fmt.Println("✅")
} else {
s.failed++
s.errors = append(s.errors, name)
fmt.Println("❌")
}
}
func (s *TestStats) summary() {
fmt.Println()
fmt.Println("═══════════════════════════════════════════════════════════════")
fmt.Printf("📊 测试结果: %d/%d 通过\n", s.passed, s.total)
if s.failed > 0 {
fmt.Println("❌ 失败的测试:")
for _, err := range s.errors {
fmt.Printf(" - %s\n", err)
}
} else {
fmt.Println("🎉 所有测试通过!uSockets 后端验证成功!")
}
fmt.Println("═══════════════════════════════════════════════════════════════")
}
func main() {
fmt.Println("╔═══════════════════════════════════════════════════════════════╗")
fmt.Println("║ uSockets 集成测试 (Go) ║")
fmt.Println("╚═══════════════════════════════════════════════════════════════╝")
fmt.Println()
// 验证服务器是否运行
fmt.Println("🔍 检查 uSockets 测试服务器...")
if !checkServerReady() {
fmt.Println("❌ 服务器未运行")
fmt.Println(" 请先启动: v -enable-globals run v-hono/tests/test_usockets_server.v")
return
}
fmt.Println("✅ 服务器已就绪")
fmt.Println()
stats := &TestStats{}
// 1. 基本路由测试
fmt.Println("📦 1. 基本路由测试")
stats.run("GET /", testGetRoot)
stats.run("GET /health", testGetHealth)
fmt.Println()
// 2. JSON 响应测试
fmt.Println("📦 2. JSON 响应测试")
stats.run("GET /api/json", testJSONResponse)
fmt.Println()
// 3. 动态路由测试
fmt.Println("📦 3. 动态路由测试")
stats.run("GET /api/users/:id", testSingleParam)
stats.run("GET /api/users/:user_id/posts/:post_id", testMultiParams)
fmt.Println()
// 4. 查询参数测试
fmt.Println("📦 4. 查询参数测试")
stats.run("GET /api/search?q=hello", testQueryParam)
fmt.Println()
// 5. 404 测试
fmt.Println("📦 5. 404 测试")
stats.run("404 Not Found", testNotFound)
fmt.Println()
// 6. Keep-Alive 测试
fmt.Println("📦 6. Keep-Alive 连接测试")
stats.run("Keep-Alive 连接复用", testKeepAlive)
fmt.Println()
// 7. 性能测试
fmt.Println("📦 7. 性能测试")
stats.run("吞吐量测试", testThroughput)
fmt.Println()
// 8. 高并发基准测试
fmt.Println("📦 8. Keep-Alive 基准测试")
testBenchmark()
fmt.Println()
// 输出总结
stats.summary()
}
func checkServerReady() bool {
client := &http.Client{Timeout: 2 * time.Second}
for i := 0; i < 5; i++ {
resp, err := client.Get(baseURL + "/health")
if err == nil && resp.StatusCode == 200 {
resp.Body.Close()
return true
}
time.Sleep(200 * time.Millisecond)
}
return false
}
func testGetRoot() bool {
resp, body := doGet("/")
return resp != nil && resp.StatusCode == 200 && strings.Contains(body, "Hello from uSockets")
}
func testGetHealth() bool {
resp, body := doGet("/health")
return resp != nil && resp.StatusCode == 200 && body == "OK"
}
func testJSONResponse() bool {
resp, body := doGet("/api/json")
if resp == nil {
return false
}
contentType := resp.Header.Get("Content-Type")
return resp.StatusCode == 200 && strings.Contains(contentType, "application/json") && strings.Contains(body, "Hello JSON")
}
func testSingleParam() bool {
resp, body := doGet("/api/users/123")
return resp != nil && resp.StatusCode == 200 && strings.Contains(body, "123")
}
func testMultiParams() bool {
resp, body := doGet("/api/users/100/posts/200")
return resp != nil && resp.StatusCode == 200 && strings.Contains(body, "100") && strings.Contains(body, "200")
}
func testQueryParam() bool {
resp, body := doGet("/api/search?q=hello")
return resp != nil && resp.StatusCode == 200 && strings.Contains(body, "hello")
}
func testNotFound() bool {
resp, _ := doGet("/nonexistent")
return resp != nil && resp.StatusCode == 404
}
func testKeepAlive() bool {
transport := &http.Transport{
MaxIdleConns: 10,
IdleConnTimeout: 30 * time.Second,
DisableKeepAlives: false,
}
client := &http.Client{Transport: transport, Timeout: 5 * time.Second}
for i := 0; i < 5; i++ {
resp, err := client.Get(baseURL + "/health")
if err != nil || resp.StatusCode != 200 {
return false
}
io.Copy(io.Discard, resp.Body)
resp.Body.Close()
}
return true
}
func testThroughput() bool {
client := &http.Client{Timeout: 5 * time.Second}
requests := 100
success := 0
start := time.Now()
for i := 0; i < requests; i++ {
resp, err := client.Get(baseURL + "/health")
if err == nil && resp.StatusCode == 200 {
success++
io.Copy(io.Discard, resp.Body)
resp.Body.Close()
}
}
elapsed := time.Since(start)
rps := float64(success) * 1000.0 / float64(elapsed.Milliseconds())
fmt.Printf("(%.0f req/s) ", rps)
return success >= requests*9/10
}
func testBenchmark() {
requests := 5000
conn, err := net.Dial("tcp", "127.0.0.1:9998")
if err != nil {
fmt.Println(" ❌ 连接失败:", err)
return
}
defer conn.Close()
// 预热
for i := 0; i < 100; i++ {
fmt.Fprintf(conn, "GET /health HTTP/1.1\r\nHost: 127.0.0.1:9998\r\nConnection: keep-alive\r\n\r\n")
buf := make([]byte, 256)
conn.Read(buf)
}
// 正式测试
start := time.Now()
success := 0
for i := 0; i < requests; i++ {
_, err := fmt.Fprintf(conn, "GET /health HTTP/1.1\r\nHost: 127.0.0.1:9998\r\nConnection: keep-alive\r\n\r\n")
if err != nil {
continue
}
buf := make([]byte, 256)
_, err = conn.Read(buf)
if err != nil {
continue
}
success++
}
elapsed := time.Since(start)
elapsedMs := float64(elapsed.Milliseconds())
rps := float64(success) * 1000.0 / elapsedMs
avgUs := elapsedMs * 1000.0 / float64(success)
fmt.Printf(" 成功请求: %d/%d\n", success, requests)
fmt.Printf(" 吞吐量: %.0f req/s\n", rps)
fmt.Printf(" 平均延迟: %.2fμs\n", avgUs)
}
func doGet(path string) (*http.Response, string) {
client := &http.Client{Timeout: 5 * time.Second}
resp, err := client.Get(baseURL + path)
if err != nil {
return nil, ""
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
return resp, string(body)
}