// Package mysqltest provides real wallet-service MySQL repositories for tests. package mysqltest import ( "context" "fmt" "testing" "time" "hyapp/internal/testutil/mysqlschema" mysqlstorage "hyapp/services/wallet-service/internal/storage/mysql" ) // Repository wraps the production MySQL wallet repository with test seed helpers. type Repository struct { *mysqlstorage.Repository t testing.TB schema *mysqlschema.Schema } // CountRows returns a simple count from a known wallet table for persistence assertions. func (r *Repository) CountRows(table string, where string, args ...any) int { r.t.Helper() query := "SELECT COUNT(*) FROM " + validateTableName(table) if where != "" { query += " WHERE " + where } var count int if err := r.schema.DB.QueryRowContext(context.Background(), query, args...).Scan(&count); err != nil { r.t.Fatalf("count %s rows failed: %v\nquery=%s", table, err, query) } return count } // InsertRawGiftPrice allows tests to seed inactive or future prices without service helpers. func (r *Repository) InsertRawGiftPrice(giftID string, priceVersion string, status string, effectiveAtMs int64) { r.t.Helper() nowMs := time.Now().UnixMilli() _, err := r.schema.DB.ExecContext(context.Background(), ` INSERT INTO wallet_gift_prices ( gift_id, price_version, status, coin_price, gift_point_amount, heat_value, effective_at_ms, created_at_ms, updated_at_ms ) VALUES (?, ?, ?, 1, 1, 1, ?, ?, ?) `, giftID, priceVersion, status, effectiveAtMs, nowMs, nowMs) if err != nil { r.t.Fatalf("insert raw gift price failed: %v", err) } } // NewRepository creates an isolated wallet schema and opens the production repository. func NewRepository(t testing.TB) *Repository { t.Helper() schema := mysqlschema.New(t, mysqlschema.Config{ EnvVar: "WALLET_SERVICE_MYSQL_TEST_DSN", InitDBPath: initDBPath(t), DatabasePrefix: "hyapp_wallet_test", }) repository, err := mysqlstorage.Open(context.Background(), schema.DSN) if err != nil { t.Fatalf("open wallet mysql repository failed: %v", err) } wrapped := &Repository{Repository: repository, t: t, schema: schema} t.Cleanup(wrapped.Close) return wrapped } // Close shuts down the repository; mysqlschema owns schema cleanup. func (r *Repository) Close() { r.t.Helper() if r.Repository != nil { _ = r.Repository.Close() } } // SetBalance prepares a sender COIN account for debit tests. func (r *Repository) SetBalance(userID int64, balance int64) { r.t.Helper() nowMs := time.Now().UnixMilli() _, err := r.schema.DB.ExecContext(context.Background(), ` INSERT INTO wallet_accounts (user_id, asset_type, available_amount, frozen_amount, version, created_at_ms, updated_at_ms) VALUES (?, 'COIN', ?, 0, 1, ?, ?) ON DUPLICATE KEY UPDATE available_amount = VALUES(available_amount), frozen_amount = VALUES(frozen_amount), version = version + 1, updated_at_ms = VALUES(updated_at_ms) `, userID, balance, nowMs, nowMs) if err != nil { r.t.Fatalf("seed wallet balance failed: %v", err) } } // SetGiftPrice overrides or inserts a gift settlement price for server-side billing tests. func (r *Repository) SetGiftPrice(giftID string, priceVersion string, coinPrice int64, giftPointAmount int64, heatValue int64) { r.t.Helper() nowMs := time.Now().UnixMilli() _, err := r.schema.DB.ExecContext(context.Background(), ` INSERT INTO wallet_gift_prices ( gift_id, price_version, status, coin_price, gift_point_amount, heat_value, effective_at_ms, created_at_ms, updated_at_ms ) VALUES (?, ?, 'active', ?, ?, ?, 0, ?, ?) ON DUPLICATE KEY UPDATE status = VALUES(status), coin_price = VALUES(coin_price), gift_point_amount = VALUES(gift_point_amount), heat_value = VALUES(heat_value), updated_at_ms = VALUES(updated_at_ms) `, giftID, priceVersion, coinPrice, giftPointAmount, heatValue, nowMs, nowMs) if err != nil { r.t.Fatalf("seed wallet gift price failed: %v", err) } } func initDBPath(t testing.TB) string { t.Helper() return mysqlschema.InitDBPath(t, mysqlschema.CallerFile(t, 1), "..", "..", "..", "deploy", "mysql", "initdb", "001_wallet_service.sql", ) } func validateTableName(table string) string { switch table { case "wallet_transactions", "wallet_entries", "wallet_outbox", "wallet_accounts": return table default: panic(fmt.Sprintf("unsupported wallet test table %q", table)) } }