diff --git a/README.md b/README.md index 980a385..8097328 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,8 @@ - [ ] Limiter - [ ] Compress +业务代码和测试代码共用的控制变量可以优化为环境变量 + channel 优化: - 重新梳理逻辑流程,简化循环 - 端口分配时加锁 diff --git a/pkg/testutil/tools.go b/pkg/testutil/tools.go new file mode 100644 index 0000000..64fb3b6 --- /dev/null +++ b/pkg/testutil/tools.go @@ -0,0 +1,26 @@ +package testutil + +import ( + "reflect" + "sort" +) + +// SliceEqual 检查两个字符串切片是否完全相等(忽略顺序) +func SliceEqual(a, b []string) bool { + if len(a) != len(b) { + return false + } + + // 复制切片以避免修改原始数据 + aCopy := make([]string, len(a)) + bCopy := make([]string, len(b)) + copy(aCopy, a) + copy(bCopy, b) + + // 排序两个切片 + sort.Strings(aCopy) + sort.Strings(bCopy) + + // 比较排序后的切片 + return reflect.DeepEqual(aCopy, bCopy) +} diff --git a/web/services/channel.go b/web/services/channel.go index 64af8c6..ab0c85a 100644 --- a/web/services/channel.go +++ b/web/services/channel.go @@ -269,7 +269,8 @@ func (s *channelService) CreateChannel( Scan(&resource) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { - return ChannelServiceErr("套餐不存在") + // 禁止 id 猜测 + return ChannelServiceErr("无权限访问") } return err } @@ -417,7 +418,7 @@ func assignEdge(q *q.Query, count int, filter NodeFilterConfig) (*AssignEdgeResu for i, proxy := range proxies { proxyIds[i] = proxy.ID } - channels, err := q.Channel. + channels, err := q.Channel.Debug(). Select( q.Channel.ProxyID, q.Channel.ProxyPort). @@ -661,8 +662,6 @@ func assignPort( Omit( q.Channel.NodeID, q.Channel.NodeHost, - q.Channel.Username, - q.Channel.Password, q.Channel.DeletedAt, ). Save(channels...) diff --git a/web/services/channel_test.go b/web/services/channel_test.go index c29ed63..fda0568 100644 --- a/web/services/channel_test.go +++ b/web/services/channel_test.go @@ -7,7 +7,7 @@ import ( "platform/pkg/remote" "platform/pkg/testutil" "platform/web/models" - "slices" + "reflect" "strings" "testing" "time" @@ -271,6 +271,7 @@ func Test_channelService_CreateChannel(t *testing.T) { mr := testutil.SetupRedisTest(t) db := testutil.SetupDBTest(t) mc := testutil.SetupCloudClientMock(t) + mg := testutil.SetupGatewayClientMock(t) type args struct { ctx context.Context @@ -297,21 +298,6 @@ func Test_channelService_CreateChannel(t *testing.T) { {ID: 3, UserID: 101, Host: "789.789.789.789"}, } db.Create(whitelists) - var resource = &models.Resource{ - ID: 1, - UserID: 101, - Active: true, - } - db.Create(resource) - var resourcePss = &models.ResourcePss{ - ID: 1, - ResourceID: 1, - Type: 1, - Live: 180, - Expire: time.Now().AddDate(1, 0, 0), - DailyLimit: 10000, - } - db.Create(resourcePss) var proxy = &models.Proxy{ ID: 1, Version: 1, @@ -324,13 +310,32 @@ func Test_channelService_CreateChannel(t *testing.T) { mc.AutoQueryMock = func() (remote.CloudConnectResp, error) { return remote.CloudConnectResp{ "test-proxy": []remote.AutoConfig{ - {Province: "河南", City: "郑州", Isp: "电信", Count: 10}, + {Province: "河南省", Count: 10}, }, }, nil } var clearDb = func() { + + db.Exec("delete from resource where true") + var resource = &models.Resource{ + ID: 1, + UserID: 101, + Active: true, + } + db.Create(resource) + + db.Exec("delete from resource_pss where true") + var resourcePss = &models.ResourcePss{ + ID: 1, + ResourceID: 1, + Type: 1, + Live: 180, + Expire: time.Now().AddDate(1, 0, 0), + DailyLimit: 10000, + } + db.Create(resourcePss) + db.Exec("delete from channel where true") - db.Exec("update resource_pss set daily_used = 0, daily_last = null, used = 0 where true") } tests := []struct { name string @@ -349,16 +354,70 @@ func Test_channelService_CreateChannel(t *testing.T) { protocol: ProtocolHTTP, authType: ChannelAuthTypePass, count: 3, - nodeFilter: []NodeFilterConfig{{Prov: "河南", City: "郑州", Isp: "电信"}}, + nodeFilter: []NodeFilterConfig{{Prov: "北京市"}}, }, + setup: func() { + mr.FlushAll() + clearDb() - want: func(t *testing.T, got []*PortInfo) error { - // 验证返回结果 - if len(got) == 0 { - return fmt.Errorf("返回的 PortInfo 不应为空") + mc.ConnectMock = func(param remote.CloudConnectReq) error { + if param.Uuid != proxy.Name { + return fmt.Errorf("代理名称不符合预期: %s", param.Uuid) + } + if len(param.Edge) != 0 { + return fmt.Errorf("边缘节点不符合预期: %v", param.Edge) + } + if !reflect.DeepEqual(param.AutoConfig, []remote.AutoConfig{ + {Province: "河南省", Count: 10}, + {Province: "北京市", Count: 6}, + }) { + return fmt.Errorf("自动配置不符合预期: %v", param.AutoConfig) + } + return nil } - // 验证协议正确 + mg.PortConfigsMock = func(c *testutil.MockGatewayClient, params []remote.PortConfigsReq) error { + if c.Host != proxy.Host { + return fmt.Errorf("代理主机不符合预期: %s", c.Host) + } + if len(params) != 3 { + return fmt.Errorf("端口数量不符合预期: %d", len(params)) + } + for _, param := range params { + if param.Status != true { + return fmt.Errorf("端口状态不符合预期: %v", param.Status) + } + if param.AutoEdgeConfig == nil { + return fmt.Errorf("自动边缘节点配置不符合预期: %v", param.AutoEdgeConfig) + } + if param.Userpass == nil || *param.Userpass == "" { + return fmt.Errorf("用户名密码不符合预期: %v", param.Userpass) + } + if param.Whitelist == nil || len(*param.Whitelist) != 0 { + return fmt.Errorf("白名单不符合预期: %v", param.Whitelist) + } + config := param.AutoEdgeConfig + if config.Province != "北京市" { + return fmt.Errorf("自动边缘节点省份不符合预期: %s", param.AutoEdgeConfig.Province) + } + if *config.Count != 1 { + return fmt.Errorf("自动边缘节点数量不符合预期: %d", param.AutoEdgeConfig.Count) + } + if config.PacketLoss != 30 { + return fmt.Errorf("自动边缘节点丢包率不符合预期: %d", param.AutoEdgeConfig.PacketLoss) + } + } + return nil + } + }, + want: func(t *testing.T, got []*PortInfo) error { + // 验证返回结果 + if len(got) != 3 { + return fmt.Errorf("返回的 PortInfo 数量不正确,期望 3,得到 %d", len(got)) + } + + // 验证结果 + var gotMap = make(map[int]PortInfo) for _, port := range got { if port.Proto != "http" { return fmt.Errorf("期望协议为 http,得到 %s", port.Proto) @@ -366,11 +425,12 @@ func Test_channelService_CreateChannel(t *testing.T) { if port.Host != proxy.Host { return fmt.Errorf("期望主机为 %s,得到 %s", proxy.Host, port.Host) } + gotMap[port.Port] = *port } // 验证数据库字段 var channels []*models.Channel - db.Where("user_id = ? AND proxy_id = ?", userAuth.Payload.Id, proxy.ID).Find(&channels) + db.Where("user_id = ? and deleted_at is null", userAuth.Payload.Id).Find(&channels) for _, ch := range channels { if ch.Protocol != "http" { return fmt.Errorf("通道协议不正确,期望 http,得到 %s", ch.Protocol) @@ -378,30 +438,60 @@ func Test_channelService_CreateChannel(t *testing.T) { if ch.UserID != userAuth.Payload.Id { return fmt.Errorf("通道用户ID不正确,期望 %d,得到 %d", userAuth.Payload.Id, ch.UserID) } + // todo 多代理分配策略,验证 proxy_host if ch.ProxyID != proxy.ID { return fmt.Errorf("通道代理ID不正确,期望 %d,得到 %d", proxy.ID, ch.ProxyID) } + var info, ok = gotMap[int(ch.ProxyPort)] + if !ok { + return fmt.Errorf("通道端口 %d 不在返回结果中", ch.ProxyPort) + } + if ch.AuthPass != true && ch.AuthIP != false { + return fmt.Errorf("通道认证类型不正确,期望 Pass,得到 %v", ch.AuthPass) + } + if ch.Protocol != info.Proto { + return fmt.Errorf("通道协议不正确,期望 %s,得到 %s", info.Proto, ch.Protocol) + } + if ch.Username != *info.Username { + return fmt.Errorf("通道用户名不正确,期望 %s,得到 %s", *info.Username, ch.Username) + } + if ch.Password != *info.Password { + return fmt.Errorf("通道密码不正确,期望 %s,得到 %s", *info.Password, ch.Password) + } + if ch.Expiration.IsZero() { + return fmt.Errorf("通道过期时间不应为空") + } // 检查Redis缓存中的字段 key := fmt.Sprintf("channel:%d", ch.ID) if !mr.Exists(key) { - return fmt.Errorf("Redis缓存中应有键 %s", key) + return fmt.Errorf("redis缓存中应有键 %s", key) } data, _ := mr.Get(key) - var cachedChannel models.Channel - err := json.Unmarshal([]byte(data), &cachedChannel) + var cache models.Channel + err := json.Unmarshal([]byte(data), &cache) if err != nil { return fmt.Errorf("无法解析缓存数据: %v", err) } - - if cachedChannel.ID != ch.ID { - return fmt.Errorf("缓存ID不正确,期望 %d,得到 %d", ch.ID, cachedChannel.ID) - } - if cachedChannel.Protocol != ch.Protocol { - return fmt.Errorf("缓存协议不正确,期望 %s,得到 %s", ch.Protocol, cachedChannel.Protocol) + if reflect.DeepEqual(cache, *ch) { + return fmt.Errorf("缓存数据与数据库不匹配: %v", cache) } } + + // 检查跨天用量更新 + var pss models.ResourcePss + db.Where("resource_id = ?", 1).First(&pss) + if pss.DailyUsed != 3 { + return fmt.Errorf("套餐每日用量不正确,期望 3,得到 %d", pss.DailyUsed) + } + if pss.DailyLast.IsZero() { + return fmt.Errorf("套餐每日最后更新时间不应为空") + } + if pss.Used != 3 { + return fmt.Errorf("套餐总用量不正确,期望 3,得到 %d", pss.Used) + } + return nil }, }, @@ -413,23 +503,289 @@ func Test_channelService_CreateChannel(t *testing.T) { resourceId: 1, protocol: ProtocolHTTP, authType: ChannelAuthTypeIp, - count: 2, + count: 3, + nodeFilter: []NodeFilterConfig{{Prov: "北京市"}}, + }, + setup: func() { + mr.FlushAll() + clearDb() + + mc.ConnectMock = func(param remote.CloudConnectReq) error { + if param.Uuid != proxy.Name { + return fmt.Errorf("代理名称不符合预期: %s", param.Uuid) + } + if len(param.Edge) != 0 { + return fmt.Errorf("边缘节点不符合预期: %v", param.Edge) + } + if !reflect.DeepEqual(param.AutoConfig, []remote.AutoConfig{ + {Province: "河南省", Count: 10}, + {Province: "北京市", Count: 6}, + }) { + return fmt.Errorf("自动配置不符合预期: %v", param.AutoConfig) + } + return nil + } + + mg.PortConfigsMock = func(c *testutil.MockGatewayClient, params []remote.PortConfigsReq) error { + if c.Host != proxy.Host { + return fmt.Errorf("代理主机不符合预期: %s", c.Host) + } + if len(params) != 3 { + return fmt.Errorf("端口数量不符合预期: %d", len(params)) + } + for _, param := range params { + if param.Status != true { + return fmt.Errorf("端口状态不符合预期: %v", param.Status) + } + if param.AutoEdgeConfig == nil { + return fmt.Errorf("自动边缘节点配置不符合预期: %v", param.AutoEdgeConfig) + } + if param.Userpass == nil || *param.Userpass != "" { + return fmt.Errorf("用户名密码不符合预期: %v", *param.Userpass) + } + if param.Whitelist == nil || len(*param.Whitelist) == 0 { + return fmt.Errorf("白名单不符合预期: %v", param.Whitelist) + } + config := param.AutoEdgeConfig + if config.Province != "北京市" { + return fmt.Errorf("自动边缘节点省份不符合预期: %s", param.AutoEdgeConfig.Province) + } + if *config.Count != 1 { + return fmt.Errorf("自动边缘节点数量不符合预期: %d", param.AutoEdgeConfig.Count) + } + if config.PacketLoss != 30 { + return fmt.Errorf("自动边缘节点丢包率不符合预期: %d", param.AutoEdgeConfig.PacketLoss) + } + } + return nil + } }, want: func(t *testing.T, got []*PortInfo) error { + // 验证返回结果 + if len(got) != 3 { + return fmt.Errorf("返回的 PortInfo 数量不正确,期望 3,得到 %d", len(got)) + } + + // 验证结果 + var gotMap = make(map[int]PortInfo) + for _, port := range got { + if port.Proto != "http" { + return fmt.Errorf("期望协议为 http,得到 %s", port.Proto) + } + if port.Host != proxy.Host { + return fmt.Errorf("期望主机为 %s,得到 %s", proxy.Host, port.Host) + } + gotMap[port.Port] = *port + } + + // 验证数据库字段 + var channels []*models.Channel + db.Where("user_id = ? and deleted_at is null", userAuth.Payload.Id).Find(&channels) + for _, ch := range channels { + if ch.Protocol != "http" { + return fmt.Errorf("通道协议不正确,期望 http,得到 %s", ch.Protocol) + } + if ch.UserID != userAuth.Payload.Id { + return fmt.Errorf("通道用户ID不正确,期望 %d,得到 %d", userAuth.Payload.Id, ch.UserID) + } + // todo 多代理分配策略,验证 proxy_host + if ch.ProxyID != proxy.ID { + return fmt.Errorf("通道代理ID不正确,期望 %d,得到 %d", proxy.ID, ch.ProxyID) + } + var info, ok = gotMap[int(ch.ProxyPort)] + if !ok { + return fmt.Errorf("通道端口 %d 不在返回结果中", ch.ProxyPort) + } + if ch.AuthPass != false && ch.AuthIP != true { + return fmt.Errorf("通道认证类型不正确,期望 Pass,得到 %v", ch.AuthPass) + } + if ch.Protocol != info.Proto { + return fmt.Errorf("通道协议不正确,期望 %s,得到 %s", info.Proto, ch.Protocol) + } + if ch.Expiration.IsZero() { + return fmt.Errorf("通道过期时间不应为空") + } + + // 检查Redis缓存中的字段 + key := fmt.Sprintf("channel:%d", ch.ID) + if !mr.Exists(key) { + return fmt.Errorf("redis缓存中应有键 %s", key) + } + + data, _ := mr.Get(key) + var cache models.Channel + err := json.Unmarshal([]byte(data), &cache) + if err != nil { + return fmt.Errorf("无法解析缓存数据: %v", err) + } + if reflect.DeepEqual(cache, *ch) { + return fmt.Errorf("缓存数据与数据库不匹配: %v", cache) + } + } + + // 检查跨天用量更新 + var pss models.ResourcePss + db.Where("resource_id = ?", 1).First(&pss) + if pss.DailyUsed != 3 { + return fmt.Errorf("套餐每日用量不正确,期望 3,得到 %d", pss.DailyUsed) + } + if pss.DailyLast.IsZero() { + return fmt.Errorf("套餐每日最后更新时间不应为空") + } + if pss.Used != 3 { + return fmt.Errorf("套餐总用量不正确,期望 3,得到 %d", pss.Used) + } + return nil }, }, { - name: "管理员创建SOCKS5密码通道", + name: "管理员替用户创建HTTP密码通道", args: args{ ctx: ctx, auth: adminAuth, resourceId: 1, protocol: ProtocolSocks5, authType: ChannelAuthTypePass, - count: 2, + count: 3, + nodeFilter: []NodeFilterConfig{{Prov: "北京市"}}, + }, + setup: func() { + mr.FlushAll() + clearDb() + + mc.ConnectMock = func(param remote.CloudConnectReq) error { + if param.Uuid != proxy.Name { + return fmt.Errorf("代理名称不符合预期: %s", param.Uuid) + } + if len(param.Edge) != 0 { + return fmt.Errorf("边缘节点不符合预期: %v", param.Edge) + } + if !reflect.DeepEqual(param.AutoConfig, []remote.AutoConfig{ + {Province: "河南省", Count: 10}, + {Province: "北京市", Count: 6}, + }) { + return fmt.Errorf("自动配置不符合预期: %v", param.AutoConfig) + } + return nil + } + + mg.PortConfigsMock = func(c *testutil.MockGatewayClient, params []remote.PortConfigsReq) error { + if c.Host != proxy.Host { + return fmt.Errorf("代理主机不符合预期: %s", c.Host) + } + if len(params) != 3 { + return fmt.Errorf("端口数量不符合预期: %d", len(params)) + } + for _, param := range params { + if param.Status != true { + return fmt.Errorf("端口状态不符合预期: %v", param.Status) + } + if param.AutoEdgeConfig == nil { + return fmt.Errorf("自动边缘节点配置不符合预期: %v", param.AutoEdgeConfig) + } + if param.Userpass == nil || *param.Userpass == "" { + return fmt.Errorf("用户名密码不符合预期: %v", param.Userpass) + } + if param.Whitelist == nil || len(*param.Whitelist) != 0 { + return fmt.Errorf("白名单不符合预期: %v", param.Whitelist) + } + config := param.AutoEdgeConfig + if config.Province != "北京市" { + return fmt.Errorf("自动边缘节点省份不符合预期: %s", param.AutoEdgeConfig.Province) + } + if *config.Count != 1 { + return fmt.Errorf("自动边缘节点数量不符合预期: %d", param.AutoEdgeConfig.Count) + } + if config.PacketLoss != 30 { + return fmt.Errorf("自动边缘节点丢包率不符合预期: %d", param.AutoEdgeConfig.PacketLoss) + } + } + return nil + } }, want: func(t *testing.T, got []*PortInfo) error { + // 验证返回结果 + if len(got) != 3 { + return fmt.Errorf("返回的 PortInfo 数量不正确,期望 3,得到 %d", len(got)) + } + + // 验证结果 + var gotMap = make(map[int]PortInfo) + for _, port := range got { + if port.Proto != "socks5" { + return fmt.Errorf("期望协议为 http,得到 %s", port.Proto) + } + if port.Host != proxy.Host { + return fmt.Errorf("期望主机为 %s,得到 %s", proxy.Host, port.Host) + } + gotMap[port.Port] = *port + } + + // 验证数据库字段 + var channels []*models.Channel + db.Where("user_id = ? and deleted_at is null", userAuth.Payload.Id).Find(&channels) + for _, ch := range channels { + if ch.Protocol != "http" { + return fmt.Errorf("通道协议不正确,期望 http,得到 %s", ch.Protocol) + } + if ch.UserID != userAuth.Payload.Id { + return fmt.Errorf("通道用户ID不正确,期望 %d,得到 %d", userAuth.Payload.Id, ch.UserID) + } + // todo 多代理分配策略,验证 proxy_host + if ch.ProxyID != proxy.ID { + return fmt.Errorf("通道代理ID不正确,期望 %d,得到 %d", proxy.ID, ch.ProxyID) + } + var info, ok = gotMap[int(ch.ProxyPort)] + if !ok { + return fmt.Errorf("通道端口 %d 不在返回结果中", ch.ProxyPort) + } + if ch.AuthPass != true && ch.AuthIP != false { + return fmt.Errorf("通道认证类型不正确,期望 Pass,得到 %v", ch.AuthPass) + } + if ch.Protocol != info.Proto { + return fmt.Errorf("通道协议不正确,期望 %s,得到 %s", info.Proto, ch.Protocol) + } + if ch.Username != *info.Username { + return fmt.Errorf("通道用户名不正确,期望 %s,得到 %s", *info.Username, ch.Username) + } + if ch.Password != *info.Password { + return fmt.Errorf("通道密码不正确,期望 %s,得到 %s", *info.Password, ch.Password) + } + if ch.Expiration.IsZero() { + return fmt.Errorf("通道过期时间不应为空") + } + + // 检查Redis缓存中的字段 + key := fmt.Sprintf("channel:%d", ch.ID) + if !mr.Exists(key) { + return fmt.Errorf("redis缓存中应有键 %s", key) + } + + data, _ := mr.Get(key) + var cache models.Channel + err := json.Unmarshal([]byte(data), &cache) + if err != nil { + return fmt.Errorf("无法解析缓存数据: %v", err) + } + if reflect.DeepEqual(cache, *ch) { + return fmt.Errorf("缓存数据与数据库不匹配: %v", cache) + } + } + + // 检查跨天用量更新 + var pss models.ResourcePss + db.Where("resource_id = ?", 1).First(&pss) + if pss.DailyUsed != 3 { + return fmt.Errorf("套餐每日用量不正确,期望 3,得到 %d", pss.DailyUsed) + } + if pss.DailyLast.IsZero() { + return fmt.Errorf("套餐每日最后更新时间不应为空") + } + if pss.Used != 3 { + return fmt.Errorf("套餐总用量不正确,期望 3,得到 %d", pss.Used) + } + return nil }, }, @@ -443,8 +799,12 @@ func Test_channelService_CreateChannel(t *testing.T) { authType: ChannelAuthTypeIp, count: 1, }, + setup: func() { + mr.FlushAll() + clearDb() + }, wantErr: true, - wantErrContains: "套餐不存在", + wantErrContains: "无权限访问", }, { name: "套餐没有权限", @@ -456,25 +816,43 @@ func Test_channelService_CreateChannel(t *testing.T) { authType: ChannelAuthTypeIp, count: 1, }, + setup: func() { + mr.FlushAll() + clearDb() + + resource2 := &models.Resource{ + ID: 2, + UserID: 102, + Active: true, + } + db.Create(resource2) + var resourcePss2 = &models.ResourcePss{ + ID: 2, + ResourceID: 2, + Type: 1, + Live: 180, + Expire: time.Now().AddDate(1, 0, 0), + DailyLimit: 10000, + } + db.Create(resourcePss2) + }, wantErr: true, wantErrContains: "无权限访问", }, { name: "套餐配额不足", args: args{ - ctx: ctx, - auth: &AuthContext{ - Payload: Payload{ - Type: PayloadUser, - Id: 100, - }, - }, + ctx: ctx, + auth: userAuth, resourceId: 2, protocol: ProtocolHTTP, authType: ChannelAuthTypeIp, count: 10, }, setup: func() { + mr.FlushAll() + clearDb() + // 创建一个配额几乎用完的资源包 resource2 := models.Resource{ ID: 2, @@ -482,8 +860,8 @@ func Test_channelService_CreateChannel(t *testing.T) { Active: true, } resourcePss2 := models.ResourcePss{ - ID: 1, - ResourceID: 1, + ID: 2, + ResourceID: 2, Type: 2, Quota: 100, Used: 91, @@ -506,20 +884,24 @@ func Test_channelService_CreateChannel(t *testing.T) { count: 1, }, setup: func() { + mr.FlushAll() + clearDb() // 创建大量占用端口的通道 - for i := 10000; i < 20000; i++ { - channel := models.Channel{ - ProxyID: 1, - ProxyPort: int32(i), - UserID: 101, + var channels = make([]models.Channel, 10000) + var expr = time.Now().Add(time.Hour) + for i := range channels { + channels[i] = models.Channel{ + ProxyID: 1, + ProxyPort: int32(i + 10000), + UserID: 101, + Expiration: expr, } - db.Create(&channel) } + db.CreateInBatches(channels, 1000) }, wantErr: true, - wantErrContains: "端口数量不足", + wantErrContains: "端口数量到达上限", }, - // todo 跨天用量更新 // todo 多地区混杂条件提取 } @@ -552,10 +934,8 @@ func Test_channelService_CreateChannel(t *testing.T) { } // 使用检查函数验证结果 - if tt.want != nil { - if err := tt.want(t, got); err != nil { - t.Errorf("结果验证失败: %v", err) - } + if err := tt.want(t, got); err != nil { + t.Errorf("结果验证失败: %v", err) } }) } @@ -681,9 +1061,10 @@ func Test_channelService_RemoveChannels(t *testing.T) { return fmt.Errorf("端口状态不符合预期: %v", param.Status) } if param.Edge == nil || len(*param.Edge) != 0 { - return fmt.Errorf("边缘节点不符合预期: %v", param.Edge) + return fmt.Errorf("边缘节点不符合预期1: %v", param.Edge) } } + return nil case m.Host == proxy2.Host: for _, param := range params { if param.Port != 10001 { @@ -693,9 +1074,10 @@ func Test_channelService_RemoveChannels(t *testing.T) { return fmt.Errorf("端口状态不符合预期: %v", param.Status) } if param.Edge == nil || len(*param.Edge) != 0 { - return fmt.Errorf("边缘节点不符合预期: %v", param.Edge) + return fmt.Errorf("边缘节点不符合预期2: %v", param.Edge) } } + return nil } return fmt.Errorf("代理主机不符合预期: %s", m.Host) } @@ -703,8 +1085,8 @@ func Test_channelService_RemoveChannels(t *testing.T) { switch { case param.Uuid == proxy.Name: var edges = []string{"edge1", "edge2", "edge4"} - if !slices.Equal(edges, param.Edge) { - return 0, fmt.Errorf("边缘节点不符合预期: %v", param.Edge) + if !testutil.SliceEqual(edges, param.Edge) { + return 0, fmt.Errorf("边缘节点不符合预期3: %v", param.Edge) } if len(param.Config) != 0 { return 0, fmt.Errorf("配置不符合预期: %v", param.Config) @@ -712,8 +1094,8 @@ func Test_channelService_RemoveChannels(t *testing.T) { return len(param.Edge), nil case param.Uuid == proxy2.Name: var edges = []string{"edge3"} - if !slices.Equal(edges, param.Edge) { - return 0, fmt.Errorf("边缘节点不符合预期: %v", param.Edge) + if !testutil.SliceEqual(edges, param.Edge) { + return 0, fmt.Errorf("边缘节点不符合预期4: %v", param.Edge) } if len(param.Config) != 0 { return 0, fmt.Errorf("配置不符合预期: %v", param.Config) @@ -793,9 +1175,10 @@ func Test_channelService_RemoveChannels(t *testing.T) { return fmt.Errorf("端口状态不符合预期: %v", param.Status) } if param.Edge == nil || len(*param.Edge) != 0 { - return fmt.Errorf("边缘节点不符合预期: %v", param.Edge) + return fmt.Errorf("边缘节点不符合预期5: %v", param.Edge) } } + return nil case m.Host == proxy2.Host: for _, param := range params { if param.Port != 10001 { @@ -805,9 +1188,10 @@ func Test_channelService_RemoveChannels(t *testing.T) { return fmt.Errorf("端口状态不符合预期: %v", param.Status) } if param.Edge == nil || len(*param.Edge) != 0 { - return fmt.Errorf("边缘节点不符合预期: %v", param.Edge) + return fmt.Errorf("边缘节点不符合预期6: %v", param.Edge) } } + return nil } return fmt.Errorf("代理主机不符合预期: %s", m.Host) } @@ -815,8 +1199,8 @@ func Test_channelService_RemoveChannels(t *testing.T) { switch { case param.Uuid == proxy.Name: var edges = []string{"edge1", "edge2", "edge4"} - if !slices.Equal(edges, param.Edge) { - return 0, fmt.Errorf("边缘节点不符合预期: %v", param.Edge) + if !testutil.SliceEqual(edges, param.Edge) { + return 0, fmt.Errorf("边缘节点不符合预期7: %v", param.Edge) } if len(param.Config) != 0 { return 0, fmt.Errorf("配置不符合预期: %v", param.Config) @@ -824,8 +1208,8 @@ func Test_channelService_RemoveChannels(t *testing.T) { return len(param.Edge), nil case param.Uuid == proxy2.Name: var edges = []string{"edge3"} - if !slices.Equal(edges, param.Edge) { - return 0, fmt.Errorf("边缘节点不符合预期: %v", param.Edge) + if !testutil.SliceEqual(edges, param.Edge) { + return 0, fmt.Errorf("边缘节点不符合预期8: %v", param.Edge) } if len(param.Config) != 0 { return 0, fmt.Errorf("配置不符合预期: %v", param.Config) @@ -911,9 +1295,9 @@ func Test_channelService_RemoveChannels(t *testing.T) { } // 检查数据库和缓存是否正确设置 - want := tt.want(t) - if tt.want(t) != nil { - t.Errorf("RemoveChannels() 结果验证失败: %v", want) + + if err := tt.want(t); err != nil { + t.Errorf("RemoveChannels() 结果验证失败: %v", err) } }) }