Apply modern Go patterns across the codebase:
- Replace `interface{}` with `any` (Go 1.18+)
- Use `for range n` instead of `for i := 0; i < n; i++` (Go 1.22+)
- Replace `sort.Slice` with `slices.Sort` from slices package
- Simplify sync.WaitGroup patterns with errgroup where appropriate
- Add Makefile targets for modernize analyzer
Total: 84 files updated, net reduction of 79 lines
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
668 lines
16 KiB
Go
668 lines
16 KiB
Go
// Package handler 提供错误页面管理器功能的测试。
|
||
//
|
||
// 该文件测试错误页面管理模块的各项功能,包括:
|
||
// - 管理器构造函数
|
||
// - 空配置处理
|
||
// - 部分加载失败处理
|
||
// - 全部加载失败处理
|
||
// - 错误页面查找
|
||
// - 默认页面 fallback
|
||
// - 状态码检查
|
||
// - 响应码覆盖
|
||
// - 配置状态检查
|
||
//
|
||
// 作者:xfy
|
||
package handler
|
||
|
||
import (
|
||
"fmt"
|
||
"os"
|
||
"path/filepath"
|
||
"strings"
|
||
"testing"
|
||
|
||
"rua.plus/lolly/internal/config"
|
||
)
|
||
|
||
// TestNewErrorPageManager_EmptyConfig 测试空配置情况
|
||
func TestNewErrorPageManager_EmptyConfig(t *testing.T) {
|
||
tests := []struct {
|
||
want *ErrorPageManager
|
||
name string
|
||
cfg config.ErrorPageConfig
|
||
}{
|
||
{
|
||
name: "完全空配置",
|
||
cfg: config.ErrorPageConfig{},
|
||
},
|
||
{
|
||
name: "空的 pages 和 default",
|
||
cfg: config.ErrorPageConfig{
|
||
Pages: map[int]string{},
|
||
Default: "",
|
||
},
|
||
},
|
||
{
|
||
name: "设置了 responseCode 但无页面",
|
||
cfg: config.ErrorPageConfig{
|
||
Pages: map[int]string{},
|
||
Default: "",
|
||
ResponseCode: 200,
|
||
},
|
||
},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
manager, err := NewErrorPageManager(&tt.cfg)
|
||
if err != nil {
|
||
t.Errorf("NewErrorPageManager() 不应返回错误, got %v", err)
|
||
}
|
||
if manager == nil {
|
||
t.Fatal("NewErrorPageManager() 返回 nil")
|
||
}
|
||
if manager.IsConfigured() {
|
||
t.Error("IsConfigured() 应返回 false")
|
||
}
|
||
if manager.GetResponseCode() != tt.cfg.ResponseCode {
|
||
t.Errorf("GetResponseCode() = %d, want %d", manager.GetResponseCode(), tt.cfg.ResponseCode)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestNewErrorPageManager_PartialLoadFailure 测试部分加载失败
|
||
func TestNewErrorPageManager_PartialLoadFailure(t *testing.T) {
|
||
tmpDir := t.TempDir()
|
||
|
||
// 创建有效的错误页面文件
|
||
validPage := filepath.Join(tmpDir, "404.html")
|
||
if err := os.WriteFile(validPage, []byte("404 page"), 0o644); err != nil {
|
||
t.Fatalf("创建测试文件失败: %v", err)
|
||
}
|
||
|
||
// 不存在的文件路径
|
||
nonExistent := filepath.Join(tmpDir, "nonexistent", "500.html")
|
||
|
||
tests := []struct {
|
||
wantPages map[int]bool
|
||
name string
|
||
cfg config.ErrorPageConfig
|
||
wantConfigured bool
|
||
wantPartialErr bool
|
||
}{
|
||
{
|
||
name: "一个成功一个失败",
|
||
cfg: config.ErrorPageConfig{
|
||
Pages: map[int]string{
|
||
404: validPage,
|
||
500: nonExistent,
|
||
},
|
||
},
|
||
wantConfigured: true,
|
||
wantPages: map[int]bool{404: true},
|
||
wantPartialErr: true,
|
||
},
|
||
{
|
||
name: "特定页面成功默认失败",
|
||
cfg: config.ErrorPageConfig{
|
||
Pages: map[int]string{
|
||
404: validPage,
|
||
},
|
||
Default: filepath.Join(tmpDir, "default.html"),
|
||
},
|
||
wantConfigured: true,
|
||
wantPages: map[int]bool{404: true},
|
||
wantPartialErr: true,
|
||
},
|
||
{
|
||
name: "默认成功特定页面失败",
|
||
cfg: config.ErrorPageConfig{
|
||
Pages: map[int]string{
|
||
404: nonExistent,
|
||
},
|
||
Default: validPage,
|
||
},
|
||
wantConfigured: true,
|
||
wantPages: map[int]bool{},
|
||
wantPartialErr: true,
|
||
},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
manager, err := NewErrorPageManager(&tt.cfg)
|
||
|
||
// 检查是否为部分错误
|
||
if tt.wantPartialErr {
|
||
if err == nil {
|
||
t.Error("期望返回部分加载错误,但 got nil")
|
||
return
|
||
}
|
||
if _, ok := err.(*PartialLoadError); !ok {
|
||
t.Errorf("期望返回 *PartialLoadError,但 got %T", err)
|
||
return
|
||
}
|
||
} else if err != nil {
|
||
t.Errorf("NewErrorPageManager() 不应返回错误, got %v", err)
|
||
return
|
||
}
|
||
|
||
if manager == nil {
|
||
t.Fatal("NewErrorPageManager() 返回 nil")
|
||
}
|
||
|
||
if got := manager.IsConfigured(); got != tt.wantConfigured {
|
||
t.Errorf("IsConfigured() = %v, want %v", got, tt.wantConfigured)
|
||
}
|
||
|
||
// 检查特定页面是否存在
|
||
for code, shouldExist := range tt.wantPages {
|
||
if got := manager.HasPage(code); got != shouldExist {
|
||
t.Errorf("HasPage(%d) = %v, want %v", code, got, shouldExist)
|
||
}
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestNewErrorPageManager_AllLoadFailure 测试全部加载失败
|
||
func TestNewErrorPageManager_AllLoadFailure(t *testing.T) {
|
||
tmpDir := t.TempDir()
|
||
|
||
// 不存在的文件路径
|
||
nonExistent1 := filepath.Join(tmpDir, "nonexistent1.html")
|
||
nonExistent2 := filepath.Join(tmpDir, "nonexistent2.html")
|
||
|
||
tests := []struct {
|
||
name string
|
||
cfg config.ErrorPageConfig
|
||
wantErr bool
|
||
}{
|
||
{
|
||
name: "单个页面加载失败",
|
||
cfg: config.ErrorPageConfig{
|
||
Pages: map[int]string{
|
||
404: nonExistent1,
|
||
},
|
||
},
|
||
wantErr: true,
|
||
},
|
||
{
|
||
name: "多个页面全部加载失败",
|
||
cfg: config.ErrorPageConfig{
|
||
Pages: map[int]string{
|
||
404: nonExistent1,
|
||
500: nonExistent2,
|
||
},
|
||
},
|
||
wantErr: true,
|
||
},
|
||
{
|
||
name: "默认页面加载失败",
|
||
cfg: config.ErrorPageConfig{
|
||
Default: nonExistent1,
|
||
},
|
||
wantErr: true,
|
||
},
|
||
{
|
||
name: "页面和默认都加载失败",
|
||
cfg: config.ErrorPageConfig{
|
||
Pages: map[int]string{
|
||
404: nonExistent1,
|
||
},
|
||
Default: nonExistent2,
|
||
},
|
||
wantErr: true,
|
||
},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
manager, err := NewErrorPageManager(&tt.cfg)
|
||
if tt.wantErr {
|
||
if err == nil {
|
||
t.Error("期望返回错误,但 got nil")
|
||
}
|
||
// 全部失败时不应返回 PartialLoadError
|
||
if _, ok := err.(*PartialLoadError); ok {
|
||
t.Error("全部失败时不应返回 *PartialLoadError")
|
||
}
|
||
if manager != nil {
|
||
t.Error("全部失败时 manager 应为 nil")
|
||
}
|
||
} else {
|
||
if err != nil {
|
||
t.Errorf("NewErrorPageManager() 不应返回错误, got %v", err)
|
||
}
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestPartialLoadError_Error 测试错误消息格式
|
||
func TestPartialLoadError_Error(t *testing.T) {
|
||
tests := []struct {
|
||
name string
|
||
errors map[int]error
|
||
want string
|
||
}{
|
||
{
|
||
name: "单个错误",
|
||
errors: map[int]error{404: os.ErrNotExist},
|
||
want: "部分错误页面加载失败: 1 个错误",
|
||
},
|
||
{
|
||
name: "多个错误",
|
||
errors: map[int]error{404: os.ErrNotExist, 500: os.ErrPermission},
|
||
want: "部分错误页面加载失败: 2 个错误",
|
||
},
|
||
{
|
||
name: "包含默认页面错误",
|
||
errors: map[int]error{0: os.ErrNotExist, 404: os.ErrNotExist},
|
||
want: "部分错误页面加载失败: 2 个错误",
|
||
},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
err := &PartialLoadError{Errors: tt.errors}
|
||
got := err.Error()
|
||
if !strings.Contains(got, tt.want) {
|
||
t.Errorf("Error() = %q, want contain %q", got, tt.want)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestErrorPageManager_GetPage 测试获取错误页面
|
||
func TestErrorPageManager_GetPage(t *testing.T) {
|
||
tmpDir := t.TempDir()
|
||
|
||
// 创建测试页面文件
|
||
page404 := filepath.Join(tmpDir, "404.html")
|
||
page500 := filepath.Join(tmpDir, "500.html")
|
||
pageDefault := filepath.Join(tmpDir, "default.html")
|
||
|
||
if err := os.WriteFile(page404, []byte("404 page content"), 0o644); err != nil {
|
||
t.Fatalf("创建 404 页面失败: %v", err)
|
||
}
|
||
if err := os.WriteFile(page500, []byte("500 page content"), 0o644); err != nil {
|
||
t.Fatalf("创建 500 页面失败: %v", err)
|
||
}
|
||
if err := os.WriteFile(pageDefault, []byte("default page content"), 0o644); err != nil {
|
||
t.Fatalf("创建默认页面失败: %v", err)
|
||
}
|
||
|
||
tests := []struct {
|
||
name string
|
||
wantContent string
|
||
cfg config.ErrorPageConfig
|
||
requestCode int
|
||
wantResponseCode int
|
||
wantFound bool
|
||
}{
|
||
{
|
||
name: "找到特定状态码页面",
|
||
cfg: config.ErrorPageConfig{
|
||
Pages: map[int]string{
|
||
404: page404,
|
||
500: page500,
|
||
},
|
||
},
|
||
requestCode: 404,
|
||
wantContent: "404 page content",
|
||
wantFound: true,
|
||
wantResponseCode: 404,
|
||
},
|
||
{
|
||
name: "找到另一个状态码页面",
|
||
cfg: config.ErrorPageConfig{
|
||
Pages: map[int]string{
|
||
404: page404,
|
||
500: page500,
|
||
},
|
||
},
|
||
requestCode: 500,
|
||
wantContent: "500 page content",
|
||
wantFound: true,
|
||
wantResponseCode: 500,
|
||
},
|
||
{
|
||
name: "未找到特定页面,使用默认页面",
|
||
cfg: config.ErrorPageConfig{
|
||
Pages: map[int]string{
|
||
404: page404,
|
||
},
|
||
Default: pageDefault,
|
||
},
|
||
requestCode: 500,
|
||
wantContent: "default page content",
|
||
wantFound: true,
|
||
wantResponseCode: 500,
|
||
},
|
||
{
|
||
name: "未找到页面且无默认页面",
|
||
cfg: config.ErrorPageConfig{
|
||
Pages: map[int]string{
|
||
404: page404,
|
||
},
|
||
},
|
||
requestCode: 500,
|
||
wantContent: "",
|
||
wantFound: false,
|
||
wantResponseCode: 500,
|
||
},
|
||
{
|
||
name: "有默认页面但请求特定页面",
|
||
cfg: config.ErrorPageConfig{
|
||
Default: pageDefault,
|
||
},
|
||
requestCode: 503,
|
||
wantContent: "default page content",
|
||
wantFound: true,
|
||
wantResponseCode: 503,
|
||
},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
manager, err := NewErrorPageManager(&tt.cfg)
|
||
if err != nil {
|
||
t.Fatalf("NewErrorPageManager() 失败: %v", err)
|
||
}
|
||
|
||
content, found, responseCode := manager.GetPage(tt.requestCode)
|
||
|
||
if found != tt.wantFound {
|
||
t.Errorf("GetPage() found = %v, want %v", found, tt.wantFound)
|
||
}
|
||
if string(content) != tt.wantContent {
|
||
t.Errorf("GetPage() content = %q, want %q", string(content), tt.wantContent)
|
||
}
|
||
if responseCode != tt.wantResponseCode {
|
||
t.Errorf("GetPage() responseCode = %d, want %d", responseCode, tt.wantResponseCode)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestErrorPageManager_GetPage_WithResponseCodeOverride 测试响应码覆盖
|
||
func TestErrorPageManager_GetPage_WithResponseCodeOverride(t *testing.T) {
|
||
tmpDir := t.TempDir()
|
||
|
||
page404 := filepath.Join(tmpDir, "404.html")
|
||
if err := os.WriteFile(page404, []byte("404 page"), 0o644); err != nil {
|
||
t.Fatalf("创建测试文件失败: %v", err)
|
||
}
|
||
|
||
cfg := config.ErrorPageConfig{
|
||
Pages: map[int]string{
|
||
404: page404,
|
||
},
|
||
ResponseCode: 200, // 覆盖响应码
|
||
}
|
||
|
||
manager, err := NewErrorPageManager(&cfg)
|
||
if err != nil {
|
||
t.Fatalf("NewErrorPageManager() 失败: %v", err)
|
||
}
|
||
|
||
_, _, responseCode := manager.GetPage(404)
|
||
if responseCode != 200 {
|
||
t.Errorf("GetPage() responseCode = %d, want 200", responseCode)
|
||
}
|
||
|
||
// 测试默认页面也受覆盖影响
|
||
cfg2 := config.ErrorPageConfig{
|
||
Default: page404,
|
||
ResponseCode: 418,
|
||
}
|
||
|
||
manager2, err := NewErrorPageManager(&cfg2)
|
||
if err != nil {
|
||
t.Fatalf("NewErrorPageManager() 失败: %v", err)
|
||
}
|
||
|
||
_, _, responseCode2 := manager2.GetPage(500)
|
||
if responseCode2 != 418 {
|
||
t.Errorf("GetPage() responseCode = %d, want 418", responseCode2)
|
||
}
|
||
}
|
||
|
||
// TestErrorPageManager_HasPage 测试页面存在检查
|
||
func TestErrorPageManager_HasPage(t *testing.T) {
|
||
tmpDir := t.TempDir()
|
||
|
||
page404 := filepath.Join(tmpDir, "404.html")
|
||
pageDefault := filepath.Join(tmpDir, "default.html")
|
||
|
||
if err := os.WriteFile(page404, []byte("404"), 0o644); err != nil {
|
||
t.Fatalf("创建测试文件失败: %v", err)
|
||
}
|
||
if err := os.WriteFile(pageDefault, []byte("default"), 0o644); err != nil {
|
||
t.Fatalf("创建测试文件失败: %v", err)
|
||
}
|
||
|
||
tests := []struct {
|
||
name string
|
||
cfg config.ErrorPageConfig
|
||
code int
|
||
expected bool
|
||
}{
|
||
{
|
||
name: "配置的特定页面存在",
|
||
cfg: config.ErrorPageConfig{
|
||
Pages: map[int]string{404: page404},
|
||
},
|
||
code: 404,
|
||
expected: true,
|
||
},
|
||
{
|
||
name: "未配置的页面但有默认页面",
|
||
cfg: config.ErrorPageConfig{
|
||
Pages: map[int]string{404: page404},
|
||
Default: pageDefault,
|
||
},
|
||
code: 500,
|
||
expected: true, // 因为有默认页面
|
||
},
|
||
{
|
||
name: "只有默认页面",
|
||
cfg: config.ErrorPageConfig{
|
||
Default: pageDefault,
|
||
},
|
||
code: 404,
|
||
expected: true,
|
||
},
|
||
{
|
||
name: "页面不存在且无默认页面",
|
||
cfg: config.ErrorPageConfig{
|
||
Pages: map[int]string{404: page404},
|
||
},
|
||
code: 500,
|
||
expected: false,
|
||
},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
manager, err := NewErrorPageManager(&tt.cfg)
|
||
if err != nil {
|
||
t.Fatalf("NewErrorPageManager() 失败: %v", err)
|
||
}
|
||
|
||
if got := manager.HasPage(tt.code); got != tt.expected {
|
||
t.Errorf("HasPage(%d) = %v, want %v", tt.code, got, tt.expected)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestErrorPageManager_GetResponseCode 测试获取响应码覆盖值
|
||
func TestErrorPageManager_GetResponseCode(t *testing.T) {
|
||
tests := []struct {
|
||
name string
|
||
code int
|
||
want int
|
||
}{
|
||
{
|
||
name: "无覆盖",
|
||
code: 0,
|
||
want: 0,
|
||
},
|
||
{
|
||
name: "覆盖为 200",
|
||
code: 200,
|
||
want: 200,
|
||
},
|
||
{
|
||
name: "覆盖为 418",
|
||
code: 418,
|
||
want: 418,
|
||
},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
cfg := config.ErrorPageConfig{
|
||
ResponseCode: tt.code,
|
||
}
|
||
manager, err := NewErrorPageManager(&cfg)
|
||
if err != nil {
|
||
t.Fatalf("NewErrorPageManager() 失败: %v", err)
|
||
}
|
||
|
||
if got := manager.GetResponseCode(); got != tt.want {
|
||
t.Errorf("GetResponseCode() = %d, want %d", got, tt.want)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestErrorPageManager_IsConfigured 测试配置状态检查
|
||
func TestErrorPageManager_IsConfigured(t *testing.T) {
|
||
tmpDir := t.TempDir()
|
||
|
||
page404 := filepath.Join(tmpDir, "404.html")
|
||
pageDefault := filepath.Join(tmpDir, "default.html")
|
||
|
||
if err := os.WriteFile(page404, []byte("404"), 0o644); err != nil {
|
||
t.Fatalf("创建测试文件失败: %v", err)
|
||
}
|
||
if err := os.WriteFile(pageDefault, []byte("default"), 0o644); err != nil {
|
||
t.Fatalf("创建测试文件失败: %v", err)
|
||
}
|
||
|
||
tests := []struct {
|
||
name string
|
||
cfg config.ErrorPageConfig
|
||
expected bool
|
||
}{
|
||
{
|
||
name: "空配置",
|
||
cfg: config.ErrorPageConfig{},
|
||
expected: false,
|
||
},
|
||
{
|
||
name: "配置特定页面",
|
||
cfg: config.ErrorPageConfig{
|
||
Pages: map[int]string{404: page404},
|
||
},
|
||
expected: true,
|
||
},
|
||
{
|
||
name: "配置默认页面",
|
||
cfg: config.ErrorPageConfig{
|
||
Default: pageDefault,
|
||
},
|
||
expected: true,
|
||
},
|
||
{
|
||
name: "配置两者",
|
||
cfg: config.ErrorPageConfig{
|
||
Pages: map[int]string{404: page404},
|
||
Default: pageDefault,
|
||
},
|
||
expected: true,
|
||
},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
manager, err := NewErrorPageManager(&tt.cfg)
|
||
if err != nil {
|
||
t.Fatalf("NewErrorPageManager() 失败: %v", err)
|
||
}
|
||
|
||
if got := manager.IsConfigured(); got != tt.expected {
|
||
t.Errorf("IsConfigured() = %v, want %v", got, tt.expected)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestErrorPageManager_SuccessfulLoad 测试成功加载场景
|
||
func TestErrorPageManager_SuccessfulLoad(t *testing.T) {
|
||
tmpDir := t.TempDir()
|
||
|
||
// 创建多个测试页面
|
||
pages := map[int]string{
|
||
404: filepath.Join(tmpDir, "404.html"),
|
||
500: filepath.Join(tmpDir, "500.html"),
|
||
403: filepath.Join(tmpDir, "403.html"),
|
||
}
|
||
defaultPage := filepath.Join(tmpDir, "default.html")
|
||
|
||
for code, path := range pages {
|
||
content := fmt.Appendf(nil, "Error %d page", code)
|
||
if err := os.WriteFile(path, content, 0o644); err != nil {
|
||
t.Fatalf("创建页面 %d 失败: %v", code, err)
|
||
}
|
||
}
|
||
if err := os.WriteFile(defaultPage, []byte("Default error page"), 0o644); err != nil {
|
||
t.Fatalf("创建默认页面失败: %v", err)
|
||
}
|
||
|
||
cfg := config.ErrorPageConfig{
|
||
Pages: map[int]string{
|
||
404: pages[404],
|
||
500: pages[500],
|
||
403: pages[403],
|
||
},
|
||
Default: defaultPage,
|
||
}
|
||
|
||
manager, err := NewErrorPageManager(&cfg)
|
||
if err != nil {
|
||
t.Fatalf("NewErrorPageManager() 失败: %v", err)
|
||
}
|
||
|
||
// 验证所有页面都能正常访问
|
||
for code := range pages {
|
||
content, found, responseCode := manager.GetPage(code)
|
||
if !found {
|
||
t.Errorf("GetPage(%d) 应返回 found=true", code)
|
||
}
|
||
if responseCode != code {
|
||
t.Errorf("GetPage(%d) responseCode = %d, want %d", code, responseCode, code)
|
||
}
|
||
wantContent := fmt.Sprintf("Error %d page", code)
|
||
if string(content) != wantContent {
|
||
t.Errorf("GetPage(%d) content = %q, want %q", code, string(content), wantContent)
|
||
}
|
||
}
|
||
|
||
// 验证未配置的状态码返回默认页面
|
||
content, found, responseCode := manager.GetPage(503)
|
||
if !found {
|
||
t.Error("GetPage(503) 应返回 found=true (默认页面)")
|
||
}
|
||
if responseCode != 503 {
|
||
t.Errorf("GetPage(503) responseCode = %d, want 503", responseCode)
|
||
}
|
||
if string(content) != "Default error page" {
|
||
t.Errorf("GetPage(503) content = %q, want default page", string(content))
|
||
}
|
||
}
|