the browser-facing portion of osu!
at master 4.1 kB view raw
1<?php 2 3// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0. 4// See the LICENCE file in the repository root for full licence text. 5 6namespace Tests\Middleware; 7 8use App\Http\Middleware\RequireScopes; 9use App\Libraries\RouteScopesHelper; 10use App\Models\Build; 11use App\Models\Changelog; 12use App\Models\Comment; 13use App\Models\UpdateStream; 14use Route; 15use Tests\TestCase; 16 17class RouteScopesTest extends TestCase 18{ 19 private static $expectations; 20 21 /** 22 * @dataProvider routeScopesDataProvider 23 */ 24 public function testApiRouteScopes($route) 25 { 26 $this->importExpectations(); 27 28 $key = RouteScopesHelper::keyForMethods($route['methods']).'@'.$route['uri']; 29 30 $this->assertSame(static::$expectations[$key], $route, $key); 31 } 32 33 /** 34 * @dataProvider routesDataProvider 35 */ 36 public function testUnscopedRequestsRequireAuthentication(string $url, string $method, array $middlewares) 37 { 38 // factory some objects so unauthed endpoints don't 404. 39 // FIXME: only create objects as necessary instead of every run; 40 // This can't be simply setup in setUpBeforeClass() because then we'd need to initialize the app container there, 41 // but the container is overriden on each test and also destroyed before the teardown 42 // making rolling back or cleaning up problematic. 43 $stream = UpdateStream::factory()->create([ 44 'name' => '1', 45 'stream_id' => 1, // Changelog stream_id is tinyint, autoincrement makes test fail too soon. 46 ]); 47 48 Changelog::factory()->create([ 49 'stream_id' => $stream->getKey(), 50 'user_id' => 1, // user doesn't need to exist and not having to create a user makes the test much faster 51 ]); 52 53 $build = Build::factory()->create([ 54 'version' => '1', 55 'stream_id' => $stream->getKey(), 56 ]); 57 58 Comment::factory()->create([ 59 'commentable_id' => $build->getKey(), 60 'commentable_type' => 'build', 61 'id' => 1, 62 ]); 63 64 $status = $this->call($method, $url)->getStatusCode(); 65 66 if ($method === 'GET' && starts_with(ltrim($url, '/').'/', RequireScopes::NO_TOKEN_REQUIRED)) { 67 $this->assertTrue(in_array($status, [200, 302, 404], true)); 68 } elseif (in_array('require-scopes', $middlewares, true)) { 69 $this->assertSame(401, $status); 70 } else { 71 $this->assertNotSame(401, $status); 72 } 73 } 74 75 public static function routesDataProvider() 76 { 77 static::createApp(); 78 79 $data = []; 80 81 foreach (Route::getRoutes() as $route) { 82 if (!starts_with($route->uri, 'api/')) { 83 continue; 84 } 85 86 foreach ($route->methods() as $method) { 87 // Only need the url to be valid so they can be routed. 88 $parameters = []; 89 foreach ($route->parameterNames() as $parameterName) { 90 $parameters[$parameterName] = '1'; 91 } 92 93 $url = app('url')->toRoute($route, $parameters, false); 94 $middlewares = $route->gatherMiddleware(); 95 96 $key = "{$method}@{$route->uri}"; // give data set a name. 97 $data[$key] = [$url, $method, $middlewares]; 98 } 99 } 100 101 return $data; 102 } 103 104 public static function routeScopesDataProvider() 105 { 106 static::createApp(); 107 108 return array_map( 109 fn ($route) => [$route], 110 (new RouteScopesHelper())->toArray(), 111 ); 112 } 113 114 private function importExpectations() 115 { 116 if (static::$expectations !== null) { 117 return; 118 } 119 120 static::$expectations = []; 121 122 $helper = new RouteScopesHelper(); 123 $helper->fromJson('tests/api_routes.json'); 124 $routes = $helper->routes; 125 foreach ($routes as $route) { 126 $key = RouteScopesHelper::keyForMethods($route['methods']).'@'.$route['uri']; 127 static::$expectations[$key] = $route; 128 } 129 } 130}