2026-04-25 01:18:30 +08:00

86 lines
2.5 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 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)
}
}