added unit and integration tests

pull/601/head
n0nag0n 6 months ago
parent f08b9bcbfb
commit f697e30afa

@ -62,9 +62,10 @@ use flight\net\Route;
* @method void jsonp(mixed $data, string $param = 'jsonp', int $code = 200, bool $encode = true, string $charset = 'utf-8', int $option = 0) * @method void jsonp(mixed $data, string $param = 'jsonp', int $code = 200, bool $encode = true, string $charset = 'utf-8', int $option = 0)
* Sends a JSONP response. * Sends a JSONP response.
* *
* # HTTP caching * # HTTP methods
* @method void etag(string $id, ('strong'|'weak') $type = 'strong') Handles ETag HTTP caching. * @method void etag(string $id, ('strong'|'weak') $type = 'strong') Handles ETag HTTP caching.
* @method void lastModified(int $time) Handles last modified HTTP caching. * @method void lastModified(int $time) Handles last modified HTTP caching.
* @method void download(string $filePath) Downloads a file
* *
* phpcs:disable PSR2.Methods.MethodDeclaration.Underscore * phpcs:disable PSR2.Methods.MethodDeclaration.Underscore
*/ */
@ -895,29 +896,43 @@ class Engine
} }
} }
public function _download(string $file): void { /**
if (!file_exists($file)) { * Downloads a file
throw new Exception("$file cannot be found."); *
* @param string $filePath The path to the file to download
* @throws Exception If the file cannot be found
*
* @return void
*/
public function _download(string $filePath): void {
if (file_exists($filePath) === false) {
throw new Exception("$filePath cannot be found.");
} }
$fileSize = filesize($file); $fileSize = filesize($filePath);
$mimeType = mime_content_type($file); $mimeType = mime_content_type($filePath);
$mimeType = $mimeType !== false ? $mimeType : 'application/octet-stream';
header('Content-Description: File Transfer'); $response = $this->response();
header('Content-Type: ' . $mimeType); $response->send();
header('Content-Disposition: attachment; filename="' . basename($file) . '"'); $response->setRealHeader('Content-Description: File Transfer');
header('Expires: 0'); $response->setRealHeader('Content-Type: ' . $mimeType);
header('Cache-Control: must-revalidate'); $response->setRealHeader('Content-Disposition: attachment; filename="' . basename($filePath) . '"');
header('Pragma: public'); $response->setRealHeader('Expires: 0');
header('Content-Length: ' . $fileSize); $response->setRealHeader('Cache-Control: must-revalidate');
$response->setRealHeader('Pragma: public');
$response->setRealHeader('Content-Length: ' . $fileSize);
// Clear the output buffer // // Clear the output buffer
ob_clean(); ob_clean();
flush(); flush();
// Read the file and send it to the output buffer // // Read the file and send it to the output buffer
readfile($file); readfile($filePath);
if(empty(getenv('PHPUNIT_TEST'))) {
exit; // @codeCoverageIgnore
}
} }
/** /**

@ -75,9 +75,10 @@ require_once __DIR__ . '/autoload.php';
* @method static void error(Throwable $exception) Sends an HTTP 500 response. * @method static void error(Throwable $exception) Sends an HTTP 500 response.
* @method static void notFound() Sends an HTTP 404 response. * @method static void notFound() Sends an HTTP 404 response.
* *
* # HTTP caching * # HTTP methods
* @method static void etag(string $id, ('strong'|'weak') $type = 'strong') Performs ETag HTTP caching. * @method static void etag(string $id, ('strong'|'weak') $type = 'strong') Performs ETag HTTP caching.
* @method static void lastModified(int $time) Performs last modified HTTP caching. * @method static void lastModified(int $time) Performs last modified HTTP caching.
* @method static void download(string $filePath) Downloads a file
*/ */
class Flight class Flight
{ {

@ -952,4 +952,38 @@ class EngineTest extends TestCase
$this->assertEquals('Method Not Allowed', $engine->response()->getBody()); $this->assertEquals('Method Not Allowed', $engine->response()->getBody());
} }
public function testDownload()
{
$engine = new class extends Engine {
public function getLoader()
{
return $this->loader;
}
};
// doing this so we can overwrite some parts of the response
$engine->getLoader()->register('response', function () {
return new class extends Response {
public function setRealHeader(
string $header_string,
bool $replace = true,
int $response_code = 0
): self {
return $this;
}
};
});
$tmpfile = tmpfile();
fwrite($tmpfile, 'I am a teapot');
$streamPath = stream_get_meta_data($tmpfile)['uri'];
$this->expectOutputString('I am a teapot');
$engine->download($streamPath);
}
public function testDownloadBadPath() {
$engine = new Engine();
$this->expectException(Exception::class);
$this->expectExceptionMessage("/path/to/nowhere cannot be found.");
$engine->download('/path/to/nowhere');
}
} }

@ -86,6 +86,7 @@ class LayoutMiddleware
<li><a href="/dice">Dice Container</a></li> <li><a href="/dice">Dice Container</a></li>
<li><a href="/no-container">No Container Registered</a></li> <li><a href="/no-container">No Container Registered</a></li>
<li><a href="/Pascal_Snake_Case">Pascal_Snake_Case</a></li> <li><a href="/Pascal_Snake_Case">Pascal_Snake_Case</a></li>
<li><a href="/download">Download File</a></li>
</ul> </ul>
HTML; HTML;
echo '<div id="container">'; echo '<div id="container">';

@ -175,6 +175,11 @@ Flight::route('/json-halt', function () {
Flight::jsonHalt(['message' => 'JSON rendered and halted successfully with no other body content!']); Flight::jsonHalt(['message' => 'JSON rendered and halted successfully with no other body content!']);
}); });
// Download a file
Flight::route('/download', function () {
Flight::download('test_file.txt');
});
Flight::map('error', function (Throwable $e) { Flight::map('error', function (Throwable $e) {
echo sprintf( echo sprintf(
<<<HTML <<<HTML

@ -0,0 +1 @@
This file downloaded successfully!
Loading…
Cancel
Save