Skip to content

Commit

Permalink
Move score link complete function to static method
Browse files Browse the repository at this point in the history
It makes more sense to have model creation as part of it and prevents
"completing" existing one.
  • Loading branch information
nanaya committed Nov 1, 2023
1 parent 9dffc09 commit b3d98fa
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 75 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -198,14 +198,7 @@ public function update($roomId, $playlistItemId, $tokenId)

$params = Score::extractParams(\Request::all(), $scoreToken);

$scoreLink = $scoreToken
->playlistItem
->scoreLinks()
->make(['user_id' => $scoreToken->user_id]);
$room->completePlay($scoreLink, $params);
$scoreToken->fill(['score_id' => $scoreLink->score_id])->saveOrExplode();

return $scoreLink;
return $room->completePlay($scoreToken, $params);
});

$score = $scoreLink->score;
Expand Down
8 changes: 4 additions & 4 deletions app/Models/Multiplayer/Room.php
Original file line number Diff line number Diff line change
Expand Up @@ -400,14 +400,14 @@ public function calculateMissingTopScores()
}
}

public function completePlay(ScoreLink $scoreLink, array $params)
public function completePlay(ScoreToken $scoreToken, array $params): ScoreLink
{
priv_check_user($scoreLink->user, 'MultiplayerScoreSubmit')->ensureCan();
priv_check_user($scoreToken->user, 'MultiplayerScoreSubmit')->ensureCan();

$this->assertValidCompletePlay();

return $scoreLink->getConnection()->transaction(function () use ($params, $scoreLink) {
$scoreLink->complete($params);
return $this->getConnection()->transaction(function () use ($params, $scoreToken) {
$scoreLink = ScoreLink::complete($scoreToken, $params);
UserScoreAggregate::new($scoreLink->user, $this)->addScoreLink($scoreLink);

return $scoreLink;
Expand Down
66 changes: 34 additions & 32 deletions app/Models/Multiplayer/ScoreLink.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

use App\Exceptions\InvariantException;
use App\Models\Model;
use App\Models\ScoreToken;
use App\Models\Solo\Score;
use App\Models\User;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
Expand All @@ -24,6 +25,39 @@
*/
class ScoreLink extends Model
{
public static function complete(ScoreToken $token, array $params): static
{
return \DB::transaction(function () use ($params, $token) {
$score = Score::createFromJsonOrExplode($params);

$playlistItem = $token->playlistItem;
$requiredMods = array_column($playlistItem->required_mods, 'acronym');
$mods = array_column($score->data->mods, 'acronym');
if (!empty($requiredMods)) {
if (!empty(array_diff($requiredMods, $mods))) {
throw new InvariantException('This play does not include the mods required.');
}
}

$allowedMods = array_column($playlistItem->allowed_mods, 'acronym');
if (!empty(array_diff($mods, $requiredMods, $allowedMods))) {
throw new InvariantException('This play includes mods that are not allowed.');
}

$scoreId = $score->getKey();
$token->fill(['score_id' => $scoreId])->saveOrExplode();

$ret = new static([
'playlist_item_id' => $playlistItem->getKey(),
'score_id' => $scoreId,
'user_id' => $score->user_id,
]);
$ret->saveOrExplode();

return $ret;
});
}

public $incrementing = false;
public $timestamps = false;

Expand Down Expand Up @@ -64,38 +98,6 @@ public function getAttribute($key)
};
}

public function complete(array $params): void
{
$this->getConnection()->transaction(function () use ($params) {
$score = Score::createFromJsonOrExplode($params);
$mods = $score->data->mods;

if (!empty($this->playlistItem->required_mods)) {
$missingMods = array_diff(
array_column($this->playlistItem->required_mods, 'acronym'),
array_column($mods, 'acronym')
);

if (!empty($missingMods)) {
throw new InvariantException('This play does not include the mods required.');
}
}

$disallowedMods = array_diff(
array_column($mods, 'acronym'),
array_column($this->playlistItem->required_mods, 'acronym'),
array_column($this->playlistItem->allowed_mods, 'acronym')
);

if (!empty($disallowedMods)) {
throw new InvariantException('This play includes mods that are not allowed.');
}

$this->score()->associate($score);
$this->save();
});
}

public function position(): ?int
{
$score = $this->score;
Expand Down
52 changes: 21 additions & 31 deletions tests/Models/Multiplayer/ScoreLinkTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,32 +10,30 @@
use App\Exceptions\InvariantException;
use App\Models\Multiplayer\PlaylistItem;
use App\Models\Multiplayer\ScoreLink;
use App\Models\User;
use App\Models\ScoreToken;
use Carbon\Carbon;
use Tests\TestCase;

class ScoreLinkTest extends TestCase
{
public function testRequiredModsMissing()
{
$user = User::factory()->create();
$playlistItem = PlaylistItem::factory()->create([
'required_mods' => [[
'acronym' => 'HD',
]],
]);
$scoreLink = ScoreLink::factory()->make([
'score_id' => null,
'user_id' => $user,
$scoreToken = ScoreToken::factory()->create([
'beatmap_id' => $playlistItem->beatmap_id,
'playlist_item_id' => $playlistItem,
]);

$this->expectException(InvariantException::class);
$this->expectExceptionMessage('This play does not include the mods required.');
$scoreLink->complete([
ScoreLink::complete($scoreToken, [
'beatmap_id' => $playlistItem->beatmap_id,
'ruleset_id' => $playlistItem->ruleset_id,
'user_id' => $user->getKey(),
'user_id' => $scoreToken->user_id,
'ended_at' => json_date(Carbon::now()),
'mods' => [],
'statistics' => [
Expand All @@ -46,23 +44,21 @@ public function testRequiredModsMissing()

public function testRequiredModsPresent()
{
$user = User::factory()->create();
$playlistItem = PlaylistItem::factory()->create([
'required_mods' => [[
'acronym' => 'HD',
]],
]);
$scoreLink = ScoreLink::factory()->make([
'score_id' => null,
'user_id' => $user,
$scoreToken = ScoreToken::factory()->create([
'beatmap_id' => $playlistItem->beatmap_id,
'playlist_item_id' => $playlistItem,
]);

$this->expectNotToPerformAssertions();
$scoreLink->complete([
ScoreLink::complete($scoreToken, [
'beatmap_id' => $playlistItem->beatmap_id,
'ruleset_id' => $playlistItem->ruleset_id,
'user_id' => $user->getKey(),
'user_id' => $scoreToken->user_id,
'ended_at' => json_date(Carbon::now()),
'mods' => [['acronym' => 'HD']],
'statistics' => [
Expand All @@ -73,7 +69,6 @@ public function testRequiredModsPresent()

public function testExpectedAllowedMod()
{
$user = User::factory()->create();
$playlistItem = PlaylistItem::factory()->create([
'required_mods' => [[
'acronym' => 'DT',
Expand All @@ -82,17 +77,16 @@ public function testExpectedAllowedMod()
'acronym' => 'HD',
]],
]);
$scoreLink = ScoreLink::factory()->make([
'score_id' => null,
'user_id' => $user,
$scoreToken = ScoreToken::factory()->create([
'beatmap_id' => $playlistItem->beatmap_id,
'playlist_item_id' => $playlistItem,
]);

$this->expectNotToPerformAssertions();
$scoreLink->complete([
ScoreLink::complete($scoreToken, [
'beatmap_id' => $playlistItem->beatmap_id,
'ruleset_id' => $playlistItem->ruleset_id,
'user_id' => $user->getKey(),
'user_id' => $scoreToken->user_id,
'ended_at' => json_date(Carbon::now()),
'mods' => [
['acronym' => 'DT'],
Expand All @@ -106,7 +100,6 @@ public function testExpectedAllowedMod()

public function testUnexpectedAllowedMod()
{
$user = User::factory()->create();
$playlistItem = PlaylistItem::factory()->create([
'required_mods' => [[
'acronym' => 'DT',
Expand All @@ -115,18 +108,17 @@ public function testUnexpectedAllowedMod()
'acronym' => 'HR',
]],
]);
$scoreLink = ScoreLink::factory()->make([
'score_id' => null,
'user_id' => $user,
$scoreToken = ScoreToken::factory()->create([
'beatmap_id' => $playlistItem->beatmap_id,
'playlist_item_id' => $playlistItem,
]);

$this->expectException(InvariantException::class);
$this->expectExceptionMessage('This play includes mods that are not allowed.');
$scoreLink->complete([
ScoreLink::complete($scoreToken, [
'beatmap_id' => $playlistItem->beatmap_id,
'ruleset_id' => $playlistItem->ruleset_id,
'user_id' => $user->getKey(),
'user_id' => $scoreToken->user_id,
'ended_at' => json_date(Carbon::now()),
'mods' => [
['acronym' => 'DT'],
Expand All @@ -140,20 +132,18 @@ public function testUnexpectedAllowedMod()

public function testUnexpectedModWhenNoModsAreAllowed()
{
$user = User::factory()->create();
$playlistItem = PlaylistItem::factory()->create(); // no required or allowed mods.
$scoreLink = ScoreLink::factory()->make([
'score_id' => null,
'user_id' => $user,
$scoreToken = ScoreToken::factory()->create([
'beatmap_id' => $playlistItem->beatmap_id,
'playlist_item_id' => $playlistItem,
]);

$this->expectException(InvariantException::class);
$this->expectExceptionMessage('This play includes mods that are not allowed.');
$scoreLink->complete([
ScoreLink::complete($scoreToken, [
'beatmap_id' => $playlistItem->beatmap_id,
'ruleset_id' => $playlistItem->ruleset_id,
'user_id' => $user->getKey(),
'user_id' => $scoreToken->user_id,
'ended_at' => json_date(Carbon::now()),
'mods' => [['acronym' => 'HD']],
'statistics' => [
Expand Down

0 comments on commit b3d98fa

Please sign in to comment.