callApiWithKey(self::METHOD_GET, '/short-urls'); self::assertEquals(200, $resp->getStatusCode()); self::assertFalse($resp->hasHeader('Access-Control-Allow-Origin')); self::assertFalse($resp->hasHeader('Access-Control-Allow-Methods')); self::assertFalse($resp->hasHeader('Access-Control-Max-Age')); self::assertFalse($resp->hasHeader('Access-Control-Allow-Headers')); } /** * @test * @dataProvider provideOrigins */ public function responseIncludesCorsHeadersIfOriginIsSent( string $origin, string $endpoint, int $expectedStatusCode ): void { $resp = $this->callApiWithKey(self::METHOD_GET, $endpoint, [ RequestOptions::HEADERS => ['Origin' => $origin], ]); self::assertEquals($expectedStatusCode, $resp->getStatusCode()); self::assertEquals($origin, $resp->getHeaderLine('Access-Control-Allow-Origin')); self::assertFalse($resp->hasHeader('Access-Control-Allow-Methods')); self::assertFalse($resp->hasHeader('Access-Control-Max-Age')); self::assertFalse($resp->hasHeader('Access-Control-Allow-Headers')); } public function provideOrigins(): iterable { yield 'foo.com' => ['foo.com', '/short-urls', 200]; yield 'bar.io' => ['bar.io', '/foo/bar', 404]; yield 'baz.dev' => ['baz.dev', '/short-urls', 200]; } /** * @test * @dataProvider providePreflightEndpoints */ public function preflightRequestsIncludeExtraCorsHeaders(string $endpoint, string $expectedAllowedMethods): void { $allowedHeaders = 'Authorization'; $resp = $this->callApiWithKey(self::METHOD_OPTIONS, $endpoint, [ RequestOptions::HEADERS => [ 'Origin' => 'foo.com', 'Access-Control-Request-Headers' => $allowedHeaders, ], ]); self::assertEquals(204, $resp->getStatusCode()); self::assertTrue($resp->hasHeader('Access-Control-Allow-Origin')); self::assertTrue($resp->hasHeader('Access-Control-Max-Age')); self::assertEquals($expectedAllowedMethods, $resp->getHeaderLine('Access-Control-Allow-Methods')); self::assertEquals($allowedHeaders, $resp->getHeaderLine('Access-Control-Allow-Headers')); } public function providePreflightEndpoints(): iterable { yield 'invalid route' => ['/foo/bar', 'GET,POST,PUT,PATCH,DELETE,OPTIONS']; yield 'short URLs routes' => ['/short-urls', 'GET,POST,PUT,PATCH,DELETE,OPTIONS']; // yield 'short URLs routes' => ['/short-urls', 'GET,POST']; // TODO This should be the good one yield 'tags routes' => ['/tags', 'GET,POST,PUT,PATCH,DELETE,OPTIONS']; // yield 'tags routes' => ['/short-urls', 'GET,POST,PUT,DELETE']; // TODO This should be the good one } }