fix(tests): broken tests after Saloon migration

This commit is contained in:
Phan An 2024-03-22 21:34:32 +01:00
parent 4d5cbf5394
commit 4b649f2f58
24 changed files with 339 additions and 328 deletions

View file

@ -15,6 +15,10 @@ final class FailedToActivateLicenseException extends Exception
public static function fromRequestException(RequestException $e): self
{
return new self(object_get($e->getResponse()->object(), 'error'), $e->getStatus());
try {
return new self(object_get($e->getResponse()->object(), 'error'), $e->getStatus());
} catch (Throwable) {
return self::fromThrowable($e);
}
}
}

View file

@ -13,6 +13,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Arr;
use Laravel\Sanctum\HasApiTokens;
use Laravel\Sanctum\PersonalAccessToken;
@ -77,11 +78,9 @@ class User extends Authenticatable
protected function avatar(): Attribute
{
return Attribute::get(function (): string {
if ($this->attributes['avatar']) {
return user_avatar_url($this->attributes['avatar']);
}
$avatar = Arr::get($this->attributes, 'avatar');
return gravatar($this->email);
return $avatar ? user_avatar_url($avatar) : gravatar($this->email);
});
}

View file

@ -18,8 +18,7 @@ class YouTubeService
return (bool) config('koel.youtube.key');
}
/** @return array<mixed>|null */
public function searchVideosRelatedToSong(Song $song, string $pageToken = ''): ?array
public function searchVideosRelatedToSong(Song $song, string $pageToken = ''): ?object
{
if (!self::enabled()) {
return null;
@ -29,9 +28,9 @@ class YouTubeService
$hash = md5(serialize($request->query()->all()));
return $this->cache->remember(
"youtube:$hash",
"youtube.$hash",
now()->addWeek(),
fn () => $this->connector->send($request)->json()
fn () => $this->connector->send($request)->object()
);
}
}

View file

@ -4,4 +4,5 @@ compiled.php
services.json
events.scanned.php
routes.scanned.php
testing/*
down

View file

@ -30,9 +30,9 @@ class AlbumCoverTest extends TestCase
$this->mediaMetadataService
->shouldReceive('writeAlbumCover')
->once()
->with(Mockery::on(static fn (Album $target) => $target->is($album)), 'Foo', 'jpeg');
->with(Mockery::on(static fn (Album $target) => $target->is($album)), 'data:image/jpeg;base64,Rm9v');
$this->putAs('api/album/' . $album->id . '/cover', ['cover' => 'data:image/jpeg;base64,Rm9v'], create_admin())
$this->putAs("api/album/$album->id/cover", ['cover' => 'data:image/jpeg;base64,Rm9v'], create_admin())
->assertOk();
}

View file

@ -23,14 +23,15 @@ class ArtistImageTest extends TestCase
public function testUpdate(): void
{
Artist::factory()->create(['id' => 9999]);
/** @var Artist $artist */
$artist = Artist::factory()->create();
$this->mediaMetadataService
->shouldReceive('writeArtistImage')
->once()
->with(Mockery::on(static fn (Artist $artist) => $artist->id === 9999), 'Foo', 'jpeg');
->with(Mockery::on(static fn (Artist $target) => $target->is($artist)), 'data:image/jpeg;base64,Rm9v');
$this->putAs('api/artist/9999/image', ['image' => 'data:image/jpeg;base64,Rm9v'], create_admin())
$this->putAs("api/artist/$artist->id/image", ['image' => 'data:image/jpeg;base64,Rm9v'], create_admin())
->assertOk();
}

View file

@ -31,7 +31,7 @@ class AlbumCoverTest extends PlusTestCase
$this->mediaMetadataService
->shouldReceive('writeAlbumCover')
->once()
->with(Mockery::on(static fn (Album $target) => $target->is($album)), 'Foo', 'jpeg');
->with(Mockery::on(static fn (Album $target) => $target->is($album)), 'data:image/jpeg;base64,Rm9v');
$this->putAs("api/albums/$album->id/cover", ['cover' => 'data:image/jpeg;base64,Rm9v'], $user)
->assertOk();
@ -65,7 +65,7 @@ class AlbumCoverTest extends PlusTestCase
$this->mediaMetadataService
->shouldReceive('writeAlbumCover')
->once()
->with(Mockery::on(static fn (Album $target) => $target->is($album)), 'Foo', 'jpeg');
->with(Mockery::on(static fn (Album $target) => $target->is($album)), 'data:image/jpeg;base64,Rm9v');
$this->putAs("api/albums/$album->id/cover", ['cover' => 'data:image/jpeg;base64,Rm9v'], create_admin())
->assertOk();

View file

@ -6,6 +6,7 @@ use App\Models\Artist;
use App\Models\Song;
use App\Services\MediaMetadataService;
use Mockery;
use Mockery\MockInterface;
use Tests\PlusTestCase;
use function Tests\create_admin;
@ -13,6 +14,8 @@ use function Tests\create_user;
class ArtistImageTest extends PlusTestCase
{
private MockInterface|MediaMetadataService $mediaMetadataService;
public function setUp(): void
{
parent::setUp();
@ -31,7 +34,7 @@ class ArtistImageTest extends PlusTestCase
$this->mediaMetadataService
->shouldReceive('writeArtistImage')
->once()
->with(Mockery::on(static fn (Artist $target) => $target->is($artist)), 'Foo', 'jpeg');
->with(Mockery::on(static fn (Artist $target) => $target->is($artist)), 'data:image/jpeg;base64,Rm9v');
$this->putAs("api/artists/$artist->id/image", ['image' => 'data:image/jpeg;base64,Rm9v'], $user)
->assertOk();
@ -65,7 +68,7 @@ class ArtistImageTest extends PlusTestCase
$this->mediaMetadataService
->shouldReceive('writeArtistImage')
->once()
->with(Mockery::on(static fn (Artist $target) => $target->is($artist)), 'Foo', 'jpeg');
->with(Mockery::on(static fn (Artist $target) => $target->is($artist)), 'data:image/jpeg;base64,Rm9v');
$this->putAs("api/artists/$artist->id/image", ['image' => 'data:image/jpeg;base64,Rm9v'], create_admin())
->assertOk();

View file

@ -31,13 +31,9 @@ class PlaylistCoverTest extends PlusTestCase
$this->mediaMetadataService
->shouldReceive('writePlaylistCover')
->once()
->with(Mockery::on(static fn (Playlist $target) => $target->is($playlist)), 'Foo', 'jpeg');
->with(Mockery::on(static fn (Playlist $target) => $target->is($playlist)), 'data:image/jpeg;base64,Rm9v');
$this->putAs(
"api/playlists/$playlist->id/cover",
['cover' => 'data:image/jpeg;base64,Rm9v'],
$collaborator
)
$this->putAs("api/playlists/$playlist->id/cover", ['cover' => 'data:image/jpeg;base64,Rm9v'], $collaborator)
->assertOk();
}
}

View file

@ -30,13 +30,9 @@ class PlaylistCoverTest extends TestCase
$this->mediaMetadataService
->shouldReceive('writePlaylistCover')
->once()
->with(Mockery::on(static fn (Playlist $target) => $target->is($playlist)), 'Foo', 'jpeg');
->with(Mockery::on(static fn (Playlist $target) => $target->is($playlist)), 'data:image/jpeg;base64,Rm9v');
$this->putAs(
"api/playlists/$playlist->id/cover",
['cover' => 'data:image/jpeg;base64,Rm9v'],
$playlist->user
)
$this->putAs("api/playlists/$playlist->id/cover", ['cover' => 'data:image/jpeg;base64,Rm9v'], $playlist->user)
->assertOk();
}
@ -47,11 +43,7 @@ class PlaylistCoverTest extends TestCase
$this->mediaMetadataService->shouldNotReceive('writePlaylistCover');
$this->putAs(
"api/playlists/$playlist->id/cover",
['cover' => 'data:image/jpeg;base64,Rm9v'],
create_user()
)
$this->putAs("api/playlists/$playlist->id/cover", ['cover' => 'data:image/jpeg;base64,Rm9v'], create_user())
->assertForbidden();
}
}

View file

@ -53,8 +53,8 @@ class PlaylistServiceTest extends PlusTestCase
public function testOwnSongsOnlyOptionOnlyWorksWithSmartPlaylistsWhenCreate(): void
{
self::expectException(InvalidArgumentException::class);
self::expectExceptionMessage('"Own songs only" option only works with smart playlists and Plus license.');
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('"Own songs only" option only works with smart playlists and Plus license.');
$this->service->createPlaylist(
name: 'foo',
@ -65,8 +65,8 @@ class PlaylistServiceTest extends PlusTestCase
public function testOwnSongsOnlyOptionOnlyWorksWithSmartPlaylistsWhenUpdate(): void
{
self::expectException(InvalidArgumentException::class);
self::expectExceptionMessage('"Own songs only" option only works with smart playlists and Plus license.');
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('"Own songs only" option only works with smart playlists and Plus license.');
/** @var Playlist */
$playlist = Playlist::factory()->create();

View file

@ -0,0 +1,72 @@
<?php
namespace Tests\Integration\Services;
use App\Http\Integrations\iTunes\Requests\GetTrackRequest;
use App\Models\Album;
use App\Models\Artist;
use App\Services\ITunesService;
use Illuminate\Support\Facades\Cache;
use Saloon\Http\Faking\MockResponse;
use Saloon\Laravel\Saloon;
use Tests\TestCase;
class ITunesServiceTest extends TestCase
{
private ITunesService $service;
public function setUp(): void
{
parent::setUp();
$this->service = app(ITunesService::class);
}
public function testConfiguration(): void
{
config(['koel.itunes.enabled' => true]);
self::assertTrue($this->service->used());
config(['koel.itunes.enabled' => false]);
self::assertFalse($this->service->used());
}
public function testGetTrackUrl(): void
{
config(['koel.itunes.enabled' => true]);
config(['koel.itunes.affiliate_id' => 'foo']);
Saloon::fake([
GetTrackRequest::class => MockResponse::make(body: [
'resultCount' => 1,
'results' => [['trackViewUrl' => 'https://itunes.apple.com/bar']],
]),
]);
/** @var Album $album */
$album = Album::factory()
->for(Artist::factory()->create(['name' => 'Queen']))
->create(['name' => 'A Night at the Opera']);
self::assertSame(
'https://itunes.apple.com/bar?at=foo',
$this->service->getTrackUrl('Bohemian Rhapsody', $album)
);
self::assertSame(
'https://itunes.apple.com/bar?at=foo',
Cache::get('itunes.track.5f0467bebbb2b26bf9dc7b19f3d85077')
);
Saloon::assertSent(static function (GetTrackRequest $request): bool {
self::assertSame([
'term' => 'Bohemian Rhapsody A Night at the Opera Queen',
'media' => 'music',
'entity' => 'song',
'limit' => 1,
], $request->query()->all());
return true;
});
}
}

View file

@ -1,16 +1,19 @@
<?php
namespace Tests\Unit\Services;
namespace Tests\Integration\Services;
use App\Http\Integrations\Lastfm\Requests\GetAlbumInfoRequest;
use App\Http\Integrations\Lastfm\Requests\GetArtistInfoRequest;
use App\Http\Integrations\Lastfm\Requests\ScrobbleRequest;
use App\Http\Integrations\Lastfm\Requests\ToggleLoveTrackRequest;
use App\Http\Integrations\Lastfm\Requests\UpdateNowPlayingRequest;
use App\Models\Album;
use App\Models\Artist;
use App\Models\Song;
use App\Services\ApiClients\LastfmClient;
use App\Services\LastfmService;
use Illuminate\Support\Facades\File;
use Mockery;
use Mockery\LegacyMockInterface;
use Mockery\MockInterface;
use Saloon\Http\Faking\MockResponse;
use Saloon\Laravel\Saloon;
use Tests\TestCase;
use function Tests\create_user;
@ -18,7 +21,6 @@ use function Tests\test_path;
class LastfmServiceTest extends TestCase
{
private LastfmClient|MockInterface|LegacyMockInterface $client;
private LastfmService $service;
public function setUp(): void
@ -30,22 +32,31 @@ class LastfmServiceTest extends TestCase
'koel.lastfm.secret' => 'secret',
]);
$this->client = Mockery::mock(LastfmClient::class);
$this->service = new LastfmService($this->client);
$this->service = app(LastfmService::class);
}
public function testGetArtistInformation(): void
{
/** @var Artist $artist */
$artist = Artist::factory()->make(['name' => 'foo']);
$artist = Artist::factory()->make(['name' => 'Kamelot']);
$this->client->shouldReceive('get')
->with('?method=artist.getInfo&autocorrect=1&artist=foo&format=json')
->once()
->andReturn(json_decode(File::get(test_path('blobs/lastfm/artist.json'))));
Saloon::fake([
GetArtistInfoRequest::class => MockResponse::make(body: File::get(test_path('blobs/lastfm/artist.json'))),
]);
$info = $this->service->getArtistInformation($artist);
Saloon::assertSent(static function (GetArtistInfoRequest $request): bool {
self::assertSame([
'method' => 'artist.getInfo',
'artist' => 'Kamelot',
'autocorrect' => 1,
'format' => 'json',
], $request->query()->all());
return true;
});
self::assertEquals([
'url' => 'https://www.last.fm/music/Kamelot',
'image' => null,
@ -61,10 +72,11 @@ class LastfmServiceTest extends TestCase
/** @var Artist $artist */
$artist = Artist::factory()->make(['name' => 'bar']);
$this->client->shouldReceive('get')
->with('?method=artist.getInfo&autocorrect=1&artist=bar&format=json')
->once()
->andReturn(json_decode(test_path('blobs/lastfm/artist-notfound.json')));
Saloon::fake([
GetArtistInfoRequest::class => MockResponse::make(
body: File::get(test_path('blobs/lastfm/artist-notfound.json'))
),
]);
self::assertNull($this->service->getArtistInformation($artist));
}
@ -72,15 +84,26 @@ class LastfmServiceTest extends TestCase
public function testGetAlbumInformation(): void
{
/** @var Album $album */
$album = Album::factory()->for(Artist::factory()->create(['name' => 'bar']))->create(['name' => 'foo']);
$album = Album::factory()->for(Artist::factory()->create(['name' => 'Kamelot']))->create(['name' => 'Epica']);
$this->client->shouldReceive('get')
->with('?method=album.getInfo&autocorrect=1&album=foo&artist=bar&format=json')
->once()
->andReturn(json_decode(File::get(test_path('blobs/lastfm/album.json'))));
Saloon::fake([
GetAlbumInfoRequest::class => MockResponse::make(body: File::get(test_path('blobs/lastfm/album.json'))),
]);
$info = $this->service->getAlbumInformation($album);
Saloon::assertSent(static function (GetAlbumInfoRequest $request): bool {
self::assertSame([
'method' => 'album.getInfo',
'artist' => 'Kamelot',
'album' => 'Epica',
'autocorrect' => 1,
'format' => 'json',
], $request->query()->all());
return true;
});
self::assertEquals([
'url' => 'https://www.last.fm/music/Kamelot/Epica',
'cover' => null,
@ -106,12 +129,13 @@ class LastfmServiceTest extends TestCase
public function testGetAlbumInformationForNonExistentAlbum(): void
{
/** @var Album $album */
$album = Album::factory()->for(Artist::factory()->create(['name' => 'bar']))->create(['name' => 'foo']);
$album = Album::factory()->for(Artist::factory()->create(['name' => 'Kamelot']))->create(['name' => 'Foo']);
$this->client->shouldReceive('get')
->with('?method=album.getInfo&autocorrect=1&album=foo&artist=bar&format=json')
->once()
->andReturn(json_decode(File::get(test_path('blobs/lastfm/album-notfound.json'))));
Saloon::fake([
GetAlbumInfoRequest::class => MockResponse::make(
body: File::get(test_path('blobs/lastfm/album-notfound.json'))
),
]);
self::assertNull($this->service->getAlbumInformation($album));
}
@ -127,18 +151,22 @@ class LastfmServiceTest extends TestCase
/** @var Song $song */
$song = Song::factory()->create();
$this->client->shouldReceive('post')
->with('/', [
Saloon::fake([ScrobbleRequest::class => MockResponse::make()]);
$this->service->scrobble($song, $user, 100);
Saloon::assertSent(static function (ScrobbleRequest $request) use ($song): bool {
self::assertSame([
'method' => 'track.scrobble',
'artist' => $song->artist->name,
'track' => $song->title,
'timestamp' => 100,
'sk' => 'my_key',
'method' => 'track.scrobble',
'album' => $song->album->name,
], false)
->once();
], $request->body()->all());
$this->service->scrobble($song, $user, 100);
return true;
});
}
/** @return array<mixed> */
@ -157,18 +185,22 @@ class LastfmServiceTest extends TestCase
]);
/** @var Song $song */
$song = Song::factory()->for(Artist::factory()->create(['name' => 'foo']))->create(['title' => 'bar']);
$song = Song::factory()->create();
$this->client->shouldReceive('post')
->with('/', [
'artist' => 'foo',
'track' => 'bar',
'sk' => 'my_key',
'method' => $method,
], false)
->once();
Saloon::fake([ToggleLoveTrackRequest::class => MockResponse::make()]);
$this->service->toggleLoveTrack($song, $user, $love);
Saloon::assertSent(static function (ToggleLoveTrackRequest $request) use ($song, $love): bool {
self::assertSame([
'method' => $love ? 'track.love' : 'track.unlove',
'sk' => 'my_key',
'artist' => $song->artist->name,
'track' => $song->title,
], $request->body()->all());
return true;
});
}
public function testUpdateNowPlaying(): void
@ -182,17 +214,21 @@ class LastfmServiceTest extends TestCase
/** @var Song $song */
$song = Song::factory()->for(Artist::factory()->create(['name' => 'foo']))->create(['title' => 'bar']);
$this->client->shouldReceive('post')
->with('/', [
'artist' => 'foo',
'track' => 'bar',
'duration' => $song->length,
'sk' => 'my_key',
'method' => 'track.updateNowPlaying',
'album' => $song->album->name,
], false)
->once();
Saloon::fake([UpdateNowPlayingRequest::class => MockResponse::make()]);
$this->service->updateNowPlaying($song, $user);
Saloon::assertSent(static function (UpdateNowPlayingRequest $request) use ($song): bool {
self::assertSame([
'method' => 'track.updateNowPlaying',
'artist' => $song->artist->name,
'track' => $song->title,
'duration' => $song->length,
'sk' => 'my_key',
'album' => $song->album->name,
], $request->body()->all());
return true;
});
}
}

View file

@ -3,19 +3,17 @@
namespace Tests\Integration\Services;
use App\Exceptions\FailedToActivateLicenseException;
use App\Http\Integrations\LemonSqueezy\Requests\ActivateLicenseRequest;
use App\Http\Integrations\LemonSqueezy\Requests\DeactivateLicenseRequest;
use App\Http\Integrations\LemonSqueezy\Requests\ValidateLicenseRequest;
use App\Models\License;
use App\Services\ApiClients\ApiClient;
use App\Services\LicenseService;
use App\Values\LicenseStatus;
use Exception;
use GuzzleHttp\Exception\ClientException;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\File;
use Mockery;
use Mockery\LegacyMockInterface;
use Mockery\MockInterface;
use Psr\Http\Message\ResponseInterface;
use Saloon\Http\Faking\MockResponse;
use Saloon\Laravel\Facades\Saloon;
use Tests\TestCase;
use function Tests\test_path;
@ -23,13 +21,11 @@ use function Tests\test_path;
class LicenseServiceTest extends TestCase
{
private LicenseService $service;
private ApiClient|MockInterface|LegacyMockInterface $apiClient;
public function setUp(): void
{
parent::setUp();
$this->apiClient = $this->mock(ApiClient::class);
$this->service = app(LicenseService::class);
}
@ -38,14 +34,11 @@ class LicenseServiceTest extends TestCase
config(['lemonsqueezy.store_id' => 42]);
$key = '38b1460a-5104-4067-a91d-77b872934d51';
$this->apiClient
->shouldReceive('post')
->with('licenses/activate', [
'license_key' => $key,
'instance_name' => 'Koel Plus',
])
->once()
->andReturn(json_decode(File::get(test_path('blobs/lemonsqueezy/license-activated-successful.json'))));
Saloon::fake([
ActivateLicenseRequest::class => MockResponse::make(
body: File::get(test_path('blobs/lemonsqueezy/license-activated-successful.json')),
),
]);
$license = $this->service->activate($key);
@ -59,49 +52,46 @@ class LicenseServiceTest extends TestCase
self::assertSame($license->key, $cachedLicenseStatus->license->key);
self::assertTrue($cachedLicenseStatus->isValid());
Saloon::assertSent(static function (ActivateLicenseRequest $request) use ($key): bool {
self::assertSame([
'license_key' => $key,
'instance_name' => 'Koel Plus',
], $request->body()->all());
return true;
});
}
public function testActivateLicenseFailsBecauseOfIncorrectStoreId(): void
{
self::expectException(FailedToActivateLicenseException::class);
self::expectExceptionMessage('This license key is not from Koels official store.');
$this->expectException(FailedToActivateLicenseException::class);
$this->expectExceptionMessage('This license key is not from Koels official store.');
config(['lemonsqueezy.store_id' => 43]);
$key = '38b1460a-5104-4067-a91d-77b872934d51';
$this->apiClient
->shouldReceive('post')
->with('licenses/activate', [
'license_key' => $key,
'instance_name' => 'Koel Plus',
])
->once()
->andReturn(json_decode(File::get(test_path('blobs/lemonsqueezy/license-activated-successful.json'))));
Saloon::fake([
ActivateLicenseRequest::class => MockResponse::make(
body: File::get(test_path('blobs/lemonsqueezy/license-activated-successful.json')),
),
]);
$this->service->activate($key);
}
public function testActivateLicenseFailsForInvalidLicenseKey(): void
{
self::expectException(FailedToActivateLicenseException::class);
self::expectExceptionMessage('license_key not found');
$this->expectException(FailedToActivateLicenseException::class);
$this->expectExceptionMessage('license_key not found');
$exception = Mockery::mock(ClientException::class, [
'getResponse' => Mockery::mock(ResponseInterface::class, [
'getBody' => File::get(test_path('blobs/lemonsqueezy/license-invalid.json')),
'getStatusCode' => Response::HTTP_NOT_FOUND,
]),
Saloon::fake([
ActivateLicenseRequest::class => MockResponse::make(
body: File::get(test_path('blobs/lemonsqueezy/license-invalid.json')),
status: Response::HTTP_NOT_FOUND,
),
]);
$this->apiClient
->shouldReceive('post')
->with('licenses/activate', [
'license_key' => 'invalid-key',
'instance_name' => 'Koel Plus',
])
->once()
->andThrow($exception);
$this->service->activate('invalid-key');
}
@ -110,40 +100,33 @@ class LicenseServiceTest extends TestCase
/** @var License $license */
$license = License::factory()->create();
$this->apiClient
->shouldReceive('post')
->with('licenses/deactivate', [
'license_key' => $license->key,
'instance_id' => $license->instance->id,
])
->once()
->andReturn(json_decode(File::get(test_path('blobs/lemonsqueezy/license-deactivated-successful.json'))));
Saloon::fake([
DeactivateLicenseRequest::class => MockResponse::make(
body: File::get(test_path('blobs/lemonsqueezy/license-deactivated-successful.json')),
status: Response::HTTP_NOT_FOUND,
),
]);
$this->service->deactivate($license);
self::assertModelMissing($license);
self::assertFalse(Cache::has('license_status'));
Saloon::assertSent(static function (DeactivateLicenseRequest $request) use ($license): bool {
self::assertSame([
'license_key' => $license->key,
'instance_id' => $license->instance->id,
], $request->body()->all());
return true;
});
}
public function testDeactivateLicenseHandlesLeftoverRecords(): void
{
/** @var License $license */
$license = License::factory()->create();
$exception = Mockery::mock(ClientException::class, [
'getResponse' => Mockery::mock(ResponseInterface::class, [
'getStatusCode' => Response::HTTP_NOT_FOUND,
]),
]);
$this->apiClient
->shouldReceive('post')
->with('licenses/deactivate', [
'license_key' => $license->key,
'instance_id' => $license->instance->id,
])
->once()
->andThrow($exception);
Saloon::fake([DeactivateLicenseRequest::class => MockResponse::make(status: Response::HTTP_NOT_FOUND)]);
$this->service->deactivate($license);
@ -152,43 +135,43 @@ class LicenseServiceTest extends TestCase
public function testDeactivateLicenseFails(): void
{
self::expectExceptionMessage('Something went horribly wrong');
$this->expectExceptionMessage('Unprocessable Entity (422) Response: Something went horrible wrong');
/** @var License $license */
$license = License::factory()->create();
$this->apiClient
->shouldReceive('post')
->with('licenses/deactivate', [
'license_key' => $license->key,
'instance_id' => $license->instance->id,
])
->once()
->andThrow(new Exception('Something went horribly wrong'));
Saloon::fake([
DeactivateLicenseRequest::class => MockResponse::make(
body: 'Something went horrible wrong',
status: Response::HTTP_UNPROCESSABLE_ENTITY
),
]);
$this->service->deactivate($license);
}
public function testGetLicenseStatusFromCache(): void
{
Saloon::fake([]);
/** @var License $license */
$license = License::factory()->create();
Cache::put('license_status', LicenseStatus::valid($license));
$this->apiClient->shouldNotReceive('post');
self::assertTrue($this->service->getStatus()->license->is($license));
self::assertTrue($this->service->getStatus()->isValid());
Saloon::assertNothingSent();
}
public function testGetLicenseStatusWithNoLicenses(): void
{
Saloon::fake([]);
License::query()->delete();
$this->apiClient->shouldNotReceive('post');
self::assertTrue($this->service->getStatus()->hasNoLicense());
Saloon::assertNothingSent();
}
public function testGetLicenseStatusValidatesWithApi(): void
@ -198,40 +181,32 @@ class LicenseServiceTest extends TestCase
self::assertFalse(Cache::has('license_status'));
$this->apiClient
->shouldReceive('post')
->with('licenses/validate', [
'license_key' => $license->key,
'instance_id' => $license->instance->id,
])
->once()
->andReturn(json_decode(File::get(test_path('blobs/lemonsqueezy/license-validated-successful.json'))));
Saloon::fake([
ValidateLicenseRequest::class => MockResponse::make(
body: File::get(test_path('blobs/lemonsqueezy/license-validated-successful.json')),
),
]);
self::assertTrue($this->service->getStatus()->isValid());
self::assertTrue(Cache::has('license_status'));
Saloon::assertSent(static function (ValidateLicenseRequest $request) use ($license): bool {
self::assertSame([
'license_key' => $license->key,
'instance_id' => $license->instance->id,
], $request->body()->all());
return true;
});
}
public function testGetLicenseStatusValidatesWithApiWithInvalidLicense(): void
{
/** @var License $license */
$license = License::factory()->create();
License::factory()->create();
self::assertFalse(Cache::has('license_status'));
$exception = Mockery::mock(ClientException::class, [
'getResponse' => Mockery::mock(ResponseInterface::class, [
'getStatusCode' => Response::HTTP_NOT_FOUND,
]),
]);
$this->apiClient
->shouldReceive('post')
->with('licenses/validate', [
'license_key' => $license->key,
'instance_id' => $license->instance->id,
])
->once()
->andThrow($exception);
Saloon::fake([ValidateLicenseRequest::class => MockResponse::make(status: Response::HTTP_NOT_FOUND)]);
self::assertFalse($this->service->getStatus()->isValid());
self::assertTrue(Cache::has('license_status'));

View file

@ -94,7 +94,7 @@ class PlaylistServiceTest extends TestCase
/** @var PlaylistFolder $folder */
$folder = PlaylistFolder::factory()->create();
self::expectException(InvalidArgumentException::class);
$this->expectException(InvalidArgumentException::class);
$this->service->createPlaylist('foo', create_user(), $folder);
}
@ -151,8 +151,8 @@ class PlaylistServiceTest extends TestCase
public function testSettingOwnsSongOnlyFailsForCommunityLicenseWhenCreate(): void
{
self::expectException(BaseInvalidArgumentException::class);
self::expectExceptionMessage('"Own songs only" option only works with smart playlists and Plus license.');
$this->expectException(BaseInvalidArgumentException::class);
$this->expectExceptionMessage('"Own songs only" option only works with smart playlists and Plus license.');
$this->service->createPlaylist(
name: 'foo',
@ -176,8 +176,8 @@ class PlaylistServiceTest extends TestCase
public function testSettingOwnsSongOnlyFailsForCommunityLicenseWhenUpdate(): void
{
self::expectException(BaseInvalidArgumentException::class);
self::expectExceptionMessage('"Own songs only" option only works with smart playlists and Plus license.');
$this->expectException(BaseInvalidArgumentException::class);
$this->expectExceptionMessage('"Own songs only" option only works with smart playlists and Plus license.');
/** @var Playlist $playlist */
$playlist = Playlist::factory()->smart()->create();

View file

@ -28,7 +28,7 @@ class LocalStorageTest extends TestCase
{
Setting::set('media_path', '');
self::expectException(MediaPathNotSetException::class);
$this->expectException(MediaPathNotSetException::class);
$this->service->storeUploadedFile(Mockery::mock(UploadedFile::class), create_user());
}
@ -36,7 +36,7 @@ class LocalStorageTest extends TestCase
{
Setting::set('media_path', public_path('sandbox/media'));
self::expectException(SongUploadFailedException::class);
$this->expectException(SongUploadFailedException::class);
$this->service->storeUploadedFile(UploadedFile::fake()->create('fake.mp3'), create_user());
}

View file

@ -23,14 +23,14 @@ class StreamerTest extends TestCase
public function testResolveAdapters(): void
{
collect(SongStorageTypes::ALL_TYPES)
->each(static function (?string $type): void {
->each(function (?string $type): void {
/** @var Song $song */
$song = Song::factory()->make(['storage' => $type]);
switch ($type) {
case SongStorageTypes::S3:
case SongStorageTypes::DROPBOX:
self::expectException(KoelPlusRequiredException::class);
$this->expectException(KoelPlusRequiredException::class);
new Streamer($song);
break;

View file

@ -0,0 +1,54 @@
<?php
namespace Tests\Integration\Services;
use App\Http\Integrations\YouTube\Requests\SearchVideosRequest;
use App\Models\Artist;
use App\Models\Song;
use App\Services\YouTubeService;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\File;
use Saloon\Http\Faking\MockResponse;
use Saloon\Laravel\Saloon;
use Tests\TestCase;
use function Tests\test_path;
class YouTubeServiceTest extends TestCase
{
private YouTubeService $service;
public function setUp(): void
{
parent::setUp();
$this->service = app(YouTubeService::class);
}
public function testSearchVideosRelatedToSong(): void
{
/** @var Song $song */
$song = Song::factory()->for(Artist::factory()->create(['name' => 'Slipknot']))->create(['title' => 'Snuff']);
Saloon::fake([
SearchVideosRequest::class => MockResponse::make(body: File::get(test_path('blobs/youtube/search.json'))),
]);
$response = $this->service->searchVideosRelatedToSong($song, 'my-token');
self::assertSame('Slipknot - Snuff [OFFICIAL VIDEO]', $response->items[0]->snippet->title);
self::assertNotNull(Cache::get('youtube.cce909a3df066c88c2666d4283697867'));
Saloon::assertSent(static function (SearchVideosRequest $request): bool {
self::assertSame([
'part' => 'snippet',
'type' => 'video',
'maxResults' => 10,
'pageToken' => 'my-token',
'q' => 'Snuff Slipknot',
], $request->query()->all());
return true;
});
}
}

View file

@ -71,7 +71,7 @@ class SpotifyClientTest extends TestCase
'koel.spotify.client_secret' => null,
]);
self::expectException(SpotifyIntegrationDisabledException::class);
$this->expectException(SpotifyIntegrationDisabledException::class);
(new SpotifyClient($this->wrapped, $this->session, $this->cache))->search('foo', 'track');
}

View file

@ -94,7 +94,7 @@ class ValidSmartPlaylistRulePayloadTest extends TestCase
/** @dataProvider provideInvalidPayloads */
public function testInvalidCases($value): void
{
self::expectException(Throwable::class);
$this->expectException(Throwable::class);
self::assertFalse((new ValidSmartPlaylistRulePayload())->passes('rules', $value));
}

View file

@ -1,87 +0,0 @@
<?php
namespace Tests\Unit\Services;
use App\Services\ApiClients\ITunesClient;
use App\Services\ITunesService;
use Illuminate\Cache\Repository as Cache;
use Mockery;
use Tests\TestCase;
class ITunesServiceTest extends TestCase
{
public function testConfiguration(): void
{
config(['koel.itunes.enabled' => true]);
/** @var ITunesService $iTunes */
$iTunes = app()->make(ITunesService::class);
self::assertTrue($iTunes->used());
config(['koel.itunes.enabled' => false]);
self::assertFalse($iTunes->used());
}
/** @return array<mixed> */
public function provideGetTrackUrlData(): array
{
return [
[
'Foo',
'Bar',
'Baz',
'Foo Bar Baz',
'https://itunes.apple.com/bar',
'https://itunes.apple.com/bar?at=foo',
'2ce68c30758ed9496c72c36ff49c50b2',
], [
'Foo',
'',
'Baz',
'Foo Baz',
'https://itunes.apple.com/bar?qux=qux',
'https://itunes.apple.com/bar?qux=qux&at=foo',
'cda57916eb80c2ee79b16e218bdb70d2',
],
];
}
/** @dataProvider provideGetTrackUrlData */
public function testGetTrackUrl(
string $term,
string $album,
string $artist,
string $constructedTerm,
string $trackViewUrl,
string $affiliateUrl,
string $cacheKey
): void {
config(['koel.itunes.affiliate_id' => 'foo']);
$cache = Mockery::mock(Cache::class);
$client = Mockery::mock(ITunesClient::class);
$client->shouldReceive('get')
->with('/', [
'query' => [
'term' => $constructedTerm,
'media' => 'music',
'entity' => 'song',
'limit' => 1,
],
])
->andReturn(json_decode(json_encode([
'resultCount' => 1,
'results' => [['trackViewUrl' => $trackViewUrl]],
])));
$service = new ITunesService($client, $cache);
$cache
->shouldReceive('remember')
->with($cacheKey, 10_080, Mockery::on(static function (callable $generator) use ($affiliateUrl): bool {
self::assertSame($generator(), $affiliateUrl);
return true;
}));
$service->getTrackUrl($term, $album, $artist);
}
}

View file

@ -10,7 +10,7 @@ class DropboxStorageTest extends TestCase
{
public function testSupported(): void
{
self::expectException(KoelPlusRequiredException::class);
$this->expectException(KoelPlusRequiredException::class);
app(DropboxStorage::class);
}
}

View file

@ -10,7 +10,7 @@ class S3CompatibleStorageTest extends TestCase
{
public function testSupported(): void
{
self::expectException(KoelPlusRequiredException::class);
$this->expectException(KoelPlusRequiredException::class);
app(S3CompatibleStorage::class);
}
}

View file

@ -1,34 +0,0 @@
<?php
namespace Tests\Unit\Services;
use App\Models\Artist;
use App\Models\Song;
use App\Services\ApiClients\YouTubeClient;
use App\Services\YouTubeService;
use Illuminate\Cache\Repository;
use Illuminate\Support\Facades\File;
use Mockery;
use Tests\TestCase;
use function Tests\test_path;
class YouTubeServiceTest extends TestCase
{
public function testSearchVideosRelatedToSong(): void
{
/** @var Song $song */
$song = Song::factory()->for(Artist::factory()->create(['name' => 'Bar']))->create(['title' => 'Foo']);
$client = Mockery::mock(YouTubeClient::class);
$client->shouldReceive('get')
->with('search?part=snippet&type=video&maxResults=10&pageToken=my-token&q=Foo+Bar')
->andReturn(json_decode(File::get(test_path('blobs/youtube/search.json'))));
$service = new YouTubeService($client, app(Repository::class));
$response = $service->searchVideosRelatedToSong($song, 'my-token');
self::assertSame('Slipknot - Snuff [OFFICIAL VIDEO]', $response->items[0]->snippet->title);
self::assertNotNull(cache()->get('5becf539115b18b2df11c39adbc2bdfa'));
}
}