package grpc import ( "context" "testing" "time" roomv1 "hyapp.local/api/proto/room/v1" walletv1 "hyapp.local/api/proto/wallet/v1" "hyapp/pkg/xerr" "hyapp/services/room-service/internal/integration" roomservice "hyapp/services/room-service/internal/room/service" "hyapp/services/room-service/internal/router" "hyapp/services/room-service/internal/testutil/mysqltest" "google.golang.org/grpc/codes" grpcstatus "google.golang.org/grpc/status" ) // fakeWallet 只满足 room-service 构造依赖;本测试走参数校验失败分支,不触发扣费。 type fakeWallet struct{} // DebitGift 返回固定结果,避免 transport 测试依赖 wallet-service 进程。 func (fakeWallet) DebitGift(context.Context, *walletv1.DebitGiftRequest) (*walletv1.DebitGiftResponse, error) { return &walletv1.DebitGiftResponse{BillingReceiptId: "receipt-test"}, nil } // newTransportTestServer 构造完整领域服务,确保测试覆盖真实 service -> transport 错误边界。 func newTransportTestServer(t *testing.T) *Server { t.Helper() svc := roomservice.New(roomservice.Config{ NodeID: "node-transport-test", LeaseTTL: 10 * time.Second, RankLimit: 20, SnapshotEveryN: 1, }, router.NewMemoryDirectory(), mysqltest.NewRepository(t), fakeWallet{}, integration.NewNoopRoomEventPublisher(), integration.NewNoopOutboxPublisher()) return NewServer(svc) } func TestCreateRoomMapsDomainErrorToGRPCReason(t *testing.T) { server := newTransportTestServer(t) _, err := server.CreateRoom(context.Background(), &roomv1.CreateRoomRequest{ // room_id 缺失属于客户端参数错误,不能在 gRPC 边界丢失 reason 后变成 INTERNAL_ERROR。 SeatCount: 8, Mode: "voice", }) if err == nil { t.Fatalf("CreateRoom without room_id should fail") } if got := grpcstatus.Code(err); got != codes.InvalidArgument { t.Fatalf("gRPC code mismatch: got %s", got) } if got := xerr.ReasonFromGRPC(err); got != xerr.InvalidArgument { t.Fatalf("reason mismatch: got %s", got) } if message := grpcstatus.Convert(err).Message(); message != "room_id is required" { t.Fatalf("message mismatch: got %q", message) } }