|
|
<?php
|
|
|
|
|
|
namespace Tests\Feature;
|
|
|
|
|
|
use App\Models\Application;
|
|
|
use App\Models\Competition;
|
|
|
use App\Models\CompetitionTrack;
|
|
|
use App\Models\Reviewer;
|
|
|
use App\Models\ReviewerScope;
|
|
|
use App\Models\User;
|
|
|
use App\Support\PortalLocale;
|
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
|
use Tests\TestCase;
|
|
|
|
|
|
class ReviewApplicationScopeAndScoreTest extends TestCase
|
|
|
{
|
|
|
use RefreshDatabase;
|
|
|
|
|
|
public function test_reviewer_cannot_list_or_open_other_region_by_query(): void
|
|
|
{
|
|
|
$ctx = $this->seedReviewerWithRegions();
|
|
|
$this->actingAsReviewer($ctx['reviewer']);
|
|
|
|
|
|
$this->getJson('/api/v1/review/applications?competition_slug=hsxt&event_location=suzhou')
|
|
|
->assertOk()
|
|
|
->assertJsonPath('meta.total', 1)
|
|
|
->assertJsonPath('data.0.id', $ctx['shanghai']->id);
|
|
|
|
|
|
$ids = collect($this->getJson('/api/v1/review/applications?competition_slug=hsxt')->json('data'))->pluck('id');
|
|
|
$this->assertTrue($ids->contains($ctx['shanghai']->id));
|
|
|
$this->assertFalse($ids->contains($ctx['suzhou']->id));
|
|
|
|
|
|
$this->getJson('/api/v1/review/applications/'.$ctx['suzhou']->id.'?competition_slug=hsxt')
|
|
|
->assertForbidden();
|
|
|
|
|
|
$this->postJson('/api/v1/review/applications/'.$ctx['suzhou']->id.'/score', [
|
|
|
'competition_slug' => 'hsxt',
|
|
|
'payload' => ['scores' => ['a' => 10, 'b' => 20]],
|
|
|
])->assertForbidden();
|
|
|
}
|
|
|
|
|
|
public function test_score_submit_requires_item_scores_and_stores_sum(): void
|
|
|
{
|
|
|
$ctx = $this->seedReviewerWithRegions();
|
|
|
$this->actingAsReviewer($ctx['reviewer']);
|
|
|
|
|
|
$this->postJson('/api/v1/review/applications/'.$ctx['shanghai']->id.'/score', [
|
|
|
'competition_slug' => 'hsxt',
|
|
|
'payload' => ['line_total' => 88],
|
|
|
])->assertUnprocessable();
|
|
|
|
|
|
$this->postJson('/api/v1/review/applications/'.$ctx['shanghai']->id.'/score', [
|
|
|
'competition_slug' => 'hsxt',
|
|
|
'payload' => ['scores' => ['a' => 40.01, 'b' => 0]],
|
|
|
])->assertUnprocessable();
|
|
|
|
|
|
$this->postJson('/api/v1/review/applications/'.$ctx['shanghai']->id.'/score', [
|
|
|
'competition_slug' => 'hsxt',
|
|
|
'payload' => ['scores' => ['a' => 12.5, 'b' => 30]],
|
|
|
])->assertOk()
|
|
|
->assertJsonPath('data.my_review_score.line_total', '42.50')
|
|
|
->assertJsonPath('data.my_review_score.payload_json.scores.a', 12.5)
|
|
|
->assertJsonPath('data.my_review_score.payload_json.line_total', 42.5);
|
|
|
}
|
|
|
|
|
|
public function test_suzhou_reviewer_sees_chinese_and_english_location_values(): void
|
|
|
{
|
|
|
$ctx = $this->seedSuzhouReviewerWithMixedLocationLabels();
|
|
|
$this->actingAsReviewer($ctx['reviewer']);
|
|
|
|
|
|
$ids = collect($this->getJson('/api/v1/review/applications?competition_slug=hsxt')->json('data'))
|
|
|
->pluck('id')
|
|
|
->all();
|
|
|
|
|
|
$this->assertContains($ctx['zh']->id, $ids);
|
|
|
$this->assertContains($ctx['en']->id, $ids);
|
|
|
$this->assertContains($ctx['code']->id, $ids);
|
|
|
$this->assertNotContains($ctx['shanghai']->id, $ids);
|
|
|
|
|
|
$this->getJson('/api/v1/review/applications/'.$ctx['zh']->id.'?competition_slug=hsxt')
|
|
|
->assertOk()
|
|
|
->assertJsonPath('data.id', $ctx['zh']->id);
|
|
|
$this->getJson('/api/v1/review/applications/'.$ctx['en']->id.'?competition_slug=hsxt')
|
|
|
->assertOk();
|
|
|
}
|
|
|
|
|
|
public function test_suzhou_reviewer_sees_other_track_in_same_region(): void
|
|
|
{
|
|
|
$ctx = $this->seedSuzhouReviewerWithMixedLocationLabels();
|
|
|
$other = Application::query()->create([
|
|
|
'user_id' => User::query()->create(['mobile' => '13800138105'])->id,
|
|
|
'competition_id' => $ctx['code']->competition_id,
|
|
|
'status' => 'submitted',
|
|
|
'review_eligible' => true,
|
|
|
'track' => 'other',
|
|
|
'event_location' => 'suzhou',
|
|
|
'project_name' => '其他赛道苏州项目',
|
|
|
'degree' => 'bachelor',
|
|
|
'location_country' => 'cn',
|
|
|
'location_province' => '江苏省',
|
|
|
'location_city' => '苏州市',
|
|
|
]);
|
|
|
$this->actingAsReviewer($ctx['reviewer']);
|
|
|
|
|
|
$list = $this->getJson('/api/v1/review/applications?competition_slug=hsxt')->assertOk();
|
|
|
$ids = collect($list->json('data'))->pluck('id')->all();
|
|
|
$this->assertContains($other->id, $ids);
|
|
|
$row = collect($list->json('data'))->firstWhere('id', $other->id);
|
|
|
$this->assertSame('本科', $row['degree']);
|
|
|
$this->assertSame('中国 / 江苏省 / 苏州市', $row['location_label']);
|
|
|
$this->assertSame('苏州', $row['event_location_label']);
|
|
|
$this->getJson('/api/v1/review/applications/'.$other->id.'?competition_slug=hsxt')
|
|
|
->assertOk();
|
|
|
}
|
|
|
|
|
|
public function test_english_signup_shows_english_option_labels(): void
|
|
|
{
|
|
|
$ctx = $this->seedSuzhouReviewerWithMixedLocationLabels();
|
|
|
$en = Application::query()->create([
|
|
|
'user_id' => User::query()->create(['mobile' => '13800138106'])->id,
|
|
|
'competition_id' => $ctx['code']->competition_id,
|
|
|
'status' => 'submitted',
|
|
|
'review_eligible' => true,
|
|
|
'track' => 'track_a',
|
|
|
'event_location' => 'suzhou',
|
|
|
'project_name' => 'English Suzhou Project',
|
|
|
'signup_locale' => PortalLocale::EN,
|
|
|
'degree' => 'bachelor',
|
|
|
'location_country' => 'cn',
|
|
|
'location_province' => 'Jiangsu',
|
|
|
'location_city' => 'Suzhou',
|
|
|
]);
|
|
|
$this->actingAsReviewer($ctx['reviewer']);
|
|
|
|
|
|
$list = $this->getJson('/api/v1/review/applications?competition_slug=hsxt')->assertOk();
|
|
|
$row = collect($list->json('data'))->firstWhere('id', $en->id);
|
|
|
$this->assertSame('Bachelor’s Degree', $row['degree']);
|
|
|
$this->assertSame('China / Jiangsu / Suzhou', $row['location_label']);
|
|
|
$this->assertSame('Suzhou', $row['event_location_label']);
|
|
|
|
|
|
$this->getJson('/api/v1/review/applications/'.$en->id.'?competition_slug=hsxt')
|
|
|
->assertOk()
|
|
|
->assertJsonPath('data.degree_label', 'Bachelor’s Degree')
|
|
|
->assertJsonPath('data.location_country_label', 'China')
|
|
|
->assertJsonPath('data.event_location_label', 'Suzhou');
|
|
|
}
|
|
|
|
|
|
private function actingAsReviewer(Reviewer $reviewer): void
|
|
|
{
|
|
|
$token = $reviewer->createToken('review')->plainTextToken;
|
|
|
$this->withToken($token);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* @return array{reviewer: Reviewer, shanghai: Application, suzhou: Application}
|
|
|
*/
|
|
|
private function seedReviewerWithRegions(): array
|
|
|
{
|
|
|
$competition = Competition::query()->create([
|
|
|
'slug' => 'hsxt',
|
|
|
'name' => '沪苏协同新兴产业科创大赛',
|
|
|
'published' => true,
|
|
|
'status' => 'signup_open',
|
|
|
]);
|
|
|
CompetitionTrack::query()->create([
|
|
|
'competition_id' => $competition->id,
|
|
|
'track_code' => 'track_a',
|
|
|
'title' => '赛道A',
|
|
|
'sort' => 1,
|
|
|
'is_enabled' => true,
|
|
|
'scoring_sheet_json' => [
|
|
|
'version' => 1,
|
|
|
'title' => '测试评分表',
|
|
|
'items' => [
|
|
|
[
|
|
|
'key' => 'a',
|
|
|
'sort' => 1,
|
|
|
'category' => '通用',
|
|
|
'title' => 'A',
|
|
|
'criteria' => '',
|
|
|
'max_score' => 40,
|
|
|
],
|
|
|
[
|
|
|
'key' => 'b',
|
|
|
'sort' => 2,
|
|
|
'category' => '通用',
|
|
|
'title' => 'B',
|
|
|
'criteria' => '',
|
|
|
'max_score' => 60,
|
|
|
],
|
|
|
],
|
|
|
],
|
|
|
]);
|
|
|
$reviewer = Reviewer::query()->create([
|
|
|
'mobile' => '13900139001',
|
|
|
'username' => 'rev_sh',
|
|
|
'name' => '上海评委',
|
|
|
'status' => 'active',
|
|
|
'password_hash' => 'unused',
|
|
|
]);
|
|
|
ReviewerScope::query()->create([
|
|
|
'reviewer_id' => $reviewer->id,
|
|
|
'competition_id' => $competition->id,
|
|
|
'track_code' => 'track_a',
|
|
|
'event_location' => 'shanghai',
|
|
|
]);
|
|
|
$userA = User::query()->create(['mobile' => '13800138001']);
|
|
|
$userB = User::query()->create(['mobile' => '13800138002']);
|
|
|
$shanghai = Application::query()->create([
|
|
|
'user_id' => $userA->id,
|
|
|
'competition_id' => $competition->id,
|
|
|
'status' => 'submitted',
|
|
|
'review_eligible' => true,
|
|
|
'track' => 'track_a',
|
|
|
'event_location' => 'shanghai',
|
|
|
'project_name' => '上海项目',
|
|
|
]);
|
|
|
$suzhou = Application::query()->create([
|
|
|
'user_id' => $userB->id,
|
|
|
'competition_id' => $competition->id,
|
|
|
'status' => 'submitted',
|
|
|
'review_eligible' => true,
|
|
|
'track' => 'track_a',
|
|
|
'event_location' => 'suzhou',
|
|
|
'project_name' => '苏州项目',
|
|
|
]);
|
|
|
|
|
|
return [
|
|
|
'reviewer' => $reviewer,
|
|
|
'shanghai' => $shanghai,
|
|
|
'suzhou' => $suzhou,
|
|
|
];
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* @return array{reviewer: Reviewer, zh: Application, en: Application, code: Application, shanghai: Application}
|
|
|
*/
|
|
|
private function seedSuzhouReviewerWithMixedLocationLabels(): array
|
|
|
{
|
|
|
$competition = Competition::query()->create([
|
|
|
'slug' => 'hsxt',
|
|
|
'name' => '沪苏协同新兴产业科创大赛',
|
|
|
'published' => true,
|
|
|
'status' => 'signup_open',
|
|
|
]);
|
|
|
CompetitionTrack::query()->create([
|
|
|
'competition_id' => $competition->id,
|
|
|
'track_code' => 'track_a',
|
|
|
'title' => '赛道A',
|
|
|
'sort' => 1,
|
|
|
'is_enabled' => true,
|
|
|
]);
|
|
|
$reviewer = Reviewer::query()->create([
|
|
|
'mobile' => '13900139008',
|
|
|
'username' => 'rev_sz',
|
|
|
'name' => '苏州评委',
|
|
|
'status' => 'active',
|
|
|
'password_hash' => 'unused',
|
|
|
]);
|
|
|
ReviewerScope::query()->create([
|
|
|
'reviewer_id' => $reviewer->id,
|
|
|
'competition_id' => $competition->id,
|
|
|
'track_code' => 'track_a',
|
|
|
'event_location' => 'suzhou',
|
|
|
]);
|
|
|
|
|
|
$makeApp = function (string $mobile, string $location, string $name) use ($competition): Application {
|
|
|
return Application::query()->create([
|
|
|
'user_id' => User::query()->create(['mobile' => $mobile])->id,
|
|
|
'competition_id' => $competition->id,
|
|
|
'status' => 'submitted',
|
|
|
'review_eligible' => true,
|
|
|
'track' => 'track_a',
|
|
|
'event_location' => $location,
|
|
|
'project_name' => $name,
|
|
|
]);
|
|
|
};
|
|
|
|
|
|
return [
|
|
|
'reviewer' => $reviewer,
|
|
|
'zh' => $makeApp('13800138101', '苏州', '中文苏州项目'),
|
|
|
'en' => $makeApp('13800138102', 'Suzhou', '英文苏州项目'),
|
|
|
'code' => $makeApp('13800138103', 'suzhou', '编码苏州项目'),
|
|
|
'shanghai' => $makeApp('13800138104', 'shanghai', '上海项目'),
|
|
|
];
|
|
|
}
|
|
|
}
|