86 lines
2.5 KiB
Go
86 lines
2.5 KiB
Go
// Package healthcheck 验证 room-service ready/live 对存储、lease 和 drain 状态的映射。
|
||
package healthcheck
|
||
|
||
import (
|
||
"context"
|
||
"errors"
|
||
"testing"
|
||
"time"
|
||
|
||
"hyapp/pkg/grpchealth"
|
||
)
|
||
|
||
// okRuntime 模拟领域服务进程内状态正常。
|
||
type okRuntime struct{}
|
||
|
||
// HealthCheck 返回 nil,让测试聚焦外部依赖和 listener 状态。
|
||
func (okRuntime) HealthCheck() error {
|
||
return nil
|
||
}
|
||
|
||
// fakeMySQL 模拟 MySQL ping 结果。
|
||
type fakeMySQL struct {
|
||
// err 是 Ping 返回的错误。
|
||
err error
|
||
}
|
||
|
||
// Ping 实现 MySQLPinger。
|
||
func (m fakeMySQL) Ping(context.Context) error {
|
||
return m.err
|
||
}
|
||
|
||
// fakeRedis 分别模拟 Redis ping 和 lease Lua 探针结果。
|
||
type fakeRedis struct {
|
||
pingErr error
|
||
leaseErr error
|
||
}
|
||
|
||
// Ping 实现 RedisProbe 的基础连通性检查。
|
||
func (r fakeRedis) Ping(context.Context) error {
|
||
return r.pingErr
|
||
}
|
||
|
||
// ProbeLease 实现 RedisProbe 的 lease 原子路径检查。
|
||
func (r fakeRedis) ProbeLease(context.Context, string, time.Time, time.Duration) error {
|
||
return r.leaseErr
|
||
}
|
||
|
||
// TestReadyRequiresStorageAndLease 验证 ready 必须同时满足 listener、runtime、MySQL 和 Redis。
|
||
func TestReadyRequiresStorageAndLease(t *testing.T) {
|
||
state := NewState("room-node-test", okRuntime{}, fakeMySQL{}, fakeRedis{})
|
||
// app.Run 进入 Serve 后会设置该标记;测试直接设置以隔离 State 行为。
|
||
state.MarkGRPCServing()
|
||
|
||
if !grpchealth.Healthy(state.Ready(context.Background())) {
|
||
t.Fatalf("ready should pass when listener, runtime, MySQL and Redis probes pass: %+v", state.Ready(context.Background()))
|
||
}
|
||
|
||
state.MarkDraining()
|
||
// drain 中的节点不能继续接管房间或接收新命令。
|
||
if grpchealth.Healthy(state.Ready(context.Background())) {
|
||
t.Fatalf("ready should fail while draining")
|
||
}
|
||
}
|
||
|
||
// TestReadyFailsWhenRedisLeaseProbeFails 验证 Redis 连通但 Lua lease 路径失败时 ready 必须失败。
|
||
func TestReadyFailsWhenRedisLeaseProbeFails(t *testing.T) {
|
||
state := NewState("room-node-test", okRuntime{}, fakeMySQL{}, fakeRedis{leaseErr: errors.New("lease eval failed")})
|
||
state.MarkGRPCServing()
|
||
|
||
checks := state.Ready(context.Background())
|
||
if grpchealth.Healthy(checks) {
|
||
t.Fatalf("ready should fail when Redis lease probe fails")
|
||
}
|
||
|
||
found := false
|
||
for _, check := range checks {
|
||
if check.Name == "redis_lease" && check.Status == grpchealth.StatusFail {
|
||
// redis_lease 单独暴露,便于区分 Redis ping 成功但 owner 仲裁不可用。
|
||
found = true
|
||
}
|
||
}
|
||
if !found {
|
||
t.Fatalf("ready failure should include redis_lease check: %+v", checks)
|
||
}
|
||
}
|