| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556 |
- <?php
- // Isolated SQLite: execute real refunds/coupon writes, replacing only notification dispatch and the ledger sink.
- namespace App\Jobs { class SendTelegramMessageJob { public static int $sent = 0; public static function dispatch(...$args) { ++self::$sent; } } }
- namespace App\Services { class BalanceLogService { public static array $logs = []; public static function addLog(...$args) { self::$logs[] = $args; } } }
- namespace {
- require dirname(__DIR__, 2) . '/vendor/autoload.php';
- $app=new \Illuminate\Foundation\Application(dirname(__DIR__, 2));
- $app->instance('config',new \Illuminate\Config\Repository(['admin_realtime'=>['enabled'=>false]]));
- $app->instance('log',new \Psr\Log\NullLogger());
- \Illuminate\Support\Facades\Facade::setFacadeApplication($app);
- $db=new \Illuminate\Database\Capsule\Manager($app);
- $db->addConnection(['driver'=>'sqlite','database'=>':memory:','prefix'=>'bot_']); $db->setAsGlobal();$db->bootEloquent();
- $app->instance('db',$db->getDatabaseManager());$app->bind('db.schema',fn()=>$db->schema());
- $sql=[
- 'CREATE TABLE bot_wallets (id INTEGER PRIMARY KEY,member_id TEXT,available_balance TEXT,created_at TEXT,updated_at TEXT)',
- 'CREATE TABLE bot_lhc_order (id INTEGER PRIMARY KEY,member_id TEXT,issue TEXT,type INTEGER,lottery_status INTEGER,amount TEXT,total_amount TEXT,coupon_discount TEXT,coupon_id INTEGER,ordernum TEXT,number TEXT,created_at INTEGER,updated_at INTEGER)',
- 'CREATE TABLE bot_lhc_order_item (id INTEGER PRIMARY KEY,ordernum TEXT,lottery_status INTEGER,updated_at INTEGER)',
- 'CREATE TABLE bot_promotion_coupons (id INTEGER PRIMARY KEY,status TEXT,used_source TEXT,used_stake TEXT,used_at TEXT,updated_at TEXT)',
- 'CREATE TABLE bot_promotion_coupon_uses (id INTEGER PRIMARY KEY,coupon_id INTEGER,source TEXT,order_id INTEGER,discount TEXT,refunded_at TEXT,updated_at TEXT)',
- ];
- foreach($sql as $q) \Illuminate\Support\Facades\DB::statement($q);
- $DB=\Illuminate\Support\Facades\DB::class;
- $DB::table('wallets')->insert(['id'=>1,'member_id'=>'1001','available_balance'=>'0']);
- $DB::table('promotion_coupons')->insert(['id'=>1,'status'=>'used']);
- foreach([1=>['1','0'],2=>['10000','1']] as $id=>[$amount,$discount]) {
- $DB::table('lhc_order')->insert(['id'=>$id,'member_id'=>'1001','issue'=>'issue','type'=>5,'lottery_status'=>0,'amount'=>$amount,'total_amount'=>$id===1?'2':$amount,'coupon_discount'=>$discount,'coupon_id'=>1,'ordernum'=>'order'.$id,'created_at'=>time(),'updated_at'=>time()]);
- $DB::table('lhc_order_item')->insert(['id'=>$id,'ordernum'=>'order'.$id,'lottery_status'=>0]);
- $DB::table('promotion_coupon_uses')->insert(['id'=>$id,'coupon_id'=>1,'source'=>'lhc_order','order_id'=>$id,'discount'=>$discount]);
- }
- $DB::table('lhc_order_item')->insert(['id'=>3,'ordernum'=>'order1','lottery_status'=>0]);
- \App\Services\BetService::betFail('issue');
- $checks = 0;
- function same($actual, $expected, string $message): void {
- global $checks; ++$checks;
- if ($actual !== $expected) throw new \RuntimeException($message . ': ' . json_encode($actual));
- }
- same(bcadd((string)$DB::table('wallets')->value('available_balance'), '0', 2), '10001.00', 'All paid stakes, including zero-discount groups, are refunded');
- same($DB::table('promotion_coupons')->value('status'), 'unused', 'Coupon returns after all orders are refunded');
- same($DB::table('promotion_coupon_uses')->whereNull('refunded_at')->count(), 0, 'Zero-discount use is also refunded');
- same($DB::table('lhc_order')->orderBy('id')->pluck('lottery_status')->all(), [1, 1], 'Existing failed-draw terminal states are preserved');
- same(count(\App\Services\BalanceLogService::$logs), 2, 'One ledger entry per order');
- same(bcadd((string)\App\Services\BalanceLogService::$logs[0][1], '0', 2), '2.00', 'First ledger uses the complete paid group stake');
- same(\App\Jobs\SendTelegramMessageJob::$sent, 2, 'Notifications are stubbed, never sent externally');
- $DB::table('promotion_coupons')->update(['status' => 'used', 'used_source' => 'sport_order', 'used_stake' => '100']);
- $DB::table('promotion_coupon_uses')->insert(['id' => 3, 'coupon_id' => 1, 'source' => 'sport_order', 'order_id' => 9, 'discount' => '1']);
- \App\Services\PromotionCouponService::release('lhc_order', 1);
- same($DB::table('promotion_coupons')->value('status'), 'used', 'Retrying an old refund never restores a reused coupon');
- $DB::beginTransaction();
- \App\Services\PromotionCouponService::release('sport_order', 9);
- same($DB::table('promotion_coupons')->value('status'), 'unused', 'Nested release completes inside refund transaction');
- $DB::rollBack();
- same($DB::table('promotion_coupons')->value('status'), 'used', 'Outer rollback restores coupon state');
- same($DB::table('promotion_coupon_uses')->where('id', 3)->value('refunded_at'), null, 'Outer rollback restores use state');
- echo "PASS {$checks} coupon failed-draw refund checks\n";
- }
|