lolly/internal/middleware/compression/compression_test.go
xfy f2352ab9cc docs(config,stream,logging,handler,proxy,cache,server,ssl,middleware): 为核心模块添加详细 GoDoc 文档注释
- config: 为 Config 和所有子配置结构添加完整文档,包含使用示例和注意事项
- stream: 为负载均衡器和服务器添加详细的参数、返回值和功能说明
- logging: 为日志格式化和输出函数添加文档,说明支持的变量替换
- handler: 为路由器、静态文件和 sendfile 处理器添加文档
- proxy: 为健康检查器和代理功能添加完整文档
- cache/server/ssl/middleware: 补充相关模块的文档注释
- config.example.yaml: 添加可信代理配置、加密套件示例,更新压缩级别说明

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-07 15:36:09 +08:00

331 lines
7.2 KiB
Go
Raw 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.

// Package compression 提供压缩功能的测试。
//
// 该文件测试压缩中间件模块的各项功能,包括:
// - 压缩中间件创建
// - gzip 和 brotli 压缩
// - 可压缩类型检查
// - 压缩级别配置
// - 响应处理
//
// 作者xfy
package compression
import (
"bytes"
"testing"
"github.com/valyala/fasthttp"
"rua.plus/lolly/internal/config"
)
func TestNew(t *testing.T) {
tests := []struct {
name string
cfg *config.CompressionConfig
}{
{
name: "nil config uses defaults",
cfg: nil,
},
{
name: "gzip config",
cfg: &config.CompressionConfig{
Type: "gzip",
Level: 6,
},
},
{
name: "brotli config",
cfg: &config.CompressionConfig{
Type: "brotli",
Level: 4,
},
},
{
name: "both config",
cfg: &config.CompressionConfig{
Type: "both",
Level: 6,
},
},
{
name: "custom types",
cfg: &config.CompressionConfig{
Type: "gzip",
Types: []string{"text/html", "application/json"},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
m, err := New(tt.cfg)
if err != nil {
t.Errorf("New() error: %v", err)
}
if m == nil {
t.Error("Expected non-nil middleware")
}
})
}
}
func TestDefaultCompressibleTypes(t *testing.T) {
types := defaultCompressibleTypes()
if len(types) == 0 {
t.Error("Expected non-empty default types")
}
// 检查关键类型
expected := []string{"text/html", "text/css", "application/json"}
for _, e := range expected {
found := false
for _, t := range types {
if t == e {
found = true
break
}
}
if !found {
t.Errorf("Expected type %s in default list", e)
}
}
}
func TestIsCompressible(t *testing.T) {
m, _ := New(&config.CompressionConfig{
Types: []string{"text/html", "text/*", "application/json"},
})
tests := []struct {
contentType string
expected bool
}{
{"text/html", true},
{"text/html; charset=utf-8", true},
{"text/css", true},
{"text/plain", true},
{"application/json", true},
{"application/json; charset=utf-8", true},
{"image/png", false},
{"application/octet-stream", false},
{"", false},
}
for _, tt := range tests {
t.Run(tt.contentType, func(t *testing.T) {
result := m.isCompressible(tt.contentType)
if result != tt.expected {
t.Errorf("isCompressible(%s) = %v, expected %v", tt.contentType, result, tt.expected)
}
})
}
}
func TestCompressGzip(t *testing.T) {
m, _ := New(&config.CompressionConfig{
Type: "gzip",
Level: 6,
})
// 测试数据
data := []byte("Hello, World! This is a test string that should be compressed.")
compressed := m.compressGzip(data)
if len(compressed) == 0 {
t.Error("Expected compressed data")
}
// 压缩后应该更小(对于重复文本)
if len(compressed) >= len(data) {
t.Logf("Warning: compressed size %d >= original %d", len(compressed), len(data))
}
}
func TestCompressBrotli(t *testing.T) {
m, _ := New(&config.CompressionConfig{
Type: "brotli",
Level: 4,
})
data := []byte("Hello, World! This is a test string that should be compressed with brotli.")
compressed := m.compressBrotli(data)
if len(compressed) == 0 {
t.Error("Expected compressed data")
}
}
func TestProcessNoCompression(t *testing.T) {
m, _ := New(&config.CompressionConfig{
Type: "gzip",
MinSize: 1000, // 大阈值
})
nextHandler := func(ctx *fasthttp.RequestCtx) {
ctx.Response.Header.Set("Content-Type", "text/html")
_, _ = ctx.WriteString("Short response")
}
handler := m.Process(nextHandler)
ctx := &fasthttp.RequestCtx{}
ctx.Request.Header.Set("Accept-Encoding", "gzip")
handler(ctx)
// 响应太短,不应压缩
encoding := ctx.Response.Header.Peek("Content-Encoding")
if string(encoding) != "" {
t.Errorf("Expected no Content-Encoding, got %s", encoding)
}
body := string(ctx.Response.Body())
if body != "Short response" {
t.Errorf("Expected 'Short response', got %s", body)
}
}
func TestProcessWithGzip(t *testing.T) {
m, _ := New(&config.CompressionConfig{
Type: "gzip",
Level: 6,
MinSize: 10, // 小阈值
Types: []string{"text/html"},
})
// 创建足够长的响应
longResponse := bytes.Repeat([]byte("Hello World! "), 100) // 1300+ bytes
nextHandler := func(ctx *fasthttp.RequestCtx) {
ctx.Response.Header.Set("Content-Type", "text/html")
_, _ = ctx.Write(longResponse)
}
handler := m.Process(nextHandler)
ctx := &fasthttp.RequestCtx{}
ctx.Request.Header.Set("Accept-Encoding", "gzip")
handler(ctx)
encoding := ctx.Response.Header.Peek("Content-Encoding")
if string(encoding) != "gzip" {
t.Errorf("Expected Content-Encoding 'gzip', got %s", encoding)
}
// 响应应该被压缩(更小)
body := ctx.Response.Body()
if len(body) >= len(longResponse) {
t.Errorf("Expected compressed body smaller than original, got %d >= %d", len(body), len(longResponse))
}
}
func TestProcessWithBrotli(t *testing.T) {
m, _ := New(&config.CompressionConfig{
Type: "brotli",
Level: 4,
MinSize: 10,
Types: []string{"text/html"},
})
longResponse := bytes.Repeat([]byte("Hello World! "), 100)
nextHandler := func(ctx *fasthttp.RequestCtx) {
ctx.Response.Header.Set("Content-Type", "text/html")
_, _ = ctx.Write(longResponse)
}
handler := m.Process(nextHandler)
ctx := &fasthttp.RequestCtx{}
ctx.Request.Header.Set("Accept-Encoding", "br")
handler(ctx)
encoding := ctx.Response.Header.Peek("Content-Encoding")
if string(encoding) != "br" {
t.Errorf("Expected Content-Encoding 'br', got %s", encoding)
}
}
func TestProcessUnsupportedEncoding(t *testing.T) {
m, _ := New(&config.CompressionConfig{
Type: "gzip",
Level: 6,
})
nextHandler := func(ctx *fasthttp.RequestCtx) {
ctx.Response.Header.Set("Content-Type", "text/html")
_, _ = ctx.WriteString("Test response")
}
handler := m.Process(nextHandler)
ctx := &fasthttp.RequestCtx{}
// 不设置 Accept-Encoding 或设置为空
ctx.Request.Header.Set("Accept-Encoding", "")
handler(ctx)
// 不应压缩
encoding := ctx.Response.Header.Peek("Content-Encoding")
if string(encoding) != "" {
t.Errorf("Expected no Content-Encoding, got %s", encoding)
}
}
func TestProcessNonCompressibleType(t *testing.T) {
m, _ := New(&config.CompressionConfig{
Type: "gzip",
MinSize: 10,
})
longResponse := bytes.Repeat([]byte("data"), 1000)
nextHandler := func(ctx *fasthttp.RequestCtx) {
ctx.Response.Header.Set("Content-Type", "image/png")
_, _ = ctx.Write(longResponse)
}
handler := m.Process(nextHandler)
ctx := &fasthttp.RequestCtx{}
ctx.Request.Header.Set("Accept-Encoding", "gzip")
handler(ctx)
// 图片不应压缩
encoding := ctx.Response.Header.Peek("Content-Encoding")
if string(encoding) != "" {
t.Errorf("Expected no Content-Encoding for image, got %s", encoding)
}
}
func TestName(t *testing.T) {
m, _ := New(nil)
if m.Name() != "compression" {
t.Errorf("Expected name 'compression', got %s", m.Name())
}
}
func TestGetters(t *testing.T) {
cfg := &config.CompressionConfig{
Type: "gzip",
Level: 5,
MinSize: 500,
Types: []string{"text/html"},
}
m, _ := New(cfg)
if m.Level() != 5 {
t.Errorf("Expected Level 5, got %d", m.Level())
}
if m.MinSize() != 500 {
t.Errorf("Expected MinSize 500, got %d", m.MinSize())
}
if len(m.Types()) != 1 {
t.Errorf("Expected 1 type, got %d", len(m.Types()))
}
}