Files
v-hono/tests/test_middleware_optimization.v
meiseayoung 3b8d21dd0d fix: update import path to VPM package name (meiseayoung.hono)
- Change all `import hono` to `import meiseayoung.hono` in code and docs
- Ensure compatibility with VPM installation via `v install meiseayoung.hono`
2026-01-02 22:05:12 +08:00

356 lines
9.1 KiB
V
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 中间件优化测试
// 测试三个优化点:
// 1. 零中间件快速路径
// 2. 中间件预计算(启动时排序)
// 3. 复用 cache key
import meiseayoung.hono
import time
import net.http
// 测试统计
struct TestStats {
mut:
total_tests int
passed_tests int
failed_tests int
}
fn (mut stats TestStats) start_test(test_name string) {
stats.total_tests++
print('🧪 ${test_name}... ')
}
fn (mut stats TestStats) pass_test() {
stats.passed_tests++
println('✅')
}
fn (mut stats TestStats) fail_test(error string) {
stats.failed_tests++
println('❌ ${error}')
}
fn (stats TestStats) print_summary() {
println('\n=== 测试总结 ===')
println('总测试数: ${stats.total_tests}')
println('通过: ${stats.passed_tests}')
println('失败: ${stats.failed_tests}')
if stats.failed_tests == 0 {
println('🎉 所有测试通过!')
} else {
println('⚠️ 有 ${stats.failed_tests} 个测试失败')
}
}
// 测试1:零中间件快速路径 - has_middlewares 标志
fn test_zero_middleware_flag(mut stats TestStats) {
stats.start_test('零中间件标志 (has_middlewares)')
mut app := hono.Hono.new()
// 新创建的应用应该没有中间件
if app.has_middlewares {
stats.fail_test('新应用不应该有中间件标志')
return
}
// 添加中间件后标志应该为 true
app.use(fn (mut c hono.Context, next fn (mut hono.Context) http.Response) http.Response {
return next(mut c)
})
if !app.has_middlewares {
stats.fail_test('添加中间件后标志应该为 true')
return
}
stats.pass_test()
}
// 测试2:中间件预计算 - sorted_middleware_prefixes
fn test_middleware_precompute(mut stats TestStats) {
stats.start_test('中间件预计算 (sorted_middleware_prefixes)')
mut app := hono.Hono.new()
// 添加路由
app.get('/test', fn (mut c hono.Context) http.Response {
return c.text('test')
})
// 创建子应用并添加中间件
mut sub_app := hono.Hono.new()
sub_app.use(fn (mut c hono.Context, next fn (mut hono.Context) http.Response) http.Response {
return next(mut c)
})
sub_app.get('/hello', fn (mut c hono.Context) http.Response {
return c.text('hello')
})
// 挂载子应用
app.route('/api', mut sub_app)
// 预计算前,sorted_middleware_prefixes 应该为空
if app.sorted_middleware_prefixes.len != 0 {
stats.fail_test('预计算前 sorted_middleware_prefixes 应该为空')
return
}
// 调用预计算
app.precompute_middleware_prefixes()
// 预计算后,sorted_middleware_prefixes 应该包含前缀
if app.sorted_middleware_prefixes.len == 0 && app.route_middlewares.len > 0 {
stats.fail_test('预计算后 sorted_middleware_prefixes 应该包含前缀')
return
}
// 验证 has_middlewares 标志
if app.route_middlewares.len > 0 && !app.has_middlewares {
stats.fail_test('有路由中间件时 has_middlewares 应该为 true')
return
}
stats.pass_test()
}
// 测试3:FastRouter cache key 复用
fn test_fast_router_cache_key_reuse(mut stats TestStats) {
stats.start_test('FastRouter cache key 复用')
mut router := hono.FastRouter.new()
// 添加静态路由
static_handler := hono.ContextHandler{
path: '/users'
handler: fn (mut c hono.Context) http.Response {
return c.text('users')
}
}
router.add_route('GET', static_handler, '') or {
stats.fail_test('添加静态路由失败')
return
}
// 添加动态路由
dynamic_handler := hono.ContextHandler{
path: '/users/:id'
handler: fn (mut c hono.Context) http.Response {
return c.text('user')
}
}
router.add_route('GET', dynamic_handler, '') or {
stats.fail_test('添加动态路由失败')
return
}
// 测试静态路由匹配
if _ := router.match_route('GET', '/users') {
// 成功
} else {
stats.fail_test('静态路由匹配失败')
return
}
// 测试动态路由匹配(第一次,会缓存)
if match1 := router.match_route('GET', '/users/123') {
if match1.params['id'] != '123' {
stats.fail_test('参数提取失败')
return
}
} else {
stats.fail_test('动态路由匹配失败')
return
}
// 测试动态路由匹配(第二次,应该命中缓存)
if match2 := router.match_route('GET', '/users/123') {
if match2.params['id'] != '123' {
stats.fail_test('缓存命中后参数提取失败')
return
}
} else {
stats.fail_test('缓存命中失败')
return
}
// 验证缓存状态
_, _, cache_count := router.get_stats()
if cache_count == 0 {
stats.fail_test('缓存应该有条目')
return
}
stats.pass_test()
}
// 测试4:HybridRouter cache key 复用
fn test_hybrid_router_cache_key_reuse(mut stats TestStats) {
stats.start_test('HybridRouter cache key 复用')
mut router := hono.ContextHybridRouter.new()
// 添加动态路由
dynamic_handler := hono.ContextHandler{
path: '/posts/:id'
handler: fn (mut c hono.Context) http.Response {
return c.text('post')
}
}
router.add_route('GET', dynamic_handler, '')
// 第一次匹配(无缓存)
if match1 := router.match_route('GET', '/posts/456') {
if match1.params['id'] != '456' {
stats.fail_test('参数提取失败')
return
}
} else {
stats.fail_test('动态路由匹配失败')
return
}
// 第二次匹配(应该命中缓存)
if match2 := router.match_route('GET', '/posts/456') {
if match2.params['id'] != '456' {
stats.fail_test('缓存命中后参数提取失败')
return
}
} else {
stats.fail_test('缓存命中失败')
return
}
stats.pass_test()
}
// 测试5:性能对比 - 有中间件 vs 无中间件
fn test_middleware_performance_comparison(mut stats TestStats) {
stats.start_test('中间件性能对比')
// 创建无中间件的路由器
mut router_no_mw := hono.FastRouter.new()
handler := hono.ContextHandler{
path: '/api/users/:id'
handler: fn (mut c hono.Context) http.Response {
return c.text('user')
}
}
router_no_mw.add_route('GET', handler, '') or {
stats.fail_test('添加路由失败')
return
}
// 预热
for _ in 0 .. 100 {
_ := router_no_mw.match_route('GET', '/api/users/123') or { continue }
}
// 测试无中间件性能
iterations := 10000
start_no_mw := time.now()
for _ in 0 .. iterations {
_ := router_no_mw.match_route('GET', '/api/users/123') or { continue }
}
duration_no_mw := time.since(start_no_mw)
// 创建有中间件的应用
mut app_with_mw := hono.Hono.new()
app_with_mw.use(fn (mut c hono.Context, next fn (mut hono.Context) http.Response) http.Response {
return next(mut c)
})
app_with_mw.get('/api/users/:id', fn (mut c hono.Context) http.Response {
return c.text('user')
})
app_with_mw.precompute_middleware_prefixes()
// 预热
for _ in 0 .. 100 {
_ := app_with_mw.fast_router.match_route('GET', '/api/users/123') or { continue }
}
// 测试有中间件性能
start_with_mw := time.now()
for _ in 0 .. iterations {
_ := app_with_mw.fast_router.match_route('GET', '/api/users/123') or { continue }
}
duration_with_mw := time.since(start_with_mw)
println('')
println(' 📊 无中间件: ${duration_no_mw.milliseconds()}ms (${iterations} 次)')
println(' 📊 有中间件: ${duration_with_mw.milliseconds()}ms (${iterations} 次)')
// 性能差异不应该太大(路由匹配本身不受中间件影响)
stats.pass_test()
}
// 测试6:预计算排序正确性
fn test_precompute_sorting(mut stats TestStats) {
stats.start_test('预计算排序正确性')
mut app := hono.Hono.new()
// 添加多个子应用,模拟不同长度的前缀
mut sub1 := hono.Hono.new()
sub1.use(fn (mut c hono.Context, next fn (mut hono.Context) http.Response) http.Response {
return next(mut c)
})
sub1.get('/test', fn (mut c hono.Context) http.Response {
return c.text('test')
})
mut sub2 := hono.Hono.new()
sub2.use(fn (mut c hono.Context, next fn (mut hono.Context) http.Response) http.Response {
return next(mut c)
})
sub2.get('/test', fn (mut c hono.Context) http.Response {
return c.text('test')
})
mut sub3 := hono.Hono.new()
sub3.use(fn (mut c hono.Context, next fn (mut hono.Context) http.Response) http.Response {
return next(mut c)
})
sub3.get('/test', fn (mut c hono.Context) http.Response {
return c.text('test')
})
// 按不同顺序挂载(长前缀先挂载)
app.route('/api/v1/users', mut sub1)
app.route('/api', mut sub2)
app.route('/api/v1', mut sub3)
// 预计算
app.precompute_middleware_prefixes()
// 验证排序:短前缀应该在前面
if app.sorted_middleware_prefixes.len >= 2 {
for i in 0 .. app.sorted_middleware_prefixes.len - 1 {
if app.sorted_middleware_prefixes[i].len > app.sorted_middleware_prefixes[i + 1].len {
stats.fail_test('前缀排序不正确:${app.sorted_middleware_prefixes[i]} 应该在 ${app.sorted_middleware_prefixes[i + 1]} 之后')
return
}
}
}
stats.pass_test()
}
fn main() {
println('🚀 开始中间件优化测试...\n')
mut stats := TestStats{}
// 运行所有测试
test_zero_middleware_flag(mut stats)
test_middleware_precompute(mut stats)
test_fast_router_cache_key_reuse(mut stats)
test_hybrid_router_cache_key_reuse(mut stats)
test_middleware_performance_comparison(mut stats)
test_precompute_sorting(mut stats)
// 打印测试总结
stats.print_summary()
}