mirror of
https://github.com/koel/koel
synced 2024-11-10 06:34:14 +00:00
feat(plus): add song interaction tests
This commit is contained in:
parent
d31479019a
commit
0407a000e8
20 changed files with 264 additions and 116 deletions
|
@ -6,7 +6,7 @@ use App\Models\User;
|
|||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class SongsBatchLiked extends Event
|
||||
class MultipleSongsLiked extends Event
|
||||
{
|
||||
use SerializesModels;
|
||||
|
|
@ -6,7 +6,7 @@ use App\Models\User;
|
|||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class SongsBatchUnliked extends Event
|
||||
class MultipleSongsUnliked extends Event
|
||||
{
|
||||
use SerializesModels;
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API\Interaction;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\API\BatchInteractionRequest;
|
||||
use App\Models\User;
|
||||
use App\Repositories\SongRepository;
|
||||
use App\Services\InteractionService;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
class BatchLikeController extends Controller
|
||||
{
|
||||
/** @param User $user */
|
||||
public function __construct(
|
||||
private SongRepository $songRepository,
|
||||
private InteractionService $interactionService,
|
||||
private ?Authenticatable $user
|
||||
) {
|
||||
}
|
||||
|
||||
public function store(BatchInteractionRequest $request)
|
||||
{
|
||||
$this->songRepository->getMany(ids: $request->songs, scopedUser: $this->user)
|
||||
->each(fn ($song) => $this->authorize('access', $song));
|
||||
|
||||
$interactions = $this->interactionService->batchLike(Arr::wrap($request->songs), $this->user);
|
||||
|
||||
return response()->json($interactions);
|
||||
}
|
||||
|
||||
public function destroy(BatchInteractionRequest $request)
|
||||
{
|
||||
$this->songRepository->getMany(ids: $request->songs, scopedUser: $this->user)
|
||||
->each(fn ($song) => $this->authorize('access', $song));
|
||||
|
||||
$this->interactionService->batchUnlike(Arr::wrap($request->songs), $this->user);
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
}
|
25
app/Http/Controllers/API/LikeMultipleSongsController.php
Normal file
25
app/Http/Controllers/API/LikeMultipleSongsController.php
Normal file
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\API\InteractWithMultipleSongsRequest;
|
||||
use App\Models\Song;
|
||||
use App\Models\User;
|
||||
use App\Services\InteractionService;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
|
||||
class LikeMultipleSongsController extends Controller
|
||||
{
|
||||
/** @param User $user */
|
||||
public function __invoke(
|
||||
InteractWithMultipleSongsRequest $request,
|
||||
InteractionService $interactionService,
|
||||
Authenticatable $user
|
||||
) {
|
||||
$songs = Song::query()->findMany($request->songs);
|
||||
$songs->each(fn (Song $song) => $this->authorize('access', $song));
|
||||
|
||||
return response()->json($interactionService->likeMany($songs, $user));
|
||||
}
|
||||
}
|
|
@ -19,7 +19,7 @@ class MakeSongsPublicController extends Controller
|
|||
SongService $songService,
|
||||
Authenticatable $user
|
||||
) {
|
||||
$songs = Song::find($request->songs);
|
||||
$songs = Song::query()->find($request->songs);
|
||||
$songs->each(fn ($song) => $this->authorize('own', $song));
|
||||
|
||||
$songService->makeSongsPublic($songs);
|
||||
|
|
|
@ -1,24 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API\Interaction;
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Events\PlaybackStarted;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\API\Interaction\IncreasePlayCountRequest;
|
||||
use App\Http\Resources\InteractionResource;
|
||||
use App\Models\Song;
|
||||
use App\Models\User;
|
||||
use App\Services\InteractionService;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
|
||||
class HandlePlaybackStartedController extends Controller
|
||||
class RegisterPlayController extends Controller
|
||||
{
|
||||
/** @param User $user */
|
||||
public function __invoke(
|
||||
IncreasePlayCountRequest $request,
|
||||
InteractionService $interactionService,
|
||||
Authenticatable $user
|
||||
?Authenticatable $user
|
||||
) {
|
||||
$interaction = $interactionService->increasePlayCount($request->song, $user);
|
||||
/** @var Song $song */
|
||||
$song = Song::query()->findOrFail($request->song);
|
||||
$this->authorize('access', $song);
|
||||
|
||||
$interaction = $interactionService->increasePlayCount($song, $user);
|
||||
event(new PlaybackStarted($interaction->song, $interaction->user));
|
||||
|
||||
return InteractionResource::make($interaction);
|
|
@ -53,7 +53,7 @@ class SongController extends Controller
|
|||
public function update(SongUpdateRequest $request)
|
||||
{
|
||||
// Don't use SongRepository::findMany() because it'd be already catered to the current user.
|
||||
Song::find($request->songs)->each(fn (Song $song) => $this->authorize('edit', $song));
|
||||
Song::query()->find($request->songs)->each(fn (Song $song) => $this->authorize('edit', $song));
|
||||
|
||||
$updatedSongs = $this->songService->updateSongs($request->songs, SongUpdateData::fromRequest($request));
|
||||
$albums = $this->albumRepository->getMany($updatedSongs->pluck('album_id')->toArray());
|
||||
|
@ -76,7 +76,7 @@ class SongController extends Controller
|
|||
public function destroy(DeleteSongsRequest $request)
|
||||
{
|
||||
// Don't use SongRepository::findMany() because it'd be already catered to the current user.
|
||||
Song::find($request->songs)->each(fn (Song $song) => $this->authorize('delete', $song));
|
||||
Song::query()->findMany($request->songs)->each(fn (Song $song) => $this->authorize('delete', $song));
|
||||
|
||||
$this->songService->deleteSongs($request->songs);
|
||||
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API\Interaction;
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\API\ToggleLikeSongRequest;
|
||||
use App\Http\Resources\InteractionResource;
|
||||
use App\Models\Song;
|
||||
use App\Models\User;
|
||||
use App\Repositories\SongRepository;
|
||||
use App\Services\InteractionService;
|
||||
|
@ -19,9 +20,10 @@ class ToggleLikeSongController extends Controller
|
|||
InteractionService $interactionService,
|
||||
?Authenticatable $user
|
||||
) {
|
||||
$song = $songRepository->getOne($request->song, $user);
|
||||
/** @var Song $song */
|
||||
$song = Song::query()->findOrFail($request->song);
|
||||
$this->authorize('access', $song);
|
||||
|
||||
return InteractionResource::make($interactionService->toggleLike($request->song, $user));
|
||||
return InteractionResource::make($interactionService->toggleLike($song, $user));
|
||||
}
|
||||
}
|
27
app/Http/Controllers/API/UnlikeMultipleSongsController.php
Normal file
27
app/Http/Controllers/API/UnlikeMultipleSongsController.php
Normal file
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\API\InteractWithMultipleSongsRequest;
|
||||
use App\Models\Song;
|
||||
use App\Models\User;
|
||||
use App\Services\InteractionService;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
|
||||
class UnlikeMultipleSongsController extends Controller
|
||||
{
|
||||
/** @param User $user */
|
||||
public function __invoke(
|
||||
InteractWithMultipleSongsRequest $request,
|
||||
InteractionService $interactionService,
|
||||
Authenticatable $user
|
||||
) {
|
||||
$songs = Song::query()->findMany($request->songs);
|
||||
$songs->each(fn (Song $song) => $this->authorize('access', $song));
|
||||
|
||||
$interactionService->unlikeMany($songs, $user);
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
}
|
|
@ -8,7 +8,7 @@ use Illuminate\Validation\Rule;
|
|||
/**
|
||||
* @property array<string> $songs
|
||||
*/
|
||||
class BatchInteractionRequest extends Request
|
||||
class InteractWithMultipleSongsRequest extends Request
|
||||
{
|
||||
/** @return array<mixed> */
|
||||
public function rules(): array
|
|
@ -3,9 +3,10 @@
|
|||
namespace App\Http\Requests\API\Interaction;
|
||||
|
||||
use App\Http\Requests\API\Request;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
/**
|
||||
* @property string $song The song's ID
|
||||
* @property-read string $song The song's ID
|
||||
*/
|
||||
class IncreasePlayCountRequest extends Request
|
||||
{
|
||||
|
@ -13,7 +14,7 @@ class IncreasePlayCountRequest extends Request
|
|||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'song' => 'required',
|
||||
'song' => ['required', Rule::exists('songs', 'id')],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,4 +5,11 @@ namespace App\Http\Requests\API;
|
|||
/** @property-read string $song */
|
||||
class ToggleLikeSongRequest extends Request
|
||||
{
|
||||
/** @return array<mixed> */
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'song' => 'required|exists:songs,id',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace App\Listeners;
|
||||
|
||||
use App\Events\SongsBatchLiked;
|
||||
use App\Events\MultipleSongsLiked;
|
||||
use App\Services\LastfmService;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
|
||||
|
@ -12,7 +12,7 @@ class LoveMultipleTracksOnLastfm implements ShouldQueue
|
|||
{
|
||||
}
|
||||
|
||||
public function handle(SongsBatchLiked $event): void
|
||||
public function handle(MultipleSongsLiked $event): void
|
||||
{
|
||||
$this->lastfm->batchToggleLoveTracks($event->songs, $event->user, true);
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace App\Listeners;
|
||||
|
||||
use App\Events\SongsBatchUnliked;
|
||||
use App\Events\MultipleSongsUnliked;
|
||||
use App\Services\LastfmService;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
|
||||
|
@ -12,7 +12,7 @@ class UnloveMultipleTracksOnLastfm implements ShouldQueue
|
|||
{
|
||||
}
|
||||
|
||||
public function handle(SongsBatchUnliked $event): void
|
||||
public function handle(MultipleSongsUnliked $event): void
|
||||
{
|
||||
$this->lastfm->batchToggleLoveTracks($event->songs, $event->user, false);
|
||||
}
|
||||
|
|
|
@ -4,10 +4,10 @@ namespace App\Providers;
|
|||
|
||||
use App\Events\LibraryChanged;
|
||||
use App\Events\MediaScanCompleted;
|
||||
use App\Events\MultipleSongsLiked;
|
||||
use App\Events\MultipleSongsUnliked;
|
||||
use App\Events\PlaybackStarted;
|
||||
use App\Events\SongLikeToggled;
|
||||
use App\Events\SongsBatchLiked;
|
||||
use App\Events\SongsBatchUnliked;
|
||||
use App\Listeners\DeleteNonExistingRecordsPostSync;
|
||||
use App\Listeners\LoveMultipleTracksOnLastfm;
|
||||
use App\Listeners\LoveTrackOnLastfm;
|
||||
|
@ -26,11 +26,11 @@ class EventServiceProvider extends BaseServiceProvider
|
|||
LoveTrackOnLastfm::class,
|
||||
],
|
||||
|
||||
SongsBatchLiked::class => [
|
||||
MultipleSongsLiked::class => [
|
||||
LoveMultipleTracksOnLastfm::class,
|
||||
],
|
||||
|
||||
SongsBatchUnliked::class => [
|
||||
MultipleSongsUnliked::class => [
|
||||
UnloveMultipleTracksOnLastfm::class,
|
||||
],
|
||||
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Events\MultipleSongsLiked;
|
||||
use App\Events\MultipleSongsUnliked;
|
||||
use App\Events\SongLikeToggled;
|
||||
use App\Events\SongsBatchLiked;
|
||||
use App\Events\SongsBatchUnliked;
|
||||
use App\Models\Interaction;
|
||||
use App\Models\Song;
|
||||
use App\Models\User;
|
||||
|
@ -12,15 +12,10 @@ use Illuminate\Support\Collection;
|
|||
|
||||
class InteractionService
|
||||
{
|
||||
/**
|
||||
* Increase the number of times a song is played by a user.
|
||||
*
|
||||
* @return Interaction The affected Interaction object
|
||||
*/
|
||||
public function increasePlayCount(string $songId, User $user): Interaction
|
||||
public function increasePlayCount(Song $song, User $user): Interaction
|
||||
{
|
||||
return tap(Interaction::query()->firstOrCreate([
|
||||
'song_id' => $songId,
|
||||
'song_id' => $song->id,
|
||||
'user_id' => $user->id,
|
||||
]), static function (Interaction $interaction): void {
|
||||
if (!$interaction->exists) {
|
||||
|
@ -37,12 +32,12 @@ class InteractionService
|
|||
/**
|
||||
* Like or unlike a song as a user.
|
||||
*
|
||||
* @return Interaction the affected Interaction object
|
||||
* @return Interaction The affected Interaction object
|
||||
*/
|
||||
public function toggleLike(string $songId, User $user): Interaction
|
||||
public function toggleLike(Song $song, User $user): Interaction
|
||||
{
|
||||
return tap(Interaction::query()->firstOrCreate([
|
||||
'song_id' => $songId,
|
||||
'song_id' => $song->id,
|
||||
'user_id' => $user->id,
|
||||
]), static function (Interaction $interaction): void {
|
||||
$interaction->liked = !$interaction->liked;
|
||||
|
@ -55,15 +50,15 @@ class InteractionService
|
|||
/**
|
||||
* Like several songs at once as a user.
|
||||
*
|
||||
* @param array<string> $songIds
|
||||
* @param array<array-key, Song>|Collection $songs
|
||||
*
|
||||
* @return array<Interaction>|Collection The array of Interaction objects
|
||||
*/
|
||||
public function batchLike(array $songIds, User $user): Collection
|
||||
public function likeMany(Collection $songs, User $user): Collection
|
||||
{
|
||||
$interactions = collect($songIds)->map(static function ($songId) use ($user): Interaction {
|
||||
$interactions = $songs->map(static function (Song $song) use ($user): Interaction {
|
||||
return tap(Interaction::query()->firstOrCreate([
|
||||
'song_id' => $songId,
|
||||
'song_id' => $song->id,
|
||||
'user_id' => $user->id,
|
||||
]), static function (Interaction $interaction): void {
|
||||
$interaction->play_count ??= 0;
|
||||
|
@ -72,7 +67,7 @@ class InteractionService
|
|||
});
|
||||
});
|
||||
|
||||
event(new SongsBatchLiked($interactions->map(static fn (Interaction $item) => $item->song), $user));
|
||||
event(new MultipleSongsLiked($songs, $user));
|
||||
|
||||
return $interactions;
|
||||
}
|
||||
|
@ -80,15 +75,15 @@ class InteractionService
|
|||
/**
|
||||
* Unlike several songs at once.
|
||||
*
|
||||
* @param array<string> $songIds
|
||||
* @param array<array-key, Song>|Collection $songs
|
||||
*/
|
||||
public function batchUnlike(array $songIds, User $user): void
|
||||
public function unlikeMany(Collection $songs, User $user): void
|
||||
{
|
||||
Interaction::query()
|
||||
->whereIn('song_id', $songIds)
|
||||
->whereIn('song_id', $songs->pluck('id')->all())
|
||||
->where('user_id', $user->id)
|
||||
->update(['liked' => false]);
|
||||
|
||||
event(new SongsBatchUnliked(Song::query()->find($songIds), $user));
|
||||
event(new MultipleSongsUnliked($songs, $user));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,9 +21,7 @@ use App\Http\Controllers\API\FetchRecentlyPlayedSongController;
|
|||
use App\Http\Controllers\API\FetchSongsForQueueController;
|
||||
use App\Http\Controllers\API\GenreController;
|
||||
use App\Http\Controllers\API\GenreSongController;
|
||||
use App\Http\Controllers\API\Interaction\BatchLikeController;
|
||||
use App\Http\Controllers\API\Interaction\HandlePlaybackStartedController;
|
||||
use App\Http\Controllers\API\Interaction\ToggleLikeSongController;
|
||||
use App\Http\Controllers\API\LikeMultipleSongsController;
|
||||
use App\Http\Controllers\API\MakeSongsPrivateController;
|
||||
use App\Http\Controllers\API\MakeSongsPublicController;
|
||||
use App\Http\Controllers\API\ObjectStorage\S3\SongController as S3SongController;
|
||||
|
@ -33,12 +31,15 @@ use App\Http\Controllers\API\PlaylistFolderPlaylistController;
|
|||
use App\Http\Controllers\API\PlaylistSongController;
|
||||
use App\Http\Controllers\API\ProfileController;
|
||||
use App\Http\Controllers\API\QueueStateController;
|
||||
use App\Http\Controllers\API\RegisterPlayController;
|
||||
use App\Http\Controllers\API\ScrobbleController;
|
||||
use App\Http\Controllers\API\SearchYouTubeController;
|
||||
use App\Http\Controllers\API\SetLastfmSessionKeyController;
|
||||
use App\Http\Controllers\API\SettingController;
|
||||
use App\Http\Controllers\API\SongController;
|
||||
use App\Http\Controllers\API\SongSearchController;
|
||||
use App\Http\Controllers\API\ToggleLikeSongController;
|
||||
use App\Http\Controllers\API\UnlikeMultipleSongsController;
|
||||
use App\Http\Controllers\API\UpdatePlaybackStatusController;
|
||||
use App\Http\Controllers\API\UploadAlbumCoverController;
|
||||
use App\Http\Controllers\API\UploadArtistImageController;
|
||||
|
@ -103,10 +104,10 @@ Route::prefix('api')->middleware('api')->group(static function (): void {
|
|||
Route::post('upload', UploadController::class);
|
||||
|
||||
// Interaction routes
|
||||
Route::post('interaction/play', HandlePlaybackStartedController::class);
|
||||
Route::post('interaction/play', RegisterPlayController::class);
|
||||
Route::post('interaction/like', ToggleLikeSongController::class);
|
||||
Route::post('interaction/batch/like', [BatchLikeController::class, 'store']);
|
||||
Route::post('interaction/batch/unlike', [BatchLikeController::class, 'destroy']);
|
||||
Route::post('interaction/batch/like', LikeMultipleSongsController::class);
|
||||
Route::post('interaction/batch/unlike', UnlikeMultipleSongsController::class);
|
||||
|
||||
Route::get('songs/recently-played', FetchRecentlyPlayedSongController::class);
|
||||
Route::get('songs/favorite', FetchFavoriteSongsController::class);
|
||||
|
|
|
@ -2,8 +2,9 @@
|
|||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Events\MultipleSongsLiked;
|
||||
use App\Events\SongLikeToggled;
|
||||
use App\Events\SongsBatchLiked;
|
||||
use App\Models\Interaction;
|
||||
use App\Models\Song;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Collection;
|
||||
|
@ -11,13 +12,6 @@ use Tests\TestCase;
|
|||
|
||||
class InteractionTest extends TestCase
|
||||
{
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
static::createSampleMediaSet();
|
||||
}
|
||||
|
||||
public function testIncreasePlayCount(): void
|
||||
{
|
||||
$this->withoutEvents();
|
||||
|
@ -26,10 +20,10 @@ class InteractionTest extends TestCase
|
|||
$user = User::factory()->create();
|
||||
|
||||
/** @var Song $song */
|
||||
$song = Song::query()->orderBy('id')->first();
|
||||
$song = Song::factory()->create();
|
||||
$this->postAs('api/interaction/play', ['song' => $song->id], $user);
|
||||
|
||||
self::assertDatabaseHas('interactions', [
|
||||
self::assertDatabaseHas(Interaction::class, [
|
||||
'user_id' => $user->id,
|
||||
'song_id' => $song->id,
|
||||
'play_count' => 1,
|
||||
|
@ -38,7 +32,7 @@ class InteractionTest extends TestCase
|
|||
// Try again
|
||||
$this->postAs('api/interaction/play', ['song' => $song->id], $user);
|
||||
|
||||
self::assertDatabaseHas('interactions', [
|
||||
self::assertDatabaseHas(Interaction::class, [
|
||||
'user_id' => $user->id,
|
||||
'song_id' => $song->id,
|
||||
'play_count' => 2,
|
||||
|
@ -53,10 +47,10 @@ class InteractionTest extends TestCase
|
|||
$user = User::factory()->create();
|
||||
|
||||
/** @var Song $song */
|
||||
$song = Song::query()->orderBy('id')->first();
|
||||
$song = Song::factory()->create();
|
||||
$this->postAs('api/interaction/like', ['song' => $song->id], $user);
|
||||
|
||||
self::assertDatabaseHas('interactions', [
|
||||
self::assertDatabaseHas(Interaction::class, [
|
||||
'user_id' => $user->id,
|
||||
'song_id' => $song->id,
|
||||
'liked' => 1,
|
||||
|
@ -65,7 +59,7 @@ class InteractionTest extends TestCase
|
|||
// Try again
|
||||
$this->postAs('api/interaction/like', ['song' => $song->id], $user);
|
||||
|
||||
self::assertDatabaseHas('interactions', [
|
||||
self::assertDatabaseHas(Interaction::class, [
|
||||
'user_id' => $user->id,
|
||||
'song_id' => $song->id,
|
||||
'liked' => 0,
|
||||
|
@ -74,19 +68,19 @@ class InteractionTest extends TestCase
|
|||
|
||||
public function testToggleLikeBatch(): void
|
||||
{
|
||||
$this->expectsEvents(SongsBatchLiked::class);
|
||||
$this->expectsEvents(MultipleSongsLiked::class);
|
||||
|
||||
/** @var User $user */
|
||||
$user = User::factory()->create();
|
||||
|
||||
/** @var Collection|array<Song> $songs */
|
||||
$songs = Song::query()->orderBy('id')->take(2)->get();
|
||||
$songs = Song::factory(2)->create();
|
||||
$songIds = $songs->pluck('id')->all();
|
||||
|
||||
$this->postAs('api/interaction/batch/like', ['songs' => $songIds], $user);
|
||||
|
||||
foreach ($songs as $song) {
|
||||
self::assertDatabaseHas('interactions', [
|
||||
self::assertDatabaseHas(Interaction::class, [
|
||||
'user_id' => $user->id,
|
||||
'song_id' => $song->id,
|
||||
'liked' => 1,
|
||||
|
@ -96,7 +90,7 @@ class InteractionTest extends TestCase
|
|||
$this->postAs('api/interaction/batch/unlike', ['songs' => $songIds], $user);
|
||||
|
||||
foreach ($songs as $song) {
|
||||
self::assertDatabaseHas('interactions', [
|
||||
self::assertDatabaseHas(Interaction::class, [
|
||||
'user_id' => $user->id,
|
||||
'song_id' => $song->id,
|
||||
'liked' => 0,
|
||||
|
|
133
tests/Feature/KoelPlus/InteractionTest.php
Normal file
133
tests/Feature/KoelPlus/InteractionTest.php
Normal file
|
@ -0,0 +1,133 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Feature\KoelPlus;
|
||||
|
||||
use App\Facades\License;
|
||||
use App\Models\Song;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Collection;
|
||||
use Tests\TestCase;
|
||||
|
||||
class InteractionTest extends TestCase
|
||||
{
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
License::fakePlusLicense();
|
||||
}
|
||||
|
||||
public function testPolicyForRegisterPlay(): void
|
||||
{
|
||||
$this->withoutEvents();
|
||||
|
||||
/** @var User $owner */
|
||||
$owner = User::factory()->create();
|
||||
|
||||
// Can't increase play count of a private song that doesn't belong to the user
|
||||
/** @var Song $externalPrivateSong */
|
||||
$externalPrivateSong = Song::factory()->private()->create();
|
||||
$this->postAs('api/interaction/play', ['song' => $externalPrivateSong->id], $owner)
|
||||
->assertForbidden();
|
||||
|
||||
// Can increase play count of a public song that doesn't belong to the user
|
||||
/** @var Song $externalPublicSong */
|
||||
$externalPublicSong = Song::factory()->public()->create();
|
||||
$this->postAs('api/interaction/play', ['song' => $externalPublicSong->id], $owner)
|
||||
->assertSuccessful();
|
||||
|
||||
// Can increase play count of a private song that belongs to the user
|
||||
/** @var Song $ownPrivateSong */
|
||||
$ownPrivateSong = Song::factory()->private()->for($owner, 'owner')->create();
|
||||
$this->postAs('api/interaction/play', ['song' => $ownPrivateSong->id], $ownPrivateSong->owner)
|
||||
->assertSuccessful();
|
||||
}
|
||||
|
||||
public function testPolicyForToggleLike(): void
|
||||
{
|
||||
$this->withoutEvents();
|
||||
|
||||
/** @var User $user */
|
||||
$owner = User::factory()->create();
|
||||
|
||||
// Can't like a private song that doesn't belong to the user
|
||||
/** @var Song $externalPrivateSong */
|
||||
$externalPrivateSong = Song::factory()->private()->create();
|
||||
$this->postAs('api/interaction/like', ['song' => $externalPrivateSong->id], $owner)
|
||||
->assertForbidden();
|
||||
|
||||
// Can like a public song that doesn't belong to the user
|
||||
/** @var Song $externalPublicSong */
|
||||
$externalPublicSong = Song::factory()->public()->create();
|
||||
$this->postAs('api/interaction/like', ['song' => $externalPublicSong->id], $owner)
|
||||
->assertSuccessful();
|
||||
|
||||
// Can like a private song that belongs to the user
|
||||
/** @var Song $ownPrivateSong */
|
||||
$ownPrivateSong = Song::factory()->private()->for($owner, 'owner')->create();
|
||||
$this->postAs('api/interaction/like', ['song' => $ownPrivateSong->id], $owner)
|
||||
->assertSuccessful();
|
||||
}
|
||||
|
||||
public function testPolicyForBatchLike(): void
|
||||
{
|
||||
$this->withoutEvents();
|
||||
|
||||
/** @var User $user */
|
||||
$owner = User::factory()->create();
|
||||
|
||||
// Can't batch like private songs that don't belong to the user
|
||||
/** @var Collection $externalPrivateSongs */
|
||||
$externalPrivateSongs = Song::factory()->count(3)->private()->create();
|
||||
$this->postAs('api/interaction/batch/like', ['songs' => $externalPrivateSongs->pluck('id')->all()], $owner)
|
||||
->assertForbidden();
|
||||
|
||||
// Can batch like public songs that don't belong to the user
|
||||
/** @var Collection $externalPublicSongs */
|
||||
$externalPublicSongs = Song::factory()->count(3)->public()->create();
|
||||
$this->postAs('api/interaction/batch/like', ['songs' => $externalPublicSongs->pluck('id')->all()], $owner)
|
||||
->assertSuccessful();
|
||||
|
||||
// Can batch like private songs that belong to the user
|
||||
/** @var Collection $ownPrivateSongs */
|
||||
$ownPrivateSongs = Song::factory()->count(3)->private()->for($owner, 'owner')->create();
|
||||
$this->postAs('api/interaction/batch/like', ['songs' => $ownPrivateSongs->pluck('id')->all()], $owner)
|
||||
->assertSuccessful();
|
||||
|
||||
// Can't batch like a mix of inaccessible and accessible songs
|
||||
$mixedSongs = $externalPrivateSongs->merge($externalPublicSongs);
|
||||
$this->postAs('api/interaction/batch/like', ['songs' => $mixedSongs->pluck('id')->all()], $owner)
|
||||
->assertForbidden();
|
||||
}
|
||||
|
||||
public function testPolicyForBatchUnlike(): void
|
||||
{
|
||||
$this->withoutEvents();
|
||||
|
||||
/** @var User $user */
|
||||
$owner = User::factory()->create();
|
||||
|
||||
// Can't batch unlike private songs that don't belong to the user
|
||||
/** @var Collection $externalPrivateSongs */
|
||||
$externalPrivateSongs = Song::factory()->count(3)->private()->create();
|
||||
$this->postAs('api/interaction/batch/unlike', ['songs' => $externalPrivateSongs->pluck('id')->all()], $owner)
|
||||
->assertForbidden();
|
||||
|
||||
// Can batch unlike public songs that don't belong to the user
|
||||
/** @var Collection $externalPublicSongs */
|
||||
$externalPublicSongs = Song::factory()->count(3)->public()->create();
|
||||
$this->postAs('api/interaction/batch/unlike', ['songs' => $externalPublicSongs->pluck('id')->all()], $owner)
|
||||
->assertSuccessful();
|
||||
|
||||
// Can batch unlike private songs that belong to the user
|
||||
/** @var Collection $ownPrivateSongs */
|
||||
$ownPrivateSongs = Song::factory()->count(3)->private()->for($owner, 'owner')->create();
|
||||
$this->postAs('api/interaction/batch/unlike', ['songs' => $ownPrivateSongs->pluck('id')->all()], $owner)
|
||||
->assertSuccessful();
|
||||
|
||||
// Can't batch unlike a mix of inaccessible and accessible songs
|
||||
$mixedSongs = $externalPrivateSongs->merge($externalPublicSongs);
|
||||
$this->postAs('api/interaction/batch/unlike', ['songs' => $mixedSongs->pluck('id')->all()], $owner)
|
||||
->assertForbidden();
|
||||
}
|
||||
}
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
namespace Tests\Integration\Services;
|
||||
|
||||
use App\Events\MultipleSongsLiked;
|
||||
use App\Events\MultipleSongsUnliked;
|
||||
use App\Events\SongLikeToggled;
|
||||
use App\Events\SongsBatchLiked;
|
||||
use App\Events\SongsBatchUnliked;
|
||||
use App\Models\Interaction;
|
||||
use App\Models\Song;
|
||||
use App\Models\User;
|
||||
|
@ -48,7 +48,7 @@ class InteractionServiceTest extends TestCase
|
|||
|
||||
public function testLikeMultipleSongs(): void
|
||||
{
|
||||
$this->expectsEvents(SongsBatchLiked::class);
|
||||
$this->expectsEvents(MultipleSongsLiked::class);
|
||||
|
||||
/** @var Collection $songs */
|
||||
$songs = Song::factory(2)->create();
|
||||
|
@ -56,7 +56,7 @@ class InteractionServiceTest extends TestCase
|
|||
/** @var User $user */
|
||||
$user = User::factory()->create();
|
||||
|
||||
$this->interactionService->batchLike($songs->pluck('id')->all(), $user);
|
||||
$this->interactionService->likeMany($songs, $user);
|
||||
|
||||
$songs->each(static function (Song $song) use ($user): void {
|
||||
/** @var Interaction $interaction */
|
||||
|
@ -71,7 +71,7 @@ class InteractionServiceTest extends TestCase
|
|||
|
||||
public function testUnlikeMultipleSongs(): void
|
||||
{
|
||||
$this->expectsEvents(SongsBatchUnliked::class);
|
||||
$this->expectsEvents(MultipleSongsUnliked::class);
|
||||
|
||||
/** @var User $user */
|
||||
$user = User::factory()->create();
|
||||
|
@ -79,7 +79,7 @@ class InteractionServiceTest extends TestCase
|
|||
/** @var Collection $interactions */
|
||||
$interactions = Interaction::factory(3)->for($user)->create(['liked' => true]);
|
||||
|
||||
$this->interactionService->batchUnlike($interactions->pluck('song.id')->all(), $user);
|
||||
$this->interactionService->unlikeMany($interactions->map(static fn (Interaction $i) => $i->song), $user);
|
||||
|
||||
$interactions->each(static function (Interaction $interaction): void {
|
||||
self::assertFalse($interaction->refresh()->liked);
|
||||
|
|
Loading…
Reference in a new issue