Refactor tests

This commit is contained in:
Phan An 2017-08-05 19:55:02 +01:00
parent 1560cf99d0
commit 39e837713f
18 changed files with 234 additions and 112 deletions

View file

@ -7,6 +7,8 @@ use App\Traits\SupportsDeleteWhereIDsNotIn;
use Exception;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Log;
/**
@ -33,16 +35,31 @@ class Album extends Model
protected $casts = ['artist_id' => 'integer'];
protected $appends = ['is_compilation'];
/**
* An album belongs to an artist.
*
* @return BelongsTo
*/
public function artist()
{
return $this->belongsTo(Artist::class);
}
/**
* An album can contain many songs.
*
* @return HasMany
*/
public function songs()
{
return $this->hasMany(Song::class);
}
/**
* Indicate if the album is unknown.
*
* @return bool
*/
public function getIsUnknownAttribute()
{
return $this->id === self::UNKNOWN_ID;
@ -166,11 +183,23 @@ class Album extends Model
return app()->publicPath().'/public/img/covers/'.uniqid('', true).".$extension";
}
/**
* Set the album cover.
*
* @param string $value
*/
public function setCoverAttribute($value)
{
$this->attributes['cover'] = $value ?: self::UNKNOWN_COVER;
}
/**
* Get the album cover.
*
* @param string $value
*
* @return string
*/
public function getCoverAttribute($value)
{
return app()->staticUrl('public/img/covers/'.($value ?: self::UNKNOWN_COVER));

View file

@ -6,7 +6,10 @@ use App\Facades\Lastfm;
use App\Facades\Util;
use App\Traits\SupportsDeleteWhereIDsNotIn;
use Exception;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
use Log;
/**
@ -15,7 +18,7 @@ use Log;
* @property string image
* @property bool is_unknown
* @property bool is_various
* @property \Illuminate\Database\Eloquent\Collection songs
* @property Collection songs
*/
class Artist extends Model
{
@ -30,21 +33,42 @@ class Artist extends Model
protected $hidden = ['created_at', 'updated_at'];
/**
* An artist can have many albums.
*
* @return HasMany
*/
public function albums()
{
return $this->hasMany(Album::class);
}
/**
* An artist can have many songs.
* Unless he is Rick Astley.
*
* @return HasManyThrough
*/
public function songs()
{
return $this->hasManyThrough(Song::class, Album::class);
}
/**
* Indicate if the artist is unknown.
*
* @return bool
*/
public function getIsUnknownAttribute()
{
return $this->id === self::UNKNOWN_ID;
}
/**
* Indicate if the artist is the special "Various Artists"
*
* @return bool
*/
public function getIsVariousAttribute()
{
return $this->id === self::VARIOUS_ID;

View file

@ -5,6 +5,7 @@ namespace App\Models;
use App\Events\SongLikeToggled;
use App\Traits\CanFilterByUser;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
/**
* @property bool liked
@ -26,11 +27,21 @@ class Interaction extends Model
protected $hidden = ['id', 'user_id', 'created_at', 'updated_at'];
/**
* An interaction belongs to a user.
*
* @return BelongsTo
*/
public function user()
{
return $this->belongsTo(User::class);
}
/**
* An interaction is associated with a song.
*
* @return BelongsTo
*/
public function song()
{
return $this->belongsTo(Song::class);

View file

@ -5,6 +5,8 @@ namespace App\Models;
use App\Traits\CanFilterByUser;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
/**
* @property int user_id
@ -22,11 +24,21 @@ class Playlist extends Model
'user_id' => 'int',
];
/**
* A playlist can have many songs.
*
* @return BelongsToMany
*/
public function songs()
{
return $this->belongsToMany(Song::class);
}
/**
* A playlist belongs to a user.
*
* @return BelongsTo
*/
public function user()
{
return $this->belongsTo(User::class);

View file

@ -17,7 +17,8 @@ class DownloadTest extends TestCase
$this->createSampleMediaSet();
}
public function testOneSong()
/** @test */
public function a_single_song_can_be_downloaded()
{
$song = Song::first();
Download::shouldReceive('from')
@ -28,7 +29,8 @@ class DownloadTest extends TestCase
->seeStatusCode(200);
}
public function testMultipleSongs()
/** @test */
public function multiple_songs_can_be_downloaded()
{
$songs = Song::take(2)->get();
Download::shouldReceive('from')
@ -39,7 +41,8 @@ class DownloadTest extends TestCase
->seeStatusCode(200);
}
public function testAlbum()
/** @test */
public function a_whole_album_can_be_downloaded()
{
$album = Album::first();
@ -51,7 +54,8 @@ class DownloadTest extends TestCase
->seeStatusCode(200);
}
public function testArtist()
/** @test */
public function a_whole_artists_biography_can_be_downloaded()
{
$artist = Artist::first();
@ -63,7 +67,8 @@ class DownloadTest extends TestCase
->seeStatusCode(200);
}
public function testPlaylist()
/** @test */
public function a_whole_playlist_can_be_downloaded()
{
$user = factory(User::class)->create();
@ -82,7 +87,8 @@ class DownloadTest extends TestCase
->seeStatusCode(200);
}
public function testFavorites()
/** @test */
public function all_favorite_songs_can_be_downloaded()
{
Download::shouldReceive('from')
->once()

View file

@ -17,7 +17,8 @@ class InteractionTest extends TestCase
$this->createSampleMediaSet();
}
public function testPlayCountRegister()
/** @test */
public function play_count_is_increased()
{
$this->withoutEvents();
$user = factory(User::class)->create();
@ -41,7 +42,8 @@ class InteractionTest extends TestCase
]);
}
public function testLikeRegister()
/** @test */
public function user_can_like_and_unlike_a_song()
{
$this->expectsEvents(SongLikeToggled::class);
@ -66,7 +68,8 @@ class InteractionTest extends TestCase
]);
}
public function testBatchLikeAndUnlike()
/** @test */
public function user_can_like_and_unlike_songs_in_batch()
{
$this->expectsEvents(SongLikeToggled::class);

View file

@ -24,6 +24,12 @@ class LastfmTest extends TestCase
{
use WithoutMiddleware;
protected function tearDown()
{
m::close();
parent::tearDown();
}
public function testGetSessionKey()
{
$client = m::mock(Client::class, [
@ -35,7 +41,8 @@ class LastfmTest extends TestCase
$this->assertEquals('foo', $api->getSessionKey('bar'));
}
public function testSetSessionKey()
/** @test */
public function session_key_can_be_set()
{
$user = factory(User::class)->create();
$this->postAsUser('api/lastfm/session-key', ['key' => 'foo'], $user);
@ -43,7 +50,8 @@ class LastfmTest extends TestCase
$this->assertEquals('foo', $user->lastfm_session_key);
}
public function testControllerConnect()
/** @test */
public function user_can_connect_to_lastfm()
{
$redirector = m::mock(Redirector::class);
$redirector->shouldReceive('to')->once();
@ -57,7 +65,8 @@ class LastfmTest extends TestCase
(new LastfmController($guard))->connect($redirector, new Lastfm(), $auth);
}
public function testControllerCallback()
/** @test */
public function lastfm_session_key_can_be_retrieved_and_stored()
{
$request = m::mock(Request::class);
$request->token = 'foo';
@ -71,7 +80,8 @@ class LastfmTest extends TestCase
$this->assertEquals('bar', $user->lastfm_session_key);
}
public function testControllerDisconnect()
/** @test */
public function user_can_disconnect_from_lastfm()
{
$user = factory(User::class)->create(['preferences' => ['lastfm_session_key' => 'bar']]);
$this->deleteAsUser('api/lastfm/disconnect', [], $user);
@ -79,7 +89,8 @@ class LastfmTest extends TestCase
$this->assertNull($user->lastfm_session_key);
}
public function testLoveTrack()
/** @test */
public function user_can_love_a_track_on_lastfm()
{
$this->withoutEvents();
$this->createSampleMediaSet();
@ -98,7 +109,8 @@ class LastfmTest extends TestCase
(new LoveTrackOnLastfm($lastfm))->handle(new SongLikeToggled($interaction, $user));
}
public function testUpdateNowPlaying()
/** @test */
public function user_now_playing_status_can_be_updated_to_lastfm()
{
$this->withoutEvents();
$this->createSampleMediaSet();

View file

@ -16,7 +16,14 @@ class MediaTest extends TestCase
{
use WithoutMiddleware;
public function testSync()
protected function tearDown()
{
m::close();
parent::tearDown();
}
/** @test */
public function songs_can_be_synced()
{
$this->expectsEvents(LibraryChanged::class);
@ -78,7 +85,8 @@ class MediaTest extends TestCase
$this->assertEquals($currentCover, Album::find($album->id)->cover);
}
public function testForceSync()
/** @test */
public function songs_can_be_force_synced()
{
$this->expectsEvents(LibraryChanged::class);
@ -112,7 +120,8 @@ class MediaTest extends TestCase
$this->assertEquals($originalLyrics, $song->lyrics);
}
public function testSyncSelectiveTags()
/** @test */
public function songs_can_be_synced_with_selectively_tags()
{
$this->expectsEvents(LibraryChanged::class);
@ -137,7 +146,8 @@ class MediaTest extends TestCase
$this->assertEquals('Booom Wroooom', $song->lyrics);
}
public function testAlwaysSyncAllTagsIfFileIsNew()
/** @test */
public function all_tags_are_catered_for_if_syncing_new_file()
{
$media = new Media();
$media->sync($this->mediaPath);
@ -155,7 +165,8 @@ class MediaTest extends TestCase
$this->assertEquals($song, $addedSong);
}
public function testWatchSingleFileAdded()
/** @test */
public function added_song_is_synced_when_watching()
{
$this->expectsEvents(LibraryChanged::class);
@ -166,7 +177,8 @@ class MediaTest extends TestCase
$this->seeInDatabase('songs', ['path' => $path]);
}
public function testWatchSingleFileDeleted()
/** @test */
public function deleted_song_is_synced_when_watching()
{
$this->expectsEvents(LibraryChanged::class);
@ -178,7 +190,8 @@ class MediaTest extends TestCase
$this->notSeeInDatabase('songs', ['id' => $song->id]);
}
public function testWatchDirectoryDeleted()
/** @test */
public function deleted_directory_is_synced_when_watching()
{
$this->expectsEvents(LibraryChanged::class);
@ -192,7 +205,8 @@ class MediaTest extends TestCase
$this->notSeeInDatabase('songs', ['path' => $this->mediaPath.'/subdir/back-in-black.mp3']);
}
public function testHtmlEntitiesInTags()
/** @test */
public function html_entities_in_tags_are_recognized_and_saved_properly()
{
$getID3 = m::mock(getID3::class, [
'analyze' => [
@ -215,7 +229,8 @@ class MediaTest extends TestCase
$this->assertEquals('水谷広実', $info['title']);
}
public function testDotDirectories()
/** @test */
public function hidden_files_can_optionally_be_ignored_when_syncing()
{
config(['koel.ignore_dot_files' => false]);
$media = new Media();

View file

@ -16,7 +16,8 @@ class S3Test extends TestCase
$this->disableMiddlewareForAllTests();
}
public function testPut()
/** @test */
public function a_song_can_be_added()
{
$this->post('api/os/s3/song', [
'bucket' => 'koel',
@ -32,7 +33,8 @@ class S3Test extends TestCase
])->seeInDatabase('songs', ['path' => 's3://koel/sample.mp3']);
}
public function testRemove()
/** @test */
public function a_song_can_be_removed()
{
$this->expectsEvents(LibraryChanged::class);
$this->post('api/os/s3/song', [

View file

@ -14,7 +14,8 @@ class PlaylistTest extends TestCase
$this->createSampleMediaSet();
}
public function testCreatePlaylist()
/** @test */
public function user_can_create_a_playlist()
{
$user = factory(User::class)->create();
@ -47,7 +48,8 @@ class PlaylistTest extends TestCase
]);
}
public function testUpdatePlaylistName()
/** @test */
public function user_can_update_a_playlists_name()
{
$user = factory(User::class)->create();
@ -67,7 +69,8 @@ class PlaylistTest extends TestCase
->seeStatusCode(403);
}
public function testSyncPlaylist()
/** @test */
public function playlists_can_be_synced()
{
$user = factory(User::class)->create();
@ -103,7 +106,8 @@ class PlaylistTest extends TestCase
]);
}
public function testDeletePlaylist()
/** @test */
public function user_can_delete_a_playlist()
{
$user = factory(User::class)->create();

View file

@ -9,7 +9,8 @@ class ProfileTest extends TestCase
{
use WithoutMiddleware;
public function testUpdate()
/** @test */
public function user_can_update_his_profile()
{
$user = factory(User::class)->create();
$this->putAsUser('api/me', ['name' => 'Foo', 'email' => 'bar@baz.com'], $user);

View file

@ -11,7 +11,14 @@ class ScrobbleTest extends TestCase
{
use WithoutMiddleware;
public function testScrobble()
protected function tearDown()
{
m::close();
parent::tearDown();
}
/** @test */
public function a_song_can_be_scrobbed_via_lastfm()
{
$this->withoutEvents();
$this->createSampleMediaSet();

View file

@ -11,42 +11,8 @@ class SettingTest extends TestCase
{
use WithoutMiddleware;
public function testSetSingleKeyValue()
{
Setting::set('foo', 'bar');
$this->seeInDatabase('settings', ['key' => 'foo', 'value' => 's:3:"bar";']);
}
public function testSetMultipleKeyValue()
{
Setting::set([
'foo' => 'bar',
'baz' => 'qux',
]);
$this->seeInDatabase('settings', ['key' => 'foo', 'value' => 's:3:"bar";']);
$this->seeInDatabase('settings', ['key' => 'baz', 'value' => 's:3:"qux";']);
}
public function testExistingShouldBeUpdated()
{
Setting::set('foo', 'bar');
Setting::set('foo', 'baz');
$this->assertEquals('baz', Setting::get('foo'));
}
public function testGet()
{
Setting::set('foo', 'bar');
Setting::set('bar', ['baz' => 'qux']);
$this->assertEquals('bar', Setting::get('foo'));
$this->assertEquals(['baz' => 'qux'], Setting::get('bar'));
}
public function testApplicationSetting()
/** @test */
public function application_setting_is_saved_properly()
{
Media::shouldReceive('sync')->once();

View file

@ -6,7 +6,8 @@ use App\Models\User;
class UserTest extends TestCase
{
public function testCreateUser()
/** @test */
public function admin_can_create_a_user()
{
// Non-admins can't do shit
$this->postAsUser('api/user', [
@ -26,7 +27,8 @@ class UserTest extends TestCase
$this->seeInDatabase('users', ['name' => 'Foo']);
}
public function testUpdateUser()
/** @test */
public function admin_can_update_a_user()
{
$user = factory(User::class)->create();
@ -39,7 +41,8 @@ class UserTest extends TestCase
$this->seeInDatabase('users', ['name' => 'Foo', 'email' => 'bar@baz.com']);
}
public function testDeleteUser()
/** @test */
public function admin_can_delete_a_user()
{
$user = factory(User::class)->create();
$admin = factory(User::class, 'admin')->create();
@ -53,7 +56,8 @@ class UserTest extends TestCase
->seeInDatabase('users', ['id' => $admin->id]);
}
public function testUserPreferences()
/** @test */
public function user_can_update_their_preferences()
{
$user = factory(User::class)->create();
$this->assertNull($user->getPreference('foo'));
@ -64,15 +68,4 @@ class UserTest extends TestCase
$user->deletePreference('foo');
$this->assertNull($user->getPreference('foo'));
}
public function testHidingUserPreferences()
{
$user = factory(User::class)->create([
'preferences' => [
'lastfm_session_key' => '123456',
],
]);
$this->assertEquals('hidden', $user->preferences['lastfm_session_key']);
}
}

View file

@ -3,41 +3,21 @@
namespace Tests\Feature;
use App\Models\Song;
use App\Services\YouTube;
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Response;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Mockery as m;
use YouTube as YouTubeFacade;
use YouTube;
class YouTubeTest extends TestCase
{
use WithoutMiddleware;
public function testSearch()
{
$this->withoutEvents();
$client = m::mock(Client::class, [
'get' => new Response(200, [], file_get_contents(__DIR__.'../../blobs/youtube/search.json')),
]);
$api = new YouTube(null, $client);
$response = $api->search('Lorem Ipsum');
$this->assertEquals('Slipknot - Snuff [OFFICIAL VIDEO]', $response->items[0]->snippet->title);
// Is it cached?
$this->assertNotNull(cache('1492972ec5c8e6b3a9323ba719655ddb'));
}
public function testSearchVideosRelatedToSong()
/** @test */
public function youtube_videos_related_to_a_song_can_be_searched()
{
$this->createSampleMediaSet();
$song = Song::first();
// We test on the facade here
YouTubeFacade::shouldReceive('searchVideosRelatedToSong')->once();
YouTube::shouldReceive('searchVideosRelatedToSong')->once();
$this->getAsUser("/api/youtube/search/song/{$song->id}");
}

View file

@ -0,0 +1,36 @@
<?php
namespace Tests\Integration;
use App\Services\YouTube;
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Response;
use Mockery as m;
use Tests\TestCase;
class YouTubeTest extends TestCase
{
protected function tearDown()
{
m::close();
parent::tearDown();
}
/** @test */
public function videos_can_be_searched_from_youtube()
{
$this->withoutEvents();
$client = m::mock(Client::class, [
'get' => new Response(200, [], file_get_contents(__DIR__.'../../blobs/youtube/search.json')),
]);
$api = new YouTube(null, $client);
$response = $api->search('Lorem Ipsum');
$this->assertEquals('Slipknot - Snuff [OFFICIAL VIDEO]', $response->items[0]->snippet->title);
// Is it cached?
$this->assertNotNull(cache('1492972ec5c8e6b3a9323ba719655ddb'));
}
}

View file

@ -1,27 +1,39 @@
<?php
namespace Tests\Feature;
namespace Tests\Unit;
use App\Services\RESTfulService;
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Response;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Mockery as m;
use Tests\TestCase;
class RESTfulAPIServiceTest extends TestCase
{
use WithoutMiddleware;
public function testUrlConstruction()
protected function tearDown()
{
$api = new RESTfulService('bar', null, 'http://foo.com', m::mock(Client::class));
m::close();
parent::tearDown();
}
/** @test */
public function a_uri_can_be_constructed()
{
/** @var Client $client */
$client = m::mock(Client::class);
$api = new RESTfulService('bar', null, 'http://foo.com', $client);
$this->assertEquals('http://foo.com/get/param?key=bar', $api->buildUrl('get/param'));
$this->assertEquals('http://foo.com/get/param?baz=moo&key=bar', $api->buildUrl('/get/param?baz=moo'));
$this->assertEquals('http://baz.com/?key=bar', $api->buildUrl('http://baz.com/'));
}
public function testRequest()
/** @test */
public function a_request_can_be_made()
{
/** @var Client $client */
$client = m::mock(Client::class, [
'get' => new Response(200, [], '{"foo":"bar"}'),
'post' => new Response(200, [], '{"foo":"bar"}'),

View file

@ -52,6 +52,15 @@ class SettingTest extends TestCase
]);
}
/** @test */
public function existing_settings_should_be_updated()
{
Setting::set('foo', 'bar');
Setting::set('foo', 'baz');
$this->assertEquals('baz', Setting::get('foo'));
}
/** @test */
public function it_gets_the_setting_value_in_an_unserialized_format()
{