package app import ( "context" "testing" "github.com/alicebob/miniredis/v2" "github.com/gochat/gochat/internal/config" "github.com/redis/go-redis/v9" ) // TestParseRedisURL 测试 parseRedisURL 函数的各种 URL 格式解析 func TestParseRedisURL(t *testing.T) { tests := []struct { name string // 测试用例名称 urlStr string // 输入的 Redis URL wantErr bool // 是否期望返回错误 want *redis.Options // 期望的解析结果(仅检查关键字段) }{ { name: "标准Redis URL - host:port/db", urlStr: "redis://localhost:6379/0", wantErr: false, want: &redis.Options{ Addr: "localhost:6379", DB: 0, }, }, { name: "带用户名密码的Redis URL", urlStr: "redis://user:password@localhost:6379/1", wantErr: false, want: &redis.Options{ Addr: "localhost:6379", DB: 1, Username: "user", Password: "password", }, }, { name: "仅密码的Redis URL", urlStr: "redis://:secretpass@localhost:6379/2", wantErr: false, want: &redis.Options{ Addr: "localhost:6379", DB: 2, Password: "secretpass", }, }, { name: "简单host:port格式 - 回退解析", urlStr: "redis://myhost:6380", wantErr: false, want: &redis.Options{ Addr: "myhost:6380", }, }, { name: "无效URL - 空字符串", urlStr: "", wantErr: true, }, { name: "无效URL - 非Redis协议", urlStr: "http://localhost:6379", wantErr: true, }, { name: "无效URL - 端口不是数字", urlStr: "redis://localhost:abc", wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { opts, err := parseRedisURL(tt.urlStr) if tt.wantErr { if err == nil { t.Errorf("parseRedisURL(%q) 期望返回错误,但未返回错误", tt.urlStr) } return } if err != nil { t.Errorf("parseRedisURL(%q) 未期望返回错误,但返回了: %v", tt.urlStr, err) return } // 检查关键字段 if opts.Addr != tt.want.Addr { t.Errorf("parseRedisURL(%q) Addr = %q, 期望 %q", tt.urlStr, opts.Addr, tt.want.Addr) } if opts.DB != tt.want.DB { t.Errorf("parseRedisURL(%q) DB = %d, 期望 %d", tt.urlStr, opts.DB, tt.want.DB) } if tt.want.Username != "" && opts.Username != tt.want.Username { t.Errorf("parseRedisURL(%q) Username = %q, 期望 %q", tt.urlStr, opts.Username, tt.want.Username) } if tt.want.Password != "" && opts.Password != tt.want.Password { t.Errorf("parseRedisURL(%q) Password = %q, 期望 %q", tt.urlStr, opts.Password, tt.want.Password) } }) } } // TestNewRedisClient_Success 使用 miniredis 模拟 Redis 服务,测试成功连接 func TestNewRedisClient_Success(t *testing.T) { // 启动 miniredis 模拟服务器 mr, err := miniredis.Run() if err != nil { t.Fatalf("启动 miniredis 失败: %v", err) } defer mr.Close() cfg := &config.RedisConfig{ URL: "redis://" + mr.Addr(), } client, err := NewRedisClient(cfg) if err != nil { t.Fatalf("NewRedisClient 返回了意外的错误: %v", err) } defer client.Close() // 验证客户端可以正常执行命令 ctx := context.Background() val, err := client.Set(ctx, "test_key", "test_value", 0).Result() if err != nil { t.Errorf("Set 命令执行失败: %v", err) } if val != "OK" { t.Errorf("Set 返回值 = %q, 期望 %q", val, "OK") } got, err := client.Get(ctx, "test_key").Result() if err != nil { t.Errorf("Get 命令执行失败: %v", err) } if got != "test_value" { t.Errorf("Get 返回值 = %q, 期望 %q", got, "test_value") } } // TestNewRedisClient_SuccessWithPassword 使用 miniredis 测试带密码的成功连接 func TestNewRedisClient_SuccessWithPassword(t *testing.T) { mr, err := miniredis.Run() if err != nil { t.Fatalf("启动 miniredis 失败: %v", err) } defer mr.Close() // 设置 miniredis 密码 mr.RequireAuth("testpassword") cfg := &config.RedisConfig{ URL: "redis://" + mr.Addr(), Password: "testpassword", } client, err := NewRedisClient(cfg) if err != nil { t.Fatalf("NewRedisClient 返回了意外的错误: %v", err) } defer client.Close() // 验证客户端可以正常执行命令 ctx := context.Background() result, err := client.Ping(ctx).Result() if err != nil { t.Errorf("Ping 命令执行失败: %v", err) } if result != "PONG" { t.Errorf("Ping 返回值 = %q, 期望 %q", result, "PONG") } } // TestNewRedisClient_InvalidURL 测试无效 URL 时 NewRedisClient 返回错误 func TestNewRedisClient_InvalidURL(t *testing.T) { tests := []struct { name string url string }{ { name: "空字符串URL", url: "", }, { name: "缺少端口的URL", url: "redis://localhost", }, { name: "端口格式错误", url: "redis://localhost:notaport", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { cfg := &config.RedisConfig{ URL: tt.url, } client, err := NewRedisClient(cfg) if err == nil { client.Close() t.Errorf("NewRedisClient(url=%q) 期望返回错误,但未返回错误", tt.url) } }) } } // TestNewRedisClient_ConnectionFailure 测试连接到不可达的 Redis 服务器时返回错误 func TestNewRedisClient_ConnectionFailure(t *testing.T) { cfg := &config.RedisConfig{ // 使用一个不会存在的端口,确保连接失败 URL: "redis://127.0.0.1:19999", } client, err := NewRedisClient(cfg) if err == nil { client.Close() t.Errorf("NewRedisClient 期望连接失败返回错误,但未返回错误") } // 期望包含 "failed to connect to Redis" 的错误信息 if err != nil && client != nil { t.Logf("连接失败返回了预期的错误: %v", err) } }