- Draw相关控制器添加provider_code和provider_name字段支持 - 允许手动录入开奖批次时指定provider_code - RNG开奖批次生成支持多provider,分别生成对应批次数据 - DrawPublishService和DrawManualResultService中支持基于provider_code管理开奖版本和发布流程 - DrawResultViewService调整,支持按provider汇总及筛选开奖结果 - Settlement处理逻辑调整,按provider区分待结算票据及批次,分开结算 - 结算期间管理相关服务支持使用站点本地结算时区计算开账建议和日期处理 - 增加AdminSite的settlement_timezone字段及相关请求验证和数据存取支持 - 优化AdminSettlementPeriod相关代码,防止存在未结清票据时关账 - Ticket明细返回接口添加结算时区信息,提升用户时间体验 - 多处查询排序和版本号处理改进,保证多provider数据正确顺序与一致性
139 lines
4.5 KiB
PHP
139 lines
4.5 KiB
PHP
<?php
|
|
|
|
use Firebase\JWT\JWT;
|
|
use App\Models\Player;
|
|
use App\Lottery\ErrorCode;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
test('player me returns profile with dev bearer', function () {
|
|
$player = Player::query()->create([
|
|
'site_code' => 'main',
|
|
'site_player_id' => 'uid-42',
|
|
'username' => 'alice',
|
|
'nickname' => 'A',
|
|
'default_currency' => 'NPR',
|
|
'status' => 0,
|
|
]);
|
|
|
|
$this->withHeaders([
|
|
'Authorization' => 'Bearer dev:'.$player->id,
|
|
'X-Locale' => 'zh',
|
|
])
|
|
->getJson('/api/v1/player/me')
|
|
->assertOk()
|
|
->assertJsonPath('code', ErrorCode::Success->value)
|
|
->assertJsonPath('data.id', $player->id)
|
|
->assertJsonPath('data.site_player_id', 'uid-42')
|
|
->assertJsonPath('data.username', 'alice')
|
|
->assertJsonPath('data.settlement_timezone', 'UTC')
|
|
->assertJsonPath('data.locale', 'zh')
|
|
->assertJsonStructure([
|
|
'data' => [
|
|
'settlement_timezone',
|
|
'last_login_at',
|
|
'created_at',
|
|
],
|
|
]);
|
|
|
|
$player->refresh();
|
|
expect($player->last_login_at)->not->toBeNull();
|
|
});
|
|
|
|
test('player auth missing bearer returns localized sso 8001', function () {
|
|
$code = ErrorCode::PlayerAuthorizationInvalid->value;
|
|
$this->withHeader('Accept-Language', 'zh-CN,zh;q=0.9')
|
|
->getJson('/api/v1/player/me')
|
|
->assertStatus(Response::HTTP_UNAUTHORIZED)
|
|
->assertJsonPath('code', $code)
|
|
->assertJsonPath('msg', __("sso.$code", [], 'zh'));
|
|
});
|
|
|
|
test('api unknown route returns unified not_found json without hitting locale middleware', function () {
|
|
$this->withHeader('X-Locale', 'zh')
|
|
->getJson('/api/v1/player/__no_route__xxx')
|
|
->assertStatus(Response::HTTP_NOT_FOUND)
|
|
->assertJsonPath('code', ErrorCode::NotFound->value)
|
|
->assertJsonPath('msg', __('api.not_found', [], 'zh'));
|
|
});
|
|
|
|
test('player me works with main site jwt when dev bypass is off', function () {
|
|
config(['lottery.player_auth.dev_bypass' => false]);
|
|
config(['lottery.main_site.sso_jwt_secret' => 'jwt-test-secret']);
|
|
|
|
$player = Player::query()->create([
|
|
'site_code' => 'main',
|
|
'site_player_id' => 'jwt-user-1',
|
|
'username' => null,
|
|
'nickname' => null,
|
|
'default_currency' => 'NPR',
|
|
'status' => 0,
|
|
]);
|
|
|
|
$now = time();
|
|
$jwt = JWT::encode([
|
|
'site_code' => 'main',
|
|
'site_player_id' => 'jwt-user-1',
|
|
'iat' => $now,
|
|
'exp' => $now + 300,
|
|
], 'jwt-test-secret', 'HS256');
|
|
|
|
$this->withHeader('Authorization', 'Bearer '.$jwt)
|
|
->getJson('/api/v1/player/me')
|
|
->assertOk()
|
|
->assertJsonPath('data.site_player_id', 'jwt-user-1');
|
|
});
|
|
|
|
test('jwt first successful login auto-registers player mapping', function () {
|
|
config(['lottery.player_auth.dev_bypass' => false]);
|
|
config(['lottery.main_site.sso_jwt_secret' => 'jwt-test-secret']);
|
|
|
|
expect(Player::query()->count())->toBe(0);
|
|
|
|
$now = time();
|
|
$jwt = JWT::encode([
|
|
'site_code' => 'main',
|
|
'site_player_id' => 'brand-new-sso-1',
|
|
'iat' => $now,
|
|
'exp' => $now + 300,
|
|
], 'jwt-test-secret', 'HS256');
|
|
|
|
$response = $this->withHeader('Authorization', 'Bearer '.$jwt)
|
|
->getJson('/api/v1/player/me')
|
|
->assertOk()
|
|
->assertJsonPath('data.site_player_id', 'brand-new-sso-1')
|
|
->assertJsonPath('data.default_currency', 'NPR');
|
|
|
|
$username = $response->json('data.username');
|
|
expect($username)->toMatch('/^nlotto\d{6}$/')
|
|
->and($response->json('data.nickname'))->toBe($username);
|
|
|
|
$player = Player::query()->where('site_player_id', 'brand-new-sso-1')->first();
|
|
expect($player)->not->toBeNull()
|
|
->and($player->username)->toBe($username)
|
|
->and($player->nickname)->toBe($username);
|
|
});
|
|
|
|
test('player me rejects non-active status with 8005', function () {
|
|
$code = ErrorCode::PlayerAccountSuspended->value;
|
|
$player = Player::query()->create([
|
|
'site_code' => 'main',
|
|
'site_player_id' => 'frozen-1',
|
|
'username' => null,
|
|
'nickname' => null,
|
|
'default_currency' => 'NPR',
|
|
'status' => 1,
|
|
]);
|
|
|
|
$this->withHeaders([
|
|
'Authorization' => 'Bearer dev:'.$player->id,
|
|
'Accept-Language' => 'zh-CN,zh;q=0.9',
|
|
])
|
|
->getJson('/api/v1/player/me')
|
|
->assertStatus(403)
|
|
->assertJsonPath('code', $code)
|
|
->assertJsonPath('msg', __("sso.$code", [], 'zh'));
|
|
});
|