package mysql import ( "context" "testing" mysqldriver "github.com/go-sql-driver/mysql" "hyapp/internal/testutil/mysqlschema" "hyapp/pkg/appcode" "hyapp/pkg/xerr" robotdomain "hyapp/services/robot-service/internal/domain/robot" ) func TestMigrateLegacyGameRobotsCopiesOnceIntoRobotDatabase(t *testing.T) { caller := mysqlschema.CallerFile(t, 1) legacy := mysqlschema.New(t, mysqlschema.Config{ InitDBPath: mysqlschema.InitDBPath(t, caller, "../../../../game-service/deploy/mysql/initdb/001_game_service.sql"), DatabasePrefix: "hy_game_legacy_test", }) robot := mysqlschema.New(t, mysqlschema.Config{ InitDBPath: mysqlschema.InitDBPath(t, caller, "../../../deploy/mysql/initdb/001_robot_service.sql"), DatabasePrefix: "hy_robot_test", }) legacyDSN, err := mysqldriver.ParseDSN(legacy.DSN) if err != nil { t.Fatalf("parse legacy dsn failed: %v", err) } ctx := appcode.WithContext(context.Background(), "lalu") nowMS := int64(1781200000000) if _, err := legacy.DB.ExecContext(ctx, `INSERT INTO game_self_game_robots ( app_code, game_id, user_id, status, created_by_admin_id, created_at_ms, updated_at_ms, last_used_at_ms, used_count ) VALUES (?, 'dice', ?, 'disabled', 7, ?, ?, 11, 1), (?, 'rock', ?, 'active', 7, ?, ?, 22, 2), (?, 'dice', ?, 'disabled', 8, ?, ?, 33, 3)`, "lalu", int64(101), nowMS, nowMS+1, "lalu", int64(101), nowMS+2, nowMS+3, "lalu", int64(202), nowMS+4, nowMS+5, ); err != nil { t.Fatalf("seed legacy robots failed: %v", err) } repo, err := Open(ctx, robot.DSN) if err != nil { t.Fatalf("open robot repo failed: %v", err) } t.Cleanup(func() { _ = repo.Close() }) result, err := repo.MigrateLegacyGameRobots(ctx, legacyDSN.DBName) if err != nil { t.Fatalf("migrate legacy robots failed: %v", err) } if !result.Applied || !result.SourceFound || result.MigratedRows != 2 { t.Fatalf("migration result mismatch: %+v", result) } items, _, err := repo.ListGameRobots(ctx, "lalu", "rock", "", 20, "") if err != nil { t.Fatalf("list migrated robots failed: %v", err) } if len(items) != 2 { t.Fatalf("migrated robots length = %d, want 2: %+v", len(items), items) } if items[0].UserID != 101 || items[0].Status != robotdomain.StatusActive || items[0].UsedCount != 2 || items[0].LastUsedAtMS != 22 { t.Fatalf("active aggregated robot mismatch: %+v", items[0]) } if items[1].UserID != 202 || items[1].Status != robotdomain.StatusDisabled || items[1].UsedCount != 3 || items[1].LastUsedAtMS != 33 { t.Fatalf("disabled migrated robot mismatch: %+v", items[1]) } if _, err := legacy.DB.ExecContext(ctx, `INSERT INTO game_self_game_robots ( app_code, game_id, user_id, status, created_by_admin_id, created_at_ms, updated_at_ms, last_used_at_ms, used_count ) VALUES (?, 'dice', ?, 'active', 9, ?, ?, 44, 4)`, "lalu", int64(303), nowMS+6, nowMS+7, ); err != nil { t.Fatalf("seed post-migration legacy robot failed: %v", err) } result, err = repo.MigrateLegacyGameRobots(ctx, legacyDSN.DBName) if err != nil { t.Fatalf("rerun legacy migration failed: %v", err) } if result.Applied { t.Fatalf("migration must be one-shot after marker, got %+v", result) } items, _, err = repo.ListGameRobots(ctx, "lalu", "dice", "", 20, "") if err != nil { t.Fatalf("list after migration rerun failed: %v", err) } if len(items) != 2 { t.Fatalf("rerun must not import new legacy rows, got %+v", items) } } func TestPickGameRobotUsesRobotTableAndExcludesParticipants(t *testing.T) { caller := mysqlschema.CallerFile(t, 1) schema := mysqlschema.New(t, mysqlschema.Config{ InitDBPath: mysqlschema.InitDBPath(t, caller, "../../../deploy/mysql/initdb/001_robot_service.sql"), DatabasePrefix: "hy_robot_test", }) ctx := appcode.WithContext(context.Background(), "lalu") repo, err := Open(ctx, schema.DSN) if err != nil { t.Fatalf("open robot repo failed: %v", err) } t.Cleanup(func() { _ = repo.Close() }) if _, err := repo.RegisterGameRobots(ctx, "lalu", "dice", []int64{101, 202}, 7, 1781200000000); err != nil { t.Fatalf("register game robots failed: %v", err) } if _, err := repo.PickGameRobot(ctx, "lalu", "dice", []int64{101, 202}, 1781200000100); !xerr.IsCode(err, xerr.NotFound) { t.Fatalf("all excluded robots should return NotFound, got %v", err) } robot, err := repo.PickGameRobot(ctx, "lalu", "rock", []int64{101}, 1781200000200) if err != nil { t.Fatalf("pick available robot failed: %v", err) } if robot.UserID != 202 || robot.GameID != "rock" || robot.LastUsedAtMS != 1781200000200 || robot.UsedCount != 1 { t.Fatalf("picked robot mismatch: %+v", robot) } }