diff --git a/app/Enums/Ruleset.php b/app/Enums/Ruleset.php
index 1fc36baeedc..3ff92da8ee4 100644
--- a/app/Enums/Ruleset.php
+++ b/app/Enums/Ruleset.php
@@ -14,16 +14,31 @@ enum Ruleset: int
case catch = 2;
case mania = 3;
- public static function fromName(string $ruleset): self
+ // for usage with tryFrom when the parameter may be null.
+ public const NULL = -1;
+
+ public static function tryFromName(?string $ruleset): ?self
{
+ if ($ruleset === null) {
+ return null;
+ }
+
static $lookupMap;
if ($lookupMap === null) {
$lookupMap = [];
foreach (self::cases() as $r) {
$lookupMap[$r->name] = $r;
}
+ $lookupMap['fruits'] = self::catch;
}
- return $lookupMap[$ruleset];
+ return $lookupMap[$ruleset] ?? null;
+ }
+
+ public function legacyName()
+ {
+ return $this === self::catch
+ ? 'fruits'
+ : $this->name;
}
}
diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php
index eb5b75ce9ee..c44353cd325 100644
--- a/app/Http/Controllers/AccountController.php
+++ b/app/Http/Controllers/AccountController.php
@@ -7,6 +7,7 @@
use App\Exceptions\ImageProcessorException;
use App\Exceptions\ModelNotSavedException;
+use App\Libraries\User\AvatarHelper;
use App\Libraries\User\CountryChange;
use App\Libraries\User\CountryChangeTarget;
use App\Libraries\UserVerification;
@@ -73,7 +74,7 @@ public function avatar()
$user = auth()->user();
try {
- $user->setAvatar(Request::file('avatar_file'));
+ AvatarHelper::set($user, Request::file('avatar_file'));
} catch (ImageProcessorException $e) {
return error_popup($e->getMessage());
}
@@ -308,7 +309,7 @@ public function verify()
public function verifyLink()
{
- $state = UserVerificationState::fromVerifyLink(request('key'));
+ $state = UserVerificationState::fromVerifyLink(get_string(request('key')) ?? '');
if ($state === null) {
UserVerification::logAttempt('link', 'fail', 'incorrect_key');
diff --git a/app/Http/Controllers/BeatmapsController.php b/app/Http/Controllers/BeatmapsController.php
index c82345bb533..97158c45737 100644
--- a/app/Http/Controllers/BeatmapsController.php
+++ b/app/Http/Controllers/BeatmapsController.php
@@ -5,6 +5,7 @@
namespace App\Http\Controllers;
+use App\Enums\Ruleset;
use App\Exceptions\InvariantException;
use App\Jobs\Notifications\BeatmapOwnerChange;
use App\Libraries\BeatmapDifficultyAttributes;
@@ -256,20 +257,24 @@ public function show($id)
abort(404);
}
- if ($beatmap->mode === 'osu') {
+ $beatmapRuleset = $beatmap->mode;
+ if ($beatmapRuleset === 'osu') {
$params = get_params(request()->all(), null, [
'm:int', // legacy parameter
- 'mode:string',
+ 'mode', // legacy parameter
+ 'ruleset',
], ['null_missing' => true]);
- $mode = Beatmap::isModeValid($params['mode'])
- ? $params['mode']
- : Beatmap::modeStr($params['m']);
+ $ruleset = (
+ Ruleset::tryFromName($params['ruleset'])
+ ?? Ruleset::tryFromName($params['mode'])
+ ?? Ruleset::tryFrom($params['m'] ?? Ruleset::NULL)
+ )?->legacyName();
}
- $mode ??= $beatmap->mode;
+ $ruleset ??= $beatmapRuleset;
- return ujs_redirect(route('beatmapsets.show', ['beatmapset' => $beatmapset->getKey()]).'#'.$mode.'/'.$beatmap->getKey());
+ return ujs_redirect(route('beatmapsets.show', ['beatmapset' => $beatmapset->getKey()]).'#'.$ruleset.'/'.$beatmap->getKey());
}
/**
diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php
index a45cf05a3df..c6062330a92 100644
--- a/app/Http/Kernel.php
+++ b/app/Http/Kernel.php
@@ -23,6 +23,7 @@ class Kernel extends HttpKernel
Middleware\AuthApi::class,
Middleware\SetLocaleApi::class,
Middleware\CheckUserBanStatus::class,
+ Middleware\UpdateUserLastvisit::class,
],
'web' => [
Middleware\StripCookies::class,
diff --git a/app/Http/Middleware/AuthApi.php b/app/Http/Middleware/AuthApi.php
index 72c46653816..592c1ef5a17 100644
--- a/app/Http/Middleware/AuthApi.php
+++ b/app/Http/Middleware/AuthApi.php
@@ -79,6 +79,7 @@ private function validTokenFromRequest($psr)
throw new AuthenticationException('invalid token');
}
+ $token->setRelation('client', $client);
$token->validate();
$user = $token->getResourceOwner();
diff --git a/app/Http/Middleware/DatadogMetrics.php b/app/Http/Middleware/DatadogMetrics.php
index 89b27a7be94..68a8f1f710f 100644
--- a/app/Http/Middleware/DatadogMetrics.php
+++ b/app/Http/Middleware/DatadogMetrics.php
@@ -32,7 +32,7 @@ protected static function logDuration(Request $request, Response $response, $sta
$duration = microtime(true) - $startTime;
$tags = [
'action' => 'error_page',
- 'api' => $request->is('api/*') ? 'true' : 'false',
+ 'api' => is_api_request() ? 'true' : 'false',
'controller' => 'error',
'namespace' => 'error',
'pod_name' => $hostname,
diff --git a/app/Http/Middleware/StripCookies.php b/app/Http/Middleware/StripCookies.php
index 1899047b2af..05168a701ab 100644
--- a/app/Http/Middleware/StripCookies.php
+++ b/app/Http/Middleware/StripCookies.php
@@ -15,13 +15,7 @@ public function handle($request, Closure $next)
if ($request->attributes->get('strip_cookies')) {
// strip all cookies from response
- foreach ($result->headers->getCookies() as $cookie) {
- $result->headers->removeCookie(
- $cookie->getName(),
- $cookie->getPath(),
- $cookie->getDomain()
- );
- }
+ $result->headers->remove('set-cookie');
}
return $result;
diff --git a/app/Http/Middleware/UpdateUserLastvisit.php b/app/Http/Middleware/UpdateUserLastvisit.php
index 063605a2cc2..a817773224a 100644
--- a/app/Http/Middleware/UpdateUserLastvisit.php
+++ b/app/Http/Middleware/UpdateUserLastvisit.php
@@ -24,24 +24,30 @@ public function handle($request, Closure $next)
$user = $this->auth->user();
if ($user !== null) {
- $isInactive = $user->isInactive();
+ $token = $user->token();
+ $shouldUpdate = $token === null || $token->client->password_client;
- if ($isInactive) {
- $isVerified = $user->isSessionVerified();
- }
+ if ($shouldUpdate) {
+ $isInactive = $user->isInactive();
+ if ($isInactive) {
+ $isVerified = $user->isSessionVerified();
+ }
- if (!$isInactive || $isVerified) {
- $recordedLastVisit = $user->getRawAttribute('user_lastvisit');
- $currentLastVisit = time();
+ if (!$isInactive || $isVerified) {
+ $recordedLastVisit = $user->getRawAttribute('user_lastvisit');
+ $currentLastVisit = time();
- if ($currentLastVisit - $recordedLastVisit > 300) {
- $user->update([
- 'user_lastvisit' => $currentLastVisit,
- ], ['skipValidations' => true]);
+ if ($currentLastVisit - $recordedLastVisit > 300) {
+ $user->update([
+ 'user_lastvisit' => $currentLastVisit,
+ ], ['skipValidations' => true]);
+ }
}
- }
- $this->recordSession($request);
+ if ($token === null) {
+ $this->recordSession($request);
+ }
+ }
}
return $next($request);
diff --git a/app/Libraries/BBCodeForDB.php b/app/Libraries/BBCodeForDB.php
index 8d3fb94e44d..88d7a8445ed 100644
--- a/app/Libraries/BBCodeForDB.php
+++ b/app/Libraries/BBCodeForDB.php
@@ -5,7 +5,6 @@
namespace App\Libraries;
-use App\Models\Smiley;
use App\Models\User;
class BBCodeForDB
@@ -325,22 +324,13 @@ public function parseSize($text)
);
}
- // copied from www/forum/includes/message_parser.php#L1196
-
public function parseSmiley($text)
{
- $smilies = Smiley::getAll();
-
- $match = [];
- $replace = [];
+ $replacer = app('smilies')->replacer();
- foreach ($smilies as $smiley) {
- $match[] = '(?<=^|[\n .])'.preg_quote($smiley['code'], '#').'(?![^<>]*>)';
- $replace[] = '';
- }
- if (count($match)) {
+ if (count($replacer['patterns']) > 0) {
// Make sure the delimiter # is added in front and at the end of every element within $match
- $text = trim(preg_replace(explode(chr(0), '#'.implode('#'.chr(0).'#', $match).'#'), $replace, $text));
+ $text = trim(preg_replace($replacer['patterns'], $replacer['replacements'], $text));
}
return $text;
diff --git a/app/Libraries/Base64Url.php b/app/Libraries/Base64Url.php
new file mode 100644
index 00000000000..9e2a5252b42
--- /dev/null
+++ b/app/Libraries/Base64Url.php
@@ -0,0 +1,23 @@
+. Licensed under the GNU Affero General Public License v3.0.
+// See the LICENCE file in the repository root for full licence text.
+
+declare(strict_types=1);
+
+namespace App\Libraries;
+
+class Base64Url
+{
+ public static function decode(string $value): ?string
+ {
+ return null_if_false(base64_decode(strtr($value, '-_', '+/'), true));
+ }
+
+ public static function encode(string $value): string
+ {
+ // url safe base64
+ // reference: https://datatracker.ietf.org/doc/html/rfc4648#section-5
+ return rtrim(strtr(base64_encode($value), '+/', '-_'), '=');
+ }
+}
diff --git a/app/Libraries/SignedRandomString.php b/app/Libraries/SignedRandomString.php
new file mode 100644
index 00000000000..7e5cc35002f
--- /dev/null
+++ b/app/Libraries/SignedRandomString.php
@@ -0,0 +1,39 @@
+. Licensed under the GNU Affero General Public License v3.0.
+// See the LICENCE file in the repository root for full licence text.
+
+declare(strict_types=1);
+
+namespace App\Libraries;
+
+class SignedRandomString
+{
+ public static function create(int $randomSize): string
+ {
+ $key = random_bytes($randomSize);
+ $hmac = static::hmac($key);
+
+ return Base64Url::encode($hmac.$key);
+ }
+
+ public static function isValid(string $input): bool
+ {
+ $bin = Base64Url::decode($input);
+ if ($bin === null) {
+ return false;
+ }
+
+ // hmac size for sha1 is 20
+ $hmac = substr($bin, 0, 20);
+ $key = substr($bin, 20);
+ $expectedHmac = static::hmac($key);
+
+ return hash_equals($expectedHmac, $hmac);
+ }
+
+ private static function hmac(string $key): string
+ {
+ return hash_hmac('sha1', $key, \Crypt::getKey(), true);
+ }
+}
diff --git a/app/Libraries/Smilies.php b/app/Libraries/Smilies.php
new file mode 100644
index 00000000000..136381ada4d
--- /dev/null
+++ b/app/Libraries/Smilies.php
@@ -0,0 +1,43 @@
+. Licensed under the GNU Affero General Public License v3.0.
+// See the LICENCE file in the repository root for full licence text.
+
+declare(strict_types=1);
+
+namespace App\Libraries;
+
+use App\Models\Smiley;
+use App\Traits\Memoizes;
+
+class Smilies
+{
+ use Memoizes;
+
+ public function all(): array
+ {
+ return $this->memoize(__FUNCTION__, fn () => $this->fetch());
+ }
+
+ public function replacer(): array
+ {
+ return $this->memoize(__FUNCTION__, function () {
+ $smilies = $this->all();
+
+ $patterns = [];
+ $replacements = [];
+
+ foreach ($smilies as $smiley) {
+ $patterns[] = '#(?<=^|[\n .])'.preg_quote($smiley['code'], '#').'(?![^<>]*>)#';
+ $replacements[] = '';
+ }
+
+ return compact('patterns', 'replacements');
+ });
+ }
+
+ private function fetch(): array
+ {
+ return Smiley::orderBy(\DB::raw('LENGTH(code)'), 'desc')->get()->toArray();
+ }
+}
diff --git a/app/Libraries/User/AvatarHelper.php b/app/Libraries/User/AvatarHelper.php
new file mode 100644
index 00000000000..6b4919dc507
--- /dev/null
+++ b/app/Libraries/User/AvatarHelper.php
@@ -0,0 +1,79 @@
+. Licensed under the GNU Affero General Public License v3.0.
+// See the LICENCE file in the repository root for full licence text.
+
+declare(strict_types=1);
+
+namespace App\Libraries\User;
+
+use App\Libraries\ImageProcessor;
+use App\Libraries\StorageUrl;
+use App\Models\User;
+
+class AvatarHelper
+{
+ public static function set(User $user, ?\SplFileInfo $src): bool
+ {
+ $id = $user->getKey();
+ $storage = \Storage::disk(static::disk());
+
+ if ($src === null) {
+ $storage->delete($id);
+ } else {
+ $srcPath = $src->getRealPath();
+ $processor = new ImageProcessor($srcPath, [256, 256], 100000);
+ $processor->process();
+
+ $storage->putFileAs('/', $src, $id, 'public');
+ $entry = $id.'_'.time().'.'.$processor->ext();
+ }
+
+ static::purgeCache($id);
+
+ return $user->update(['user_avatar' => $entry ?? '']);
+ }
+
+ public static function url(User $user): string
+ {
+ $value = $user->getRawAttribute('user_avatar');
+
+ return present($value)
+ ? StorageUrl::make(static::disk(), strtr($value, '_', '?'))
+ : \Config::get('osu.avatar.default');
+ }
+
+ private static function disk(): string
+ {
+ return \Config::get('osu.avatar.storage');
+ }
+
+ private static function purgeCache(int $id): void
+ {
+ $prefix = presence(\Config::get('osu.avatar.cache_purge_prefix'));
+
+ if ($prefix === null) {
+ return;
+ }
+
+ $method = \Config::get('osu.avatar.cache_purge_method') ?? 'GET';
+ $auth = \Config::get('osu.avatar.cache_purge_authorization_key');
+ $ctx = [
+ 'http' => [
+ 'method' => $method,
+ 'header' => present($auth) ? "Authorization: {$auth}" : null,
+ ],
+ ];
+ $suffix = $method === 'GET' ? '?'.time() : ''; // Bypass CloudFlare cache if using GET
+ $url = "{$prefix}{$id}{$suffix}";
+
+ try {
+ file_get_contents($url, false, stream_context_create($ctx));
+ } catch (\ErrorException $e) {
+ // ignores 404 errors, throws everything else
+ if (!ends_with($e->getMessage(), "404 Not Found\r\n")) {
+ throw $e;
+ }
+ }
+ }
+}
diff --git a/app/Libraries/UserVerificationState.php b/app/Libraries/UserVerificationState.php
index 266ff6abe11..61c336ee09a 100644
--- a/app/Libraries/UserVerificationState.php
+++ b/app/Libraries/UserVerificationState.php
@@ -23,6 +23,10 @@ public static function fromCurrentRequest()
public static function fromVerifyLink($linkKey)
{
+ if (!SignedRandomString::isValid($linkKey)) {
+ return null;
+ }
+
$params = cache()->get("verification:{$linkKey}");
if ($params !== null) {
@@ -76,7 +80,7 @@ public function issue()
// 1 byte = 2^8 bits = 16^2 bits = 2 hex characters
$key = bin2hex(random_bytes(config('osu.user.verification_key_length_hex') / 2));
- $linkKey = bin2hex(random_bytes(32));
+ $linkKey = SignedRandomString::create(32);
$expires = now()->addHours(5);
$this->session->put('verification_key', $key);
diff --git a/app/Models/Smiley.php b/app/Models/Smiley.php
index e5d54ff431d..ae298595245 100644
--- a/app/Models/Smiley.php
+++ b/app/Models/Smiley.php
@@ -5,9 +5,6 @@
namespace App\Models;
-use Cache;
-use DB;
-
/**
* @property string $code
* @property int $display_on_posting
@@ -21,11 +18,4 @@
class Smiley extends Model
{
protected $table = 'phpbb_smilies';
-
- public static function getAll()
- {
- return Cache::rememberForever('smilies', function () {
- return self::orderBy(DB::raw('LENGTH(code)'), 'desc')->get()->toArray();
- });
- }
}
diff --git a/app/Models/Traits/UserAvatar.php b/app/Models/Traits/UserAvatar.php
deleted file mode 100644
index 252d6411665..00000000000
--- a/app/Models/Traits/UserAvatar.php
+++ /dev/null
@@ -1,67 +0,0 @@
-. Licensed under the GNU Affero General Public License v3.0.
-// See the LICENCE file in the repository root for full licence text.
-
-namespace App\Models\Traits;
-
-use App\Libraries\ImageProcessor;
-use App\Libraries\StorageUrl;
-use ErrorException;
-
-trait UserAvatar
-{
- private static function avatarDisk(): string
- {
- return \Config::get('osu.avatar.storage');
- }
-
- public function setAvatar($file)
- {
- $storage = \Storage::disk(static::avatarDisk());
- if ($file === null) {
- $storage->delete($this->user_id);
- } else {
- $filePath = $file->getRealPath();
- $processor = new ImageProcessor($filePath, [256, 256], 100000);
- $processor->process();
-
- $storage->put($this->user_id, file_get_contents($filePath), 'public');
-
- $entry = $this->user_id.'_'.time().'.'.$processor->ext();
- }
-
- if (present(\Config::get('osu.avatar.cache_purge_prefix'))) {
- try {
- $ctx = [
- 'http' => [
- 'method' => \Config::get('osu.avatar.cache_purge_method') ?? 'GET',
- 'header' => present(\Config::get('osu.avatar.cache_purge_authorization_key'))
- ? 'Authorization: '.\Config::get('osu.avatar.cache_purge_authorization_key')
- : null,
- ],
- ];
- $prefix = \Config::get('osu.avatar.cache_purge_prefix');
- $suffix = $ctx['http']['method'] === 'GET' ? '?'.time() : ''; // Bypass CloudFlare cache if using GET
- $url = $prefix.$this->user_id.$suffix;
- file_get_contents($url, false, stream_context_create($ctx));
- } catch (ErrorException $e) {
- // ignores 404 errors, throws everything else
- if (!ends_with($e->getMessage(), "404 Not Found\r\n")) {
- throw $e;
- }
- }
- }
-
- return $this->update(['user_avatar' => $entry ?? '']);
- }
-
- protected function getUserAvatar()
- {
- $value = $this->getRawAttribute('user_avatar');
-
- return present($value)
- ? StorageUrl::make(static::avatarDisk(), strtr($value, '_', '?'))
- : \Config::get('osu.avatar.default');
- }
-}
diff --git a/app/Models/User.php b/app/Models/User.php
index 013951fe5a7..04a34a07aa9 100644
--- a/app/Models/User.php
+++ b/app/Models/User.php
@@ -14,6 +14,7 @@
use App\Libraries\Elasticsearch\Indexable;
use App\Libraries\Session\Store as SessionStore;
use App\Libraries\Transactions\AfterCommit;
+use App\Libraries\User\AvatarHelper;
use App\Libraries\User\DatadogLoginAttempt;
use App\Libraries\User\ProfileBeatmapset;
use App\Libraries\User\UsernamesForDbLookup;
@@ -212,7 +213,7 @@
*/
class User extends Model implements AfterCommit, AuthenticatableContract, HasLocalePreference, Indexable, Traits\ReportableInterface
{
- use Authenticatable, HasApiTokens, Memoizes, Traits\Es\UserSearch, Traits\Reportable, Traits\UserAvatar, Traits\UserScoreable, Traits\UserStore, Validatable;
+ use Authenticatable, HasApiTokens, Memoizes, Traits\Es\UserSearch, Traits\Reportable, Traits\UserScoreable, Traits\UserStore, Validatable;
const PLAYSTYLES = [
'mouse' => 1,
@@ -837,7 +838,7 @@ public function getAttribute($key)
'displayed_last_visit' => $this->getDisplayedLastVisit(),
'osu_playstyle' => $this->getOsuPlaystyle(),
'playmode' => $this->getPlaymode(),
- 'user_avatar' => $this->getUserAvatar(),
+ 'user_avatar' => AvatarHelper::url($this),
'user_colour' => $this->getUserColour(),
'user_rank' => $this->getUserRank(),
'user_website' => $this->getUserWebsite(),
diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php
index 52ec5525f68..8dc6a20424d 100644
--- a/app/Providers/AppServiceProvider.php
+++ b/app/Providers/AppServiceProvider.php
@@ -22,6 +22,7 @@
use App\Libraries\OsuMessageSelector;
use App\Libraries\RateLimiter;
use App\Libraries\RouteSection;
+use App\Libraries\Smilies;
use App\Libraries\User\ScorePins;
use Datadog;
use Illuminate\Database\Eloquent\Relations\Relation;
@@ -43,6 +44,7 @@ class AppServiceProvider extends ServiceProvider
'groups' => Groups::class,
'layout-cache' => LayoutCache::class,
'medals' => Medals::class,
+ 'smilies' => Smilies::class,
];
const SINGLETONS = [
diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php
index bcb8f9ea713..719bcb38c75 100644
--- a/app/Providers/AuthServiceProvider.php
+++ b/app/Providers/AuthServiceProvider.php
@@ -12,6 +12,7 @@
use Carbon\Carbon;
use Illuminate\Contracts\Auth\StatefulGuard;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
+use Laravel\Passport\Http\Controllers\AccessTokenController;
use Laravel\Passport\Http\Controllers\ApproveAuthorizationController;
use Laravel\Passport\Http\Controllers\DenyAuthorizationController;
use Laravel\Passport\Passport;
@@ -52,7 +53,7 @@ public function boot()
// RouteServiceProvider current runs before our provider, so Passport's default routes will override
// those set in routes/web.php.
Route::group(['prefix' => 'oauth', 'as' => 'oauth.'], function () {
- Route::post('token', '\Laravel\Passport\Http\Controllers\AccessTokenController@issueToken')->middleware('throttle')->name('passport.token');
+ Route::post('token', AccessTokenController::class.'@issueToken')->middleware('throttle')->name('passport.token');
Route::get('authorize', AuthorizationController::class.'@authorize')
->middleware(['web', 'verify-user'])
->name('authorizations.authorize');
diff --git a/app/helpers.php b/app/helpers.php
index 6e22360737c..dbfe75eacf5 100644
--- a/app/helpers.php
+++ b/app/helpers.php
@@ -3,6 +3,7 @@
// Copyright (c) ppy Pty Ltd . Licensed under the GNU Affero General Public License v3.0.
// See the LICENCE file in the repository root for full licence text.
+use App\Libraries\Base64Url;
use App\Libraries\LocaleMeta;
use App\Models\LoginAttempt;
use Egulias\EmailValidator\EmailValidator;
@@ -298,7 +299,7 @@ function current_locale_meta(): LocaleMeta
function cursor_decode($cursorString): ?array
{
if (is_string($cursorString) && present($cursorString)) {
- $cursor = json_decode(base64_decode(strtr($cursorString, '-_', '+/'), true), true);
+ $cursor = json_decode(Base64Url::decode($cursorString) ?? '', true);
if (is_array($cursor)) {
return $cursor;
@@ -310,13 +311,9 @@ function cursor_decode($cursorString): ?array
function cursor_encode(?array $cursor): ?string
{
- if ($cursor === null) {
- return null;
- }
-
- // url safe base64
- // reference: https://datatracker.ietf.org/doc/html/rfc4648#section-5
- return rtrim(strtr(base64_encode(json_encode($cursor)), '+/', '-_'), '=');
+ return $cursor === null
+ ? null
+ : Base64Url::encode(json_encode($cursor));
}
function cursor_for_response(?array $cursor): array
@@ -791,14 +788,14 @@ function forum_user_link(int $id, string $username, string|null $colour, int|nul
return "{$icon} {$link}";
}
-function is_api_request()
+function is_api_request(): bool
{
- return request()->is('api/*');
+ return str_starts_with(rawurldecode(Request::getPathInfo()), '/api/');
}
-function is_json_request()
+function is_json_request(): bool
{
- return is_api_request() || request()->expectsJson();
+ return is_api_request() || Request::expectsJson();
}
function is_valid_email_format(?string $email): bool
diff --git a/composer.json b/composer.json
index fdad702a921..a77c864bf5a 100644
--- a/composer.json
+++ b/composer.json
@@ -15,6 +15,8 @@
}
],
"require": {
+ "ext-ds": "*",
+ "ext-redis": "*",
"anhskohbo/no-captcha": "^3.2",
"chaseconey/laravel-datadog-helper": ">=1.2.0",
"egulias/email-validator": "*",
@@ -40,7 +42,6 @@
"maennchen/zipstream-php": "^2.1",
"mariuzzo/laravel-js-localization": "*",
"paypal/paypal-checkout-sdk": "*",
- "php-ds/php-ds": "^1.3",
"sentry/sentry-laravel": "*",
"symfony/yaml": "*",
"tightenco/ziggy": ">=0.8.1",
diff --git a/composer.lock b/composer.lock
index 91ddc6d9a0e..45ae87b2800 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "cc91b3026bfa983837950f0e96fb7c8a",
+ "content-hash": "c748b63fdc90f32f3850b615cb80eb80",
"packages": [
{
"name": "anhskohbo/no-captcha",
@@ -5807,61 +5807,6 @@
},
"time": "2021-09-14T21:35:26+00:00"
},
- {
- "name": "php-ds/php-ds",
- "version": "v1.4.1",
- "source": {
- "type": "git",
- "url": "https://github.com/php-ds/polyfill.git",
- "reference": "43d2df301a9e2017f67b8c11d94a5222f9c00fd1"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/php-ds/polyfill/zipball/43d2df301a9e2017f67b8c11d94a5222f9c00fd1",
- "reference": "43d2df301a9e2017f67b8c11d94a5222f9c00fd1",
- "shasum": ""
- },
- "require": {
- "ext-json": "*",
- "php": ">=7.0"
- },
- "provide": {
- "ext-ds": "1.3.0"
- },
- "require-dev": {
- "php-ds/tests": "^1.3"
- },
- "suggest": {
- "ext-ds": "to improve performance and reduce memory usage"
- },
- "type": "library",
- "autoload": {
- "psr-4": {
- "Ds\\": "src"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Rudi Theunissen",
- "email": "rudolf.theunissen@gmail.com"
- }
- ],
- "keywords": [
- "data structures",
- "ds",
- "php",
- "polyfill"
- ],
- "support": {
- "issues": "https://github.com/php-ds/polyfill/issues",
- "source": "https://github.com/php-ds/polyfill/tree/v1.4.1"
- },
- "time": "2022-03-09T20:39:30+00:00"
- },
{
"name": "php-http/cache-plugin",
"version": "1.8.0",
@@ -6434,16 +6379,16 @@
},
{
"name": "phpseclib/phpseclib",
- "version": "3.0.20",
+ "version": "3.0.34",
"source": {
"type": "git",
"url": "https://github.com/phpseclib/phpseclib.git",
- "reference": "543a1da81111a0bfd6ae7bbc2865c5e89ed3fc67"
+ "reference": "56c79f16a6ae17e42089c06a2144467acc35348a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/543a1da81111a0bfd6ae7bbc2865c5e89ed3fc67",
- "reference": "543a1da81111a0bfd6ae7bbc2865c5e89ed3fc67",
+ "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/56c79f16a6ae17e42089c06a2144467acc35348a",
+ "reference": "56c79f16a6ae17e42089c06a2144467acc35348a",
"shasum": ""
},
"require": {
@@ -6524,7 +6469,7 @@
],
"support": {
"issues": "https://github.com/phpseclib/phpseclib/issues",
- "source": "https://github.com/phpseclib/phpseclib/tree/3.0.20"
+ "source": "https://github.com/phpseclib/phpseclib/tree/3.0.34"
},
"funding": [
{
@@ -6540,7 +6485,7 @@
"type": "tidelift"
}
],
- "time": "2023-06-13T06:30:34+00:00"
+ "time": "2023-11-27T11:13:31+00:00"
},
{
"name": "psr/cache",
@@ -13882,7 +13827,10 @@
},
"prefer-stable": false,
"prefer-lowest": false,
- "platform": [],
+ "platform": {
+ "ext-ds": "*",
+ "ext-redis": "*"
+ },
"platform-dev": [],
"platform-overrides": {
"php": "8.2.0"
diff --git a/config/app.php b/config/app.php
index 1c483773537..d39297d1714 100644
--- a/config/app.php
+++ b/config/app.php
@@ -1,6 +1,7 @@
[
-
- /*
- * Laravel Framework Service Providers...
- */
- Illuminate\Auth\AuthServiceProvider::class,
- Illuminate\Broadcasting\BroadcastServiceProvider::class,
- Illuminate\Bus\BusServiceProvider::class,
- Illuminate\Cache\CacheServiceProvider::class,
- Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class,
- // Illuminate\Cookie\CookieServiceProvider::class,
- Illuminate\Database\DatabaseServiceProvider::class,
- Illuminate\Encryption\EncryptionServiceProvider::class,
- Illuminate\Filesystem\FilesystemServiceProvider::class,
- Illuminate\Foundation\Providers\FoundationServiceProvider::class,
- // Illuminate\Hashing\HashServiceProvider::class,
- Illuminate\Mail\MailServiceProvider::class,
- Illuminate\Notifications\NotificationServiceProvider::class,
- Illuminate\Pagination\PaginationServiceProvider::class,
- Illuminate\Pipeline\PipelineServiceProvider::class,
- Illuminate\Queue\QueueServiceProvider::class,
- Illuminate\Redis\RedisServiceProvider::class,
- Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
- // We're using our own SessionServiceProvider so we can override the session id naming (for redis key namespacing)
- App\Providers\SessionServiceProvider::class,
- Illuminate\Translation\TranslationServiceProvider::class,
- Illuminate\Validation\ValidationServiceProvider::class,
- Illuminate\View\ViewServiceProvider::class,
-
- /*
- * Package Service Providers...
- */
- GrahamCampbell\GitHub\GitHubServiceProvider::class,
- Mariuzzo\LaravelJsLocalization\LaravelJsLocalizationServiceProvider::class,
- Laravel\Tinker\TinkerServiceProvider::class,
-
- /*
- * Application Service Providers...
- */
+ 'providers' => ServiceProvider::defaultProviders()->except([
+ Illuminate\Cookie\CookieServiceProvider::class,
+ Illuminate\Hashing\HashServiceProvider::class,
+ Illuminate\Session\SessionServiceProvider::class,
+ ])->merge([
App\Providers\AppServiceProvider::class,
+ App\Providers\AuthServiceProvider::class,
App\Providers\EventServiceProvider::class,
+ // Override default migrate:fresh
+ App\Providers\MigrationServiceProvider::class,
App\Providers\RouteServiceProvider::class,
-
- /*
- * After DB transaction commit support
- */
+ // Override the session id naming (for redis key namespacing)
+ App\Providers\SessionServiceProvider::class,
+ // After DB transaction commit support
App\Providers\TransactionStateServiceProvider::class,
- /*
- * OAuth2 Setup
- */
-
- App\Providers\AuthServiceProvider::class,
- Laravel\Passport\PassportServiceProvider::class,
-
- /* Datadog Metrics */
- ChaseConey\LaravelDatadogHelper\LaravelDatadogHelperServiceProvider::class,
-
- /* Override default migrate:fresh */
- App\Providers\MigrationServiceProvider::class,
- ],
+ Mariuzzo\LaravelJsLocalization\LaravelJsLocalizationServiceProvider::class,
+ ])->toArray(),
/*
|--------------------------------------------------------------------------
diff --git a/database/migrations/2023_11_22_055714_change_multiplayer_scores_high_attempts_to_int.php b/database/migrations/2023_11_22_055714_change_multiplayer_scores_high_attempts_to_int.php
new file mode 100644
index 00000000000..fc174c84ae7
--- /dev/null
+++ b/database/migrations/2023_11_22_055714_change_multiplayer_scores_high_attempts_to_int.php
@@ -0,0 +1,27 @@
+. Licensed under the GNU Affero General Public License v3.0.
+// See the LICENCE file in the repository root for full licence text.
+
+declare(strict_types=1);
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+ public function up(): void
+ {
+ Schema::table('multiplayer_scores_high', function (Blueprint $table) {
+ $table->unsignedInteger('attempts')->default(0)->change();
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::table('multiplayer_scores_high', function (Blueprint $table) {
+ $table->unsignedTinyInteger('attempts')->default(0)->change();
+ });
+ }
+};
diff --git a/phpunit.dusk.xml b/phpunit.dusk.xml
index 0fe4666ae8f..ef830df7a43 100644
--- a/phpunit.dusk.xml
+++ b/phpunit.dusk.xml
@@ -1,24 +1,22 @@
-
+
-
+
./tests/Browser
-
-
- ./app
-
-
diff --git a/resources/css/bem/chat-conversation-panel.less b/resources/css/bem/chat-conversation-panel.less
index de9dabc370e..27d373d547f 100644
--- a/resources/css/bem/chat-conversation-panel.less
+++ b/resources/css/bem/chat-conversation-panel.less
@@ -5,9 +5,15 @@
display: flex;
flex-direction: column;
flex: 1;
- overflow-y: auto; // without this, firefox scroll breaks
position: relative;
+ @media @mobile {
+ overflow-y: hidden; // Needed to limit the height of the panel so chat-conversation is scrollable and
+ // chat-conversation-list doesn't become 0 height on mobile.
+ // height: 100% alone doesn't work.
+ // The panel itself should not be scrollable.
+ }
+
&__instructions {
margin-top: 10px;
}
diff --git a/resources/js/components/textarea-autosize.tsx b/resources/js/components/textarea-autosize.tsx
index b47d3da401a..10deca89f37 100644
--- a/resources/js/components/textarea-autosize.tsx
+++ b/resources/js/components/textarea-autosize.tsx
@@ -3,9 +3,9 @@
import autosize from 'autosize';
import React from 'react';
+import { present } from 'utils/string';
interface Props extends React.TextareaHTMLAttributes {
- async: boolean;
innerRef?: React.RefObject;
maxRows?: number;
}
@@ -14,13 +14,14 @@ interface State {
lineHeight?: number;
}
-export default class TextareaAutosize extends React.Component {
+export default class TextareaAutosize extends React.PureComponent {
static readonly defaultProps = {
async: false,
rows: 1,
};
private readonly ref = this.props.innerRef ?? React.createRef();
+ private shouldUpdate = true;
private get maxHeight() {
return this.props.maxRows != null && this.state.lineHeight != null
@@ -36,7 +37,7 @@ export default class TextareaAutosize extends React.Component {
componentDidMount() {
if (this.ref.current == null) return;
- if (this.props.maxRows != null || this.props.async) {
+ if (this.props.maxRows != null || present(this.props.value?.toString())) {
window.setTimeout(() => {
if (this.ref.current != null) {
if (this.props.maxRows != null) {
@@ -62,7 +63,12 @@ export default class TextareaAutosize extends React.Component {
this.ref.current.style.overflowX = 'hidden';
}
- autosize.update(this.ref.current);
+ // Avoid double updating since autosize automatically triggers update on input.
+ if (this.shouldUpdate) {
+ autosize.update(this.ref.current);
+ } else {
+ this.shouldUpdate = true;
+ }
}
componentWillUnmount() {
@@ -71,16 +77,22 @@ export default class TextareaAutosize extends React.Component {
}
render() {
- const { async, innerRef, maxRows, style, ...otherProps } = this.props;
+ const { innerRef, onInput, maxRows, style, ...otherProps } = this.props;
const maxHeight = this.maxHeight;
return (
);
}
+
+ private readonly handleInput = (event: React.SyntheticEvent) => {
+ this.shouldUpdate = false;
+ this.props.onInput?.(event);
+ };
}
diff --git a/resources/js/oauth/client-details.tsx b/resources/js/oauth/client-details.tsx
index 204e866ee49..f358d8b17a3 100644
--- a/resources/js/oauth/client-details.tsx
+++ b/resources/js/oauth/client-details.tsx
@@ -82,7 +82,6 @@ export class ClientDetails extends React.Component {
{trans('oauth.client.redirect')}
refreshApplication();
-
- $path = app()->path('Jobs/Notifications');
- $files = Finder::create()->files()->in($path)->sortByName();
+ $files = Finder::create()->files()->in(__DIR__.'/../app/Jobs/Notifications')->sortByName();
foreach ($files as $file) {
$baseName = $file->getBasename(".{$file->getExtension()}");
$classes[] = ["\\App\\Jobs\\Notifications\\{$baseName}"];
@@ -116,7 +113,7 @@ public function notificationJobClassesDataProvider()
return $classes;
}
- public function notificationNamesDataProvider()
+ public static function notificationNamesDataProvider()
{
// TODO: move notification names to different class instead of filtering
$constants = collect((new ReflectionClass(Notification::class))->getReflectionConstants())
@@ -129,7 +126,7 @@ public function notificationNamesDataProvider()
return $constants->map(fn (ReflectionClassConstant $constant) => [$constant->getValue()])->all();
}
- public function userNotificationDetailsDataProvider()
+ public static function userNotificationDetailsDataProvider()
{
return [
[null], // for testing defaults.
diff --git a/tests/Browser/SanityTest.php b/tests/Browser/SanityTest.php
index 1cf3bf25431..6b3bfc836b4 100644
--- a/tests/Browser/SanityTest.php
+++ b/tests/Browser/SanityTest.php
@@ -75,7 +75,7 @@ private static function cleanup()
return;
}
- (new static())->createApplication();
+ static::createApp();
// Tear down in reverse-order so that dependants get destroyed before their dependencies.
$nukingOrder = array_reverse(self::$scaffolding);
@@ -118,7 +118,7 @@ private static function createScaffolding()
return;
}
- (new static())->createApplication();
+ static::createApp();
self::$scaffolding['country'] = Country::first() ?? Country::factory()->create();
// user to login as and to use for requests
self::$scaffolding['user'] = User::factory()->create([
@@ -308,7 +308,7 @@ private static function output($text)
}
}
- public function routesDataProvider()
+ public static function routesDataProvider()
{
static $bypass = [
'__clockwork',
@@ -321,7 +321,7 @@ public function routesDataProvider()
];
static $types = ['user', 'guest'];
- $this->refreshApplication();
+ static::createApp();
$data = [];
foreach (app()->routes->get('GET') as $uri => $route) {
diff --git a/tests/Commands/EsIndexScoresQueueTest.php b/tests/Commands/EsIndexScoresQueueTest.php
index d3cb2b9bd50..e2b288f5c6c 100644
--- a/tests/Commands/EsIndexScoresQueueTest.php
+++ b/tests/Commands/EsIndexScoresQueueTest.php
@@ -63,7 +63,7 @@ public function testQueueScores(callable $setUp, array|callable $params, int $ch
);
}
- public function dataProviderForTestParameterValidity(): array
+ public static function dataProviderForTestParameterValidity(): array
{
return [
[[], false],
@@ -83,7 +83,7 @@ public function dataProviderForTestParameterValidity(): array
];
}
- public function dataProviderForTestQueueScores(): array
+ public static function dataProviderForTestQueueScores(): array
{
$userId = 0;
$setUp = function () use ($userId) {
diff --git a/tests/Commands/ModdingRankCommandTest.php b/tests/Commands/ModdingRankCommandTest.php
index 7866b229b01..2aa7ad92f74 100644
--- a/tests/Commands/ModdingRankCommandTest.php
+++ b/tests/Commands/ModdingRankCommandTest.php
@@ -104,7 +104,7 @@ public function testRankQuotaSeparateRuleset(): void
}
- public function rankDataProvider()
+ public static function rankDataProvider()
{
// 1 day ago isn't used because it might or might not be equal to the cutoff depending on how fast it runs.
return [
@@ -113,7 +113,7 @@ public function rankDataProvider()
];
}
- public function rankHybridDataProvider()
+ public static function rankHybridDataProvider()
{
return [
// hybrid counts as ruleset with lowest enum value
diff --git a/tests/Controllers/AccountControllerTest.php b/tests/Controllers/AccountControllerTest.php
index a9fb9fb177f..3d54f1fe4d8 100644
--- a/tests/Controllers/AccountControllerTest.php
+++ b/tests/Controllers/AccountControllerTest.php
@@ -231,7 +231,7 @@ public function testUpdatePasswordWeakPassword()
->assertStatus(422);
}
- public function dataProviderForUpdateCountry(): array
+ public static function dataProviderForUpdateCountry(): array
{
return [
['_A', '_A', true],
diff --git a/tests/Controllers/BeatmapDiscussionsControllerTest.php b/tests/Controllers/BeatmapDiscussionsControllerTest.php
index 4a0b855a728..13bf667b1dc 100644
--- a/tests/Controllers/BeatmapDiscussionsControllerTest.php
+++ b/tests/Controllers/BeatmapDiscussionsControllerTest.php
@@ -198,7 +198,7 @@ public function testPostReviewDocumentValidWithIssues()
$this->assertSame($discussionPostCount + 3, BeatmapDiscussionPost::count());
}
- public function putVoteDataProvider()
+ public static function putVoteDataProvider()
{
return [
['graveyard', 403, 0],
@@ -211,7 +211,7 @@ public function putVoteDataProvider()
];
}
- public function putVoteAgainDataProvider()
+ public static function putVoteAgainDataProvider()
{
return [
'voting again has no effect' => ['1', 0],
@@ -219,7 +219,7 @@ public function putVoteAgainDataProvider()
];
}
- public function putVoteChangeToDownDataProvider()
+ public static function putVoteChangeToDownDataProvider()
{
return [
'bng can change to down vote' => ['bng', 200, -2],
@@ -227,7 +227,7 @@ public function putVoteChangeToDownDataProvider()
];
}
- public function putVoteDownDataProvider()
+ public static function putVoteDownDataProvider()
{
return [
'bng can down vote' => ['bng', 200, 1, -1],
diff --git a/tests/Controllers/BeatmapsControllerSoloScoresTest.php b/tests/Controllers/BeatmapsControllerSoloScoresTest.php
index 18354dee94e..eedbaf7c9f8 100644
--- a/tests/Controllers/BeatmapsControllerSoloScoresTest.php
+++ b/tests/Controllers/BeatmapsControllerSoloScoresTest.php
@@ -202,7 +202,7 @@ public function testQuery(array $scoreKeys, array $params)
}
}
- public function dataProviderForTestQuery(): array
+ public static function dataProviderForTestQuery(): array
{
return [
'no parameters' => [[
diff --git a/tests/Controllers/BeatmapsControllerTest.php b/tests/Controllers/BeatmapsControllerTest.php
index c15694a30c7..629272e79fd 100644
--- a/tests/Controllers/BeatmapsControllerTest.php
+++ b/tests/Controllers/BeatmapsControllerTest.php
@@ -594,7 +594,7 @@ public function testUpdateOwnerSameOwner(): void
$this->assertSame($beatmapsetEventCount, BeatmapsetEvent::count());
}
- public function dataProviderForTestLookupForApi(): array
+ public static function dataProviderForTestLookupForApi(): array
{
return [
'checksum' => ['checksum', fn (Beatmap $b) => $b->checksum],
@@ -603,7 +603,7 @@ public function dataProviderForTestLookupForApi(): array
];
}
- public function dataProviderForTestUpdateOwnerLoved(): array
+ public static function dataProviderForTestUpdateOwnerLoved(): array
{
return [
[Beatmapset::STATES['graveyard'], true],
diff --git a/tests/Controllers/BeatmapsetsControllerTest.php b/tests/Controllers/BeatmapsetsControllerTest.php
index b26b32d4b99..54f8c79d629 100644
--- a/tests/Controllers/BeatmapsetsControllerTest.php
+++ b/tests/Controllers/BeatmapsetsControllerTest.php
@@ -330,14 +330,14 @@ public function testBeatmapsetUpdateTags(string $userGroupOrOwner, bool $ok): vo
$this->assertSame($expectedTags, $beatmapset->fresh()->tags);
}
- public function beatmapsetStatesDataProvider()
+ public static function beatmapsetStatesDataProvider()
{
return array_map(function ($state) {
return [$state];
}, array_keys(Beatmapset::STATES));
}
- public function dataProviderForTestBeatmapsetUpdateOffset(): array
+ public static function dataProviderForTestBeatmapsetUpdateOffset(): array
{
return [
['admin', true],
@@ -349,7 +349,7 @@ public function dataProviderForTestBeatmapsetUpdateOffset(): array
];
}
- public function dataProviderForTestBeatmapsetUpdateTags(): array
+ public static function dataProviderForTestBeatmapsetUpdateTags(): array
{
return [
['admin', true],
@@ -361,7 +361,7 @@ public function dataProviderForTestBeatmapsetUpdateTags(): array
];
}
- public function dataProviderForTestBeatmapsetUpdateDescriptionAsOwner(): array
+ public static function dataProviderForTestBeatmapsetUpdateDescriptionAsOwner(): array
{
return [
[false, null, true],
diff --git a/tests/Controllers/Chat/ChannelsControllerTest.php b/tests/Controllers/Chat/ChannelsControllerTest.php
index dbd7a5b4813..dac9bbc3b51 100644
--- a/tests/Controllers/Chat/ChannelsControllerTest.php
+++ b/tests/Controllers/Chat/ChannelsControllerTest.php
@@ -378,7 +378,7 @@ public function testChannelLeavePublicWhenGuest() // fail
//endregion
- public function dataProvider()
+ public static function dataProvider()
{
return [
['private', false],
diff --git a/tests/Controllers/Chat/ChatControllerTest.php b/tests/Controllers/Chat/ChatControllerTest.php
index 68a55a212cf..2500ff03332 100644
--- a/tests/Controllers/Chat/ChatControllerTest.php
+++ b/tests/Controllers/Chat/ChatControllerTest.php
@@ -288,7 +288,7 @@ public function testChatUpdatesJoinChannel()
//endregion
- public function createPmWithAuthorizedGrantDataProvider()
+ public static function createPmWithAuthorizedGrantDataProvider()
{
return [
[['*'], 200],
@@ -297,7 +297,7 @@ public function createPmWithAuthorizedGrantDataProvider()
];
}
- public function createPmWithClientCredentialsDataProvider()
+ public static function createPmWithClientCredentialsDataProvider()
{
return [
// TODO: need to add test that validates auth guard calls Token::validate
@@ -305,7 +305,7 @@ public function createPmWithClientCredentialsDataProvider()
];
}
- public function createPmWithClientCredentialsBotGroupDataProvider()
+ public static function createPmWithClientCredentialsBotGroupDataProvider()
{
return [
[['chat.write', 'delegate'], 200],
diff --git a/tests/Controllers/CommentsControllerTest.php b/tests/Controllers/CommentsControllerTest.php
index 5b2247cab0b..e7a9e9bd281 100644
--- a/tests/Controllers/CommentsControllerTest.php
+++ b/tests/Controllers/CommentsControllerTest.php
@@ -250,7 +250,7 @@ public function testApiRequiresAuthentication($method, $routeName)
->assertUnauthorized();
}
- public function apiRequiresAuthenticationDataProvider()
+ public static function apiRequiresAuthenticationDataProvider()
{
return [
['DELETE', 'comments.vote'],
@@ -270,7 +270,7 @@ public function apiRequiresAuthenticationDataProvider()
* - Whether the commentable already has a pinned comment
* - Whether pinning should be allowed
*/
- public function pinPermissionsDataProvider(): array
+ public static function pinPermissionsDataProvider(): array
{
return [
['admin', true, true, true, true, true],
diff --git a/tests/Controllers/InterOp/UserGroupsControllerTest.php b/tests/Controllers/InterOp/UserGroupsControllerTest.php
index 8bf9537dfcb..c7084fe3c54 100644
--- a/tests/Controllers/InterOp/UserGroupsControllerTest.php
+++ b/tests/Controllers/InterOp/UserGroupsControllerTest.php
@@ -370,7 +370,7 @@ public function testWithInvalidActor(string $type, string $method, string $route
->assertStatus(404);
}
- public function userGroupRoutesDataProvider(): array
+ public static function userGroupRoutesDataProvider(): array
{
return [
'add' =>
diff --git a/tests/Controllers/LegacyApiKeyControllerTest.php b/tests/Controllers/LegacyApiKeyControllerTest.php
index 562ede9b84a..268541b7e16 100644
--- a/tests/Controllers/LegacyApiKeyControllerTest.php
+++ b/tests/Controllers/LegacyApiKeyControllerTest.php
@@ -98,7 +98,7 @@ public function testStoreGuest(): void
$this->post(route('legacy-api-key.store'))->assertStatus(401);
}
- public function dataProviderForStoreWithInvalidParams(): array
+ public static function dataProviderForStoreWithInvalidParams(): array
{
return [
[[
diff --git a/tests/Controllers/Multiplayer/Rooms/Playlist/ScoresControllerTest.php b/tests/Controllers/Multiplayer/Rooms/Playlist/ScoresControllerTest.php
index a6bb4cf1273..418bba27501 100644
--- a/tests/Controllers/Multiplayer/Rooms/Playlist/ScoresControllerTest.php
+++ b/tests/Controllers/Multiplayer/Rooms/Playlist/ScoresControllerTest.php
@@ -145,7 +145,7 @@ public function testUpdate($bodyParams, $status)
$this->json('PUT', $url, $bodyParams)->assertStatus($status);
}
- public function dataProviderForTestStore()
+ public static function dataProviderForTestStore()
{
return [
'ok' => [true, true, 200],
@@ -155,7 +155,7 @@ public function dataProviderForTestStore()
];
}
- public function dataProviderForTestUpdate()
+ public static function dataProviderForTestUpdate()
{
static $validBodyParams = [
'accuracy' => 1,
diff --git a/tests/Controllers/Multiplayer/RoomsControllerTest.php b/tests/Controllers/Multiplayer/RoomsControllerTest.php
index b952db44d75..c96d66a55b9 100644
--- a/tests/Controllers/Multiplayer/RoomsControllerTest.php
+++ b/tests/Controllers/Multiplayer/RoomsControllerTest.php
@@ -15,6 +15,7 @@
use App\Models\Multiplayer\UserScoreAggregate;
use App\Models\OAuth\Token;
use App\Models\User;
+use Illuminate\Support\Arr;
use Tests\TestCase;
class RoomsControllerTest extends TestCase
@@ -428,10 +429,10 @@ public function testJoinWithPassword()
$this->assertSame($initialUserChannelCount + 1, UserChannel::count());
}
- public function dataProviderForTestStoreWithInvalidPlayableMods(): array
+ public static function dataProviderForTestStoreWithInvalidPlayableMods(): array
{
$ret = [];
- foreach ([array_rand_val(Room::REALTIME_TYPES), Room::PLAYLIST_TYPE] as $type) {
+ foreach ([Arr::random(Room::REALTIME_TYPES), Room::PLAYLIST_TYPE] as $type) {
foreach (['allowed', 'required'] as $modType) {
$ret[] = [$type, $modType];
}
@@ -440,18 +441,18 @@ public function dataProviderForTestStoreWithInvalidPlayableMods(): array
return $ret;
}
- public function dataProviderForTestStoreWithInvalidRealtimeAllowedMods(): array
+ public static function dataProviderForTestStoreWithInvalidRealtimeAllowedMods(): array
{
return [
- [array_rand_val(Room::REALTIME_TYPES), false],
+ [Arr::random(Room::REALTIME_TYPES), false],
[Room::PLAYLIST_TYPE, true],
];
}
- public function dataProviderForTestStoreWithInvalidRealtimeMods(): array
+ public static function dataProviderForTestStoreWithInvalidRealtimeMods(): array
{
return [
- [array_rand_val(Room::REALTIME_TYPES), false],
+ [Arr::random(Room::REALTIME_TYPES), false],
[Room::PLAYLIST_TYPE, true],
];
}
diff --git a/tests/Controllers/OAuth/ClientsControllerTest.php b/tests/Controllers/OAuth/ClientsControllerTest.php
index 851d489fcd5..5eb3c2ddef3 100644
--- a/tests/Controllers/OAuth/ClientsControllerTest.php
+++ b/tests/Controllers/OAuth/ClientsControllerTest.php
@@ -122,7 +122,7 @@ public function testOnlyRedirectIsUpdated()
$this->assertSame('https://nowhere.local', $this->client->redirect);
}
- public function emptyStringsTestDataProvider()
+ public static function emptyStringsTestDataProvider()
{
return [
[null, 'https://nowhere.local'],
diff --git a/tests/Controllers/ScoreTokensControllerTest.php b/tests/Controllers/ScoreTokensControllerTest.php
index 70c4579c4a8..93910a7c6ca 100644
--- a/tests/Controllers/ScoreTokensControllerTest.php
+++ b/tests/Controllers/ScoreTokensControllerTest.php
@@ -90,7 +90,7 @@ public function testStoreInvalidParameter(string $paramKey, ?string $paramValue,
]);
}
- public function dataProviderForTestStore(): array
+ public static function dataProviderForTestStore(): array
{
return [
['deleted', 404],
@@ -101,7 +101,7 @@ public function dataProviderForTestStore(): array
];
}
- public function dataProviderForTestStoreInvalidParameter(): array
+ public static function dataProviderForTestStoreInvalidParameter(): array
{
return [
'invalid build hash' => ['version_hash', md5('invalid_'), 422],
diff --git a/tests/Controllers/Solo/ScoresControllerTest.php b/tests/Controllers/Solo/ScoresControllerTest.php
index 41e65bddffe..a5dc22bebd1 100644
--- a/tests/Controllers/Solo/ScoresControllerTest.php
+++ b/tests/Controllers/Solo/ScoresControllerTest.php
@@ -129,7 +129,7 @@ public function tearDown(): void
{
parent::tearDown();
- $this->refreshApplication();
+ static::createApp();
LaravelRedis::del(Score::PROCESSING_QUEUE);
}
diff --git a/tests/Controllers/UsersControllerTest.php b/tests/Controllers/UsersControllerTest.php
index 0566992e82b..b436451cce7 100644
--- a/tests/Controllers/UsersControllerTest.php
+++ b/tests/Controllers/UsersControllerTest.php
@@ -332,7 +332,7 @@ public function testUsernameAtPrefixedRedirectToIdForApi()
->assertSuccessful();
}
- public function dataProviderForStoreWebInvalidParams(): array
+ public static function dataProviderForStoreWebInvalidParams(): array
{
return [
['user1', 'user@email.com', 'user@email.com', 'short', 'short'],
diff --git a/tests/CreatesApplication.php b/tests/CreatesApplication.php
index 40bb7fc1a68..80a945924b9 100644
--- a/tests/CreatesApplication.php
+++ b/tests/CreatesApplication.php
@@ -9,6 +9,14 @@
trait CreatesApplication
{
+ public static function createApp()
+ {
+ $app = require __DIR__.'/../bootstrap/app.php';
+ $app->make(Kernel::class)->bootstrap();
+
+ return $app;
+ }
+
/**
* Creates the application.
*
@@ -16,9 +24,6 @@ trait CreatesApplication
*/
public function createApplication()
{
- $app = require __DIR__.'/../bootstrap/app.php';
- $app->make(Kernel::class)->bootstrap();
-
- return $app;
+ return static::createApp();
}
}
diff --git a/tests/HelpersTest.php b/tests/HelpersTest.php
index c25fbce22d3..5946c848a6f 100644
--- a/tests/HelpersTest.php
+++ b/tests/HelpersTest.php
@@ -56,7 +56,7 @@ public function testIsSqlUniqueException(): void
$this->assertTrue(is_sql_unique_exception($exception));
}
- public function dataForClassWithModifiers(): array
+ public static function dataForClassWithModifiers(): array
{
return [
'no modifiers' =>
@@ -84,7 +84,7 @@ public function dataForClassWithModifiers(): array
];
}
- public function dataForGetStringSplit(): array
+ public static function dataForGetStringSplit(): array
{
return [
["hello\nworld\n!", ['hello', 'world', '!']],
diff --git a/tests/Jobs/RemoveBeatmapsetSoloScoresTest.php b/tests/Jobs/RemoveBeatmapsetSoloScoresTest.php
index 7fff28a472f..e378f83e857 100644
--- a/tests/Jobs/RemoveBeatmapsetSoloScoresTest.php
+++ b/tests/Jobs/RemoveBeatmapsetSoloScoresTest.php
@@ -64,24 +64,19 @@ public function testHandle()
$search->indexWait();
$this->beforeApplicationDestroyed(function () use ($search) {
- $this->refreshApplication();
- $db = app('db');
- Beatmap::truncate();
- Beatmapset::truncate();
- Country::truncate();
- Genre::truncate();
- Language::truncate();
- Score::select()->delete(); // TODO: revert to truncate after the table is actually renamed
- ScorePerformance::select()->delete(); // TODO: revert to truncate after the table is actually renamed
- User::truncate();
- UserGroup::truncate();
- UserGroupEvent::truncate();
- $search->deleteAll();
- foreach (config('database.connections') as $name => $_dbConfig) {
- $conn = $db->connection($name);
- $conn->rollBack();
- $conn->disconnect();
- }
+ static::withDbAccess(function () use ($search) {
+ Beatmap::truncate();
+ Beatmapset::truncate();
+ Country::truncate();
+ Genre::truncate();
+ Language::truncate();
+ Score::select()->delete(); // TODO: revert to truncate after the table is actually renamed
+ ScorePerformance::select()->delete(); // TODO: revert to truncate after the table is actually renamed
+ User::truncate();
+ UserGroup::truncate();
+ UserGroupEvent::truncate();
+ $search->deleteAll();
+ });
});
}
diff --git a/tests/Libraries/BBCodeForDBTest.php b/tests/Libraries/BBCodeForDBTest.php
index cbe3074af1c..7921906aabf 100644
--- a/tests/Libraries/BBCodeForDBTest.php
+++ b/tests/Libraries/BBCodeForDBTest.php
@@ -72,9 +72,9 @@ public function testProfileInvalidUser()
$this->assertSame($expectedOutput, $output);
}
- public function examples()
+ public static function examples()
{
- return $this->fileList(__DIR__.'/bbcode_examples', '.base.txt');
+ return static::fileList(__DIR__.'/bbcode_examples', '.base.txt');
}
protected function setUp(): void
diff --git a/tests/Libraries/BBCodeFromDBTest.php b/tests/Libraries/BBCodeFromDBTest.php
index 9ce97543465..f7e91809879 100644
--- a/tests/Libraries/BBCodeFromDBTest.php
+++ b/tests/Libraries/BBCodeFromDBTest.php
@@ -40,14 +40,14 @@ public function testRemoveBlockQuotes($name, $path)
$this->assertStringEqualsFile($expectedFilePath, $text);
}
- public function examples()
+ public static function examples()
{
- return $this->fileList(__DIR__.'/bbcode_examples', '.db.txt');
+ return static::fileList(__DIR__.'/bbcode_examples', '.db.txt');
}
- public function removeQuoteExamples()
+ public static function removeQuoteExamples()
{
- return $this->fileList(__DIR__.'/bbcode_examples/remove_quotes', '.db.txt');
+ return static::fileList(__DIR__.'/bbcode_examples/remove_quotes', '.db.txt');
}
protected function setUp(): void
diff --git a/tests/Libraries/BeatmapsetDiscussion/DiscussionTest.php b/tests/Libraries/BeatmapsetDiscussion/DiscussionTest.php
index 52883b4899d..c1b8105ae87 100644
--- a/tests/Libraries/BeatmapsetDiscussion/DiscussionTest.php
+++ b/tests/Libraries/BeatmapsetDiscussion/DiscussionTest.php
@@ -35,11 +35,11 @@ class DiscussionTest extends TestCase
/**
* @dataProvider minPlaysVerificationDataProvider
*/
- public function testMinPlaysVerification(?int $minPlays, bool $verified, bool $success)
+ public function testMinPlaysVerification(\Closure $minPlays, bool $verified, bool $success)
{
config()->set('osu.user.post_action_verification', false);
- $user = User::factory()->withPlays($minPlays)->create();
+ $user = User::factory()->withPlays($minPlays())->create();
$beatmapset = $this->beatmapsetFactory()->create();
$beatmapset->watches()->create(['user_id' => User::factory()->create()->getKey()]);
@@ -284,17 +284,17 @@ public function testProblemOnQualifiedBeatmapsetModesNotification(string $mode,
//endregion
- public function minPlaysVerificationDataProvider()
+ public static function minPlaysVerificationDataProvider()
{
return [
- [config('osu.user.min_plays_for_posting') - 1, false, false],
- [config('osu.user.min_plays_for_posting') - 1, true, true],
- [null, false, true],
- [null, true, true],
+ [fn () => config('osu.user.min_plays_for_posting') - 1, false, false],
+ [fn () => config('osu.user.min_plays_for_posting') - 1, true, true],
+ [fn () => null, false, true],
+ [fn () => null, true, true],
];
}
- public function problemOnQualifiedBeatmapsetDataProvider()
+ public static function problemOnQualifiedBeatmapsetDataProvider()
{
return [
['pending', 'Event::assertNotDispatched'],
@@ -302,7 +302,7 @@ public function problemOnQualifiedBeatmapsetDataProvider()
];
}
- public function problemOnQualifiedBeatmapsetModesNotificationDataProvider()
+ public static function problemOnQualifiedBeatmapsetModesNotificationDataProvider()
{
return [
'with matching notification mode' => ['osu', ['osu'], true],
@@ -310,7 +310,7 @@ public function problemOnQualifiedBeatmapsetModesNotificationDataProvider()
];
}
- public function newDiscussionQueuesJobsDataProvider()
+ public static function newDiscussionQueuesJobsDataProvider()
{
return [
[
@@ -352,7 +352,7 @@ public function newDiscussionQueuesJobsDataProvider()
];
}
- public function newMapperNoteByOtherUsersDataProvider()
+ public static function newMapperNoteByOtherUsersDataProvider()
{
return [
['bng', true],
@@ -363,7 +363,7 @@ public function newMapperNoteByOtherUsersDataProvider()
];
}
- public function shouldDisqualifyOrResetNominationsDataProvider()
+ public static function shouldDisqualifyOrResetNominationsDataProvider()
{
return [
['pending', 'bng', 'problem', true],
@@ -383,7 +383,7 @@ public function shouldDisqualifyOrResetNominationsDataProvider()
];
}
- public function userGroupsDataProvider()
+ public static function userGroupsDataProvider()
{
return [
['admin'],
diff --git a/tests/Libraries/BeatmapsetDiscussion/ReplyTest.php b/tests/Libraries/BeatmapsetDiscussion/ReplyTest.php
index 862cc822621..be8bfa66f42 100644
--- a/tests/Libraries/BeatmapsetDiscussion/ReplyTest.php
+++ b/tests/Libraries/BeatmapsetDiscussion/ReplyTest.php
@@ -341,7 +341,7 @@ public function testRequestingSameResolveStateDoesNotChangeResovled()
$this->assertFalse($discussion->fresh()->resolved);
}
- public function reopeningProblemDoesNotDisqualifyOrResetNominationsDataProvider()
+ public static function reopeningProblemDoesNotDisqualifyOrResetNominationsDataProvider()
{
return [
['bng', 'pending'],
@@ -353,7 +353,7 @@ public function reopeningProblemDoesNotDisqualifyOrResetNominationsDataProvider(
];
}
- public function replyQueuesNotificationDataProviderToStarter()
+ public static function replyQueuesNotificationDataProviderToStarter()
{
return [
['praise', false],
@@ -362,7 +362,7 @@ public function replyQueuesNotificationDataProviderToStarter()
];
}
- public function resolveDiscussionByStarterDataProvider()
+ public static function resolveDiscussionByStarterDataProvider()
{
return [
['praise', false],
@@ -371,7 +371,7 @@ public function resolveDiscussionByStarterDataProvider()
];
}
- public function resolveDiscussionByMapperDataProvider()
+ public static function resolveDiscussionByMapperDataProvider()
{
return [
['pending', true],
@@ -379,7 +379,7 @@ public function resolveDiscussionByMapperDataProvider()
];
}
- public function resolveDiscussionByOtherUsersDataProvider()
+ public static function resolveDiscussionByOtherUsersDataProvider()
{
return [
['bng', false],
@@ -390,7 +390,7 @@ public function resolveDiscussionByOtherUsersDataProvider()
];
}
- public function userGroupsDataProvider()
+ public static function userGroupsDataProvider()
{
return [
['admin'],
diff --git a/tests/Libraries/BeatmapsetDiscussion/ReviewTest.php b/tests/Libraries/BeatmapsetDiscussion/ReviewTest.php
index aadcff4efd9..611383ba6b1 100644
--- a/tests/Libraries/BeatmapsetDiscussion/ReviewTest.php
+++ b/tests/Libraries/BeatmapsetDiscussion/ReviewTest.php
@@ -717,7 +717,7 @@ public function testUpdateDocumentRemoveIssue()
//endregion
- public function dataProviderForQualifiedProblem()
+ public static function dataProviderForQualifiedProblem()
{
return [
['qualified', true],
diff --git a/tests/Libraries/ChatTest.php b/tests/Libraries/ChatTest.php
index 573fd5807e0..3446e73d0fe 100644
--- a/tests/Libraries/ChatTest.php
+++ b/tests/Libraries/ChatTest.php
@@ -265,7 +265,7 @@ public function testSendPMSecondTime()
Chat::sendPrivateMessage($sender, $target, 'test message again', false);
}
- public function createAnnouncementApiDataProvider()
+ public static function createAnnouncementApiDataProvider()
{
return [
[null, false, false],
@@ -285,7 +285,7 @@ public function createAnnouncementApiDataProvider()
];
}
- public function minPlaysDataProvider()
+ public static function minPlaysDataProvider()
{
return [
'bot group with minplays' => ['bot', true, true],
@@ -295,7 +295,7 @@ public function minPlaysDataProvider()
];
}
- public function sendPmFriendsOnlyGroupsDataProvider()
+ public static function sendPmFriendsOnlyGroupsDataProvider()
{
return [
['admin', true],
@@ -307,7 +307,7 @@ public function sendPmFriendsOnlyGroupsDataProvider()
];
}
- public function sendPmSenderFriendsOnlyGroupsDataProvider()
+ public static function sendPmSenderFriendsOnlyGroupsDataProvider()
{
return [
// admin skip because OsuAuthorize skips the check when admin.
@@ -319,7 +319,7 @@ public function sendPmSenderFriendsOnlyGroupsDataProvider()
];
}
- public function verifiedDataProvider()
+ public static function verifiedDataProvider()
{
return [
[false, VerificationRequiredException::class],
diff --git a/tests/Libraries/Fulfillments/SupporterTagFulfillmentTest.php b/tests/Libraries/Fulfillments/SupporterTagFulfillmentTest.php
index 8373e0c4c43..b71a696988c 100644
--- a/tests/Libraries/Fulfillments/SupporterTagFulfillmentTest.php
+++ b/tests/Libraries/Fulfillments/SupporterTagFulfillmentTest.php
@@ -291,7 +291,7 @@ public function testInsufficientAmountMultiple()
(new SupporterTagFulfillment($this->order))->run();
}
- public function boolDataProvider()
+ public static function boolDataProvider()
{
return [
[true],
diff --git a/tests/Libraries/Ip2AsnTest.php b/tests/Libraries/Ip2AsnTest.php
index 72b65fe1c03..1af58a59343 100644
--- a/tests/Libraries/Ip2AsnTest.php
+++ b/tests/Libraries/Ip2AsnTest.php
@@ -18,7 +18,7 @@ public function testLookup(string $ip, string $asn)
$this->assertSame((new Ip2Asn())->lookup($ip), $asn);
}
- public function dataProviderForLookup(): array
+ public static function dataProviderForLookup(): array
{
return [
'cloudflare 1' => ['2606:4700::6810:85e5', '13335'],
diff --git a/tests/Libraries/Markdown/ChatMarkdownTest.php b/tests/Libraries/Markdown/ChatMarkdownTest.php
index 718d002cb4b..e0431b8f9a8 100644
--- a/tests/Libraries/Markdown/ChatMarkdownTest.php
+++ b/tests/Libraries/Markdown/ChatMarkdownTest.php
@@ -22,9 +22,9 @@ public function testChat($name, $path)
);
}
- public function chatExamples()
+ public static function chatExamples()
{
- return $this->fileList(__DIR__.'/chat_markdown_examples', '.md');
+ return static::fileList(__DIR__.'/chat_markdown_examples', '.md');
}
private function loadOutputTest(string $name, string $path)
diff --git a/tests/Libraries/Markdown/ProcessorTest.php b/tests/Libraries/Markdown/ProcessorTest.php
index 120dc2a9efd..c29398bcf03 100644
--- a/tests/Libraries/Markdown/ProcessorTest.php
+++ b/tests/Libraries/Markdown/ProcessorTest.php
@@ -52,14 +52,14 @@ public function testTocImage()
$this->assertSame('some header', $parsed['toc']['some-header']['title']);
}
- public function htmlExamples()
+ public static function htmlExamples()
{
- return $this->fileList(__DIR__.'/html_markdown_examples', '.md');
+ return static::fileList(__DIR__.'/html_markdown_examples', '.md');
}
- public function indexableExamples()
+ public static function indexableExamples()
{
- return $this->fileList(__DIR__.'/indexable_markdown_examples', '.md');
+ return static::fileList(__DIR__.'/indexable_markdown_examples', '.md');
}
private function loadOutputTest(string $name, string $path, string $extension)
diff --git a/tests/Libraries/ModsTest.php b/tests/Libraries/ModsTest.php
index 8c8d3c75687..9e2fcb9e885 100644
--- a/tests/Libraries/ModsTest.php
+++ b/tests/Libraries/ModsTest.php
@@ -100,7 +100,7 @@ public function testValidateSelection(Ruleset $ruleset, $modCombo, $isValid)
}
}
- public function modCombos()
+ public static function modCombos()
{
return [
// valid
@@ -140,7 +140,7 @@ public function modCombos()
];
}
- public function modComboExclusives()
+ public static function modComboExclusives()
{
return [
// non-exclusive required mods and no allowed mods
diff --git a/tests/Libraries/Search/BeatmapsetQueryParserTest.php b/tests/Libraries/Search/BeatmapsetQueryParserTest.php
index d2883bb4cca..7d1ea0587d0 100644
--- a/tests/Libraries/Search/BeatmapsetQueryParserTest.php
+++ b/tests/Libraries/Search/BeatmapsetQueryParserTest.php
@@ -19,7 +19,7 @@ public function testParse(?string $query, ?array $expected)
$this->assertSame(json_encode($expected), json_encode(BeatmapsetQueryParser::parse($query)));
}
- public function queryDataProvider()
+ public static function queryDataProvider()
{
return [
// basic options
diff --git a/tests/Libraries/Search/BeatmapsetSearchRequestParamsTest.php b/tests/Libraries/Search/BeatmapsetSearchRequestParamsTest.php
index 28ff30fa572..5d179c3ba6f 100644
--- a/tests/Libraries/Search/BeatmapsetSearchRequestParamsTest.php
+++ b/tests/Libraries/Search/BeatmapsetSearchRequestParamsTest.php
@@ -59,7 +59,7 @@ public function testCursorsGuest(?string $sort, ?array $cursor, bool $throws, ?a
$this->assertSame($expected, $searchAfter);
}
- public function cursorsDataProvider()
+ public static function cursorsDataProvider()
{
return [
[null, null, false, null],
@@ -74,7 +74,7 @@ public function cursorsDataProvider()
];
}
- public function cursorsGuestDataProvider()
+ public static function cursorsGuestDataProvider()
{
return [
[null, null, false, null],
diff --git a/tests/Libraries/SignedRandomStringTest.php b/tests/Libraries/SignedRandomStringTest.php
new file mode 100644
index 00000000000..a3244d58974
--- /dev/null
+++ b/tests/Libraries/SignedRandomStringTest.php
@@ -0,0 +1,37 @@
+. Licensed under the GNU Affero General Public License v3.0.
+// See the LICENCE file in the repository root for full licence text.
+
+declare(strict_types=1);
+
+namespace Tests\Libraries;
+
+use App\Libraries\Base64Url;
+use App\Libraries\SignedRandomString;
+use Tests\TestCase;
+
+class SignedRandomStringTest extends TestCase
+{
+ public function testIsValid(): void
+ {
+ $this->assertTrue(SignedRandomString::isValid(SignedRandomString::create(40)));
+ }
+
+ /**
+ * @dataProvider dataProviderForTestIsValidInvalid
+ */
+ public function testIsValidInvalid(string $value): void
+ {
+ $this->assertFalse(SignedRandomString::isValid($value));
+ }
+
+ public static function dataProviderForTestIsValidInvalid(): array
+ {
+ return [
+ ['invalid'],
+ [''],
+ [Base64Url::encode('test')],
+ ];
+ }
+}
diff --git a/tests/Libraries/UsernameValidationTest.php b/tests/Libraries/UsernameValidationTest.php
index 7b97bee4877..7b491c5a12c 100644
--- a/tests/Libraries/UsernameValidationTest.php
+++ b/tests/Libraries/UsernameValidationTest.php
@@ -179,7 +179,7 @@ public function testValidateUsersOfUsernameHasGuestBeatmaps(string $state, bool
* - Beatmap or beatmapset state
* - Whether the username should be available
*/
- public function usernameAvailabilityWithBeatmapStateDataProvider(): array
+ public static function usernameAvailabilityWithBeatmapStateDataProvider(): array
{
return [
['graveyard', true],
@@ -197,7 +197,7 @@ public function usernameAvailabilityWithBeatmapStateDataProvider(): array
* - Username
* - Whether the username should be valid
*/
- public function usernameValidationDataProvider(): array
+ public static function usernameValidationDataProvider(): array
{
return [
'alphabetic' => ['Username', true],
@@ -223,7 +223,7 @@ public function usernameValidationDataProvider(): array
* - Whether the user lookup should have its underscores replaced with spaces
* - Whether the user lookup should return the user
*/
- public function usersOfUsernameLookupDataProvider(): array
+ public static function usersOfUsernameLookupDataProvider(): array
{
return [
[true, true, false],
diff --git a/tests/LocaleTest.php b/tests/LocaleTest.php
index b9f2328a926..07167f89330 100644
--- a/tests/LocaleTest.php
+++ b/tests/LocaleTest.php
@@ -145,10 +145,11 @@ public function testLocaleWebWithAcceptHeaderAndUserLang()
$this->assertSame('fr', App::getLocale());
}
- public function availableLocalesProvider()
+ public static function availableLocalesProvider()
{
- return array_map(function ($locale) {
- return [$locale];
- }, config('app.available_locales'));
+ return array_map(
+ fn ($locale) => [$locale],
+ (require __DIR__.'/../config/app.php')['available_locales'],
+ );
}
}
diff --git a/tests/Middleware/RequireScopesTest.php b/tests/Middleware/RequireScopesTest.php
index a39e14ffc42..cbe7f8218ca 100644
--- a/tests/Middleware/RequireScopesTest.php
+++ b/tests/Middleware/RequireScopesTest.php
@@ -112,7 +112,7 @@ public function testUserScopes($requiredScopes, $userScopes, $expectedException)
$this->assertTrue(!oauth_token()->isClientCredentials());
}
- public function clientCredentialsTestDataProvider()
+ public static function clientCredentialsTestDataProvider()
{
return [
'null is not a valid scope' => [null, MissingScopeException::class],
@@ -132,7 +132,7 @@ public function clientCredentialsTestWhenAllScopeRequiredDataProvider()
];
}
- public function userScopesTestDataProvider()
+ public static function userScopesTestDataProvider()
{
return [
'All scopes' => [null, ['*'], null],
@@ -154,7 +154,7 @@ protected function setRequest(?array $scopes = null, $request = null)
};
// so request() works
- $this->app->instance('request', $this->request);
+ \Request::swap($this->request);
// set a fake route resolver
$this->request->setRouteResolver(function () use ($scopes) {
diff --git a/tests/Middleware/RouteScopesTest.php b/tests/Middleware/RouteScopesTest.php
index b971c4001f7..94e6e94ebe5 100644
--- a/tests/Middleware/RouteScopesTest.php
+++ b/tests/Middleware/RouteScopesTest.php
@@ -72,10 +72,9 @@ public function testUnscopedRequestsRequireAuthentication(string $url, string $m
}
}
- public function routesDataProvider()
+ public static function routesDataProvider()
{
- // note that $this->app does not carry over to the tests.
- $this->refreshApplication();
+ static::createApp();
$data = [];
@@ -103,14 +102,14 @@ public function routesDataProvider()
return $data;
}
- public function routeScopesDataProvider()
+ public static function routeScopesDataProvider()
{
- // note that $this->app does not carry over to the tests.
- $this->refreshApplication();
+ static::createApp();
- return array_map(function ($route) {
- return [$route];
- }, (new RouteScopesHelper())->toArray());
+ return array_map(
+ fn ($route) => [$route],
+ (new RouteScopesHelper())->toArray(),
+ );
}
private function importExpectations()
diff --git a/tests/Middleware/ThrottleRequestsTest.php b/tests/Middleware/ThrottleRequestsTest.php
index e329e11dbc4..f289072cc18 100644
--- a/tests/Middleware/ThrottleRequestsTest.php
+++ b/tests/Middleware/ThrottleRequestsTest.php
@@ -45,7 +45,7 @@ public function testThrottleMultipleRequests()
->assertHeader('X-Ratelimit-Remaining', 58);
}
- public function throttleDataProvider()
+ public static function throttleDataProvider()
{
return [
'throttle' => [['throttle:60,10'], 59],
diff --git a/tests/Models/BeatmapDiscussionTest.php b/tests/Models/BeatmapDiscussionTest.php
index cae37a3034e..60f1bda9dde 100644
--- a/tests/Models/BeatmapDiscussionTest.php
+++ b/tests/Models/BeatmapDiscussionTest.php
@@ -184,7 +184,7 @@ public function testSoftDeleteOrExplode()
$this->assertFalse($discussion->trashed());
}
- public function validBeatmapsetStatuses()
+ public static function validBeatmapsetStatuses()
{
return array_map(function ($status) {
return [camel_case($status)];
diff --git a/tests/Models/BeatmapPackUserCompletionTest.php b/tests/Models/BeatmapPackUserCompletionTest.php
index 12122c1225a..253a0290951 100644
--- a/tests/Models/BeatmapPackUserCompletionTest.php
+++ b/tests/Models/BeatmapPackUserCompletionTest.php
@@ -58,7 +58,7 @@ public function testBasic(string $userType, ?string $packRuleset, bool $complete
$this->assertSame($completed, $data['completed']);
}
- public function dataProviderForTestBasic(): array
+ public static function dataProviderForTestBasic(): array
{
return [
['convertOsu', 'osu', true],
diff --git a/tests/Models/BeatmapsetTest.php b/tests/Models/BeatmapsetTest.php
index 5165eee3749..b9cb14bea58 100644
--- a/tests/Models/BeatmapsetTest.php
+++ b/tests/Models/BeatmapsetTest.php
@@ -507,7 +507,7 @@ public function testDisqualifyOrResetNominations(string $state, string $pushed)
//end region
- public function disqualifyOrResetNominationsDataProvider()
+ public static function disqualifyOrResetNominationsDataProvider()
{
return [
['pending', BeatmapsetResetNominations::class],
@@ -515,7 +515,7 @@ public function disqualifyOrResetNominationsDataProvider()
];
}
- public function dataProviderForTestRank(): array
+ public static function dataProviderForTestRank(): array
{
return [
['pending', false],
@@ -523,7 +523,7 @@ public function dataProviderForTestRank(): array
];
}
- public function rankWithOpenIssueDataProvider()
+ public static function rankWithOpenIssueDataProvider()
{
return [
['problem'],
diff --git a/tests/Models/ChangelogEntryTest.php b/tests/Models/ChangelogEntryTest.php
index 74e192be98d..367da68468b 100644
--- a/tests/Models/ChangelogEntryTest.php
+++ b/tests/Models/ChangelogEntryTest.php
@@ -131,7 +131,7 @@ public function testIsPrivate()
$this->assertTrue(ChangelogEntry::isPrivate($data));
}
- public function dataForPublicMessageHtmlVisibility()
+ public static function dataForPublicMessageHtmlVisibility()
{
return [
['Hidden', null],
diff --git a/tests/Models/Chat/ChannelTest.php b/tests/Models/Chat/ChannelTest.php
index 071111b51bf..372bf003d54 100644
--- a/tests/Models/Chat/ChannelTest.php
+++ b/tests/Models/Chat/ChannelTest.php
@@ -9,6 +9,7 @@
use App\Events\ChatChannelEvent;
use App\Jobs\Notifications\ChannelAnnouncement;
+use App\Libraries\User\AvatarHelper;
use App\Models\Chat\Channel;
use App\Models\User;
use App\Models\UserRelation;
@@ -254,8 +255,8 @@ public function testPmChannelIcon()
$otherUser = User::factory()->create();
$testFile = new SplFileInfo(public_path('images/layout/avatar-guest.png'));
- $user->setAvatar($testFile);
- $otherUser->setAvatar($testFile);
+ AvatarHelper::set($user, $testFile);
+ AvatarHelper::set($otherUser, $testFile);
$channel = Channel::factory()->type('pm', [$user, $otherUser])->create();
$this->assertSame($otherUser->user_avatar, $channel->displayIconFor($user));
@@ -300,7 +301,7 @@ public function testResetMemoized()
$this->assertEmpty($memoized);
}
- public function channelCanMessageModeratedChannelDataProvider()
+ public static function channelCanMessageModeratedChannelDataProvider()
{
return [
[null, false],
@@ -312,7 +313,7 @@ public function channelCanMessageModeratedChannelDataProvider()
];
}
- public function channelCanMessageWhenBlockedDataProvider()
+ public static function channelCanMessageWhenBlockedDataProvider()
{
return [
[null, false],
@@ -324,7 +325,7 @@ public function channelCanMessageWhenBlockedDataProvider()
];
}
- public function channelWithBlockedUserVisibilityDataProvider()
+ public static function channelWithBlockedUserVisibilityDataProvider()
{
return [
[null, false],
@@ -336,7 +337,7 @@ public function channelWithBlockedUserVisibilityDataProvider()
];
}
- public function leaveChannelDataProvider()
+ public static function leaveChannelDataProvider()
{
return [
['announce', true],
diff --git a/tests/Models/CommentTest.php b/tests/Models/CommentTest.php
index 104a680a636..a918752b16b 100644
--- a/tests/Models/CommentTest.php
+++ b/tests/Models/CommentTest.php
@@ -88,7 +88,7 @@ public function testUnpinOnDelete()
$this->assertFalse($comment->fresh()->pinned);
}
- public function commentReplyOptionDataProvider()
+ public static function commentReplyOptionDataProvider()
{
return [
[null, true],
@@ -97,7 +97,7 @@ public function commentReplyOptionDataProvider()
];
}
- public function dataProviderForSetCommentableInvalid()
+ public static function dataProviderForSetCommentableInvalid()
{
return [
[null, null],
diff --git a/tests/Models/ContestTest.php b/tests/Models/ContestTest.php
index f435f3a3c5d..acad06b2096 100644
--- a/tests/Models/ContestTest.php
+++ b/tests/Models/ContestTest.php
@@ -124,7 +124,7 @@ public function testShowEntryUser(bool $showVotes, ?bool $showEntryUserOption, b
$this->assertSame($result, $contest->showEntryUser());
}
- public function dataProviderForTestAssertVoteRequirementPlaylistBeatmapsets(): array
+ public static function dataProviderForTestAssertVoteRequirementPlaylistBeatmapsets(): array
{
return [
// when passing is required
@@ -145,7 +145,7 @@ public function dataProviderForTestAssertVoteRequirementPlaylistBeatmapsets(): a
];
}
- public function dataProviderForTestShowEntryUser(): array
+ public static function dataProviderForTestShowEntryUser(): array
{
return [
[false, null, false],
diff --git a/tests/Models/ModelCompositePrimaryKeysTest.php b/tests/Models/ModelCompositePrimaryKeysTest.php
index b379b3218b0..a09a8da799c 100644
--- a/tests/Models/ModelCompositePrimaryKeysTest.php
+++ b/tests/Models/ModelCompositePrimaryKeysTest.php
@@ -71,7 +71,7 @@ public function testUpdate(string $class, array $baseParams, array $item2Params,
$this->assertSame($cast($check[2]), $cast($item2->fresh()->$key));
}
- public function dataProviderBase()
+ public static function dataProviderBase()
{
// 0: class name
// 1: base params
diff --git a/tests/Models/ModelTest.php b/tests/Models/ModelTest.php
index 4a0873f84a9..880c459527a 100644
--- a/tests/Models/ModelTest.php
+++ b/tests/Models/ModelTest.php
@@ -130,7 +130,7 @@ public function testIncrementInstanceSpecifyCount(callable $countryFn, bool $isC
$anotherCountry->refresh();
}
- public function dataProviderForDecrementInstance(): array
+ public static function dataProviderForDecrementInstance(): array
{
return [
[fn () => Country::factory()->create(), true],
diff --git a/tests/Models/Multiplayer/RoomTest.php b/tests/Models/Multiplayer/RoomTest.php
index 869ee13bed5..1a0bd1077ac 100644
--- a/tests/Models/Multiplayer/RoomTest.php
+++ b/tests/Models/Multiplayer/RoomTest.php
@@ -186,9 +186,10 @@ public function testCannotStartPlayedItem()
(new Room())->startGame($user, $params);
}
- public function startGameDurationDataProvider()
+ public static function startGameDurationDataProvider()
{
static $dayMinutes = 1440;
+ static::createApp();
$maxDuration = config('osu.user.max_multiplayer_duration');
$maxDurationSupporter = config('osu.user.max_multiplayer_duration_supporter');
diff --git a/tests/Models/OAuth/GroupPermissionTest.php b/tests/Models/OAuth/GroupPermissionTest.php
index 6a80a8e826d..936334cbff2 100644
--- a/tests/Models/OAuth/GroupPermissionTest.php
+++ b/tests/Models/OAuth/GroupPermissionTest.php
@@ -41,7 +41,7 @@ public function testGroupWithoutOAuth(string $group, string $method)
$this->assertTrue(auth()->user()->$method());
}
- public function groupsDataProvider()
+ public static function groupsDataProvider()
{
return [
['admin', 'isAdmin', false],
diff --git a/tests/Models/OAuth/TokenTest.php b/tests/Models/OAuth/TokenTest.php
index 2becf0c7465..b902098d8bc 100644
--- a/tests/Models/OAuth/TokenTest.php
+++ b/tests/Models/OAuth/TokenTest.php
@@ -192,7 +192,7 @@ public function testRevokeRecursive()
Event::assertDispatched(UserSessionEvent::class, fn (UserSessionEvent $event) => $event->action === 'logout');
}
- public function authCodeChatWriteRequiresBotGroupDataProvider()
+ public static function authCodeChatWriteRequiresBotGroupDataProvider()
{
return [
[null, InvalidScopeException::class],
@@ -204,7 +204,7 @@ public function authCodeChatWriteRequiresBotGroupDataProvider()
];
}
- public function delegationNotAllowedScopesDataProvider()
+ public static function delegationNotAllowedScopesDataProvider()
{
return Passport::scopes()
->pluck('id')
@@ -213,7 +213,7 @@ public function delegationNotAllowedScopesDataProvider()
->values();
}
- public function delegationRequiredScopesDataProvider()
+ public static function delegationRequiredScopesDataProvider()
{
return [
'chat.write requires delegation' => [['chat.write'], InvalidScopeException::class],
@@ -221,7 +221,7 @@ public function delegationRequiredScopesDataProvider()
];
}
- public function delegationRequiresChatBotDataProvider()
+ public static function delegationRequiresChatBotDataProvider()
{
return [
[null, InvalidScopeException::class],
@@ -233,7 +233,7 @@ public function delegationRequiresChatBotDataProvider()
];
}
- public function scopesDataProvider()
+ public static function scopesDataProvider()
{
return [
'null is not a valid scope' => [null, InvalidScopeException::class],
@@ -242,7 +242,7 @@ public function scopesDataProvider()
];
}
- public function scopesClientCredentialsDataProvider()
+ public static function scopesClientCredentialsDataProvider()
{
return [
'null is not a valid scope' => [null, InvalidScopeException::class],
diff --git a/tests/Models/Score/ModelTest.php b/tests/Models/Score/ModelTest.php
index 6b49014e6e8..524a264f508 100644
--- a/tests/Models/Score/ModelTest.php
+++ b/tests/Models/Score/ModelTest.php
@@ -29,7 +29,7 @@ public function testGetClassInvalidRuleset(string $ruleset)
Model::getClass($ruleset);
}
- public function dataProviderForTestGetClassInvalidRuleset(): array
+ public static function dataProviderForTestGetClassInvalidRuleset(): array
{
return [
['does'],
diff --git a/tests/Models/Solo/ScoreEsIndexTest.php b/tests/Models/Solo/ScoreEsIndexTest.php
index a0fa84260f4..2b7fcf6d117 100644
--- a/tests/Models/Solo/ScoreEsIndexTest.php
+++ b/tests/Models/Solo/ScoreEsIndexTest.php
@@ -125,7 +125,7 @@ public function testUserRank(string $key, ?array $params, int $rank): void
$this->assertSame($rank, $score->userRank($params));
}
- public function dataProviderForTestUserRank(): array
+ public static function dataProviderForTestUserRank(): array
{
return [
['user', null, 4],
diff --git a/tests/Models/Store/OrderItemTest.php b/tests/Models/Store/OrderItemTest.php
index b4b1db8c1a3..44afd698716 100644
--- a/tests/Models/Store/OrderItemTest.php
+++ b/tests/Models/Store/OrderItemTest.php
@@ -104,7 +104,7 @@ public function testReleaseWhenStockIsZero()
$this->assertSame($product->stock, 0);
}
- public function deleteDataProvider()
+ public static function deleteDataProvider()
{
return [
['checkout', InvariantException::class],
diff --git a/tests/Models/UserNotificationTest.php b/tests/Models/UserNotificationTest.php
index 6d740e8cdcb..15ce3c1751d 100644
--- a/tests/Models/UserNotificationTest.php
+++ b/tests/Models/UserNotificationTest.php
@@ -178,7 +178,7 @@ public function testScopeHasMailDelivery()
);
}
- public function deliveryMaskDataProvider()
+ public static function deliveryMaskDataProvider()
{
return [
[0, 'mail', false],
diff --git a/tests/Models/UserReportTest.php b/tests/Models/UserReportTest.php
index b09fb3f6be6..3da5be0efe7 100644
--- a/tests/Models/UserReportTest.php
+++ b/tests/Models/UserReportTest.php
@@ -192,7 +192,7 @@ public function testReportableNotificationEndpoint(string $class): void
$this->assertTrue(true, 'should not fail getting notification routing url');
}
- public function reportableClasses(): array
+ public static function reportableClasses(): array
{
$reportables = [];
diff --git a/tests/Models/UserStatistics/ModelTest.php b/tests/Models/UserStatistics/ModelTest.php
index ef67d2ad3de..d61baaa7a57 100644
--- a/tests/Models/UserStatistics/ModelTest.php
+++ b/tests/Models/UserStatistics/ModelTest.php
@@ -30,7 +30,7 @@ public function testGetClassByThrowsExceptionIfModeDoesNotExist($mode, $variant)
Model::getClass($mode, $variant);
}
- public function invalidModes()
+ public static function invalidModes()
{
return [
['does', null],
@@ -41,7 +41,7 @@ public function invalidModes()
];
}
- public function validModes()
+ public static function validModes()
{
$modes = [];
diff --git a/tests/Models/UserTest.php b/tests/Models/UserTest.php
index 9bdf91dd3b0..3cc898371b7 100644
--- a/tests/Models/UserTest.php
+++ b/tests/Models/UserTest.php
@@ -104,7 +104,7 @@ public function testValidDiscordUsername(string $username, bool $valid)
}
}
- public function dataProviderForAttributeTwitter(): array
+ public static function dataProviderForAttributeTwitter(): array
{
return [
['@hello', 'hello'],
@@ -115,7 +115,7 @@ public function dataProviderForAttributeTwitter(): array
];
}
- public function dataProviderValidDiscordUsername(): array
+ public static function dataProviderValidDiscordUsername(): array
{
return [
['username', true],
diff --git a/tests/OAuthAuthCodeRequestTest.php b/tests/OAuthAuthCodeRequestTest.php
index 59b4492ca25..d6b3940f7dc 100644
--- a/tests/OAuthAuthCodeRequestTest.php
+++ b/tests/OAuthAuthCodeRequestTest.php
@@ -49,7 +49,7 @@ public function testNonBotClientCannotRequestChatWriteScope()
->assertStatus(400);
}
- public function botClientDataProvider()
+ public static function botClientDataProvider()
{
return [
'cannot request delegation with auth_code' => ['delegate', false],
diff --git a/tests/OAuthClientCredentialsRequestTest.php b/tests/OAuthClientCredentialsRequestTest.php
index 23a97001eef..f5830976a87 100644
--- a/tests/OAuthClientCredentialsRequestTest.php
+++ b/tests/OAuthClientCredentialsRequestTest.php
@@ -48,7 +48,7 @@ public function testNonBotRequestingScope($scope, $status)
->assertStatus($status);
}
- public function botRequestingScopeDataProvider()
+ public static function botRequestingScopeDataProvider()
{
return [
'* cannot be requested' => ['*', 400],
@@ -60,7 +60,7 @@ public function botRequestingScopeDataProvider()
];
}
- public function nonBotRequestingScopeDataProvider()
+ public static function nonBotRequestingScopeDataProvider()
{
return [
'* cannot be requested' => ['*', 400],
diff --git a/tests/Providers/AuthServiceProviderTest.php b/tests/Providers/AuthServiceProviderTest.php
new file mode 100644
index 00000000000..1e2b7bde0f8
--- /dev/null
+++ b/tests/Providers/AuthServiceProviderTest.php
@@ -0,0 +1,58 @@
+. Licensed under the GNU Affero General Public License v3.0.
+// See the LICENCE file in the repository root for full licence text.
+
+declare(strict_types=1);
+
+namespace Tests\Providers;
+
+use App\Http\Controllers\OAuth\AuthorizedClientsController;
+use App\Http\Controllers\OAuth\ClientsController;
+use App\Http\Controllers\Passport\AuthorizationController;
+use Laravel\Passport\Http\Controllers\AccessTokenController;
+use Laravel\Passport\Http\Controllers\ApproveAuthorizationController;
+use Laravel\Passport\Http\Controllers\DenyAuthorizationController;
+use Request;
+use Route;
+use Tests\TestCase;
+
+class AuthServiceProviderTest extends TestCase
+{
+ /**
+ * @dataProvider oauthRoutesRegisteredDataProvider
+ */
+ public function testOAuthRoutesRegistered($url, $method, $uses)
+ {
+ $route = Route::getRoutes()->match(Request::create($url, $method));
+ $this->assertSame($uses, $route->action['uses']);
+ }
+
+ public function testPassportDefaultRoutesNotRegistered()
+ {
+ $routeNames = array_keys(Route::getRoutes()->getRoutesByName());
+
+ foreach ($routeNames as $routeName) {
+ $this->assertStringStartsNotWith('passport.', $routeName);
+ }
+ }
+
+ public static function oauthRoutesRegisteredDataProvider()
+ {
+ return [
+ ['oauth/authorize', 'GET', AuthorizationController::class.'@authorize'],
+ ['oauth/authorize', 'POST', ApproveAuthorizationController::class.'@approve'],
+ ['oauth/authorize', 'DELETE', DenyAuthorizationController::class.'@deny'],
+
+ ['oauth/authorized-clients/1', 'DELETE', AuthorizedClientsController::class.'@destroy'],
+
+ ['oauth/clients', 'GET', ClientsController::class.'@index'],
+ ['oauth/clients', 'POST', ClientsController::class.'@store'],
+ ['oauth/clients/1', 'PUT', ClientsController::class.'@update'],
+ ['oauth/clients/1', 'DELETE', ClientsController::class.'@destroy'],
+ ['oauth/clients/1/reset-secret', 'POST', ClientsController::class.'@resetSecret'],
+
+ ['oauth/token', 'POST', AccessTokenController::class.'@issueToken'],
+ ];
+ }
+}
diff --git a/tests/TestCase.php b/tests/TestCase.php
index fb3b10e1670..ab6f5838adf 100644
--- a/tests/TestCase.php
+++ b/tests/TestCase.php
@@ -34,13 +34,21 @@ class TestCase extends BaseTestCase
public static function withDbAccess(callable $callback): void
{
- $db = (new static())->createApplication()->make('db');
+ $db = static::createApp()->make('db');
$callback();
static::resetAppDb($db);
}
+ protected static function fileList($path, $suffix)
+ {
+ return array_map(
+ fn ($file) => [basename($file, $suffix), $path],
+ glob("{$path}/*{$suffix}"),
+ );
+ }
+
protected static function reindexScores()
{
$search = new ScoreSearch();
@@ -73,7 +81,7 @@ protected static function resetAppDb(DatabaseManager $database): void
protected array $expectedCountsCallbacks = [];
- public function regularOAuthScopesDataProvider()
+ public static function regularOAuthScopesDataProvider()
{
$data = [];
@@ -267,13 +275,6 @@ protected function expectCountChange(callable $callback, int $change, string $me
];
}
- protected function fileList($path, $suffix)
- {
- return array_map(function ($file) use ($path, $suffix) {
- return [basename($file, $suffix), $path];
- }, glob("{$path}/*{$suffix}"));
- }
-
protected function inReceivers(Model $model, NewPrivateNotificationEvent|BroadcastNotificationBase $obj): bool
{
return in_array($model->getKey(), $obj->getReceiverIds(), true);
diff --git a/tests/Transformers/BeatmapDiscussionPostTransformerTest.php b/tests/Transformers/BeatmapDiscussionPostTransformerTest.php
index c5b3edd86e5..0fb906c46f9 100644
--- a/tests/Transformers/BeatmapDiscussionPostTransformerTest.php
+++ b/tests/Transformers/BeatmapDiscussionPostTransformerTest.php
@@ -48,7 +48,7 @@ public function testWithoutOAuth(?string $groupIdentifier, bool $visible)
}
}
- public function groupsDataProvider()
+ public static function groupsDataProvider()
{
return [
['admin', true],
diff --git a/tests/Transformers/BeatmapDiscussionTransformerTest.php b/tests/Transformers/BeatmapDiscussionTransformerTest.php
index f9d855bd2d8..2915f3d660c 100644
--- a/tests/Transformers/BeatmapDiscussionTransformerTest.php
+++ b/tests/Transformers/BeatmapDiscussionTransformerTest.php
@@ -44,7 +44,7 @@ public function testWithoutOAuth(?string $groupIdentifier, bool $visible)
}
}
- public function groupsDataProvider()
+ public static function groupsDataProvider()
{
return [
['admin', true],
diff --git a/tests/Transformers/BeatmapTransformerTest.php b/tests/Transformers/BeatmapTransformerTest.php
index 6a2d208cfcc..d00a635d9c8 100644
--- a/tests/Transformers/BeatmapTransformerTest.php
+++ b/tests/Transformers/BeatmapTransformerTest.php
@@ -45,7 +45,7 @@ public function testWithoutOAuth(?string $groupIdentifier, bool $visible)
}
}
- public function groupsDataProvider()
+ public static function groupsDataProvider()
{
return [
['admin', true],
diff --git a/tests/Transformers/BeatmapsetCompactTransformerTest.php b/tests/Transformers/BeatmapsetCompactTransformerTest.php
index 6750dd5dd13..d25cb152fa1 100644
--- a/tests/Transformers/BeatmapsetCompactTransformerTest.php
+++ b/tests/Transformers/BeatmapsetCompactTransformerTest.php
@@ -64,7 +64,7 @@ public function testPropertyIsVisibleWithoutOAuth(string $property)
$this->assertArrayHasKey($property, $json);
}
- public function propertyPermissionsDataProvider()
+ public static function propertyPermissionsDataProvider()
{
$data = [];
$transformer = new BeatmapsetCompactTransformer();
diff --git a/tests/Transformers/BeatmapsetDescriptionTransformerTest.php b/tests/Transformers/BeatmapsetDescriptionTransformerTest.php
index 0177356af51..dd7b5f221ad 100644
--- a/tests/Transformers/BeatmapsetDescriptionTransformerTest.php
+++ b/tests/Transformers/BeatmapsetDescriptionTransformerTest.php
@@ -72,7 +72,7 @@ public function testUserIsNotMapper()
$this->assertArrayNotHasKey('bbcode', $json);
}
- public function groupsDataProvider()
+ public static function groupsDataProvider()
{
return [
['admin', true],
diff --git a/tests/Transformers/BeatmapsetEventTransformerTest.php b/tests/Transformers/BeatmapsetEventTransformerTest.php
index d83590199bb..1a77bb00d8c 100644
--- a/tests/Transformers/BeatmapsetEventTransformerTest.php
+++ b/tests/Transformers/BeatmapsetEventTransformerTest.php
@@ -57,7 +57,7 @@ public function testWithoutOAuth(?string $groupIdentifier, string $eventType, bo
}
}
- public function dataProvider()
+ public static function dataProvider()
{
// one event type of each priviledge type.
return [
diff --git a/tests/Transformers/BeatmapsetTransformerTest.php b/tests/Transformers/BeatmapsetTransformerTest.php
index 5af0c769d60..d6bf00c3fc3 100644
--- a/tests/Transformers/BeatmapsetTransformerTest.php
+++ b/tests/Transformers/BeatmapsetTransformerTest.php
@@ -43,7 +43,7 @@ public function testDeletedBeatmapsetGroupPermissionsWithoutOAuth(?string $group
}
}
- public function groupsDataProvider()
+ public static function groupsDataProvider()
{
return [
['admin', true],
diff --git a/tests/Transformers/CommentTransformerTest.php b/tests/Transformers/CommentTransformerTest.php
index 812ddac17a5..40b6b187d53 100644
--- a/tests/Transformers/CommentTransformerTest.php
+++ b/tests/Transformers/CommentTransformerTest.php
@@ -46,7 +46,7 @@ public function testWithoutOAuth(?string $groupIdentifier, bool $visible)
}
}
- public function groupsDataProvider()
+ public static function groupsDataProvider()
{
return [
['admin', true],
diff --git a/tests/Transformers/DeclaredPermissionsTest.php b/tests/Transformers/DeclaredPermissionsTest.php
index 3bb5cafd2dd..b6767dfd8ad 100644
--- a/tests/Transformers/DeclaredPermissionsTest.php
+++ b/tests/Transformers/DeclaredPermissionsTest.php
@@ -38,7 +38,7 @@ public function testPrivilegeExists($class, ?string $include, string $privilege)
$this->assertTrue(method_exists(app('OsuAuthorize'), "check{$privilege}"), "{$class} uses check{$privilege} but is not implemented.");
}
- public function transformerClassesDataProvider()
+ public static function transformerClassesDataProvider()
{
return array_map(
function ($class) {
@@ -48,7 +48,7 @@ function ($class) {
);
}
- public function privilegeDataProvider()
+ public static function privilegeDataProvider()
{
$data = [];
diff --git a/tests/Transformers/OAuth/ClientTransformerTest.php b/tests/Transformers/OAuth/ClientTransformerTest.php
index e2bacff25e1..5e8d0912718 100644
--- a/tests/Transformers/OAuth/ClientTransformerTest.php
+++ b/tests/Transformers/OAuth/ClientTransformerTest.php
@@ -48,7 +48,7 @@ public function testRedirectAndSecretNotVisibleToGroup($groupIdentifier)
$this->assertArrayNotHasKey('secret', $json);
}
- public function groupsDataProvider()
+ public static function groupsDataProvider()
{
return [
['admin'],
diff --git a/tests/Transformers/PollOptionTransformerTest.php b/tests/Transformers/PollOptionTransformerTest.php
index f4857104970..b7e9aa31cb4 100644
--- a/tests/Transformers/PollOptionTransformerTest.php
+++ b/tests/Transformers/PollOptionTransformerTest.php
@@ -64,7 +64,7 @@ public function testVoteCountPermissions(
* - Authenticated user's group identifier
* - Whether vote count should be visible
*/
- public function voteCountPermissionsDataProvider(): array
+ public static function voteCountPermissionsDataProvider(): array
{
return [
[true, true, true, 'admin', true],
diff --git a/tests/Transformers/UserCompactTransformerTest.php b/tests/Transformers/UserCompactTransformerTest.php
index f08c259bd2b..cc5bcd3b462 100644
--- a/tests/Transformers/UserCompactTransformerTest.php
+++ b/tests/Transformers/UserCompactTransformerTest.php
@@ -109,7 +109,7 @@ public function testPropertyIsVisibleWithoutOAuth(string $property)
$this->assertArrayHasKey($property, $json);
}
- public function groupsDataProvider()
+ public static function groupsDataProvider()
{
return [
['admin', true],
@@ -120,7 +120,7 @@ public function groupsDataProvider()
];
}
- public function propertyPermissionsDataProvider()
+ public static function propertyPermissionsDataProvider()
{
$data = [];
$transformer = new UserTransformer();
diff --git a/tests/ZalgoTest.php b/tests/ZalgoTest.php
index a03983954bb..09b54ac50ba 100644
--- a/tests/ZalgoTest.php
+++ b/tests/ZalgoTest.php
@@ -50,7 +50,7 @@ public function combinationExamples()
];
}
- public function zalgoExamples()
+ public static function zalgoExamples()
{
return [
['testing', 0],