|
|
@@ -2,6 +2,7 @@
|
|
|
|
|
|
namespace Tests\Integration;
|
|
|
|
|
|
+use App\Console\Commands\SettleSharing;
|
|
|
use App\Services\Sharing\ShareDefaults;
|
|
|
use App\Services\Sharing\ShareRelations;
|
|
|
use App\Services\Sharing\ShareSettlementService;
|
|
|
@@ -13,6 +14,7 @@ use Illuminate\Foundation\Application;
|
|
|
use Illuminate\Support\Facades\DB;
|
|
|
use Illuminate\Support\Facades\Facade;
|
|
|
use PHPUnit\Framework\TestCase;
|
|
|
+use Symfony\Component\Console\Tester\CommandTester;
|
|
|
|
|
|
class SharingSettlementTest extends TestCase
|
|
|
{
|
|
|
@@ -289,13 +291,118 @@ class SharingSettlementTest extends TestCase
|
|
|
$this->service->settle('2026-10-24'); $this->assertSame('0.2000000000',$this->amount(1));
|
|
|
}
|
|
|
|
|
|
- public function test_today_never_becomes_claimable_and_foreign_currency_is_not_combined(): void
|
|
|
+ public function test_today_is_rejected_without_debug_opt_in_and_foreign_currency_is_not_combined(): void
|
|
|
{
|
|
|
$this->fact(2,'200'); $this->fact(3,'20000','2026-10-24',['currency'=>'USD']);
|
|
|
$this->assertSame('0',$this->service->projection('2026-10-24')['rows'][1]['third:2']['indirect_performance']);
|
|
|
$this->expectException(\InvalidArgumentException::class); $this->service->settle('2026-10-25');
|
|
|
}
|
|
|
|
|
|
+ private function command(array $options): CommandTester
|
|
|
+ {
|
|
|
+ $command = new SettleSharing();
|
|
|
+ $command->setLaravel(app());
|
|
|
+ $tester = new CommandTester($command);
|
|
|
+ $tester->execute($options);
|
|
|
+ return $tester;
|
|
|
+ }
|
|
|
+
|
|
|
+ public function test_debug_today_credits_once_revises_only_the_delta_and_closes_normally_after_midnight(): void
|
|
|
+ {
|
|
|
+ $this->service->settle('2026-10-24');
|
|
|
+ $fact = $this->fact(2, '200', '2026-10-25');
|
|
|
+ $wallets = DB::table('wallets')->get()->toJson();
|
|
|
+ $first = $this->service->settle('2026-10-25', true, true);
|
|
|
+ $this->assertTrue($first['written']);
|
|
|
+ $this->assertSame('0.2000000000', $this->amount(1));
|
|
|
+ $this->assertSame('2026-10-24', DB::table('share_system_state')->where('key', 'last_settlement_date')->value('value'));
|
|
|
+ $this->assertContains('2026-10-25', json_decode(DB::table('share_system_state')->where('key', 'facts_dirty_dates')->value('value'), true));
|
|
|
+ $entries = DB::table('share_ledger')->count();
|
|
|
+ $this->assertSame(0, $this->service->settle('2026-10-25', true, true)['changed']);
|
|
|
+ $this->assertSame($entries, DB::table('share_ledger')->count());
|
|
|
+ DB::table('share_bet_facts')->where('id', $fact)->update(['effective_amount' => 400]);
|
|
|
+ $this->assertSame('0.2000000000', $this->service->settle('2026-10-25', true, true)['delta']);
|
|
|
+ $this->assertSame('0.4000000000', $this->amount(1));
|
|
|
+ $this->assertSame(0, $this->service->reconcile()['dates']);
|
|
|
+ Carbon::setTestNow(Carbon::parse('2026-10-26 00:01:00', 'Asia/Shanghai'));
|
|
|
+ $entries = DB::table('share_ledger')->count();
|
|
|
+ $this->assertSame(1, $this->service->reconcile()['dates']);
|
|
|
+ $this->assertSame('2026-10-25', DB::table('share_system_state')->where('key', 'last_settlement_date')->value('value'));
|
|
|
+ $this->assertSame($entries, DB::table('share_ledger')->count());
|
|
|
+ $this->assertSame('0.4000000000', $this->amount(1));
|
|
|
+ $this->assertSame($wallets, DB::table('wallets')->get()->toJson());
|
|
|
+ }
|
|
|
+
|
|
|
+ public function test_allow_today_flag_defaults_to_today_and_ordinary_command_still_defaults_to_yesterday(): void
|
|
|
+ {
|
|
|
+ $this->fact(2, '200'); $this->fact(2, '300', '2026-10-25');
|
|
|
+ $normal = $this->command([]);
|
|
|
+ $this->assertSame(0, $normal->getStatusCode());
|
|
|
+ $this->assertSame('2026-10-24', json_decode($normal->getDisplay(), true)['date']);
|
|
|
+ $this->assertSame('0.2000000000', $this->amount(1));
|
|
|
+ $blocked = $this->command(['date' => '2026-10-25']);
|
|
|
+ $this->assertSame(1, $blocked->getStatusCode());
|
|
|
+ $this->assertStringContainsString('--allow-today', $blocked->getDisplay());
|
|
|
+ $debug = $this->command(['--allow-today' => true]);
|
|
|
+ $this->assertSame(0, $debug->getStatusCode());
|
|
|
+ $this->assertSame('2026-10-25', json_decode($debug->getDisplay(), true)['date']);
|
|
|
+ $this->assertSame('0.5000000000', $this->amount(1));
|
|
|
+ $again = $this->command(['date' => '2026-10-25', '--allow-today' => true]);
|
|
|
+ $this->assertSame(0, $again->getStatusCode());
|
|
|
+ $this->assertSame(0, json_decode($again->getDisplay(), true)['changed']);
|
|
|
+ $this->assertSame('2026-10-24', DB::table('share_system_state')->where('key', 'last_settlement_date')->value('value'));
|
|
|
+ }
|
|
|
+
|
|
|
+ public function test_debug_preview_today_is_read_only_including_levels_markers_and_reconciliation_queue(): void
|
|
|
+ {
|
|
|
+ $this->fact(2, '50000', '2026-10-25');
|
|
|
+ DB::table('share_levels')->insert(['level' => 2, 'name' => '二级', 'incremental_commission' => 100]);
|
|
|
+ $tables = ['share_accounts', 'share_settlements', 'share_ledger', 'share_members', 'share_system_state', 'wallets'];
|
|
|
+ $before = [];
|
|
|
+ foreach ($tables as $table) $before[$table] = DB::table($table)->get()->toJson();
|
|
|
+ $preview = $this->command(['--allow-today' => true, '--preview' => true]);
|
|
|
+ $this->assertSame(0, $preview->getStatusCode());
|
|
|
+ $data = json_decode($preview->getDisplay(), true);
|
|
|
+ $this->assertSame('2026-10-25', $data['date']);
|
|
|
+ $this->assertFalse($data['written']);
|
|
|
+ $this->assertGreaterThan(0, $data['changed']);
|
|
|
+ foreach ($tables as $table) $this->assertSame($before[$table], DB::table($table)->get()->toJson(), $table);
|
|
|
+ }
|
|
|
+
|
|
|
+ /** @dataProvider forbiddenDebugOptions */
|
|
|
+ public function test_future_dates_and_incompatible_debug_options_are_rejected_before_writing(array $options): void
|
|
|
+ {
|
|
|
+ $this->fact(2, '200', '2026-10-25');
|
|
|
+ $before = DB::table('share_system_state')->get()->toJson();
|
|
|
+ $command = $this->command($options);
|
|
|
+ $this->assertSame(1, $command->getStatusCode());
|
|
|
+ $this->assertSame(0, DB::table('share_ledger')->count());
|
|
|
+ $this->assertSame(0, DB::table('share_settlements')->count());
|
|
|
+ $this->assertSame('0.0000000000', $this->amount(1));
|
|
|
+ $this->assertSame($before, DB::table('share_system_state')->get()->toJson());
|
|
|
+ }
|
|
|
+
|
|
|
+ public static function forbiddenDebugOptions(): array
|
|
|
+ {
|
|
|
+ return [
|
|
|
+ 'future' => [['date' => '2026-10-26']],
|
|
|
+ 'future with opt in' => [['date' => '2026-10-26', '--allow-today' => true]],
|
|
|
+ 'future preview' => [['date' => '2026-10-26', '--allow-today' => true, '--preview' => true]],
|
|
|
+ 'invalid calendar date' => [['date' => '2026-02-30', '--allow-today' => true]],
|
|
|
+ 'reconcile' => [['--allow-today' => true, '--reconcile' => true]],
|
|
|
+ 'reconcile and preview' => [['--allow-today' => true, '--reconcile' => true, '--preview' => true]],
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ public function test_debug_today_uses_beijing_date_even_when_utc_is_still_yesterday(): void
|
|
|
+ {
|
|
|
+ Carbon::setTestNow(Carbon::parse('2026-10-25 00:01:00', 'Asia/Shanghai'));
|
|
|
+ $command = $this->command(['--allow-today' => true]);
|
|
|
+ $this->assertSame(0, $command->getStatusCode());
|
|
|
+ $this->assertSame('2026-10-25', json_decode($command->getDisplay(), true)['date']);
|
|
|
+ $this->assertFalse(DB::table('share_system_state')->where('key', 'last_settlement_date')->exists());
|
|
|
+ }
|
|
|
+
|
|
|
public function test_open_day_stays_queued_and_a_missed_midnight_run_is_automatically_recovered(): void
|
|
|
{
|
|
|
Carbon::setTestNow(Carbon::parse('2026-10-24 12:00:00','Asia/Shanghai'));
|