Add a UserPreferenceService

This commit is contained in:
Phan An 2018-09-04 12:34:02 +07:00
parent 0f9bb32956
commit 373870fefb
19 changed files with 320 additions and 216 deletions

View file

@ -13,9 +13,9 @@ class SongLikeToggled extends Event
public $interaction;
public $user;
public function __construct(Interaction $interaction, User $user = null)
public function __construct(Interaction $interaction, User $user)
{
$this->interaction = $interaction;
$this->user = $user ?: auth()->user();
$this->user = $user;
}
}

View file

@ -18,7 +18,11 @@ class LastfmController extends Controller
private $lastfmService;
private $jwtAuth;
public function __construct(Guard $auth, LastfmService $lastfmService, JWTAuth $jwtAuth)
public function __construct(
Guard $auth,
LastfmService $lastfmService,
JWTAuth $jwtAuth
)
{
$this->auth = $auth;
$this->lastfmService = $lastfmService;
@ -54,11 +58,11 @@ class LastfmController extends Controller
*/
public function callback(LastfmCallbackRequest $request)
{
$sessionKey = $this->lastfmService->getSessionKey($request->token);
$sessionKey = $this->lastfmService->fetchSessionKeyUsingToken($request->token);
abort_unless($sessionKey, 500, 'Invalid token key.');
$this->auth->user()->savePreference('lastfm_session_key', $sessionKey);
$this->lastfmService->setUserSessionKey($this->auth->user(), $sessionKey);
return view('api.lastfm.callback');
}
@ -72,7 +76,7 @@ class LastfmController extends Controller
*/
public function setSessionKey(LastfmSetSessionKeyRequest $request)
{
$this->auth->user()->savePreference('lastfm_session_key', trim($request->key));
$this->lastfmService->setUserSessionKey($this->auth->user(), $request->key);
return response()->json();
}
@ -84,7 +88,7 @@ class LastfmController extends Controller
*/
public function disconnect()
{
$this->auth->user()->deletePreference('lastfm_session_key');
$this->lastfmService->deleteUserSessionKey($this->auth->user());
return response()->json();
}

View file

@ -26,13 +26,13 @@ class ScrobbleController extends Controller
*/
public function store(Request $request, Song $song, string $timestamp)
{
if (!$song->artist->is_unknown && $request->user()->connectedToLastfm()) {
if (!$song->artist->is_unknown && $this->lastfmService->isUserConnected($request->user())) {
$this->lastfmService->scrobble(
$song->artist->name,
$song->title,
(int) $timestamp,
$song->album->name === Album::UNKNOWN_NAME ? '' : $song->album->name,
$request->user()->lastfm_session_key
$this->lastfmService->getUserSessionKey($request->user())
);
}

View file

@ -16,18 +16,22 @@ class LoveTrackOnLastfm
public function handle(SongLikeToggled $event): void
{
if (!$this->lastfm->enabled() ||
!($sessionKey = $event->user->lastfm_session_key) ||
$event->interaction->song->album->artist->is_unknown
) {
if (!$this->shouldHandle($event)) {
return;
}
$this->lastfm->toggleLoveTrack(
$event->interaction->song->title,
$event->interaction->song->album->artist->name,
$sessionKey,
$event->interaction->song->artist->name,
$this->lastfm->getUserSessionKey($event->user),
$event->interaction->liked
);
}
private function shouldHandle(SongLikeToggled $event): bool
{
return $this->lastfm->enabled()
&& $this->lastfm->isUserConnected($event->user)
&& !$event->interaction->song->artist->is_unknown;
}
}

View file

@ -17,10 +17,7 @@ class UpdateLastfmNowPlaying
public function handle(SongStartedPlaying $event): void
{
if (!$this->lastfm->enabled() ||
!($sessionKey = $event->user->lastfm_session_key) ||
$event->song->artist->is_unknown
) {
if (!$this->shouldHandle($event)) {
return;
}
@ -29,7 +26,14 @@ class UpdateLastfmNowPlaying
$event->song->title,
$event->song->album->name === Album::UNKNOWN_NAME ? '' : $event->song->album->name,
$event->song->length,
$sessionKey
$this->lastfm->getUserSessionKey($event->user)
);
}
private function shouldHandle(SongStartedPlaying $event): bool
{
return $this->lastfm->enabled()
&& $this->lastfm->isUserConnected($event->user)
&& !$event->song->artist->is_unknown;
}
}

View file

@ -2,6 +2,7 @@
namespace App\Models;
use App\Services\LastfmService;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
@ -10,7 +11,6 @@ use Illuminate\Notifications\Notifiable;
* @property array $preferences
* @property int $id
* @property bool $is_admin
* @property string $lastfm_session_key
*/
class User extends Authenticatable
{
@ -21,7 +21,7 @@ class User extends Authenticatable
*
* @var array
*/
private const HIDDEN_PREFERENCES = ['lastfm_session_key'];
private const HIDDEN_PREFERENCES = [LastfmService::SESSION_KEY_PREFERENCE_KEY];
protected $guarded = ['id'];
protected $casts = [
@ -40,66 +40,6 @@ class User extends Authenticatable
return $this->hasMany(Interaction::class);
}
/**
* @return mixed|null
*/
public function getPreference(string $key)
{
// We can't use $this->preferences directly, since the data has been tampered
// by getPreferencesAttribute().
return array_get((array) unserialize($this->attributes['preferences']), $key);
}
/**
* @param mixed $val
*/
public function savePreference(string $key, $val): void
{
$preferences = $this->preferences;
$preferences[$key] = $val;
$this->preferences = $preferences;
$this->save();
}
/**
* An alias to savePreference().
*
* @param mixed $val
*
* @see self::savePreference
*/
public function setPreference(string $key, $val): void
{
$this->savePreference($key, $val);
}
public function deletePreference(string $key): void
{
$preferences = $this->preferences;
array_forget($preferences, $key);
$this->update(compact('preferences'));
}
/**
* Determine if the user is connected to Last.fm.
*/
public function connectedToLastfm(): bool
{
return (bool) $this->lastfm_session_key;
}
/**
* Get the user's Last.fm session key.
*
* @return string|null The key if found, or null if user isn't connected to Last.fm
*/
public function getLastfmSessionKeyAttribute(): ?string
{
return $this->getPreference('lastfm_session_key');
}
/**
* User preferences are stored as a serialized associative array.
*

View file

@ -70,9 +70,9 @@ abstract class ApiClient
return $body;
} catch (ClientException $e) {
$this->logger->error($e);
return;
}
return null;
}
/**

View file

@ -45,11 +45,11 @@ class InteractionService
return tap($this->interaction->firstOrCreate([
'song_id' => $songId,
'user_id' => $user->id,
]), static function (Interaction $interaction): void {
]), static function (Interaction $interaction) use ($user): void {
$interaction->liked = !$interaction->liked;
$interaction->save();
event(new SongLikeToggled($interaction));
event(new SongLikeToggled($interaction, $user));
});
}
@ -66,7 +66,7 @@ class InteractionService
return tap($this->interaction->firstOrCreate([
'song_id' => $songId,
'user_id' => $user->id,
]), static function (Interaction $interaction): void {
]), static function (Interaction $interaction) use ($user): void {
if (!$interaction->exists) {
$interaction->play_count = 0;
}
@ -74,7 +74,7 @@ class InteractionService
$interaction->liked = true;
$interaction->save();
event(new SongLikeToggled($interaction));
event(new SongLikeToggled($interaction, $user));
});
})->all();
}
@ -90,11 +90,11 @@ class InteractionService
->whereIn('song_id', $songIds)
->where('user_id', $user->id)
->get()
->each(static function (Interaction $interaction): void {
->each(static function (Interaction $interaction) use ($user): void {
$interaction->liked = false;
$interaction->save();
event(new SongLikeToggled($interaction));
event(new SongLikeToggled($interaction, $user));
}
);
}

View file

@ -2,10 +2,16 @@
namespace App\Services;
use App\Models\User;
use Exception;
use GuzzleHttp\Client;
use Illuminate\Contracts\Cache\Repository as Cache;
use Illuminate\Log\Logger;
class LastfmService extends ApiClient implements ApiConsumerInterface
{
public const SESSION_KEY_PREFERENCE_KEY = 'lastfm_session_key';
/**
* Specify the response format, since Last.fm only returns XML.
*
@ -20,6 +26,19 @@ class LastfmService extends ApiClient implements ApiConsumerInterface
*/
protected $keyParam = 'api_key';
private $userPreferenceService;
public function __construct(
Client $client,
Cache $cache,
Logger $logger,
UserPreferenceService $userPreferenceService
)
{
parent::__construct($client, $cache, $logger);
$this->userPreferenceService = $userPreferenceService;
}
/**
* Determine if our application is using Last.fm.
*/
@ -167,7 +186,7 @@ class LastfmService extends ApiClient implements ApiConsumerInterface
*
* @link http://www.last.fm/api/webauth#4
*/
public function getSessionKey(string $token): ?string
public function fetchSessionKeyUsingToken(string $token): ?string
{
$query = $this->buildAuthCallParams([
'method' => 'auth.getSession',
@ -307,6 +326,26 @@ class LastfmService extends ApiClient implements ApiConsumerInterface
return trim(str_replace('Read more on Last.fm', '', nl2br(strip_tags(html_entity_decode($str)))));
}
public function getUserSessionKey(User $user): ?string
{
return $this->userPreferenceService->get($user, self::SESSION_KEY_PREFERENCE_KEY);
}
public function setUserSessionKey(User $user, string $sessionKey): void
{
$this->userPreferenceService->set($user, self::SESSION_KEY_PREFERENCE_KEY, $sessionKey);
}
public function deleteUserSessionKey(User $user): void
{
$this->userPreferenceService->delete($user, self::SESSION_KEY_PREFERENCE_KEY);
}
public function isUserConnected(User $user): bool
{
return (bool) $this->getUserSessionKey($user);
}
public function getKey(): ?string
{
return config('koel.lastfm.key');

View file

@ -0,0 +1,36 @@
<?php
namespace App\Services;
use App\Models\User;
class UserPreferenceService
{
/**
* @return mixed
*/
public function get(User $user, string $key)
{
return array_get((array) unserialize($user->getOriginal('preferences')), $key);
}
/**
* @param mixed $val
*/
public function set(User $user, string $key, $val): void
{
$preferences = $user->preferences;
$preferences[$key] = $val;
$user->preferences = $preferences;
$user->save();
}
public function delete(User $user, string $key): void
{
$preferences = $user->preferences;
array_forget($preferences, $key);
$user->update(compact('preferences'));
}
}

View file

@ -4,34 +4,33 @@ namespace Tests\Feature;
use App\Models\User;
use App\Services\LastfmService;
use App\Services\UserPreferenceService;
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Response;
use Illuminate\Contracts\Cache\Repository as Cache;
use Illuminate\Log\Logger;
use Mockery;
use Tymon\JWTAuth\JWTAuth;
class LastfmTest extends TestCase
{
public function testGetSessionKey(): void
{
/** @var Client $client */
$client = Mockery::mock(Client::class, [
'get' => new Response(200, [], file_get_contents(__DIR__.'../../blobs/lastfm/session-key.xml')),
]);
/** @var UserPreferenceService */
private $userPreferenceService;
$service = new LastfmService($client, app(Cache::class), app(Logger::class));
self::assertEquals('foo', $service->getSessionKey('bar'));
public function setUp()
{
parent::setUp();
$this->userPreferenceService = app(UserPreferenceService::class);
}
public function testSetSessionKey(): void
{
/** @var User $user */
$user = factory(User::class)->create();
$this->postAsUser('api/lastfm/session-key', ['key' => 'foo'], $user)
->assertResponseOk();
$user = User::find($user->id);
self::assertEquals('foo', $user->lastfm_session_key);
$user->refresh();
self::assertEquals('foo', app(LastfmService::class)->getUserSessionKey($user));
}
public function testConnectToLastfm(): void
@ -47,18 +46,19 @@ class LastfmTest extends TestCase
public function testRetrieveAndStoreSessionKey(): void
{
$lastfm = $this->mockIocDependency(LastfmService::class);
$lastfm->shouldReceive('getSessionKey')
->once()
->with('foo')
->andReturn('bar');
/** @var Client $client */
$client = Mockery::mock(Client::class, [
'get' => new Response(200, [], file_get_contents(__DIR__.'../../blobs/lastfm/session-key.xml')),
]);
app()->instance(Client::class, $client);
/** @var User $user */
$user = factory(User::class)->create();
$this->getAsUser('api/lastfm/callback?token=foo', $user);
$user->refresh();
$this->assertEquals('bar', $user->lastfm_session_key);
self::assertSame('foo', app(LastfmService::class)->getUserSessionKey($user));
}
public function testDisconnectUser(): void
@ -68,6 +68,6 @@ class LastfmTest extends TestCase
$this->deleteAsUser('api/lastfm/disconnect', [], $user);
$user->refresh();
$this->assertNull($user->lastfm_session_key);
$this->assertNull(app(LastfmService::class)->getUserSessionKey($user));
}
}

View file

@ -5,30 +5,26 @@ namespace Tests\Feature;
use App\Models\Song;
use App\Models\User;
use App\Services\LastfmService;
use Exception;
use Mockery;
class ScrobbleTest extends TestCase
{
/**
* @throws Exception
*/
public function testLastfmScrobble()
{
$this->withoutEvents();
$this->createSampleMediaSet();
$song = Song::first();
/** @var User $user */
$song = factory(Song::class)->create();
$user = factory(User::class)->create();
$user->setPreference('lastfm_session_key', 'foo');
$ts = time();
$this->mockIocDependency(LastfmService::class)
->shouldReceive('scrobble')
$lastfm = Mockery::mock(LastfmService::class)->makePartial();
$lastfm->shouldReceive('enabled')->andReturn(true);
$lastfm->shouldReceive('getUserSessionKey')->andReturn('foo');
$lastfm->shouldReceive('scrobble')
->with($song->album->artist->name, $song->title, $ts, $song->album->name, 'foo')
->once();
app()->instance(LastfmService::class, $lastfm);
$this->postAsUser("/api/{$song->id}/scrobble/$ts", [], $user)
->assertResponseOk();
}

View file

@ -94,16 +94,4 @@ class UserTest extends TestCase
->seeStatusCode(403)
->seeInDatabase('users', ['id' => $admin->id]);
}
public function testUpdateUserProfile()
{
$user = factory(User::class)->create();
$this->assertNull($user->getPreference('foo'));
$user->setPreference('foo', 'bar');
$this->assertEquals('bar', $user->getPreference('foo'));
$user->deletePreference('foo');
$this->assertNull($user->getPreference('foo'));
}
}

View file

@ -8,33 +8,31 @@ use App\Models\Interaction;
use App\Models\Song;
use App\Models\User;
use App\Services\LastfmService;
use Exception;
use Mockery;
use Mockery\MockInterface;
use Tests\Feature\TestCase;
class LoveTrackOnLastfmTest extends TestCase
{
/**
* @throws Exception
*/
public function testHandle()
{
$this->withoutEvents();
$this->createSampleMediaSet();
$user = factory(User::class)->create(['preferences' => ['lastfm_session_key' => 'bar']]);
$user = factory(User::class)->create();
$song = factory(Song::class)->create();
$interaction = Interaction::create([
'user_id' => $user->id,
'song_id' => Song::first()->id,
'song_id' => $song->id,
]);
/** @var LastfmService|MockInterface $lastfm */
$lastfm = Mockery::mock(LastfmService::class, ['enabled' => true]);
$lastfm = Mockery::mock(LastfmService::class);
$lastfm->shouldReceive('enabled')->andReturn(true);
$lastfm->shouldReceive('getUserSessionKey')->andReturn('foo');
$lastfm->shouldReceive('isUserConnected')->andReturn(true);
$lastfm->shouldReceive('toggleLoveTrack')
->once()
->with($interaction->song->title, $interaction->song->album->artist->name, 'bar', false);
->with($interaction->song->title, $interaction->song->album->artist->name, 'foo', false)
->once();
(new LoveTrackOnLastfm($lastfm))->handle(new SongLikeToggled($interaction, $user));
}

View file

@ -7,29 +7,26 @@ use App\Listeners\UpdateLastfmNowPlaying;
use App\Models\Song;
use App\Models\User;
use App\Services\LastfmService;
use Exception;
use Mockery;
use Mockery\MockInterface;
use Tests\Feature\TestCase;
class UpdateLastfmNowPlayingTest extends TestCase
{
/**
* @throws Exception
*/
public function testUpdateNowPlayingStatus()
{
$this->withoutEvents();
$this->createSampleMediaSet();
$user = factory(User::class)->create(['preferences' => ['lastfm_session_key' => 'bar']]);
$song = Song::first();
$user = factory(User::class)->make();
$song = factory(Song::class)->make();
/** @var LastfmService|MockInterface $lastfm */
$lastfm = Mockery::mock(LastfmService::class, ['enabled' => true]);
$lastfm = Mockery::mock(LastfmService::class);
$lastfm->shouldReceive('enabled')->andReturn(true);
$lastfm->shouldReceive('getUserSessionKey')->andReturn('foo');
$lastfm->shouldReceive('isUserConnected')->andReturn(true);
$lastfm->shouldReceive('updateNowPlaying')
->once()
->with($song->album->artist->name, $song->title, $song->album->name, $song->length, 'bar');
->with($song->album->artist->name, $song->title, $song->album->name, $song->length, 'foo');
(new UpdateLastfmNowPlaying($lastfm))->handle(new SongStartedPlaying($song, $user));
}

View file

@ -7,65 +7,15 @@ use Tests\TestCase;
class UserTest extends TestCase
{
/** @test */
public function user_preferences_can_be_set()
{
// Given a user
/** @var User $user */
$user = factory(User::class)->create();
// When I see the user's preference
$user->setPreference('foo', 'bar');
// Then I see the preference properly set
$this->assertArraySubset(['foo' => 'bar'], $user->preferences);
}
/** @test */
public function user_preferences_can_be_retrieved()
{
// Given a user with some preferences
/** @var User $user */
$user = factory(User::class)->create([
'preferences' => ['foo' => 'bar'],
]);
// When I get a preference by its key
$value = $user->getPreference('foo');
// Then I retrieve the preference value
$this->assertEquals('bar', $value);
}
/** @test */
public function user_preferences_can_be_deleted()
{
// Given a user with some preferences
/** @var User $user */
$user = factory(User::class)->create([
'preferences' => ['foo' => 'bar'],
]);
// When I delete the preference by its key
$user->deletePreference('foo');
// Then I see the preference gets deleted
$this->assertArrayNotHasKey('foo', $user->preferences);
}
/** @test */
public function sensitive_preferences_are_hidden()
{
// Given a user with sensitive preferences
/** @var User $user */
$user = factory(User::class)->create([
'preferences' => ['lastfm_session_key' => 'foo'],
]);
// When I try to access the sensitive preferences
$value = $user->preferences['lastfm_session_key'];
// Then the sensitive preferences are replaced with "hidden"
$this->assertEquals('hidden', $value);
self::assertEquals('hidden', $value);
}
}

View file

@ -4,7 +4,9 @@ namespace Tests\Integration\Services;
use App\Models\Album;
use App\Models\Artist;
use App\Models\User;
use App\Services\LastfmService;
use App\Services\UserPreferenceService;
use Exception;
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Response;
@ -28,8 +30,7 @@ class LastfmServiceTest extends TestCase
'get' => new Response(200, [], file_get_contents(__DIR__.'../../../blobs/lastfm/artist.xml')),
]);
$api = new LastfmService($client, app(Cache::class), app(Logger::class));
$info = $api->getArtistInformation($artist->name);
$info = $this->createServiceWithMockedClient($client)->getArtistInformation($artist->name);
$this->assertEquals([
'url' => 'http://www.last.fm/music/Kamelot',
@ -53,9 +54,7 @@ class LastfmServiceTest extends TestCase
'get' => new Response(400, [], file_get_contents(__DIR__.'../../../blobs/lastfm/artist-notfound.xml')),
]);
$api = new LastfmService($client, app(Cache::class), app(Logger::class));
self::assertNull($api->getArtistInformation($artist->name));
self::assertNull($this->createServiceWithMockedClient($client)->getArtistInformation($artist->name));
}
/**
@ -74,8 +73,7 @@ class LastfmServiceTest extends TestCase
'get' => new Response(200, [], file_get_contents(__DIR__.'../../../blobs/lastfm/album.xml')),
]);
$api = new LastfmService($client, app(Cache::class), app(Logger::class));
$info = $api->getAlbumInformation($album->name, $album->artist->name);
$info = $this->createServiceWithMockedClient($client)->getAlbumInformation($album->name, $album->artist->name);
// Then I get the album's info
$this->assertEquals([
@ -112,8 +110,38 @@ class LastfmServiceTest extends TestCase
'get' => new Response(400, [], file_get_contents(__DIR__.'../../../blobs/lastfm/album-notfound.xml')),
]);
$api = new LastfmService($client, app(Cache::class), app(Logger::class));
$api = $this->createServiceWithMockedClient($client);
self::assertNull($api->getAlbumInformation($album->name, $album->artist->name));
}
public function testFetchSessionKeyUsingToken(): void
{
/** @var Client $client */
$client = Mockery::mock(Client::class, [
'get' => new Response(200, [], file_get_contents(__DIR__.'../../../blobs/lastfm/session-key.xml')),
]);
self::assertEquals('foo', $this->createServiceWithMockedClient($client)->fetchSessionKeyUsingToken('bar'));
}
public function testIsUserConnected(): void
{
$user = factory(User::class)->create([
'preferences' => ['lastfm_session_key' => 'foo'],
]);
self::assertTrue(app(LastfmService::class)->isUserConnected($user));
$user = factory(User::class)->create([
'preferences' => [],
]);
self::assertFalse(app(LastfmService::class)->isUserConnected($user));
}
private function createServiceWithMockedClient(Client $client): LastfmService
{
return new LastfmService($client, app(Cache::class), app(Logger::class), app(UserPreferenceService::class));
}
}

View file

@ -0,0 +1,49 @@
<?php
namespace Tests\Integration\Services;
use App\Models\User;
use App\Services\UserPreferenceService;
use Tests\TestCase;
class UserPreferenceServiceTest extends TestCase
{
/** @var UserPreferenceService */
private $userPreferenceService;
public function setUp()
{
parent::setUp();
$this->userPreferenceService = new UserPreferenceService();
}
public function testGet(): void
{
/** @var User $user */
$user = factory(User::class)->create([
'preferences' => ['foo' => 'bar'],
]);
self::assertEquals('bar', $this->userPreferenceService->get($user, 'foo'));
}
public function testSet(): void
{
/** @var User $user */
$user = factory(User::class)->create();
$this->userPreferenceService->set($user, 'foo', 'bar');
self::assertArraySubset(['foo' => 'bar'], $user->preferences);
}
public function testDelete(): void
{
/** @var User $user */
$user = factory(User::class)->create([
'preferences' => ['foo' => 'bar'],
]);
$this->userPreferenceService->delete($user, 'foo');
self::assertArrayNotHasKey('foo', $user->preferences);
}
}

View file

@ -2,13 +2,50 @@
namespace Tests\Unit\Services;
use App\Models\User;
use App\Services\LastfmService;
use App\Services\UserPreferenceService;
use GuzzleHttp\Client;
use Illuminate\Contracts\Cache\Repository as Cache;
use Illuminate\Log\Logger;
use Mockery;
use Mockery\Mock;
use Mockery\MockInterface;
use Tests\TestCase;
class LastfmServiceTest extends TestCase
{
/** @var Client */
private $client;
/** @var Cache */
private $cache;
/** @var Logger */
private $logger;
/** @var UserPreferenceService|MockInterface */
private $userPreferenceService;
/** @var LastfmService */
private $lastfmService;
public function setUp()
{
parent::setUp();
$this->client = Mockery::mock(Client::class);
$this->cache = Mockery::mock(Cache::class);
$this->logger = Mockery::mock(Logger::class);
$this->userPreferenceService = Mockery::mock(UserPreferenceService::class);
$this->lastfmService = new LastfmService(
$this->client,
$this->cache,
$this->logger,
$this->userPreferenceService
);
}
public function testBuildAuthCallParams(): void
{
/** @var Mock|LastfmService $lastfm */
@ -39,4 +76,38 @@ class LastfmServiceTest extends TestCase
$builtParamsAsString
);
}
public function testGetUserSessionKey(): void
{
/** @var User $user */
$user = Mockery::mock(User::class);
$this->userPreferenceService->shouldReceive('get')
->with($user, 'lastfm_session_key')
->andReturn('foo');
self::assertSame('foo', $this->lastfmService->getUserSessionKey($user));
}
public function testSetUserSessionKey(): void
{
/** @var User $user */
$user = Mockery::mock(User::class);
$this->userPreferenceService->shouldReceive('set')
->with($user, 'lastfm_session_key', 'foo');
$this->lastfmService->setUserSessionKey($user, 'foo');
}
public function testDeleteUserSessionKey(): void
{
/** @var User $user */
$user = Mockery::mock(User::class);
$this->userPreferenceService->shouldReceive('delete')
->with($user, 'lastfm_session_key');
$this->lastfmService->deleteUserSessionKey($user);
}
}