package service_test import ( "context" "errors" "testing" "time" roomv1 "hyapp.local/api/proto/room/v1" "hyapp/pkg/tencentim" "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" ) const testRoomCoverURL = "https://cdn.example.com/room-cover.png" type createRoomIMPublisher struct { err error roomID string ownerUserID int64 callCount int } func (p *createRoomIMPublisher) EnsureRoomGroup(_ context.Context, roomID string, ownerUserID int64) error { p.callCount++ p.roomID = roomID p.ownerUserID = ownerUserID return p.err } func (p *createRoomIMPublisher) PublishRoomEvent(context.Context, tencentim.RoomEvent) error { return nil } func TestCreateRoomRequiresRoomAvatar(t *testing.T) { ctx := context.Background() repository := mysqltest.NewRepository(t) svc := roomservice.New(roomservice.Config{ NodeID: "node-create-room-avatar-test", LeaseTTL: 10 * time.Second, RankLimit: 20, SnapshotEveryN: 1, }, router.NewMemoryDirectory(), repository, followTestWallet{}, integration.NewNoopRoomEventPublisher(), integration.NewNoopOutboxPublisher()) _, err := svc.CreateRoom(ctx, &roomv1.CreateRoomRequest{ Meta: roomservice.NewRequestMeta("room-avatar-required", 7000), SeatCount: 10, Mode: "voice", RoomName: "Avatar Required", RoomShortId: "avatar-required", }) if !xerr.IsCode(err, xerr.InvalidArgument) { // 房间封面为空必须在 Room owner service 拒绝,避免 gateway 以外的内部调用创建空 cover_url 房间。 t.Fatalf("expected INVALID_ARGUMENT without room avatar, got %v", err) } if _, exists, err := repository.GetRoomMeta(ctx, "room-avatar-required"); err != nil || exists { t.Fatalf("room meta must not persist without avatar: exists=%t err=%v", exists, err) } } func TestCreateRoomSynchronouslyEnsuresIMGroup(t *testing.T) { ctx := context.Background() repository := mysqltest.NewRepository(t) publisher := &createRoomIMPublisher{} svc := roomservice.New(roomservice.Config{ NodeID: "node-create-room-im-test", LeaseTTL: 10 * time.Second, RankLimit: 20, SnapshotEveryN: 1, }, router.NewMemoryDirectory(), repository, followTestWallet{}, publisher, integration.NewNoopOutboxPublisher()) roomID := "room-sync-im-ok" ownerID := int64(7001) resp, err := svc.CreateRoom(ctx, &roomv1.CreateRoomRequest{ Meta: roomservice.NewRequestMeta(roomID, ownerID), SeatCount: 10, Mode: "voice", VisibleRegionId: 1001, RoomName: "Sync IM Room", RoomAvatar: testRoomCoverURL, RoomShortId: "sync-im-ok", }) if err != nil { t.Fatalf("create room failed: %v", err) } if publisher.callCount != 1 || publisher.roomID != roomID || publisher.ownerUserID != ownerID { t.Fatalf("IM group must be ensured once before create returns: %+v", publisher) } if resp.GetRoom().GetRoomId() != roomID || !resp.GetResult().GetApplied() { t.Fatalf("create response mismatch: %+v", resp) } } func TestCreateRoomDoesNotPersistWhenIMGroupEnsureFails(t *testing.T) { ctx := context.Background() repository := mysqltest.NewRepository(t) publisher := &createRoomIMPublisher{err: errors.New("tencent im unavailable")} svc := roomservice.New(roomservice.Config{ NodeID: "node-create-room-im-fail-test", LeaseTTL: 10 * time.Second, RankLimit: 20, SnapshotEveryN: 1, }, router.NewMemoryDirectory(), repository, followTestWallet{}, publisher, integration.NewNoopOutboxPublisher()) roomID := "room-sync-im-fail" ownerID := int64(7002) if _, err := svc.CreateRoom(ctx, &roomv1.CreateRoomRequest{ Meta: roomservice.NewRequestMeta(roomID, ownerID), SeatCount: 10, Mode: "voice", VisibleRegionId: 1001, RoomName: "Sync IM Failure", RoomAvatar: testRoomCoverURL, RoomShortId: "sync-im-fail", }); err == nil { t.Fatalf("create room must fail when IM group cannot be created") } if publisher.callCount != 1 || publisher.roomID != roomID || publisher.ownerUserID != ownerID { t.Fatalf("IM group ensure call mismatch: %+v", publisher) } if meta, exists, err := repository.GetRoomMeta(ctx, roomID); err != nil || exists { t.Fatalf("room meta must not persist after IM failure: meta=%+v exists=%t err=%v", meta, exists, err) } }