You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

184 lines
6.3 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<?php
namespace Tests\Feature;
use App\Models\AdminUser;
use App\Models\AudienceRegistration;
use App\Models\AudienceSession;
use App\Models\Competition;
use App\Models\CompetitionAdmin;
use App\Models\User;
use App\Support\BizDateTime;
use Carbon\Carbon;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Laravel\Sanctum\Sanctum;
use Tests\TestCase;
class CompetitionAdminAccessTest extends TestCase
{
use RefreshDatabase;
protected function setUp(): void
{
parent::setUp();
Carbon::setTestNow(Carbon::parse('2026-08-18 13:00:00', BizDateTime::TIMEZONE));
}
protected function tearDown(): void
{
Carbon::setTestNow();
parent::tearDown();
}
public function test_platform_admin_can_login_via_manage_portal(): void
{
$competition = $this->publishedCompetition();
$this->platformAdmin('admin', 'Admin123456');
$this->postJson('/api/v1/manage/auth/login', [
'username' => 'admin',
'password' => 'Admin123456',
'competition_slug' => $competition->slug,
])->assertOk()
->assertJsonPath('kind', 'platform')
->assertJsonPath('competition.slug', $competition->slug);
}
public function test_competition_admin_can_login_only_to_own_event(): void
{
$competition = $this->publishedCompetition();
$other = $this->publishedCompetition('other-2026');
$this->competitionAdmin($competition, 'hsxt_admin', 'Pass123456');
$this->postJson('/api/v1/manage/auth/login', [
'username' => 'hsxt_admin',
'password' => 'Pass123456',
'competition_slug' => $competition->slug,
])->assertOk()
->assertJsonPath('kind', 'competition');
$this->postJson('/api/v1/manage/auth/login', [
'username' => 'hsxt_admin',
'password' => 'Pass123456',
'competition_slug' => $other->slug,
])->assertUnprocessable();
}
public function test_competition_admin_cannot_access_platform_admin_users(): void
{
$competition = $this->publishedCompetition();
Sanctum::actingAs($this->competitionAdmin($competition));
$this->getJson('/api/v1/admin/admin-users')->assertForbidden();
$this->getJson('/api/v1/admin/competitions')->assertForbidden();
}
public function test_competition_admin_can_manage_own_audience_but_not_other_event(): void
{
$competition = $this->publishedCompetition();
$other = $this->publishedCompetition('other-2026');
Sanctum::actingAs($this->competitionAdmin($competition));
$this->getJson("/api/v1/admin/competitions/{$competition->id}/audience-registrations")
->assertOk();
$this->getJson("/api/v1/admin/competitions/{$other->id}/audience-registrations")
->assertForbidden();
}
public function test_platform_admin_can_crud_competition_admins(): void
{
Sanctum::actingAs($this->platformAdmin());
$competition = $this->publishedCompetition();
$this->postJson("/api/v1/admin/competitions/{$competition->id}/admins", [
'username' => 'event_admin',
'name' => '赛事管理员',
'password' => 'Pass123456',
'status' => 'active',
])->assertCreated()
->assertJsonPath('username', 'event_admin');
$this->assertDatabaseHas('competition_admins', [
'competition_id' => $competition->id,
'username' => 'event_admin',
]);
}
public function test_competition_admin_can_checkin(): void
{
$competition = $this->publishedCompetition();
$session = $this->openSession($competition);
$user = User::query()->create(['mobile' => '13800138200']);
$registration = AudienceRegistration::query()->create([
'competition_id' => $competition->id,
'audience_session_id' => $session->id,
'user_id' => $user->id,
'name' => '现场观众',
'phone' => '13800138200',
'purpose' => '项目交流',
'registered_at' => now(),
]);
Sanctum::actingAs($this->competitionAdmin($competition));
$this->postJson("/api/v1/admin/competitions/{$competition->id}/audience-checkin", [
'ticket_code' => $registration->ticket_code,
])->assertOk()
->assertJsonPath('already_checked_in', false);
$this->assertNotNull($registration->fresh()->checked_in_by_competition_admin_id);
}
private function publishedCompetition(string $slug = 'husu-2026'): Competition
{
return Competition::query()->create([
'slug' => $slug,
'name' => '沪苏协同新兴产业科创大赛',
'published' => true,
'status' => 'signup_open',
]);
}
/**
* @param array<string, mixed> $overrides
*/
private function openSession(Competition $competition, array $overrides = []): AudienceSession
{
return AudienceSession::query()->create(array_merge([
'competition_id' => $competition->id,
'code' => 'shanghai-preliminary',
'name' => '上海预赛路演',
'event_date_text' => '2026年9月18日星期五',
'address' => '上海',
'capacity' => 300,
'signup_start_at' => Carbon::parse('2026-08-10 00:00:00', BizDateTime::TIMEZONE),
'signup_end_at' => Carbon::parse('2026-09-10 23:59:59', BizDateTime::TIMEZONE),
'is_final' => false,
'sort' => 1,
'enabled' => true,
], $overrides));
}
private function platformAdmin(string $username = 'platform', string $password = 'Admin123456'): AdminUser
{
return AdminUser::query()->create([
'username' => $username,
'password_hash' => $password,
'name' => '平台管理员',
'status' => 'active',
]);
}
private function competitionAdmin(Competition $competition, string $username = 'event_admin', string $password = 'Pass123456'): CompetitionAdmin
{
return CompetitionAdmin::query()->create([
'competition_id' => $competition->id,
'username' => $username,
'password_hash' => $password,
'name' => '赛事管理员',
'status' => 'active',
]);
}
}