mirror of
https://github.com/koel/koel
synced 2024-11-10 06:34:14 +00:00
feat: upgrade Laravel to 7.x
This commit is contained in:
parent
f9d0017d11
commit
e356e72814
76 changed files with 3220 additions and 2853 deletions
|
@ -17,9 +17,6 @@ DB_PASSWORD=SoSecureMuchWow
|
|||
# A random 32-char string. You can leave this empty if use php artisan koel:init.
|
||||
APP_KEY=
|
||||
|
||||
# Another random 32-char string. You can leave this empty if use php artisan koel:init.
|
||||
JWT_SECRET=
|
||||
|
||||
# Credentials and other info to be used when Koel is installed in non-interactive mode
|
||||
# (php artisan koel:init --no-interaction)
|
||||
# By default (interactive mode), Koel will still prompt for these information during installation,
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -81,3 +81,4 @@ cypress/videos
|
|||
/log
|
||||
coverage.xml
|
||||
.phpunit.result.cache
|
||||
log.json
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
From 653ea67ccf5987b16815d0d4cd4ae4f6147b8649 Mon Sep 17 00:00:00 2001
|
||||
From: An Phan <me@phanan.net>
|
||||
Date: Mon, 24 Feb 2020 22:28:43 +0100
|
||||
Subject: [PATCH] Replace fire() with dispatch()
|
||||
|
||||
---
|
||||
src/Middleware/BaseMiddleware.php | 2 +-
|
||||
src/Middleware/GetUserFromToken.php | 2 +-
|
||||
2 files changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/src/Middleware/BaseMiddleware.php b/src/Middleware/BaseMiddleware.php
|
||||
index 9715f0c..cba7f8f 100644
|
||||
--- a/src/Middleware/BaseMiddleware.php
|
||||
+++ b/src/Middleware/BaseMiddleware.php
|
||||
@@ -57,7 +57,7 @@ abstract class BaseMiddleware
|
||||
*/
|
||||
protected function respond($event, $error, $status, $payload = [])
|
||||
{
|
||||
- $response = $this->events->fire($event, $payload, true);
|
||||
+ $response = $this->events->dispatch($event, $payload, true);
|
||||
|
||||
return $response ?: $this->response->json(['error' => $error], $status);
|
||||
}
|
||||
diff --git a/src/Middleware/GetUserFromToken.php b/src/Middleware/GetUserFromToken.php
|
||||
index af3b21c..7542158 100644
|
||||
--- a/src/Middleware/GetUserFromToken.php
|
||||
+++ b/src/Middleware/GetUserFromToken.php
|
||||
@@ -41,7 +41,7 @@ class GetUserFromToken extends BaseMiddleware
|
||||
return $this->respond('tymon.jwt.user_not_found', 'user_not_found', 404);
|
||||
}
|
||||
|
||||
- $this->events->fire('tymon.jwt.valid', $user);
|
||||
+ $this->events->dispatch('tymon.jwt.valid', $user);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
--
|
||||
2.24.0
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Str;
|
||||
use Jackiedo\DotenvEditor\DotenvEditor;
|
||||
|
||||
class GenerateJwtSecretCommand extends Command
|
||||
{
|
||||
protected $name = 'koel:generate-jwt-secret';
|
||||
protected $description = 'Set the JWTAuth secret key used to sign the tokens';
|
||||
private $dotenvEditor;
|
||||
|
||||
public function __construct(DotenvEditor $dotenvEditor)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->dotenvEditor = $dotenvEditor;
|
||||
}
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
if (config('jwt.secret')) {
|
||||
$this->comment('JWT secret exists -- skipping');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->info('Generating JWT secret');
|
||||
$this->dotenvEditor->setKey('JWT_SECRET', Str::random(32))->save();
|
||||
}
|
||||
}
|
|
@ -59,7 +59,6 @@ class InitCommand extends Command
|
|||
|
||||
try {
|
||||
$this->maybeGenerateAppKey();
|
||||
$this->maybeGenerateJwtSecret();
|
||||
$this->maybeSetUpDatabase();
|
||||
$this->migrateDatabase();
|
||||
$this->maybeSeedDatabase();
|
||||
|
@ -200,16 +199,6 @@ class InitCommand extends Command
|
|||
}
|
||||
}
|
||||
|
||||
private function maybeGenerateJwtSecret(): void
|
||||
{
|
||||
if (!config('jwt.secret')) {
|
||||
$this->info('Generating JWT secret');
|
||||
$this->artisan->call('koel:generate-jwt-secret');
|
||||
} else {
|
||||
$this->comment('JWT secret exists -- skipping');
|
||||
}
|
||||
}
|
||||
|
||||
private function maybeSeedDatabase(): void
|
||||
{
|
||||
if (!User::count()) {
|
||||
|
|
|
@ -2,24 +2,18 @@
|
|||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Auth\Access\AuthorizationException;
|
||||
use Illuminate\Auth\AuthenticationException;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Throwable;
|
||||
|
||||
class Handler extends ExceptionHandler
|
||||
{
|
||||
/**
|
||||
* A list of the exception types that should not be reported.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $dontReport = [
|
||||
AuthorizationException::class,
|
||||
HttpException::class,
|
||||
|
@ -27,24 +21,12 @@ class Handler extends ExceptionHandler
|
|||
ValidationException::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Report or log an exception.
|
||||
*
|
||||
* This is a great spot to send exceptions to Sentry, Bugsnag, etc.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function report(Exception $e): void
|
||||
public function report(Throwable $e): void
|
||||
{
|
||||
parent::report($e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render an exception into an HTTP response.
|
||||
*
|
||||
* @param Request $request
|
||||
*/
|
||||
public function render($request, Exception $e): Response
|
||||
public function render($request, Throwable $e): Response
|
||||
{
|
||||
if ($e instanceof ModelNotFoundException) {
|
||||
$e = new NotFoundHttpException($e->getMessage(), $e);
|
||||
|
@ -53,17 +35,12 @@ class Handler extends ExceptionHandler
|
|||
return parent::render($request, $e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an authentication exception into an unauthenticated response.
|
||||
*
|
||||
* @param Request $request
|
||||
*/
|
||||
protected function unauthenticated($request, AuthenticationException $exception): Response
|
||||
{
|
||||
if ($request->expectsJson()) {
|
||||
return response()->json(['error' => 'Unauthenticated.'], 401);
|
||||
}
|
||||
|
||||
return redirect()->guest('login');
|
||||
return redirect()->guest('/');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,23 +3,37 @@
|
|||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Http\Requests\API\UserLoginRequest;
|
||||
use Exception;
|
||||
use App\Models\User;
|
||||
use App\Repositories\UserRepository;
|
||||
use App\Services\TokenManager;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Hashing\HashManager;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Log\Logger;
|
||||
use Tymon\JWTAuth\JWTAuth;
|
||||
use Illuminate\Http\Response;
|
||||
|
||||
/**
|
||||
* @group 1. Authentication
|
||||
*/
|
||||
class AuthController extends Controller
|
||||
{
|
||||
private $auth;
|
||||
private $logger;
|
||||
private $userRepository;
|
||||
private $hash;
|
||||
private $tokenManager;
|
||||
|
||||
public function __construct(JWTAuth $auth, Logger $logger)
|
||||
/** @var User|null */
|
||||
private $currentUser;
|
||||
|
||||
public function __construct(
|
||||
UserRepository $userRepository,
|
||||
HashManager $hash,
|
||||
TokenManager $tokenManager,
|
||||
?Authenticatable $currentUser
|
||||
)
|
||||
{
|
||||
$this->auth = $auth;
|
||||
$this->logger = $logger;
|
||||
$this->userRepository = $userRepository;
|
||||
$this->hash = $hash;
|
||||
$this->currentUser = $currentUser;
|
||||
$this->tokenManager = $tokenManager;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -46,10 +60,16 @@ class AuthController extends Controller
|
|||
*/
|
||||
public function login(UserLoginRequest $request)
|
||||
{
|
||||
$token = $this->auth->attempt($request->only('email', 'password'));
|
||||
abort_unless($token, 401, 'Invalid credentials');
|
||||
/** @var User $user */
|
||||
$user = $this->userRepository->getFirstWhere('email', $request->email);
|
||||
|
||||
return response()->json(compact('token'));
|
||||
if (!$user || !$this->hash->check($request->password, $user->password)) {
|
||||
abort(Response::HTTP_UNAUTHORIZED, 'Invalid credentials');
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'token' => $this->tokenManager->createToken($user)->plainTextToken
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -59,13 +79,7 @@ class AuthController extends Controller
|
|||
*/
|
||||
public function logout()
|
||||
{
|
||||
if ($token = $this->auth->getToken()) {
|
||||
try {
|
||||
$this->auth->invalidate($token);
|
||||
} catch (Exception $e) {
|
||||
$this->logger->error($e);
|
||||
}
|
||||
}
|
||||
$this->tokenManager->destroyTokens($this->currentUser);
|
||||
|
||||
return response()->json();
|
||||
}
|
||||
|
|
|
@ -4,27 +4,29 @@ namespace App\Http\Controllers\API;
|
|||
|
||||
use App\Http\Requests\API\LastfmCallbackRequest;
|
||||
use App\Http\Requests\API\LastfmSetSessionKeyRequest;
|
||||
use App\Models\User;
|
||||
use App\Services\LastfmService;
|
||||
use Illuminate\Contracts\Auth\Guard;
|
||||
use App\Services\TokenManager;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Tymon\JWTAuth\Exceptions\JWTException;
|
||||
use Tymon\JWTAuth\JWTAuth;
|
||||
|
||||
/**
|
||||
* @group Last.fm integration
|
||||
*/
|
||||
class LastfmController extends Controller
|
||||
{
|
||||
protected $auth;
|
||||
private $lastfmService;
|
||||
private $jwtAuth;
|
||||
private $tokenManager;
|
||||
|
||||
public function __construct(Guard $auth, LastfmService $lastfmService, JWTAuth $jwtAuth)
|
||||
/** @var User */
|
||||
private $currentUser;
|
||||
|
||||
public function __construct(LastfmService $lastfmService, TokenManager $tokenManager, Authenticatable $currentUser)
|
||||
{
|
||||
$this->auth = $auth;
|
||||
$this->lastfmService = $lastfmService;
|
||||
$this->jwtAuth = $jwtAuth;
|
||||
$this->tokenManager = $tokenManager;
|
||||
$this->currentUser = $currentUser;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -35,23 +37,22 @@ class LastfmController extends Controller
|
|||
* which will send them to Last.fm for authentication. After authentication is successful, the user will be
|
||||
* redirected back to `api/lastfm/callback?token=<Last.fm token>`.
|
||||
*
|
||||
* @queryParam jwt-token required The JWT token of the user.
|
||||
* @queryParam jwt-token required The JWT token of the user. (Deprecated. Use api_token instead).
|
||||
* @queryParam api_token required Authentication token of the current user.
|
||||
* @response []
|
||||
*
|
||||
* @throws JWTException
|
||||
*
|
||||
* @return RedirectResponse
|
||||
*/
|
||||
public function connect()
|
||||
{
|
||||
abort_unless($this->lastfmService->enabled(), 401, 'Koel is not configured to use with Last.fm yet.');
|
||||
|
||||
// A workaround to make sure Tymon's JWTAuth get the correct token via our custom
|
||||
// "jwt-token" query string instead of the default "token".
|
||||
// This is due to the problem that Last.fm returns the token via "token" as well.
|
||||
$this->jwtAuth->parseToken('', '', 'jwt-token');
|
||||
$callbackUrl = urlencode(sprintf(
|
||||
'%s?api_token=%s',
|
||||
route('lastfm.callback'),
|
||||
request('api_token')
|
||||
));
|
||||
|
||||
$callbackUrl = urlencode(sprintf('%s?jwt-token=%s', route('lastfm.callback'), $this->jwtAuth->getToken()));
|
||||
$url = sprintf('https://www.last.fm/api/auth/?api_key=%s&cb=%s', $this->lastfmService->getKey(), $callbackUrl);
|
||||
|
||||
return redirect($url);
|
||||
|
@ -66,7 +67,7 @@ class LastfmController extends Controller
|
|||
|
||||
abort_unless($sessionKey, 500, 'Invalid token key.');
|
||||
|
||||
$this->auth->user()->savePreference('lastfm_session_key', $sessionKey);
|
||||
$this->currentUser->savePreference('lastfm_session_key', $sessionKey);
|
||||
|
||||
return view('api.lastfm.callback');
|
||||
}
|
||||
|
@ -86,7 +87,7 @@ class LastfmController extends Controller
|
|||
*/
|
||||
public function setSessionKey(LastfmSetSessionKeyRequest $request)
|
||||
{
|
||||
$this->auth->user()->savePreference('lastfm_session_key', trim($request->key));
|
||||
$this->currentUser->savePreference('lastfm_session_key', trim($request->key));
|
||||
|
||||
return response()->json();
|
||||
}
|
||||
|
@ -98,7 +99,7 @@ class LastfmController extends Controller
|
|||
*/
|
||||
public function disconnect()
|
||||
{
|
||||
$this->auth->user()->deletePreference('lastfm_session_key');
|
||||
$this->currentUser->deletePreference('lastfm_session_key');
|
||||
|
||||
return response()->json();
|
||||
}
|
||||
|
|
|
@ -5,12 +5,15 @@ namespace App\Http\Controllers\API;
|
|||
use App\Http\Requests\API\PlaylistStoreRequest;
|
||||
use App\Http\Requests\API\PlaylistSyncRequest;
|
||||
use App\Models\Playlist;
|
||||
use App\Models\User;
|
||||
use App\Repositories\PlaylistRepository;
|
||||
use App\Services\SmartPlaylistService;
|
||||
use Exception;
|
||||
use Illuminate\Auth\Access\AuthorizationException;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
|
||||
/**
|
||||
* @group 4. Playlist management
|
||||
|
@ -20,10 +23,18 @@ class PlaylistController extends Controller
|
|||
private $playlistRepository;
|
||||
private $smartPlaylistService;
|
||||
|
||||
public function __construct(PlaylistRepository $playlistRepository, SmartPlaylistService $smartPlaylistService)
|
||||
/** @var User */
|
||||
private $currentUser;
|
||||
|
||||
public function __construct(
|
||||
PlaylistRepository $playlistRepository,
|
||||
SmartPlaylistService $smartPlaylistService,
|
||||
Authenticatable $currentUser
|
||||
)
|
||||
{
|
||||
$this->playlistRepository = $playlistRepository;
|
||||
$this->smartPlaylistService = $smartPlaylistService;
|
||||
$this->currentUser = $currentUser;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -50,7 +61,7 @@ class PlaylistController extends Controller
|
|||
public function store(PlaylistStoreRequest $request)
|
||||
{
|
||||
/** @var Playlist $playlist */
|
||||
$playlist = $request->user()->playlists()->create([
|
||||
$playlist = $this->currentUser->playlists()->create([
|
||||
'name' => $request->name,
|
||||
'rules' => $request->rules,
|
||||
]);
|
||||
|
@ -78,7 +89,7 @@ class PlaylistController extends Controller
|
|||
*/
|
||||
public function update(Request $request, Playlist $playlist)
|
||||
{
|
||||
$this->authorize('owner', $playlist);
|
||||
abort_unless($this->currentUser->can('owner', $playlist), Response::HTTP_FORBIDDEN);
|
||||
|
||||
$playlist->update($request->only('name', 'rules'));
|
||||
|
||||
|
|
|
@ -4,10 +4,8 @@ namespace App\Http;
|
|||
|
||||
use App\Http\Middleware\Authenticate;
|
||||
use App\Http\Middleware\ForceHttps;
|
||||
use App\Http\Middleware\GetUserFromToken;
|
||||
use App\Http\Middleware\ObjectStorageAuthenticate;
|
||||
use App\Http\Middleware\TrimStrings;
|
||||
use App\Http\Middleware\UseDifferentConfigIfE2E;
|
||||
use Illuminate\Auth\Middleware\Authorize;
|
||||
use Illuminate\Foundation\Http\Kernel as HttpKernel;
|
||||
use Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode;
|
||||
|
@ -15,6 +13,7 @@ use Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull;
|
|||
use Illuminate\Foundation\Http\Middleware\ValidatePostSize;
|
||||
use Illuminate\Routing\Middleware\SubstituteBindings;
|
||||
use Illuminate\Routing\Middleware\ThrottleRequests;
|
||||
use Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful;
|
||||
|
||||
class Kernel extends HttpKernel
|
||||
{
|
||||
|
@ -25,7 +24,6 @@ class Kernel extends HttpKernel
|
|||
*/
|
||||
protected $middleware = [
|
||||
CheckForMaintenanceMode::class,
|
||||
UseDifferentConfigIfE2E::class,
|
||||
ValidatePostSize::class,
|
||||
TrimStrings::class,
|
||||
ConvertEmptyStringsToNull::class,
|
||||
|
@ -54,7 +52,6 @@ class Kernel extends HttpKernel
|
|||
*/
|
||||
protected $routeMiddleware = [
|
||||
'auth' => Authenticate::class,
|
||||
'jwt.auth' => GetUserFromToken::class,
|
||||
'os.auth' => ObjectStorageAuthenticate::class,
|
||||
'bindings' => SubstituteBindings::class,
|
||||
'can' => Authorize::class,
|
||||
|
|
|
@ -21,7 +21,7 @@ class Authenticate
|
|||
if ($request->ajax() || $request->route()->getName() === 'play') {
|
||||
return response('Unauthorized.', 401);
|
||||
} else {
|
||||
return redirect()->guest('login');
|
||||
return redirect()->guest('/');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use App\JWTAuth;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
use Illuminate\Contracts\Routing\ResponseFactory;
|
||||
use Tymon\JWTAuth\Middleware\BaseMiddleware as JWTBaseMiddleware;
|
||||
|
||||
abstract class BaseMiddleware extends JWTBaseMiddleware
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(ResponseFactory $response, Dispatcher $events, JWTAuth $auth)
|
||||
{
|
||||
parent::__construct($response, $events, $auth);
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Tymon\JWTAuth\Exceptions\TokenInvalidException;
|
||||
|
||||
class GetUserFromToken extends BaseMiddleware
|
||||
{
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
if (!$token = $this->auth->setRequest($request)->getToken()) {
|
||||
return $this->respond('tymon.jwt.absent', 'token_not_provided', 401);
|
||||
}
|
||||
|
||||
try {
|
||||
$user = $this->auth->authenticate($token);
|
||||
} catch (TokenInvalidException $exception) {
|
||||
abort(401, 'Invalid or expired token');
|
||||
}
|
||||
|
||||
if (!$user) {
|
||||
return $this->respond('tymon.jwt.user_not_found', 'user_not_found', 401);
|
||||
}
|
||||
|
||||
$this->events->dispatch('tymon.jwt.valid', $user);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
|
@ -2,6 +2,10 @@
|
|||
|
||||
namespace App\Http\Requests\API;
|
||||
|
||||
/**
|
||||
* @property string $email
|
||||
* @property string $password
|
||||
*/
|
||||
class UserLoginRequest extends Request
|
||||
{
|
||||
public function rules(): array
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Tymon\JWTAuth\JWTAuth as BaseJWTAuth;
|
||||
use Tymon\JWTAuth\JWTManager;
|
||||
use Tymon\JWTAuth\Providers\Auth\AuthInterface;
|
||||
use Tymon\JWTAuth\Providers\User\UserInterface;
|
||||
|
||||
class JWTAuth extends BaseJWTAuth
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(JWTManager $manager, UserInterface $user, AuthInterface $auth, Request $request)
|
||||
{
|
||||
parent::__construct($manager, $user, $auth, $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function parseToken($method = 'bearer', $header = 'authorization', $query = 'jwt-token'): BaseJWTAuth
|
||||
{
|
||||
return parent::parseToken($method, $header, $query);
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
namespace App\Models;
|
||||
|
||||
use App\Traits\SupportsDeleteWhereIDsNotIn;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
@ -28,6 +29,7 @@ use function App\Helpers\album_cover_url;
|
|||
*
|
||||
* @method static self firstOrCreate(array $where, array $params = [])
|
||||
* @method static self|null find(int $id)
|
||||
* @method static Builder where(...$params)
|
||||
*/
|
||||
class Album extends Model
|
||||
{
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace App\Models;
|
|||
|
||||
use App\Facades\Util;
|
||||
use App\Traits\SupportsDeleteWhereIDsNotIn;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
@ -23,6 +24,7 @@ use function App\Helpers\artist_image_url;
|
|||
*
|
||||
* @method static self find(int $id)
|
||||
* @method static self firstOrCreate(array $where, array $params = [])
|
||||
* @method static Builder where(...$params)
|
||||
*/
|
||||
class Artist extends Model
|
||||
{
|
||||
|
|
|
@ -16,6 +16,7 @@ use Illuminate\Support\Collection;
|
|||
* @property bool $is_smart
|
||||
* @property string $name
|
||||
* @property user $user
|
||||
* @method static \Illuminate\Database\Eloquent\Collection orderBy(string $field, string $order = 'asc')
|
||||
*/
|
||||
class Playlist extends Model
|
||||
{
|
||||
|
|
|
@ -5,6 +5,7 @@ namespace App\Models;
|
|||
use App\Events\LibraryChanged;
|
||||
use App\Traits\SupportsDeleteWhereIDsNotIn;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
|
@ -30,7 +31,9 @@ use Illuminate\Support\Collection;
|
|||
* @method static Builder select(string $string)
|
||||
* @method static Builder inDirectory(string $path)
|
||||
* @method static self first()
|
||||
* @method static Collection orderBy(...$args)
|
||||
* @method static EloquentCollection orderBy(...$args)
|
||||
* @method static int count()
|
||||
* @method static self|null find($id)
|
||||
*/
|
||||
class Song extends Model
|
||||
{
|
||||
|
@ -57,6 +60,8 @@ class Song extends Model
|
|||
'disc' => 'int',
|
||||
];
|
||||
|
||||
protected $keyType = 'string';
|
||||
|
||||
/**
|
||||
* Indicates if the IDs are auto-incrementing.
|
||||
*
|
||||
|
@ -234,7 +239,7 @@ class Song extends Model
|
|||
return null;
|
||||
}
|
||||
|
||||
list($bucket, $key) = explode('/', $matches[1], 2);
|
||||
[$bucket, $key] = explode('/', $matches[1], 2);
|
||||
|
||||
return compact('bucket', 'key');
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ use Illuminate\Database\Eloquent\Collection;
|
|||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Laravel\Sanctum\HasApiTokens;
|
||||
|
||||
/**
|
||||
* @property array $preferences
|
||||
|
@ -22,6 +23,7 @@ use Illuminate\Notifications\Notifiable;
|
|||
class User extends Authenticatable
|
||||
{
|
||||
use Notifiable;
|
||||
use HasApiTokens;
|
||||
|
||||
/**
|
||||
* The preferences that we don't want to show to the client.
|
||||
|
|
|
@ -43,4 +43,9 @@ abstract class AbstractRepository implements RepositoryInterface
|
|||
{
|
||||
return $this->model->all();
|
||||
}
|
||||
|
||||
public function getFirstWhere(...$params): Model
|
||||
{
|
||||
return $this->model->where(...$params)->first();
|
||||
}
|
||||
}
|
||||
|
|
19
app/Services/TokenManager.php
Normal file
19
app/Services/TokenManager.php
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\User;
|
||||
use Laravel\Sanctum\NewAccessToken;
|
||||
|
||||
class TokenManager
|
||||
{
|
||||
public function createToken(User $user, array $abilities = ['*']): NewAccessToken
|
||||
{
|
||||
return $user->createToken(config('app.name'), $abilities);
|
||||
}
|
||||
|
||||
public function destroyTokens(User $user): void
|
||||
{
|
||||
$user->tokens()->delete();
|
||||
}
|
||||
}
|
|
@ -11,9 +11,7 @@
|
|||
|
|
||||
*/
|
||||
|
||||
$app = new App\Application(
|
||||
realpath(__DIR__.'/../')
|
||||
);
|
||||
$app = new App\Application(__DIR__.'/../');
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
|
@ -5,15 +5,13 @@
|
|||
"license": "MIT",
|
||||
"type": "project",
|
||||
"require": {
|
||||
"php": ">=7.1.3",
|
||||
"laravel/framework": "5.8.*",
|
||||
"php": ">=7.2.5",
|
||||
"laravel/framework": "^7.0",
|
||||
"james-heinrich/getid3": "^1.9",
|
||||
"guzzlehttp/guzzle": "^6.1",
|
||||
"tymon/jwt-auth": "^0.5.12",
|
||||
"aws/aws-sdk-php-laravel": "^3.1",
|
||||
"pusher/pusher-php-server": "^4.0",
|
||||
"predis/predis": "~1.0",
|
||||
"doctrine/dbal": "^2.5",
|
||||
"jackiedo/dotenv-editor": "^1.0",
|
||||
"ext-exif": "*",
|
||||
"ext-fileinfo": "*",
|
||||
|
@ -22,24 +20,24 @@
|
|||
"fideloper/proxy": "^4.0",
|
||||
"daverandom/resume": "^0.0.3",
|
||||
"laravel/helpers": "^1.0",
|
||||
"cweagans/composer-patches": "^1.6",
|
||||
"intervention/image": "^2.5"
|
||||
"intervention/image": "^2.5",
|
||||
"laravel/sanctum": "^2.6",
|
||||
"doctrine/dbal": "^2.10"
|
||||
},
|
||||
"require-dev": {
|
||||
"filp/whoops": "~2.0",
|
||||
"fzaninotto/faker": "~1.4",
|
||||
"mockery/mockery": "~1.0",
|
||||
"phpunit/phpunit": "~7.5",
|
||||
"symfony/css-selector": "~3.1",
|
||||
"symfony/dom-crawler": "^3.2",
|
||||
"phpunit/phpunit": "^8.5",
|
||||
"symfony/css-selector": "~5.0",
|
||||
"symfony/dom-crawler": "^5.0",
|
||||
"facebook/webdriver": "^1.2",
|
||||
"barryvdh/laravel-ide-helper": "^2.1",
|
||||
"laravel/tinker": "^1.0",
|
||||
"laravel/browser-kit-testing": "^2.0",
|
||||
"laravel/tinker": "^2.0",
|
||||
"laravel/browser-kit-testing": "^6.0",
|
||||
"mikey179/vfsstream": "^1.6",
|
||||
"php-mock/php-mock-mockery": "^1.3",
|
||||
"mpociot/laravel-apidoc-generator": "^3.1",
|
||||
"nunomaduro/larastan": "^0.4.0"
|
||||
"mpociot/laravel-apidoc-generator": "^4.1"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-zip": "Allow downloading multiple songs as Zip archives"
|
||||
|
@ -83,7 +81,7 @@
|
|||
"post-create-project-cmd": [
|
||||
"@php artisan key:generate"
|
||||
],
|
||||
"test": "phpunit --colors=always --order-by=defects --stop-on-defect",
|
||||
"test": "phpunit --colors=always --order-by=defects",
|
||||
"coverage": "phpunit --colors=always --coverage-clover=coverage.xml",
|
||||
"analyze": "phpstan analyse app --level=5",
|
||||
"gen-api-docs": "@php artisan apidoc:generate"
|
||||
|
@ -93,12 +91,5 @@
|
|||
"optimize-autoloader": true
|
||||
},
|
||||
"minimum-stability": "stable",
|
||||
"prefer-stable": false,
|
||||
"extra": {
|
||||
"patches": {
|
||||
"tymon/jwt-auth": {
|
||||
"Replace fire() with dispatch()": ".patches/tymon/jwt-auth/replace-fire-with-dispatch.patch"
|
||||
}
|
||||
}
|
||||
}
|
||||
"prefer-stable": false
|
||||
}
|
||||
|
|
4654
composer.lock
generated
4654
composer.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -4,6 +4,7 @@ return [
|
|||
'tagline' => 'Personal audio streaming service that works.',
|
||||
|
||||
'env' => env('APP_ENV', 'production'),
|
||||
'name' => 'Koel',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
@ -123,7 +124,6 @@ return [
|
|||
Illuminate\Translation\TranslationServiceProvider::class,
|
||||
Illuminate\Validation\ValidationServiceProvider::class,
|
||||
Illuminate\View\ViewServiceProvider::class,
|
||||
'Tymon\JWTAuth\Providers\JWTAuthServiceProvider', // hard-coding to make it compatible with patching procedure
|
||||
Aws\Laravel\AwsServiceProvider::class,
|
||||
Jackiedo\DotenvEditor\DotenvEditorServiceProvider::class,
|
||||
Intervention\Image\ImageServiceProvider::class,
|
||||
|
@ -195,8 +195,6 @@ return [
|
|||
'Util' => App\Facades\Util::class,
|
||||
'YouTube' => App\Facades\YouTube::class,
|
||||
'Download' => App\Facades\Download::class,
|
||||
'JWTAuth' => Tymon\JWTAuth\Facades\JWTAuth::class,
|
||||
'JWTFactory' => Tymon\JWTAuth\Facades\JWTFactory::class,
|
||||
'AWS' => Aws\Laravel\AwsFacade::class,
|
||||
'iTunes' => App\Facades\iTunes::class,
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ return [
|
|||
|
|
||||
*/
|
||||
'defaults' => [
|
||||
'guard' => 'web',
|
||||
'guard' => 'api',
|
||||
'passwords' => 'users',
|
||||
],
|
||||
/*
|
||||
|
@ -38,7 +38,7 @@ return [
|
|||
'provider' => 'users',
|
||||
],
|
||||
'api' => [
|
||||
'driver' => 'token',
|
||||
'driver' => 'sanctum',
|
||||
'provider' => 'users',
|
||||
],
|
||||
],
|
||||
|
|
164
config/jwt.php
164
config/jwt.php
|
@ -1,164 +0,0 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| JWT Authentication Secret
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Don't forget to set this, as it will be used to sign your tokens.
|
||||
| A helper command is provided for this: `php artisan jwt:generate`
|
||||
|
|
||||
*/
|
||||
|
||||
'secret' => env('JWT_SECRET'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| JWT time to live
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Specify the length of time (in minutes) that the token will be valid for.
|
||||
| Defaults to 1 hour
|
||||
|
|
||||
*/
|
||||
|
||||
'ttl' => 60 * 24 * 7,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Refresh time to live
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Specify the length of time (in minutes) that the token can be refreshed
|
||||
| within. I.E. The user can refresh their token within a 2 week window of
|
||||
| the original token being created until they must re-authenticate.
|
||||
| Defaults to 2 weeks
|
||||
|
|
||||
*/
|
||||
|
||||
'refresh_ttl' => 20160,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| JWT hashing algorithm
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Specify the hashing algorithm that will be used to sign the token.
|
||||
|
|
||||
| See here: https://github.com/namshi/jose/tree/2.2.0/src/Namshi/JOSE/Signer
|
||||
| for possible values
|
||||
|
|
||||
*/
|
||||
|
||||
'algo' => 'HS256',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| User Model namespace
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Specify the full namespace to your User model.
|
||||
| e.g. 'Acme\Entities\User'
|
||||
|
|
||||
*/
|
||||
|
||||
'user' => App\Models\User::class,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| User identifier
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Specify a unique property of the user that will be added as the 'sub'
|
||||
| claim of the token payload.
|
||||
|
|
||||
*/
|
||||
|
||||
'identifier' => 'id',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Required Claims
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Specify the required claims that must exist in any token.
|
||||
| A TokenInvalidException will be thrown if any of these claims are not
|
||||
| present in the payload.
|
||||
|
|
||||
*/
|
||||
|
||||
'required_claims' => ['iss', 'iat', 'exp', 'nbf', 'sub', 'jti'],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Blacklist Enabled
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| In order to invalidate tokens, you must have the the blacklist enabled.
|
||||
| If you do not want or need this functionality, then set this to false.
|
||||
|
|
||||
*/
|
||||
|
||||
'blacklist_enabled' => env('JWT_BLACKLIST_ENABLED', true),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Providers
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Specify the various providers used throughout the package.
|
||||
|
|
||||
*/
|
||||
|
||||
'providers' => [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| User Provider
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Specify the provider that is used to find the user based
|
||||
| on the subject claim
|
||||
|
|
||||
*/
|
||||
|
||||
'user' => Tymon\JWTAuth\Providers\User\EloquentUserAdapter::class,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| JWT Provider
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Specify the provider that is used to create and decode the tokens.
|
||||
|
|
||||
*/
|
||||
|
||||
'jwt' => Tymon\JWTAuth\Providers\JWT\NamshiAdapter::class,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Authentication Provider
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Specify the provider that is used to authenticate users.
|
||||
|
|
||||
*/
|
||||
|
||||
'auth' => 'Tymon\JWTAuth\Providers\Auth\IlluminateAuthAdapter',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Storage Provider
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Specify the provider that is used to store tokens in the blacklist
|
||||
|
|
||||
*/
|
||||
|
||||
'storage' => 'Tymon\JWTAuth\Providers\Storage\IlluminateCacheAdapter',
|
||||
|
||||
],
|
||||
|
||||
];
|
45
config/sanctum.php
Normal file
45
config/sanctum.php
Normal file
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Stateful Domains
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Requests from the following domains / hosts will receive stateful API
|
||||
| authentication cookies. Typically, these should include your local
|
||||
| and production domains which access your API via a frontend SPA.
|
||||
|
|
||||
*/
|
||||
|
||||
'stateful' => [],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Expiration Minutes
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This value controls the number of minutes until an issued token will be
|
||||
| considered expired. If this value is null, personal access tokens do
|
||||
| not expire. This won't tweak the lifetime of first-party sessions.
|
||||
|
|
||||
*/
|
||||
|
||||
'expiration' => null,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Sanctum Middleware
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When authenticating your first-party SPA with Sanctum you may need to
|
||||
| customize some of the middleware Sanctum uses while processing the
|
||||
| request. You may change the middleware listed below as required.
|
||||
|
|
||||
*/
|
||||
|
||||
'middleware' => [
|
||||
],
|
||||
|
||||
];
|
|
@ -8,22 +8,23 @@ use App\Models\Setting;
|
|||
use App\Models\Song;
|
||||
use App\Models\User;
|
||||
use Faker\Generator as Faker;
|
||||
use Illuminate\Database\Eloquent\Factory;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
|
||||
/** @var Factory $factory */
|
||||
$factory->define(User::class, function ($faker) {
|
||||
return [
|
||||
'name' => $faker->name,
|
||||
'email' => $faker->email,
|
||||
'password' => bcrypt(str_random(10)),
|
||||
'password' => Hash::make('secret'),
|
||||
'is_admin' => false,
|
||||
'preferences' => [],
|
||||
'remember_token' => str_random(10),
|
||||
];
|
||||
});
|
||||
|
||||
$factory->defineAs(User::class, 'admin', function () use ($factory) {
|
||||
$user = $factory->raw(User::class);
|
||||
|
||||
return array_merge($user, ['is_admin' => true]);
|
||||
$factory->state(User::class, 'admin', function () use ($factory) {
|
||||
return ['is_admin' => true];
|
||||
});
|
||||
|
||||
$factory->define(Artist::class, function (Faker $faker) {
|
||||
|
@ -35,7 +36,9 @@ $factory->define(Artist::class, function (Faker $faker) {
|
|||
|
||||
$factory->define(Album::class, function (Faker $faker) {
|
||||
return [
|
||||
'artist_id' => factory(Artist::class)->create()->id,
|
||||
'artist_id' => static function (): int {
|
||||
return factory(Artist::class)->create()->id;
|
||||
},
|
||||
'name' => ucwords($faker->words(random_int(2, 5), true)),
|
||||
'cover' => md5(uniqid()).'.jpg',
|
||||
];
|
||||
|
@ -60,7 +63,7 @@ $factory->define(Song::class, function (Faker $faker) {
|
|||
$factory->define(Playlist::class, function (Faker $faker) {
|
||||
return [
|
||||
'user_id' => static function (): int {
|
||||
throw new InvalidArgumentException('A user_id must be supplied');
|
||||
return factory(User::class)->create()->id;
|
||||
},
|
||||
'name' => $faker->name,
|
||||
'rules' => null,
|
||||
|
@ -69,8 +72,12 @@ $factory->define(Playlist::class, function (Faker $faker) {
|
|||
|
||||
$factory->define(Interaction::class, function (Faker $faker) {
|
||||
return [
|
||||
'song_id' => factory(Song::class)->create()->id,
|
||||
'user_id' => factory(User::class)->create()->id,
|
||||
'song_id' => static function (): string {
|
||||
return factory(Song::class)->create()->id;
|
||||
},
|
||||
'user_id' => static function (): int {
|
||||
return factory(User::class)->create()->id;
|
||||
},
|
||||
'liked' => $faker->boolean,
|
||||
'play_count' => $faker->randomNumber,
|
||||
];
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateAlbumsTable extends Migration
|
||||
{
|
||||
|
@ -18,7 +20,9 @@ class CreateAlbumsTable extends Migration
|
|||
$table->string('name');
|
||||
$table->string('cover')->default('');
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::table('albums', function (Blueprint $table) {
|
||||
$table->foreign('artist_id')->references('id')->on('artists')->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateSongsTable extends Migration
|
||||
{
|
||||
|
@ -13,7 +14,7 @@ class CreateSongsTable extends Migration
|
|||
public function up()
|
||||
{
|
||||
Schema::create('songs', function (Blueprint $table) {
|
||||
$table->string('id', 32);
|
||||
$table->string('id', 32)->primary();
|
||||
$table->integer('album_id')->unsigned();
|
||||
$table->string('title');
|
||||
$table->float('length');
|
||||
|
@ -21,8 +22,9 @@ class CreateSongsTable extends Migration
|
|||
$table->text('path');
|
||||
$table->integer('mtime');
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
$table->primary('id');
|
||||
Schema::table('songs', function (Blueprint $table) {
|
||||
$table->foreign('album_id')->references('id')->on('albums');
|
||||
});
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreatePlaylistsTable extends Migration
|
||||
{
|
||||
|
@ -17,7 +18,9 @@ class CreatePlaylistsTable extends Migration
|
|||
$table->integer('user_id')->unsigned();
|
||||
$table->string('name');
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::table('playlists', function (Blueprint $table) {
|
||||
$table->foreign('user_id')->references('id')->on('users');
|
||||
});
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateInteractionsTable extends Migration
|
||||
{
|
||||
|
@ -19,7 +20,9 @@ class CreateInteractionsTable extends Migration
|
|||
$table->boolean('liked')->default(false);
|
||||
$table->integer('play_count')->default(0);
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::table('interactions', function (Blueprint $table) {
|
||||
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
|
||||
$table->foreign('song_id')->references('id')->on('songs')->onDelete('cascade');
|
||||
});
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreatePlaylistSongTable extends Migration
|
||||
{
|
||||
|
@ -16,7 +17,9 @@ class CreatePlaylistSongTable extends Migration
|
|||
$table->increments('id');
|
||||
$table->integer('playlist_id')->unsigned();
|
||||
$table->string('song_id', 32);
|
||||
});
|
||||
|
||||
Schema::table('playlist_song', function (Blueprint $table) {
|
||||
$table->foreign('playlist_id')->references('id')->on('playlists')->onDelete('cascade');
|
||||
$table->foreign('song_id')->references('id')->on('songs')->onDelete('cascade');
|
||||
});
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class DropIsComplicationFromAlbums extends Migration
|
||||
|
@ -12,7 +13,7 @@ class DropIsComplicationFromAlbums extends Migration
|
|||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('albums', function ($table) {
|
||||
Schema::table('albums', function (Blueprint $table) {
|
||||
$table->dropColumn('is_compilation');
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreatePersonalAccessTokensTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('personal_access_tokens', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->morphs('tokenable');
|
||||
$table->string('name');
|
||||
$table->string('token', 64)->unique();
|
||||
$table->text('abilities')->nullable();
|
||||
$table->timestamp('last_used_at')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('personal_access_tokens');
|
||||
}
|
||||
}
|
|
@ -31,7 +31,6 @@
|
|||
<env name="APP_ENV" value="testing"/>
|
||||
<env name="APP_URL" value="http://localhost/"/>
|
||||
<env name="APP_KEY" value="16efa6c23c2e8c705826b0e66778fbe7"/>
|
||||
<env name="JWT_SECRET" value="ki8jSvMf5wFrlSRBAWcGbmAzBUJfc8p8"/>
|
||||
<env name="CACHE_DRIVER" value="array"/>
|
||||
<env name="SESSION_DRIVER" value="array"/>
|
||||
<env name="QUEUE_DRIVER" value="sync"/>
|
||||
|
@ -44,5 +43,7 @@
|
|||
<env name="ADMIN_PASSWORD" value="SoSecureK0el"/>
|
||||
<env name="BROADCAST_DRIVER" value="log"/>
|
||||
<env name="CACHE_MEDIA" value="true"/>
|
||||
|
||||
<ini name="memory_limit" value="512M" />
|
||||
</php>
|
||||
</phpunit>
|
||||
|
|
|
@ -6,7 +6,7 @@ Route::group(['namespace' => 'API'], function () {
|
|||
Route::post('me', 'AuthController@login')->name('auth.login');
|
||||
Route::delete('me', 'AuthController@logout');
|
||||
|
||||
Route::group(['middleware' => 'jwt.auth'], function () {
|
||||
Route::group(['middleware' => 'auth'], function () {
|
||||
Route::get('/ping', function () {
|
||||
// Only acting as a ping service.
|
||||
});
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
Route::get('/', function () {
|
||||
return view('index');
|
||||
});
|
||||
|
|
|
@ -7,11 +7,9 @@ use App\Models\Album;
|
|||
use App\Models\User;
|
||||
use App\Services\MediaMetadataService;
|
||||
use Mockery;
|
||||
use Mockery\MockInterface;
|
||||
|
||||
class AlbumCoverTest extends TestCase
|
||||
{
|
||||
/** @var MockInterface|MediaMetadataService */
|
||||
private $mediaMetadataService;
|
||||
|
||||
public function setUp(): void
|
||||
|
@ -23,7 +21,9 @@ class AlbumCoverTest extends TestCase
|
|||
public function testUpdate(): void
|
||||
{
|
||||
$this->expectsEvents(LibraryChanged::class);
|
||||
factory(Album::class)->create(['id' => 9999]);
|
||||
|
||||
/** @var Album $album */
|
||||
$album = factory(Album::class)->create(['id' => 9999]);
|
||||
|
||||
$this->mediaMetadataService
|
||||
->shouldReceive('writeAlbumCover')
|
||||
|
@ -32,23 +32,25 @@ class AlbumCoverTest extends TestCase
|
|||
return $album->id === 9999;
|
||||
}), 'Foo', 'jpeg');
|
||||
|
||||
$this->putAsUser('api/album/9999/cover', [
|
||||
'cover' => 'data:image/jpeg;base64,Rm9v'
|
||||
], factory(User::class, 'admin')->create())
|
||||
->seeStatusCode(200);
|
||||
$response = $this->putAsUser('api/album/'.$album->id.'/cover', [
|
||||
'cover' => 'data:image/jpeg;base64,Rm9v',
|
||||
], factory(User::class)->states('admin')->create());
|
||||
|
||||
$response->assertStatus(200);
|
||||
}
|
||||
|
||||
public function testUpdateNotAllowedForNormalUsers(): void
|
||||
{
|
||||
factory(Album::class)->create(['id' => 9999]);
|
||||
/** @var Album $album */
|
||||
$album = factory(Album::class)->create();
|
||||
|
||||
$this->mediaMetadataService
|
||||
->shouldReceive('writeAlbumCover')
|
||||
->never();
|
||||
|
||||
$this->putAsUser('api/album/9999/cover', [
|
||||
'cover' => 'data:image/jpeg;base64,Rm9v'
|
||||
$this->putAsUser('api/album/'.$album->id.'/cover', [
|
||||
'cover' => 'data:image/jpeg;base64,Rm9v',
|
||||
], factory(User::class)->create())
|
||||
->seeStatusCode(403);
|
||||
->assertStatus(403);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,14 +5,9 @@ namespace Tests\Feature;
|
|||
use App\Models\Album;
|
||||
use App\Services\MediaMetadataService;
|
||||
use Mockery;
|
||||
use Mockery\MockInterface;
|
||||
use function App\Helpers\album_cover_url;
|
||||
|
||||
class AlbumThumbnailTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var MediaMetadataService|MockInterface
|
||||
*/
|
||||
private $mediaMetadataService;
|
||||
|
||||
public function setUp(): void
|
||||
|
@ -26,9 +21,7 @@ class AlbumThumbnailTest extends TestCase
|
|||
return [['http://localhost/public/img/covers/foo_thumbnail.jpg'], [null]];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideAlbumThumbnailData
|
||||
*/
|
||||
/** @dataProvider provideAlbumThumbnailData */
|
||||
public function testGetAlbumThumbnail(?string $thumbnailUrl): void
|
||||
{
|
||||
/** @var Album $createdAlbum */
|
||||
|
@ -42,7 +35,7 @@ class AlbumThumbnailTest extends TestCase
|
|||
}))
|
||||
->andReturn($thumbnailUrl);
|
||||
|
||||
$this->getAsUser("api/album/{$createdAlbum->id}/thumbnail")
|
||||
->seeJson(['thumbnailUrl' => $thumbnailUrl]);
|
||||
$response = $this->getAsUser("api/album/{$createdAlbum->id}/thumbnail");
|
||||
$response->assertJson(['thumbnailUrl' => $thumbnailUrl]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,8 +34,8 @@ class ArtistImageTest extends TestCase
|
|||
|
||||
$this->putAsUser('api/artist/9999/image', [
|
||||
'image' => 'data:image/jpeg;base64,Rm9v'
|
||||
], factory(User::class, 'admin')->create())
|
||||
->seeStatusCode(200);
|
||||
], factory(User::class)->states('admin')->create())
|
||||
->assertStatus(200);
|
||||
}
|
||||
|
||||
public function testUpdateNotAllowedForNormalUsers(): void
|
||||
|
@ -49,6 +49,6 @@ class ArtistImageTest extends TestCase
|
|||
$this->putAsUser('api/artist/9999/image', [
|
||||
'image' => 'data:image/jpeg;base64,Rm9v'
|
||||
], factory(User::class)->create())
|
||||
->seeStatusCode(403);
|
||||
->assertStatus(403);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ use App\Repositories\InteractionRepository;
|
|||
use App\Services\DownloadService;
|
||||
use Exception;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Testing\TestResponse;
|
||||
use Mockery;
|
||||
use Mockery\MockInterface;
|
||||
|
||||
|
@ -44,7 +45,7 @@ class DownloadTest extends TestCase
|
|||
->andReturn($this->mediaPath.'/blank.mp3');
|
||||
|
||||
$this->getAsUser("api/download/songs?songs[]={$song->id}")
|
||||
->assertResponseOk();
|
||||
->assertOk();
|
||||
}
|
||||
|
||||
public function testDownloadMultipleSongs(): void
|
||||
|
@ -63,7 +64,7 @@ class DownloadTest extends TestCase
|
|||
->andReturn($this->mediaPath.'/blank.mp3'); // should be a zip file, but we're testing here…
|
||||
|
||||
$this->getAsUser("api/download/songs?songs[]={$songs[0]->id}&songs[]={$songs[1]->id}")
|
||||
->assertResponseOk();
|
||||
->assertOk();
|
||||
}
|
||||
|
||||
public function testDownloadAlbum(): void
|
||||
|
@ -79,7 +80,7 @@ class DownloadTest extends TestCase
|
|||
->andReturn($this->mediaPath.'/blank.mp3');
|
||||
|
||||
$this->getAsUser("api/download/album/{$album->id}")
|
||||
->assertResponseOk();
|
||||
->assertOk();
|
||||
}
|
||||
|
||||
public function testDownloadArtist(): void
|
||||
|
@ -95,11 +96,12 @@ class DownloadTest extends TestCase
|
|||
->andReturn($this->mediaPath.'/blank.mp3');
|
||||
|
||||
$this->getAsUser("api/download/artist/{$artist->id}")
|
||||
->assertResponseOk();
|
||||
->assertOk();
|
||||
}
|
||||
|
||||
public function testDownloadPlaylist(): void
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = factory(User::class)->create();
|
||||
|
||||
/** @var Playlist $playlist */
|
||||
|
@ -107,9 +109,6 @@ class DownloadTest extends TestCase
|
|||
'user_id' => $user->id,
|
||||
]);
|
||||
|
||||
$this->getAsUser("api/download/playlist/{$playlist->id}")
|
||||
->assertResponseStatus(403);
|
||||
|
||||
$this->downloadService
|
||||
->shouldReceive('from')
|
||||
->with(Mockery::on(static function (Playlist $retrievedPlaylist) use ($playlist) {
|
||||
|
@ -119,7 +118,16 @@ class DownloadTest extends TestCase
|
|||
->andReturn($this->mediaPath.'/blank.mp3');
|
||||
|
||||
$this->getAsUser("api/download/playlist/{$playlist->id}", $user)
|
||||
->assertResponseOk();
|
||||
->assertOk();
|
||||
}
|
||||
|
||||
public function testNonOwnerCannotDownloadPlaylist(): void
|
||||
{
|
||||
/** @var Playlist $playlist */
|
||||
$playlist = factory(Playlist::class)->create();
|
||||
|
||||
$this->getAsUser("api/download/playlist/{$playlist->id}")
|
||||
->assertStatus(403);
|
||||
}
|
||||
|
||||
public function testDownloadFavorites(): void
|
||||
|
@ -143,6 +151,6 @@ class DownloadTest extends TestCase
|
|||
->andReturn($this->mediaPath.'/blank.mp3');
|
||||
|
||||
$this->getAsUser('api/download/favorites', $user)
|
||||
->seeStatusCode(200);
|
||||
->assertStatus(200);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ class InteractionTest extends TestCase
|
|||
$song = Song::orderBy('id')->first();
|
||||
$this->postAsUser('api/interaction/play', ['song' => $song->id], $user);
|
||||
|
||||
$this->seeInDatabase('interactions', [
|
||||
self::assertDatabaseHas('interactions', [
|
||||
'user_id' => $user->id,
|
||||
'song_id' => $song->id,
|
||||
'play_count' => 1,
|
||||
|
@ -36,7 +36,7 @@ class InteractionTest extends TestCase
|
|||
// Try again
|
||||
$this->postAsUser('api/interaction/play', ['song' => $song->id], $user);
|
||||
|
||||
$this->seeInDatabase('interactions', [
|
||||
self::assertDatabaseHas('interactions', [
|
||||
'user_id' => $user->id,
|
||||
'song_id' => $song->id,
|
||||
'play_count' => 2,
|
||||
|
@ -57,7 +57,7 @@ class InteractionTest extends TestCase
|
|||
$song = Song::orderBy('id')->first();
|
||||
$this->postAsUser('api/interaction/like', ['song' => $song->id], $user);
|
||||
|
||||
$this->seeInDatabase('interactions', [
|
||||
self::assertDatabaseHas('interactions', [
|
||||
'user_id' => $user->id,
|
||||
'song_id' => $song->id,
|
||||
'liked' => 1,
|
||||
|
@ -66,7 +66,7 @@ class InteractionTest extends TestCase
|
|||
// Try again
|
||||
$this->postAsUser('api/interaction/like', ['song' => $song->id], $user);
|
||||
|
||||
$this->seeInDatabase('interactions', [
|
||||
self::assertDatabaseHas('interactions', [
|
||||
'user_id' => $user->id,
|
||||
'song_id' => $song->id,
|
||||
'liked' => 0,
|
||||
|
@ -90,7 +90,7 @@ class InteractionTest extends TestCase
|
|||
$this->postAsUser('api/interaction/batch/like', ['songs' => $songIds], $user);
|
||||
|
||||
foreach ($songs as $song) {
|
||||
$this->seeInDatabase('interactions', [
|
||||
self::assertDatabaseHas('interactions', [
|
||||
'user_id' => $user->id,
|
||||
'song_id' => $song->id,
|
||||
'liked' => 1,
|
||||
|
@ -100,7 +100,7 @@ class InteractionTest extends TestCase
|
|||
$this->postAsUser('api/interaction/batch/unlike', ['songs' => $songIds], $user);
|
||||
|
||||
foreach ($songs as $song) {
|
||||
$this->seeInDatabase('interactions', [
|
||||
self::assertDatabaseHas('interactions', [
|
||||
'user_id' => $user->id,
|
||||
'song_id' => $song->id,
|
||||
'liked' => 0,
|
||||
|
|
|
@ -4,12 +4,13 @@ namespace Tests\Feature;
|
|||
|
||||
use App\Models\User;
|
||||
use App\Services\LastfmService;
|
||||
use App\Services\TokenManager;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use Illuminate\Contracts\Cache\Repository as Cache;
|
||||
use Illuminate\Log\Logger;
|
||||
use Laravel\Sanctum\NewAccessToken;
|
||||
use Mockery;
|
||||
use Tymon\JWTAuth\JWTAuth;
|
||||
|
||||
class LastfmTest extends TestCase
|
||||
{
|
||||
|
@ -28,7 +29,7 @@ class LastfmTest extends TestCase
|
|||
{
|
||||
$user = factory(User::class)->create();
|
||||
$this->postAsUser('api/lastfm/session-key', ['key' => 'foo'], $user)
|
||||
->assertResponseOk();
|
||||
->assertOk();
|
||||
|
||||
$user = User::find($user->id);
|
||||
self::assertEquals('foo', $user->lastfm_session_key);
|
||||
|
@ -36,13 +37,15 @@ class LastfmTest extends TestCase
|
|||
|
||||
public function testConnectToLastfm(): void
|
||||
{
|
||||
static::mockIocDependency(JWTAuth::class, [
|
||||
'parseToken' => null,
|
||||
'getToken' => 'foo',
|
||||
]);
|
||||
/** @var User $user */
|
||||
$user = factory(User::class)->create();
|
||||
$token = $user->createToken('Koel')->plainTextToken;
|
||||
|
||||
$this->getAsUser('api/lastfm/connect')
|
||||
->assertRedirectedTo('https://www.last.fm/api/auth/?api_key=foo&cb=http%3A%2F%2Flocalhost%2Fapi%2Flastfm%2Fcallback%3Fjwt-token%3Dfoo');
|
||||
$this->getAsUser('api/lastfm/connect?api_token='.$token, $user)
|
||||
->assertRedirect(
|
||||
'https://www.last.fm/api/auth/?api_key=foo&cb=http%3A%2F%2Flocalhost%2Fapi%2Flastfm%2Fcallback%3Fapi_token%3D'
|
||||
.urlencode($token)
|
||||
);
|
||||
}
|
||||
|
||||
public function testRetrieveAndStoreSessionKey(): void
|
||||
|
@ -58,7 +61,7 @@ class LastfmTest extends TestCase
|
|||
$this->getAsUser('api/lastfm/callback?token=foo', $user);
|
||||
$user->refresh();
|
||||
|
||||
$this->assertEquals('bar', $user->lastfm_session_key);
|
||||
self::assertEquals('bar', $user->lastfm_session_key);
|
||||
}
|
||||
|
||||
public function testDisconnectUser(): void
|
||||
|
@ -68,6 +71,6 @@ class LastfmTest extends TestCase
|
|||
$this->deleteAsUser('api/lastfm/disconnect', [], $user);
|
||||
$user->refresh();
|
||||
|
||||
$this->assertNull($user->lastfm_session_key);
|
||||
self::assertNull($user->lastfm_session_key);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,45 +38,45 @@ class MediaSyncTest extends TestCase
|
|||
$this->mediaService->sync($this->mediaPath);
|
||||
|
||||
// Standard mp3 files under root path should be recognized
|
||||
$this->seeInDatabase('songs', [
|
||||
self::assertDatabaseHas('songs', [
|
||||
'path' => $this->mediaPath.'/full.mp3',
|
||||
// Track # should be recognized
|
||||
'track' => 5,
|
||||
]);
|
||||
|
||||
// Ogg files and audio files in subdirectories should be recognized
|
||||
$this->seeInDatabase('songs', ['path' => $this->mediaPath.'/subdir/back-in-black.ogg']);
|
||||
self::assertDatabaseHas('songs', ['path' => $this->mediaPath.'/subdir/back-in-black.ogg']);
|
||||
|
||||
// GitHub issue #380. folder.png should be copied and used as the cover for files
|
||||
// under subdir/
|
||||
$song = Song::wherePath($this->mediaPath.'/subdir/back-in-black.ogg')->first();
|
||||
$this->assertNotNull($song->album->cover);
|
||||
self::assertNotNull($song->album->cover);
|
||||
|
||||
// File search shouldn't be case-sensitive.
|
||||
$this->seeInDatabase('songs', ['path' => $this->mediaPath.'/subdir/no-name.mp3']);
|
||||
self::assertDatabaseHas('songs', ['path' => $this->mediaPath.'/subdir/no-name.mp3']);
|
||||
|
||||
// Non-audio files shouldn't be recognized
|
||||
$this->notSeeInDatabase('songs', ['path' => $this->mediaPath.'/rubbish.log']);
|
||||
self::assertDatabaseMissing('songs', ['path' => $this->mediaPath.'/rubbish.log']);
|
||||
|
||||
// Broken/corrupted audio files shouldn't be recognized
|
||||
$this->notSeeInDatabase('songs', ['path' => $this->mediaPath.'/fake.mp3']);
|
||||
self::assertDatabaseMissing('songs', ['path' => $this->mediaPath.'/fake.mp3']);
|
||||
|
||||
// Artists should be created
|
||||
$this->seeInDatabase('artists', ['name' => 'Cuckoo']);
|
||||
$this->seeInDatabase('artists', ['name' => 'Koel']);
|
||||
self::assertDatabaseHas('artists', ['name' => 'Cuckoo']);
|
||||
self::assertDatabaseHas('artists', ['name' => 'Koel']);
|
||||
|
||||
// Albums should be created
|
||||
$this->seeInDatabase('albums', ['name' => 'Koel Testing Vol. 1']);
|
||||
self::assertDatabaseHas('albums', ['name' => 'Koel Testing Vol. 1']);
|
||||
|
||||
// Albums and artists should be correctly linked
|
||||
$album = Album::whereName('Koel Testing Vol. 1')->first();
|
||||
$this->assertEquals('Koel', $album->artist->name);
|
||||
self::assertEquals('Koel', $album->artist->name);
|
||||
|
||||
// Compilation albums, artists and songs must be recognized
|
||||
$song = Song::whereTitle('This song belongs to a compilation')->first();
|
||||
$this->assertNotNull($song->artist_id);
|
||||
$this->assertTrue($song->album->is_compilation);
|
||||
$this->assertEquals(Artist::VARIOUS_ID, $song->album->artist_id);
|
||||
self::assertNotNull($song->artist_id);
|
||||
self::assertTrue($song->album->is_compilation);
|
||||
self::assertEquals(Artist::VARIOUS_ID, $song->album->artist_id);
|
||||
|
||||
$currentCover = $album->cover;
|
||||
|
||||
|
@ -86,10 +86,10 @@ class MediaSyncTest extends TestCase
|
|||
touch($song->path, $time = time());
|
||||
$this->mediaService->sync($this->mediaPath);
|
||||
$song = Song::find($song->id);
|
||||
$this->assertEquals($time, $song->mtime);
|
||||
self::assertEquals($time, $song->mtime);
|
||||
|
||||
// Albums with a non-default cover should have their covers overwritten
|
||||
$this->assertEquals($currentCover, Album::find($album->id)->cover);
|
||||
self::assertEquals($currentCover, Album::find($album->id)->cover);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -118,16 +118,16 @@ class MediaSyncTest extends TestCase
|
|||
|
||||
// Validate that the changes are not lost
|
||||
$song = Song::orderBy('id', 'desc')->first();
|
||||
$this->assertEquals("It's John Cena!", $song->title);
|
||||
$this->assertEquals('Booom Wroooom', $song->lyrics);
|
||||
self::assertEquals("It's John Cena!", $song->title);
|
||||
self::assertEquals('Booom Wroooom', $song->lyrics);
|
||||
|
||||
// Resync with force
|
||||
$this->mediaService->sync($this->mediaPath, [], true);
|
||||
|
||||
// All is lost.
|
||||
$song = Song::orderBy('id', 'desc')->first();
|
||||
$this->assertEquals($originalTitle, $song->title);
|
||||
$this->assertEquals($originalLyrics, $song->lyrics);
|
||||
self::assertEquals($originalTitle, $song->title);
|
||||
self::assertEquals($originalLyrics, $song->lyrics);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -155,8 +155,8 @@ class MediaSyncTest extends TestCase
|
|||
|
||||
// Validate that the specified tags are changed, other remains the same
|
||||
$song = Song::orderBy('id', 'desc')->first();
|
||||
$this->assertEquals($originalTitle, $song->title);
|
||||
$this->assertEquals('Booom Wroooom', $song->lyrics);
|
||||
self::assertEquals($originalTitle, $song->title);
|
||||
self::assertEquals('Booom Wroooom', $song->lyrics);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -181,7 +181,7 @@ class MediaSyncTest extends TestCase
|
|||
$song = $song->toArray();
|
||||
array_forget($addedSong, 'created_at');
|
||||
array_forget($song, 'created_at');
|
||||
$this->assertEquals($song, $addedSong);
|
||||
self::assertEquals($song, $addedSong);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -197,7 +197,7 @@ class MediaSyncTest extends TestCase
|
|||
|
||||
$this->mediaService->syncByWatchRecord(new InotifyWatchRecord("CLOSE_WRITE,CLOSE $path"));
|
||||
|
||||
$this->seeInDatabase('songs', ['path' => $path]);
|
||||
self::assertDatabaseHas('songs', ['path' => $path]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -214,7 +214,7 @@ class MediaSyncTest extends TestCase
|
|||
|
||||
$this->mediaService->syncByWatchRecord(new InotifyWatchRecord("DELETE {$song->path}"));
|
||||
|
||||
$this->notSeeInDatabase('songs', ['id' => $song->id]);
|
||||
self::assertDatabaseMissing('songs', ['id' => $song->id]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -230,9 +230,9 @@ class MediaSyncTest extends TestCase
|
|||
|
||||
$this->mediaService->syncByWatchRecord(new InotifyWatchRecord("MOVED_FROM,ISDIR {$this->mediaPath}/subdir"));
|
||||
|
||||
$this->notSeeInDatabase('songs', ['path' => $this->mediaPath.'/subdir/sic.mp3']);
|
||||
$this->notSeeInDatabase('songs', ['path' => $this->mediaPath.'/subdir/no-name.mp3']);
|
||||
$this->notSeeInDatabase('songs', ['path' => $this->mediaPath.'/subdir/back-in-black.mp3']);
|
||||
self::assertDatabaseMissing('songs', ['path' => $this->mediaPath.'/subdir/sic.mp3']);
|
||||
self::assertDatabaseMissing('songs', ['path' => $this->mediaPath.'/subdir/no-name.mp3']);
|
||||
self::assertDatabaseMissing('songs', ['path' => $this->mediaPath.'/subdir/back-in-black.mp3']);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
|
@ -270,10 +270,10 @@ class MediaSyncTest extends TestCase
|
|||
{
|
||||
config(['koel.ignore_dot_files' => false]);
|
||||
$this->mediaService->sync($this->mediaPath);
|
||||
$this->seeInDatabase('albums', ['name' => 'Hidden Album']);
|
||||
self::assertDatabaseHas('albums', ['name' => 'Hidden Album']);
|
||||
|
||||
config(['koel.ignore_dot_files' => true]);
|
||||
$this->mediaService->sync($this->mediaPath);
|
||||
$this->notSeeInDatabase('albums', ['name' => 'Hidden Album']);
|
||||
self::assertDatabaseMissing('albums', ['name' => 'Hidden Album']);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,9 @@ class S3Test extends TestCase
|
|||
'duration' => 10,
|
||||
'track' => 5,
|
||||
],
|
||||
])->seeInDatabase('songs', ['path' => 's3://koel/sample.mp3']);
|
||||
]);
|
||||
|
||||
self::assertDatabaseHas('songs', ['path' => 's3://koel/sample.mp3']);
|
||||
}
|
||||
|
||||
public function testRemovingASong(): void
|
||||
|
@ -44,6 +46,8 @@ class S3Test extends TestCase
|
|||
$this->delete('api/os/s3/song', [
|
||||
'bucket' => 'koel',
|
||||
'key' => 'sample.mp3',
|
||||
])->notSeeInDatabase('songs', ['path' => 's3://koel/sample.mp3']);
|
||||
]);
|
||||
|
||||
self::assertDatabaseMissing('songs', ['path' => 's3://koel/sample.mp3']);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,13 +5,9 @@ namespace Tests\Feature;
|
|||
use App\Models\Playlist;
|
||||
use App\Models\Song;
|
||||
use App\Models\User;
|
||||
use Exception;
|
||||
|
||||
class PlaylistTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
@ -20,6 +16,7 @@ class PlaylistTest extends TestCase
|
|||
|
||||
public function testCreatingPlaylist(): void
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = factory(User::class)->create();
|
||||
$songs = Song::orderBy('id')->take(3)->get();
|
||||
|
||||
|
@ -29,7 +26,7 @@ class PlaylistTest extends TestCase
|
|||
'rules' => [],
|
||||
], $user);
|
||||
|
||||
$this->seeInDatabase('playlists', [
|
||||
self::assertDatabaseHas('playlists', [
|
||||
'user_id' => $user->id,
|
||||
'name' => 'Foo Bar',
|
||||
]);
|
||||
|
@ -37,45 +34,46 @@ class PlaylistTest extends TestCase
|
|||
$playlist = Playlist::orderBy('id', 'desc')->first();
|
||||
|
||||
foreach ($songs as $song) {
|
||||
$this->seeInDatabase('playlist_song', [
|
||||
self::assertDatabaseHas('playlist_song', [
|
||||
'playlist_id' => $playlist->id,
|
||||
'song_id' => $song->id,
|
||||
]);
|
||||
}
|
||||
|
||||
$this->getAsUser('api/playlist', $user)
|
||||
->seeJson([
|
||||
'id' => $playlist->id,
|
||||
'name' => 'Foo Bar',
|
||||
]);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function user_can_update_a_playlists_name(): void
|
||||
public function testUpdatePlaylistName(): void
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = factory(User::class)->create();
|
||||
|
||||
/** @var Playlist $playlist */
|
||||
$playlist = factory(Playlist::class)->create([
|
||||
'user_id' => $user->id,
|
||||
'name' => 'Foo',
|
||||
]);
|
||||
|
||||
$this->putAsUser("api/playlist/{$playlist->id}", ['name' => 'Foo Bar'], $user);
|
||||
$this->putAsUser("api/playlist/{$playlist->id}", ['name' => 'Bar'], $user);
|
||||
|
||||
$this->seeInDatabase('playlists', [
|
||||
'user_id' => $user->id,
|
||||
'name' => 'Foo Bar',
|
||||
]);
|
||||
|
||||
// Other users can't modify it
|
||||
$this->putAsUser("api/playlist/{$playlist->id}", ['name' => 'Foo Bar'])
|
||||
->seeStatusCode(403);
|
||||
self::assertSame('Bar', $playlist->refresh()->name);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function playlists_can_be_synced(): void
|
||||
public function testNonOwnerCannotUpdatePlaylist(): void
|
||||
{
|
||||
/** @var Playlist $playlist */
|
||||
$playlist = factory(Playlist::class)->create([
|
||||
'name' => 'Foo',
|
||||
]);
|
||||
|
||||
$response = $this->putAsUser("api/playlist/{$playlist->id}", ['name' => 'Qux']);
|
||||
$response->assertStatus(403);
|
||||
}
|
||||
|
||||
public function testSyncPlaylist(): void
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = factory(User::class)->create();
|
||||
|
||||
/** @var Playlist $playlist */
|
||||
$playlist = factory(Playlist::class)->create([
|
||||
'user_id' => $user->id,
|
||||
]);
|
||||
|
@ -85,50 +83,53 @@ class PlaylistTest extends TestCase
|
|||
|
||||
$removedSong = $songs->pop();
|
||||
|
||||
$this->putAsUser("api/playlist/{$playlist->id}/sync", [
|
||||
'songs' => $songs->pluck('id')->toArray(),
|
||||
])
|
||||
->seeStatusCode(403);
|
||||
|
||||
$this->putAsUser("api/playlist/{$playlist->id}/sync", [
|
||||
'songs' => $songs->pluck('id')->toArray(),
|
||||
], $user);
|
||||
|
||||
// We should still see the first 3 songs, but not the removed one
|
||||
foreach ($songs as $song) {
|
||||
$this->seeInDatabase('playlist_song', [
|
||||
self::assertDatabaseHas('playlist_song', [
|
||||
'playlist_id' => $playlist->id,
|
||||
'song_id' => $song->id,
|
||||
]);
|
||||
}
|
||||
|
||||
$this->notSeeInDatabase('playlist_song', [
|
||||
self::assertDatabaseMissing('playlist_song', [
|
||||
'playlist_id' => $playlist->id,
|
||||
'song_id' => $removedSong->id,
|
||||
]);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function user_can_delete_a_playlist(): void
|
||||
public function testDeletePlaylist(): void
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = factory(User::class)->create();
|
||||
|
||||
/** @var Playlist $playlist */
|
||||
$playlist = factory(Playlist::class)->create([
|
||||
'user_id' => $user->id,
|
||||
]);
|
||||
|
||||
$this->deleteAsUser("api/playlist/{$playlist->id}")
|
||||
->seeStatusCode(403);
|
||||
|
||||
$this->deleteAsUser("api/playlist/{$playlist->id}", [], $user)
|
||||
->notSeeInDatabase('playlists', ['id' => $playlist->id]);
|
||||
$this->deleteAsUser("api/playlist/{$playlist->id}", [], $user);
|
||||
self::assertDatabaseMissing('playlists', ['id' => $playlist->id]);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function playlist_content_can_be_retrieved(): void
|
||||
public function testNonOwnerCannotDeletePlaylist(): void
|
||||
{
|
||||
/** @var Playlist $playlist */
|
||||
$playlist = factory(Playlist::class)->create();
|
||||
|
||||
$this->deleteAsUser("api/playlist/{$playlist->id}")
|
||||
->assertStatus(403);
|
||||
}
|
||||
|
||||
public function testGetPlaylist(): void
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = factory(User::class)->create();
|
||||
|
||||
/** @var Playlist $playlist */
|
||||
$playlist = factory(Playlist::class)->create([
|
||||
'user_id' => $user->id,
|
||||
]);
|
||||
|
@ -137,6 +138,6 @@ class PlaylistTest extends TestCase
|
|||
$playlist->songs()->saveMany($songs);
|
||||
|
||||
$this->getAsUser("api/playlist/{$playlist->id}/songs", $user)
|
||||
->seeJson($songs->pluck('id')->all());
|
||||
->assertJson($songs->pluck('id')->all());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ class ProfileTest extends TestCase
|
|||
|
||||
$this->putAsUser('api/me', ['name' => 'Foo', 'email' => 'bar@baz.com'], $user);
|
||||
|
||||
$this->seeInDatabase('users', ['name' => 'Foo', 'email' => 'bar@baz.com']);
|
||||
self::assertDatabaseHas('users', ['name' => 'Foo', 'email' => 'bar@baz.com']);
|
||||
}
|
||||
|
||||
public function testUpdateProfileWithPassword(): void
|
||||
|
@ -46,7 +46,7 @@ class ProfileTest extends TestCase
|
|||
'password' => 'qux',
|
||||
], $user);
|
||||
|
||||
$this->seeInDatabase('users', [
|
||||
self::assertDatabaseHas('users', [
|
||||
'id' => $user->id,
|
||||
'name' => 'Foo',
|
||||
'email' => 'bar@baz.com',
|
||||
|
|
|
@ -30,6 +30,6 @@ class ScrobbleTest extends TestCase
|
|||
->once();
|
||||
|
||||
$this->postAsUser("/api/{$song->id}/scrobble/$ts", [], $user)
|
||||
->assertResponseOk();
|
||||
->assertOk();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,11 +5,9 @@ namespace Tests\Feature;
|
|||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use App\Services\MediaSyncService;
|
||||
use Mockery\MockInterface;
|
||||
|
||||
class SettingTest extends TestCase
|
||||
{
|
||||
/** @var MockInterface */
|
||||
private $mediaSyncService;
|
||||
|
||||
public function setUp(): void
|
||||
|
@ -22,9 +20,8 @@ class SettingTest extends TestCase
|
|||
{
|
||||
$this->mediaSyncService->shouldReceive('sync')->once();
|
||||
|
||||
$user = factory(User::class, 'admin')->create();
|
||||
file_put_contents('log', $this->postAsUser('/api/settings', ['media_path' => __DIR__], $user)
|
||||
->response->content());
|
||||
$user = factory(User::class)->states('admin')->create();
|
||||
$this->postAsUser('/api/settings', ['media_path' => __DIR__], $user);
|
||||
|
||||
self::assertEquals(__DIR__, Setting::get('media_path'));
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ class SongTest extends TestCase
|
|||
$this->expectsEvents(LibraryChanged::class);
|
||||
$song = Song::orderBy('id', 'desc')->first();
|
||||
|
||||
$user = factory(User::class, 'admin')->create();
|
||||
$user = factory(User::class)->states('admin')->create();
|
||||
$this->putAsUser('/api/songs', [
|
||||
'songs' => [$song->id],
|
||||
'data' => [
|
||||
|
@ -41,15 +41,15 @@ class SongTest extends TestCase
|
|||
'compilationState' => 0,
|
||||
],
|
||||
], $user)
|
||||
->seeStatusCode(200);
|
||||
->assertStatus(200);
|
||||
|
||||
$artist = Artist::whereName('John Cena')->first();
|
||||
$this->assertNotNull($artist);
|
||||
$artist = Artist::where('name', 'John Cena')->first();
|
||||
self::assertNotNull($artist);
|
||||
|
||||
$album = Album::whereName('One by One')->first();
|
||||
$this->assertNotNull($album);
|
||||
$album = Album::where('name', 'One by One')->first();
|
||||
self::assertNotNull($album);
|
||||
|
||||
$this->seeInDatabase('songs', [
|
||||
self::assertDatabaseHas('songs', [
|
||||
'id' => $song->id,
|
||||
'album_id' => $album->id,
|
||||
'lyrics' => 'Lorem ipsum dolor sic amet.',
|
||||
|
@ -62,7 +62,7 @@ class SongTest extends TestCase
|
|||
$song = Song::orderBy('id', 'desc')->first();
|
||||
$originalArtistId = $song->album->artist->id;
|
||||
|
||||
$user = factory(User::class, 'admin')->create();
|
||||
$user = factory(User::class)->states('admin')->create();
|
||||
$this->putAsUser('/api/songs', [
|
||||
'songs' => [$song->id],
|
||||
'data' => [
|
||||
|
@ -74,20 +74,20 @@ class SongTest extends TestCase
|
|||
'compilationState' => 0,
|
||||
],
|
||||
], $user)
|
||||
->seeStatusCode(200);
|
||||
->assertStatus(200);
|
||||
|
||||
// We don't expect the song's artist to change
|
||||
$this->assertEquals($originalArtistId, Song::find($song->id)->album->artist->id);
|
||||
self::assertEquals($originalArtistId, Song::find($song->id)->album->artist->id);
|
||||
|
||||
// But we expect a new album to be created for this artist and contain this song
|
||||
$this->assertEquals('One by One', Song::find($song->id)->album->name);
|
||||
self::assertEquals('One by One', Song::find($song->id)->album->name);
|
||||
}
|
||||
|
||||
public function testMultipleUpdateAllInfoNoCompilation(): void
|
||||
{
|
||||
$songIds = Song::orderBy('id', 'desc')->take(3)->pluck('id')->toArray();
|
||||
|
||||
$user = factory(User::class, 'admin')->create();
|
||||
$user = factory(User::class)->states('admin')->create();
|
||||
$this->putAsUser('/api/songs', [
|
||||
'songs' => $songIds,
|
||||
'data' => [
|
||||
|
@ -99,24 +99,24 @@ class SongTest extends TestCase
|
|||
'compilationState' => 0,
|
||||
],
|
||||
], $user)
|
||||
->seeStatusCode(200);
|
||||
->assertStatus(200);
|
||||
|
||||
$songs = Song::orderBy('id', 'desc')->take(3)->get();
|
||||
|
||||
// Even though we post the title, lyrics, and tracks, we don't expect them to take any effect
|
||||
// because we're updating multiple songs here.
|
||||
$this->assertNotEquals('foo', $songs[0]->title);
|
||||
$this->assertNotEquals('bar', $songs[2]->lyrics);
|
||||
$this->assertNotEquals(9999, $songs[2]->track);
|
||||
self::assertNotEquals('foo', $songs[0]->title);
|
||||
self::assertNotEquals('bar', $songs[2]->lyrics);
|
||||
self::assertNotEquals(9999, $songs[2]->track);
|
||||
|
||||
// But all of these songs must now belong to a new album and artist set
|
||||
$this->assertEquals('One by One', $songs[0]->album->name);
|
||||
$this->assertEquals('One by One', $songs[1]->album->name);
|
||||
$this->assertEquals('One by One', $songs[2]->album->name);
|
||||
self::assertEquals('One by One', $songs[0]->album->name);
|
||||
self::assertEquals('One by One', $songs[1]->album->name);
|
||||
self::assertEquals('One by One', $songs[2]->album->name);
|
||||
|
||||
$this->assertEquals('John Cena', $songs[0]->album->artist->name);
|
||||
$this->assertEquals('John Cena', $songs[1]->album->artist->name);
|
||||
$this->assertEquals('John Cena', $songs[2]->album->artist->name);
|
||||
self::assertEquals('John Cena', $songs[0]->album->artist->name);
|
||||
self::assertEquals('John Cena', $songs[1]->album->artist->name);
|
||||
self::assertEquals('John Cena', $songs[2]->album->artist->name);
|
||||
}
|
||||
|
||||
public function testMultipleUpdateSomeInfoNoCompilation(): void
|
||||
|
@ -124,7 +124,7 @@ class SongTest extends TestCase
|
|||
$originalSongs = Song::orderBy('id', 'desc')->take(3)->get();
|
||||
$songIds = $originalSongs->pluck('id')->toArray();
|
||||
|
||||
$user = factory(User::class, 'admin')->create();
|
||||
$user = factory(User::class)->states('admin')->create();
|
||||
$this->putAsUser('/api/songs', [
|
||||
'songs' => $songIds,
|
||||
'data' => [
|
||||
|
@ -136,30 +136,30 @@ class SongTest extends TestCase
|
|||
'compilationState' => 0,
|
||||
],
|
||||
], $user)
|
||||
->seeStatusCode(200);
|
||||
->assertStatus(200);
|
||||
|
||||
$songs = Song::orderBy('id', 'desc')->take(3)->get();
|
||||
|
||||
// Even though the album name doesn't change, a new artist should have been created
|
||||
// and thus, a new album with the same name was created as well.
|
||||
$this->assertEquals($songs[0]->album->name, $originalSongs[0]->album->name);
|
||||
$this->assertNotEquals($songs[0]->album->id, $originalSongs[0]->album->id);
|
||||
$this->assertEquals($songs[1]->album->name, $originalSongs[1]->album->name);
|
||||
$this->assertNotEquals($songs[1]->album->id, $originalSongs[1]->album->id);
|
||||
$this->assertEquals($songs[2]->album->name, $originalSongs[2]->album->name);
|
||||
$this->assertNotEquals($songs[2]->album->id, $originalSongs[2]->album->id);
|
||||
self::assertEquals($songs[0]->album->name, $originalSongs[0]->album->name);
|
||||
self::assertNotEquals($songs[0]->album->id, $originalSongs[0]->album->id);
|
||||
self::assertEquals($songs[1]->album->name, $originalSongs[1]->album->name);
|
||||
self::assertNotEquals($songs[1]->album->id, $originalSongs[1]->album->id);
|
||||
self::assertEquals($songs[2]->album->name, $originalSongs[2]->album->name);
|
||||
self::assertNotEquals($songs[2]->album->id, $originalSongs[2]->album->id);
|
||||
|
||||
// And of course, the new artist is...
|
||||
$this->assertEquals('John Cena', $songs[0]->album->artist->name); // JOHN CENA!!!
|
||||
$this->assertEquals('John Cena', $songs[1]->album->artist->name); // JOHN CENA!!!
|
||||
$this->assertEquals('John Cena', $songs[2]->album->artist->name); // And... JOHN CENAAAAAAAAAAA!!!
|
||||
self::assertEquals('John Cena', $songs[0]->album->artist->name); // JOHN CENA!!!
|
||||
self::assertEquals('John Cena', $songs[1]->album->artist->name); // JOHN CENA!!!
|
||||
self::assertEquals('John Cena', $songs[2]->album->artist->name); // And... JOHN CENAAAAAAAAAAA!!!
|
||||
}
|
||||
|
||||
public function testSingleUpdateAllInfoYesCompilation(): void
|
||||
{
|
||||
$song = Song::orderBy('id', 'desc')->first();
|
||||
|
||||
$user = factory(User::class, 'admin')->create();
|
||||
$user = factory(User::class)->states('admin')->create();
|
||||
$this->putAsUser('/api/songs', [
|
||||
'songs' => [$song->id],
|
||||
'data' => [
|
||||
|
@ -171,15 +171,15 @@ class SongTest extends TestCase
|
|||
'compilationState' => 1,
|
||||
],
|
||||
], $user)
|
||||
->seeStatusCode(200);
|
||||
->assertStatus(200);
|
||||
|
||||
$compilationAlbum = Album::whereArtistIdAndName(Artist::VARIOUS_ID, 'One by One')->first();
|
||||
$this->assertNotNull($compilationAlbum);
|
||||
self::assertNotNull($compilationAlbum);
|
||||
|
||||
$artist = Artist::whereName('John Cena')->first();
|
||||
$this->assertNotNull($artist);
|
||||
self::assertNotNull($artist);
|
||||
|
||||
$this->seeInDatabase('songs', [
|
||||
self::assertDatabaseHas('songs', [
|
||||
'id' => $song->id,
|
||||
'artist_id' => $artist->id,
|
||||
'album_id' => $compilationAlbum->id,
|
||||
|
@ -200,15 +200,20 @@ class SongTest extends TestCase
|
|||
'compilationState' => 2,
|
||||
],
|
||||
], $user)
|
||||
->seeStatusCode(200);
|
||||
->assertStatus(200);
|
||||
|
||||
$compilationAlbum = Album::whereArtistIdAndName(Artist::VARIOUS_ID, 'Two by Two')->first();
|
||||
$this->assertNotNull($compilationAlbum);
|
||||
/** @var Album $compilationAlbum */
|
||||
$compilationAlbum = Album::where([
|
||||
'artist_id' => Artist::VARIOUS_ID,
|
||||
'name' => 'Two by Two'
|
||||
])->first();
|
||||
|
||||
$contributingArtist = Artist::whereName('John Cena')->first();
|
||||
$this->assertNotNull($contributingArtist);
|
||||
self::assertNotNull($compilationAlbum);
|
||||
|
||||
$this->seeInDatabase('songs', [
|
||||
$contributingArtist = Artist::where('name', 'John Cena')->first();
|
||||
self::assertNotNull($contributingArtist);
|
||||
|
||||
self::assertDatabaseHas('songs', [
|
||||
'id' => $song->id,
|
||||
'artist_id' => $contributingArtist->id,
|
||||
'album_id' => $compilationAlbum->id,
|
||||
|
@ -226,15 +231,19 @@ class SongTest extends TestCase
|
|||
'compilationState' => 2,
|
||||
],
|
||||
], $user)
|
||||
->seeStatusCode(200);
|
||||
->assertStatus(200);
|
||||
|
||||
$compilationAlbum = Album::whereArtistIdAndName(Artist::VARIOUS_ID, 'One by One')->first();
|
||||
$this->assertNotNull($compilationAlbum);
|
||||
$compilationAlbum = Album::where([
|
||||
'artist_id' => Artist::VARIOUS_ID,
|
||||
'name' => 'One by One'
|
||||
])->first();
|
||||
self::assertNotNull($compilationAlbum);
|
||||
|
||||
$contributingArtist = Artist::whereName('Foo Fighters')->first();
|
||||
$this->assertNotNull($contributingArtist);
|
||||
/** @var Artist $contributingArtist */
|
||||
$contributingArtist = Artist::where('name', 'Foo Fighters')->first();
|
||||
self::assertNotNull($contributingArtist);
|
||||
|
||||
$this->seeInDatabase('songs', [
|
||||
self::assertDatabaseHas('songs', [
|
||||
'id' => $song->id,
|
||||
'artist_id' => $contributingArtist->id,
|
||||
'album_id' => $compilationAlbum->id,
|
||||
|
@ -252,14 +261,19 @@ class SongTest extends TestCase
|
|||
'compilationState' => 0,
|
||||
],
|
||||
], $user)
|
||||
->seeStatusCode(200);
|
||||
->assertStatus(200);
|
||||
|
||||
$artist = Artist::whereName('Foo Fighters')->first();
|
||||
$this->assertNotNull($artist);
|
||||
$album = Album::whereArtistIdAndName($artist->id, 'One by One')->first();
|
||||
$this->assertNotNull($album);
|
||||
/** @var Artist $artist */
|
||||
$artist = Artist::where('name', 'Foo Fighters')->first();
|
||||
self::assertNotNull($artist);
|
||||
|
||||
$this->seeInDatabase('songs', [
|
||||
$album = Album::where([
|
||||
'artist_id' => $artist->id,
|
||||
'name' => 'One by One',
|
||||
])->first();
|
||||
self::assertNotNull($album);
|
||||
|
||||
self::assertDatabaseHas('songs', [
|
||||
'id' => $song->id,
|
||||
'artist_id' => $artist->id,
|
||||
'album_id' => $album->id,
|
||||
|
@ -277,25 +291,29 @@ class SongTest extends TestCase
|
|||
'track' => 1,
|
||||
'compilationState' => 1,
|
||||
],
|
||||
], $user)
|
||||
->putAsUser('/api/songs', [
|
||||
'songs' => [$song->id],
|
||||
'data' => [
|
||||
'title' => 'Twilight of the Thunder God',
|
||||
'artistName' => 'Amon Amarth',
|
||||
'albumName' => 'Twilight of the Thunder God',
|
||||
'lyrics' => 'Thor! Nanananananana Batman.',
|
||||
'track' => 1,
|
||||
'compilationState' => 0,
|
||||
],
|
||||
], $user)
|
||||
->seeStatusCode(200);
|
||||
], $user);
|
||||
|
||||
$artist = Artist::whereName('Amon Amarth')->first();
|
||||
$this->assertNotNull($artist);
|
||||
$album = Album::whereArtistIdAndName($artist->id, 'Twilight of the Thunder God')->first();
|
||||
$this->assertNotNull($album);
|
||||
$this->seeInDatabase('songs', [
|
||||
$this->putAsUser('/api/songs', [
|
||||
'songs' => [$song->id],
|
||||
'data' => [
|
||||
'title' => 'Twilight of the Thunder God',
|
||||
'artistName' => 'Amon Amarth',
|
||||
'albumName' => 'Twilight of the Thunder God',
|
||||
'lyrics' => 'Thor! Nanananananana Batman.',
|
||||
'track' => 1,
|
||||
'compilationState' => 0,
|
||||
],
|
||||
], $user)
|
||||
->assertStatus(200);
|
||||
|
||||
$artist = Artist::where('name', 'Amon Amarth')->first();
|
||||
self::assertNotNull($artist);
|
||||
$album = Album::where([
|
||||
'artist_id' => $artist->id,
|
||||
'name' => 'Twilight of the Thunder God',
|
||||
])->first();
|
||||
self::assertNotNull($album);
|
||||
self::assertDatabaseHas('songs', [
|
||||
'id' => $song->id,
|
||||
'artist_id' => $artist->id,
|
||||
'album_id' => $album->id,
|
||||
|
@ -303,14 +321,11 @@ class SongTest extends TestCase
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function testDeletingByChunk(): void
|
||||
{
|
||||
$this->assertNotEquals(0, Song::count());
|
||||
self::assertNotEquals(0, Song::count());
|
||||
$ids = Song::select('id')->get()->pluck('id')->all();
|
||||
Song::deleteByChunk($ids, 'id', 1);
|
||||
$this->assertEquals(0, Song::count());
|
||||
self::assertEquals(0, Song::count());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,13 +8,13 @@ use App\Models\Song;
|
|||
use App\Models\User;
|
||||
use Exception;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Laravel\BrowserKitTesting\TestCase as BaseTestCase;
|
||||
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
|
||||
use Illuminate\Testing\TestResponse;
|
||||
use Mockery;
|
||||
use ReflectionClass;
|
||||
use Tests\Traits\CreatesApplication;
|
||||
use Tests\Traits\InteractsWithIoc;
|
||||
use Tests\Traits\SandboxesTests;
|
||||
use Tymon\JWTAuth\JWTAuth;
|
||||
|
||||
abstract class TestCase extends BaseTestCase
|
||||
{
|
||||
|
@ -23,14 +23,10 @@ abstract class TestCase extends BaseTestCase
|
|||
use InteractsWithIoc;
|
||||
use SandboxesTests;
|
||||
|
||||
/** @var JWTAuth */
|
||||
private $auth;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->auth = app(JWTAuth::class);
|
||||
$this->prepareForTests();
|
||||
self::createSandbox();
|
||||
}
|
||||
|
@ -60,37 +56,33 @@ abstract class TestCase extends BaseTestCase
|
|||
}
|
||||
}
|
||||
|
||||
protected function getAsUser($url, $user = null): self
|
||||
private function jsonAsUser(?User $user, string $method, $uri, array $data = [], array $headers = []): TestResponse
|
||||
{
|
||||
return $this->get($url, [
|
||||
'Authorization' => 'Bearer '.$this->generateJwtToken($user),
|
||||
]);
|
||||
$user = $user ?: factory(User::class)->create();
|
||||
$headers['X-Requested-With'] = 'XMLHttpRequest';
|
||||
$headers['Authorization'] = 'Bearer '.$user->createToken('koel')->plainTextToken;
|
||||
|
||||
return parent::json($method, $uri, $data, $headers);
|
||||
}
|
||||
|
||||
protected function deleteAsUser($url, $data = [], $user = null): self
|
||||
protected function getAsUser(string $url, ?User $user = null): TestResponse
|
||||
{
|
||||
return $this->delete($url, $data, [
|
||||
'Authorization' => 'Bearer '.$this->generateJwtToken($user),
|
||||
]);
|
||||
return $this->jsonAsUser($user, 'get', $url);
|
||||
}
|
||||
|
||||
protected function postAsUser($url, $data, $user = null): self
|
||||
protected function deleteAsUser(string $url, array $data = [], ?User $user = null): TestResponse
|
||||
{
|
||||
return $this->post($url, $data, [
|
||||
'Authorization' => 'Bearer '.$this->generateJwtToken($user),
|
||||
]);
|
||||
return $this->jsonAsUser($user, 'delete', $url, $data);
|
||||
}
|
||||
|
||||
protected function putAsUser($url, $data, $user = null): self
|
||||
protected function postAsUser(string $url, array $data, ?User $user = null): TestResponse
|
||||
{
|
||||
return $this->put($url, $data, [
|
||||
'Authorization' => 'Bearer '.$this->generateJwtToken($user),
|
||||
]);
|
||||
return $this->jsonAsUser($user, 'post', $url, $data);
|
||||
}
|
||||
|
||||
private function generateJwtToken(?User $user): string
|
||||
protected function putAsUser(string $url, array $data, ?User $user = null): TestResponse
|
||||
{
|
||||
return $this->auth->fromUser($user ?: factory(User::class)->create());
|
||||
return $this->jsonAsUser($user, 'put', $url, $data);
|
||||
}
|
||||
|
||||
protected static function getNonPublicProperty($object, string $property)
|
||||
|
@ -107,6 +99,7 @@ abstract class TestCase extends BaseTestCase
|
|||
$this->addToAssertionCount(Mockery::getContainer()->mockery_getExpectationCount());
|
||||
Mockery::close();
|
||||
self::destroySandbox();
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ class UploadTest extends TestCase
|
|||
'/api/upload',
|
||||
['file' => $file],
|
||||
factory(User::class)->create()
|
||||
)->seeStatusCode(403);
|
||||
)->assertStatus(403);
|
||||
}
|
||||
|
||||
public function provideUploadExceptions(): array
|
||||
|
@ -67,8 +67,8 @@ class UploadTest extends TestCase
|
|||
$this->postAsUser(
|
||||
'/api/upload',
|
||||
['file' => $file],
|
||||
factory(User::class, 'admin')->create()
|
||||
)->seeStatusCode($statusCode);
|
||||
factory(User::class)->states('admin')->create()
|
||||
)->assertStatus($statusCode);
|
||||
}
|
||||
|
||||
public function testPost(): void
|
||||
|
@ -87,8 +87,8 @@ class UploadTest extends TestCase
|
|||
$this->postAsUser(
|
||||
'/api/upload',
|
||||
['file' => $file],
|
||||
factory(User::class, 'admin')->create()
|
||||
)->seeJsonStructure([
|
||||
factory(User::class)->states('admin')->create()
|
||||
)->assertJsonStructure([
|
||||
'album',
|
||||
'artist',
|
||||
]);
|
||||
|
|
|
@ -24,7 +24,7 @@ class UserTest extends TestCase
|
|||
'email' => 'bar@baz.com',
|
||||
'password' => 'qux',
|
||||
'is_admin' => false
|
||||
])->seeStatusCode(403);
|
||||
])->assertStatus(403);
|
||||
}
|
||||
|
||||
public function testAdminCreatesUser(): void
|
||||
|
@ -40,9 +40,9 @@ class UserTest extends TestCase
|
|||
'email' => 'bar@baz.com',
|
||||
'password' => 'qux',
|
||||
'is_admin' => true
|
||||
], factory(User::class, 'admin')->create());
|
||||
], factory(User::class)->states('admin')->create());
|
||||
|
||||
self::seeInDatabase('users', [
|
||||
self::assertDatabaseHas('users', [
|
||||
'name' => 'Foo',
|
||||
'email' => 'bar@baz.com',
|
||||
'password' => 'hashed',
|
||||
|
@ -71,9 +71,9 @@ class UserTest extends TestCase
|
|||
'email' => 'bar@baz.com',
|
||||
'password' => 'qux',
|
||||
'is_admin' => false,
|
||||
], factory(User::class, 'admin')->create());
|
||||
], factory(User::class)->states('admin')->create());
|
||||
|
||||
self::seeInDatabase('users', [
|
||||
self::assertDatabaseHas('users', [
|
||||
'id' => $user->id,
|
||||
'name' => 'Foo',
|
||||
'email' => 'bar@baz.com',
|
||||
|
@ -84,32 +84,35 @@ class UserTest extends TestCase
|
|||
|
||||
public function testAdminDeletesUser(): void
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = factory(User::class)->create();
|
||||
$admin = factory(User::class, 'admin')->create();
|
||||
$admin = factory(User::class)->states('admin')->create();
|
||||
|
||||
$this->deleteAsUser("api/user/{$user->id}", [], $admin)
|
||||
->notSeeInDatabase('users', ['id' => $user->id]);
|
||||
$this->deleteAsUser("api/user/{$user->id}", [], $admin);
|
||||
self::assertDatabaseMissing('users', ['id' => $user->id]);
|
||||
}
|
||||
|
||||
public function testSeppukuNotAllowed(): void
|
||||
{
|
||||
$admin = factory(User::class, 'admin')->create();
|
||||
/** @var User $admin */
|
||||
$admin = factory(User::class)->states('admin')->create();
|
||||
|
||||
// A user can't delete himself
|
||||
$this->deleteAsUser("api/user/{$admin->id}", [], $admin)
|
||||
->seeStatusCode(403)
|
||||
->seeInDatabase('users', ['id' => $admin->id]);
|
||||
->assertStatus(403);
|
||||
|
||||
self::assertDatabaseHas('users', ['id' => $admin->id]);
|
||||
}
|
||||
|
||||
public function testUpdateUserProfile(): void
|
||||
{
|
||||
$user = factory(User::class)->create();
|
||||
$this->assertNull($user->getPreference('foo'));
|
||||
self::assertNull($user->getPreference('foo'));
|
||||
|
||||
$user->setPreference('foo', 'bar');
|
||||
$this->assertEquals('bar', $user->getPreference('foo'));
|
||||
self::assertEquals('bar', $user->getPreference('foo'));
|
||||
|
||||
$user->deletePreference('foo');
|
||||
$this->assertNull($user->getPreference('foo'));
|
||||
self::assertNull($user->getPreference('foo'));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,6 @@ class YouTubeTest extends TestCase
|
|||
->once();
|
||||
|
||||
$this->getAsUser("/api/youtube/search/song/{$song->id}?pageToken=foo")
|
||||
->assertResponseOk();
|
||||
->assertOk();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Integration\Commands;
|
||||
|
||||
use App\Console\Commands\GenerateJwtSecretCommand;
|
||||
use App\Console\Kernel;
|
||||
use Jackiedo\DotenvEditor\DotenvEditor;
|
||||
use Mockery;
|
||||
use Mockery\MockInterface;
|
||||
use Tests\TestCase;
|
||||
|
||||
class GenerateJwtSecretCommandTest extends TestCase
|
||||
{
|
||||
/** @var DotenvEditor|MockInterface */
|
||||
private $dotenvEditor;
|
||||
|
||||
/** @var GenerateJwtSecretCommand */
|
||||
private $command;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->dotenvEditor = static::mockIocDependency(DotenvEditor::class);
|
||||
$this->command = app(GenerateJwtSecretCommand::class);
|
||||
app(Kernel::class)->registerCommand($this->command);
|
||||
}
|
||||
|
||||
public function testGenerateJwtSecret(): void
|
||||
{
|
||||
config(['jwt.secret' => false]);
|
||||
|
||||
$this->dotenvEditor
|
||||
->shouldReceive('setKey')
|
||||
->with('JWT_SECRET', Mockery::on(function ($key) {
|
||||
return preg_match('/[a-f0-9]{32}$/i', $key);
|
||||
}));
|
||||
|
||||
$this->artisan('koel:generate-jwt-secret');
|
||||
}
|
||||
|
||||
public function testNotRegenerateJwtSecret(): void
|
||||
{
|
||||
config(['jwt.secret' => '12345678901234567890123456789012']);
|
||||
|
||||
$this->dotenvEditor
|
||||
->shouldReceive('setKey')
|
||||
->never();
|
||||
|
||||
$this->artisan('koel:generate-jwt-secret');
|
||||
}
|
||||
}
|
|
@ -23,7 +23,7 @@ class AlbumTest extends TestCase
|
|||
$gottenAlbum = Album::get($artist, $album->name);
|
||||
|
||||
// Then I get the album
|
||||
$this->assertSame($album->id, $gottenAlbum->id);
|
||||
self::assertSame($album->id, $gottenAlbum->id);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
|
@ -34,13 +34,13 @@ class AlbumTest extends TestCase
|
|||
$name = 'Foo';
|
||||
|
||||
// And an album with such details doesn't exist yet
|
||||
$this->assertNull(Album::whereArtistIdAndName($artist->id, $name)->first());
|
||||
self::assertNull(Album::whereArtistIdAndName($artist->id, $name)->first());
|
||||
|
||||
// When I try to get the album by such artist and name
|
||||
$album = Album::get($artist, $name);
|
||||
|
||||
// Then I get the new album
|
||||
$this->assertNotNull($album);
|
||||
self::assertNotNull($album);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
|
@ -53,7 +53,7 @@ class AlbumTest extends TestCase
|
|||
$album = Album::get(factory(Artist::class)->create(), $name);
|
||||
|
||||
// Then the album's name is "Unknown Album"
|
||||
$this->assertEquals('Unknown Album', $album->name);
|
||||
self::assertEquals('Unknown Album', $album->name);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
|
@ -66,6 +66,6 @@ class AlbumTest extends TestCase
|
|||
$album = Album::get(factory(Artist::class)->create(), 'Foo', $isCompilation);
|
||||
|
||||
// Then its artist is Various Artist
|
||||
$this->assertTrue($album->artist->is_various);
|
||||
self::assertTrue($album->artist->is_various);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ class ArtistTest extends TestCase
|
|||
$gottenArtist = Artist::get('Foo');
|
||||
|
||||
// Then I get the artist
|
||||
$this->assertEquals($artist->id, $gottenArtist->id);
|
||||
self::assertEquals($artist->id, $gottenArtist->id);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
|
@ -28,13 +28,13 @@ class ArtistTest extends TestCase
|
|||
$name = 'Foo';
|
||||
|
||||
// And an artist with such a name doesn't exist yet
|
||||
$this->assertNull(Artist::whereName($name)->first());
|
||||
self::assertNull(Artist::whereName($name)->first());
|
||||
|
||||
// When I get the artist by name
|
||||
$artist = Artist::get($name);
|
||||
|
||||
// Then I get the newly created artist
|
||||
$this->assertInstanceOf(Artist::class, $artist);
|
||||
self::assertInstanceOf(Artist::class, $artist);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
|
@ -47,7 +47,7 @@ class ArtistTest extends TestCase
|
|||
$artist = Artist::get($name);
|
||||
|
||||
// Then I get the artist as Unknown Artist
|
||||
$this->assertTrue($artist->is_unknown);
|
||||
self::assertTrue($artist->is_unknown);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
|
@ -61,6 +61,6 @@ class ArtistTest extends TestCase
|
|||
$retrieved = Artist::get($name);
|
||||
|
||||
// Then I receive the artist
|
||||
$this->assertEquals($artist->id, $retrieved->id);
|
||||
self::assertEquals($artist->id, $retrieved->id);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ class SettingTest extends TestCase
|
|||
Setting::set($key, $value);
|
||||
|
||||
// Then I see the key and serialized value in the database
|
||||
$this->assertDatabaseHas('settings', [
|
||||
self::assertDatabaseHas('settings', [
|
||||
'key' => 'foo',
|
||||
'value' => serialize('bar'),
|
||||
]);
|
||||
|
@ -37,7 +37,7 @@ class SettingTest extends TestCase
|
|||
Setting::set($settings);
|
||||
|
||||
// Then I see all settings the database
|
||||
$this->assertDatabaseHas('settings', [
|
||||
self::assertDatabaseHas('settings', [
|
||||
'key' => 'foo',
|
||||
'value' => serialize('bar'),
|
||||
])->assertDatabaseHas('settings', [
|
||||
|
@ -52,7 +52,7 @@ class SettingTest extends TestCase
|
|||
Setting::set('foo', 'bar');
|
||||
Setting::set('foo', 'baz');
|
||||
|
||||
$this->assertEquals('baz', Setting::get('foo'));
|
||||
self::assertEquals('baz', Setting::get('foo'));
|
||||
}
|
||||
|
||||
/** @test */
|
||||
|
@ -68,6 +68,6 @@ class SettingTest extends TestCase
|
|||
$value = Setting::get('foo');
|
||||
|
||||
// Then I receive the value in an unserialized format
|
||||
$this->assertSame('bar', $value);
|
||||
self::assertSame('bar', $value);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ class SongTest extends TestCase
|
|||
$lyrics = $song->lyrics;
|
||||
|
||||
// Then I see the new line characters replaced by <br />
|
||||
$this->assertEquals('foo<br />bar<br />baz<br />qux', $lyrics);
|
||||
self::assertEquals('foo<br />bar<br />baz<br />qux', $lyrics);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
|
@ -34,6 +34,6 @@ class SongTest extends TestCase
|
|||
$params = $song->s3_params;
|
||||
|
||||
// Then I receive the correct parameters
|
||||
$this->assertEquals(['bucket' => 'foo', 'key' => 'bar'], $params);
|
||||
self::assertEquals(['bucket' => 'foo', 'key' => 'bar'], $params);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,8 +18,8 @@ class SongZipArchiveTest extends TestCase
|
|||
$songZipArchive = new SongZipArchive();
|
||||
$songZipArchive->addSong($song);
|
||||
|
||||
$this->assertEquals(1, $songZipArchive->getArchive()->numFiles);
|
||||
$this->assertEquals('full.mp3', $songZipArchive->getArchive()->getNameIndex(0));
|
||||
self::assertEquals(1, $songZipArchive->getArchive()->numFiles);
|
||||
self::assertEquals('full.mp3', $songZipArchive->getArchive()->getNameIndex(0));
|
||||
}
|
||||
|
||||
/** @test */
|
||||
|
@ -37,8 +37,8 @@ class SongZipArchiveTest extends TestCase
|
|||
$songZipArchive = new SongZipArchive();
|
||||
$songZipArchive->addSongs($songs);
|
||||
|
||||
$this->assertEquals(2, $songZipArchive->getArchive()->numFiles);
|
||||
$this->assertEquals('full.mp3', $songZipArchive->getArchive()->getNameIndex(0));
|
||||
$this->assertEquals('lorem.mp3', $songZipArchive->getArchive()->getNameIndex(1));
|
||||
self::assertEquals(2, $songZipArchive->getArchive()->numFiles);
|
||||
self::assertEquals('full.mp3', $songZipArchive->getArchive()->getNameIndex(0));
|
||||
self::assertEquals('lorem.mp3', $songZipArchive->getArchive()->getNameIndex(1));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,65 +7,43 @@ use Tests\TestCase;
|
|||
|
||||
class UserTest extends TestCase
|
||||
{
|
||||
/** @test */
|
||||
public function user_preferences_can_be_set()
|
||||
public function testSetUserPreferences(): void
|
||||
{
|
||||
// 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);
|
||||
self::assertSame('bar', $user->preferences['foo']);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function user_preferences_can_be_retrieved()
|
||||
public function testGetUserPreferences(): void
|
||||
{
|
||||
// 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);
|
||||
self::assertEquals('bar', $user->getPreference('foo'));
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function user_preferences_can_be_deleted()
|
||||
public function testDeleteUserPreferences(): void
|
||||
{
|
||||
// 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);
|
||||
self::assertArrayNotHasKey('foo', $user->preferences);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function sensitive_preferences_are_hidden()
|
||||
public function testSensitivePreferencesAreHidden(): void
|
||||
{
|
||||
// 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', $user->preferences['lastfm_session_key']);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ class FileSynchronizerTest extends TestCase
|
|||
];
|
||||
|
||||
self::assertArraySubset($expectedData, $info);
|
||||
self::assertEquals(10.083, $info['length'], '', 0.001);
|
||||
self::assertEqualsWithDelta(10.083, $info['length'], 0.001);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
|
|
|
@ -31,7 +31,7 @@ class LastfmServiceTest extends TestCase
|
|||
$api = new LastfmService($client, app(Cache::class), app(Logger::class));
|
||||
$info = $api->getArtistInformation($artist->name);
|
||||
|
||||
$this->assertEquals([
|
||||
self::assertEquals([
|
||||
'url' => 'https://www.last.fm/music/Kamelot',
|
||||
'image' => 'http://foo.bar/extralarge.jpg',
|
||||
'bio' => [
|
||||
|
@ -78,7 +78,7 @@ class LastfmServiceTest extends TestCase
|
|||
$info = $api->getAlbumInformation($album->name, $album->artist->name);
|
||||
|
||||
// Then I get the album's info
|
||||
$this->assertEquals([
|
||||
self::assertEquals([
|
||||
'url' => 'https://www.last.fm/music/Kamelot/Epica',
|
||||
'image' => 'http://foo.bar/extralarge.jpg',
|
||||
'tracks' => [
|
||||
|
|
|
@ -43,9 +43,9 @@ class MediaCacheServiceTest extends TestCase
|
|||
|
||||
$data = $this->mediaCacheService->get();
|
||||
|
||||
$this->assertCount(6, $data['albums']); // 5 new albums and the default Unknown Album
|
||||
$this->assertCount(7, $data['artists']); // 5 new artists and the default Various and Unknown Artist
|
||||
$this->assertCount(5, $data['songs']);
|
||||
self::assertCount(6, $data['albums']); // 5 new albums and the default Unknown Album
|
||||
self::assertCount(7, $data['artists']); // 5 new artists and the default Various and Unknown Artist
|
||||
self::assertCount(5, $data['songs']);
|
||||
}
|
||||
|
||||
public function testGetIfCacheIsAvailable(): void
|
||||
|
@ -56,7 +56,7 @@ class MediaCacheServiceTest extends TestCase
|
|||
|
||||
$data = $this->mediaCacheService->get();
|
||||
|
||||
$this->assertEquals(['dummy'], $data);
|
||||
self::assertEquals(['dummy'], $data);
|
||||
}
|
||||
|
||||
public function testCacheDisabled(): void
|
||||
|
|
|
@ -24,7 +24,7 @@ class MediaMetadataServiceTest extends TestCase
|
|||
|
||||
self::assertSame(
|
||||
album_cover_url('album-cover-for-thumbnail-test_thumb.jpg'),
|
||||
app()->get(MediaMetadataService::class)->getAlbumThumbnailUrl($album)
|
||||
app(MediaMetadataService::class)->getAlbumThumbnailUrl($album)
|
||||
);
|
||||
|
||||
self::assertFileExists(album_cover_path('album-cover-for-thumbnail-test_thumb.jpg'));
|
||||
|
@ -33,7 +33,7 @@ class MediaMetadataServiceTest extends TestCase
|
|||
public function testGetAlbumThumbnailUrlWithNoCover(): void
|
||||
{
|
||||
$album = factory(Album::class)->create(['cover' => null]);
|
||||
self::assertNull(app()->get(MediaMetadataService::class)->getAlbumThumbnailUrl($album));
|
||||
self::assertNull(app(MediaMetadataService::class)->getAlbumThumbnailUrl($album));
|
||||
}
|
||||
|
||||
private function cleanUp(): void
|
||||
|
|
|
@ -111,11 +111,11 @@ class SmartPlaylistServiceTest extends TestCase
|
|||
public function testBuildQueryForRules(array $rules, string $sql, array $bindings): void
|
||||
{
|
||||
$query = $this->service->buildQueryFromRules($rules);
|
||||
$this->assertSame($sql, $query->toSql());
|
||||
self::assertSame($sql, $query->toSql());
|
||||
$queryBinding = $query->getBindings();
|
||||
|
||||
for ($i = 0, $count = count($queryBinding); $i < $count; $i++) {
|
||||
$this->assertSame(
|
||||
self::assertSame(
|
||||
$bindings[$i],
|
||||
is_object($queryBinding[$i]) ? (string) $queryBinding[$i] : $queryBinding[$i]
|
||||
);
|
||||
|
@ -129,7 +129,7 @@ class SmartPlaylistServiceTest extends TestCase
|
|||
/** @var User $user */
|
||||
$user = factory(User::class)->create();
|
||||
|
||||
$this->assertEquals([
|
||||
self::assertEquals([
|
||||
'model' => 'interactions.user_id',
|
||||
'operator' => 'is',
|
||||
'value' => [$user->id],
|
||||
|
@ -152,6 +152,6 @@ class SmartPlaylistServiceTest extends TestCase
|
|||
}
|
||||
}
|
||||
|
||||
$this->assertSame(count(Rule::VALID_OPERATORS), count(array_unique($operators)));
|
||||
self::assertSame(count(Rule::VALID_OPERATORS), count(array_unique($operators)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ class YouTubeServiceTest extends TestCase
|
|||
$api = new YouTubeService($client, app(Repository::class), app(Logger::class));
|
||||
$response = $api->search('Lorem Ipsum');
|
||||
|
||||
$this->assertEquals('Slipknot - Snuff [OFFICIAL VIDEO]', $response->items[0]->snippet->title);
|
||||
$this->assertNotNull(cache('1492972ec5c8e6b3a9323ba719655ddb'));
|
||||
self::assertEquals('Slipknot - Snuff [OFFICIAL VIDEO]', $response->items[0]->snippet->title);
|
||||
self::assertNotNull(cache('1492972ec5c8e6b3a9323ba719655ddb'));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ trait CreatesApplication
|
|||
{
|
||||
$this->artisan->call('migrate');
|
||||
|
||||
if (!User::all()->count()) {
|
||||
if (!User::count()) {
|
||||
$this->artisan->call('db:seed');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,8 +23,8 @@ class ApplicationTest extends TestCase
|
|||
$assetURL = app()->staticUrl('/foo.css ');
|
||||
|
||||
// Then I see they're constructed correctly
|
||||
$this->assertEquals('http://localhost/', $root);
|
||||
$this->assertEquals('http://localhost/foo.css', $assetURL);
|
||||
self::assertEquals('http://localhost/', $root);
|
||||
self::assertEquals('http://localhost/foo.css', $assetURL);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
|
@ -38,8 +38,8 @@ class ApplicationTest extends TestCase
|
|||
$assetURL = app()->staticUrl('/foo.css ');
|
||||
|
||||
// Then I see they're constructed correctly
|
||||
$this->assertEquals('http://cdn.tld/', $root);
|
||||
$this->assertEquals('http://cdn.tld/foo.css', $assetURL);
|
||||
self::assertEquals('http://cdn.tld/', $root);
|
||||
self::assertEquals('http://cdn.tld/foo.css', $assetURL);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
|
@ -55,7 +55,7 @@ class ApplicationTest extends TestCase
|
|||
$assetURL = app()->rev('/foo.css', $manifestFile);
|
||||
|
||||
// Then I see they're constructed correctly
|
||||
$this->assertEquals('http://localhost/public/foo00.css', $assetURL);
|
||||
self::assertEquals('http://localhost/public/foo00.css', $assetURL);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
|
@ -71,6 +71,6 @@ class ApplicationTest extends TestCase
|
|||
$assetURL = app()->rev('/foo.css', $manifestFile);
|
||||
|
||||
// Then I see they're constructed correctly
|
||||
$this->assertEquals('http://cdn.tld/public/foo00.css', $assetURL);
|
||||
self::assertEquals('http://cdn.tld/public/foo00.css', $assetURL);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ class ForceHttpsTest extends TestCase
|
|||
return $request;
|
||||
};
|
||||
|
||||
$this->assertSame($request, $this->middleware->handle($request, $next));
|
||||
self::assertSame($request, $this->middleware->handle($request, $next));
|
||||
}
|
||||
|
||||
public function testNotHandle(): void
|
||||
|
@ -60,6 +60,6 @@ class ForceHttpsTest extends TestCase
|
|||
return $request;
|
||||
};
|
||||
|
||||
$this->assertSame($request, $this->middleware->handle($request, $next));
|
||||
self::assertSame($request, $this->middleware->handle($request, $next));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ class LastfmServiceTest extends TestCase
|
|||
$builtParamsAsString = $lastfm->buildAuthCallParams($params, true);
|
||||
|
||||
// Then I receive the Last.fm-compatible API parameters
|
||||
$this->assertEquals([
|
||||
self::assertEquals([
|
||||
'api_key' => 'key',
|
||||
'bar' => 'baz',
|
||||
'qux' => '安',
|
||||
|
@ -34,7 +34,7 @@ class LastfmServiceTest extends TestCase
|
|||
], $builtParams);
|
||||
|
||||
// And the string version as well
|
||||
$this->assertEquals(
|
||||
self::assertEquals(
|
||||
'api_key=key&bar=baz&qux=安&api_sig=7f21233b54edea994aa0f23cf55f18a2',
|
||||
$builtParamsAsString
|
||||
);
|
||||
|
|
|
@ -42,7 +42,7 @@ class MediaMetadataServiceTest extends TestCase
|
|||
->with('/koel/public/images/album/foo.jpg', 'dummy');
|
||||
|
||||
$this->mediaMetadataService->writeAlbumCover($album, $coverContent, 'jpg', $coverPath);
|
||||
$this->assertEquals(album_cover_url('foo.jpg'), Album::find($album->id)->cover);
|
||||
self::assertEquals(album_cover_url('foo.jpg'), Album::find($album->id)->cover);
|
||||
}
|
||||
|
||||
public function testWriteArtistImage(): void
|
||||
|
@ -58,6 +58,6 @@ class MediaMetadataServiceTest extends TestCase
|
|||
->with('/koel/public/images/artist/foo.jpg', 'dummy');
|
||||
|
||||
$this->mediaMetadataService->writeArtistImage($artist, $imageContent, 'jpg', $imagePath);
|
||||
$this->assertEquals(artist_image_url('foo.jpg'), Artist::find($artist->id)->image);
|
||||
self::assertEquals(artist_image_url('foo.jpg'), Artist::find($artist->id)->image);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,10 +19,10 @@ class iTunesServiceTest extends TestCase
|
|||
config(['koel.itunes.enabled' => true]);
|
||||
/** @var iTunesService $iTunes */
|
||||
$iTunes = app()->make(iTunesService::class);
|
||||
$this->assertTrue($iTunes->used());
|
||||
self::assertTrue($iTunes->used());
|
||||
|
||||
config(['koel.itunes.enabled' => false]);
|
||||
$this->assertFalse($iTunes->used());
|
||||
self::assertFalse($iTunes->used());
|
||||
}
|
||||
|
||||
public function provideGetTrackUrlData(): array
|
||||
|
|
Loading…
Reference in a new issue