Reputation: 39
I'm currently developing an application written using Slim 3 framework, and trying to use the TDD concept, but I'm facing several issues, including when using the test for the POST method, the headers that I embed are always considered missing.
Below is my code
<?php
namespace Tests\routes\shopify;
use PHPUnit\Framework\TestCase;
use Slim\App;
use Slim\Http\Environment;
use Slim\Http\Headers;
use Slim\Http\Request;
use Slim\Http\RequestBody;
use Slim\Http\Response;
use Slim\Http\UploadedFile;
use Slim\Http\Uri;
class ShopifyRoutesTest extends TestCase
{
private $app;
protected function setUp(): void
{
// Use the application settings
$settings = require __DIR__ . '/../../../src/settings.php';
// Instantiate the application
$this->app = new App($settings);
// Set up dependencies
$dependencies = require __DIR__ . '/../../../src/dependencies.php';
$dependencies($this->app);
// Register middleware
$middleware = require __DIR__ . '/../../../src/middleware.php';
$middleware($this->app);
// Register routes
$routes = require __DIR__ . '/../../../src/app/routes/api/shopify/routes.php';
$routes($this->app);
}
public function testPostSyncProductBySkuWithEmptyApikeyShouldReturnBadRequest()
{
// Create a mock environment for testing with
$environment = Environment::mock();
$uri = Uri::createFromString("/channel/shopify/v1/product/sync-by-sku");
$headers = new Headers(array(
"Content-Type" => "application/json",
"Authorization" => "client-apikey",
"x-api-key" => ""
));
$cookies = [];
$serverParams = $environment->all();
$body = new RequestBody();
$uploadedFiles = UploadedFile::createFromEnvironment($environment);
// Set up a request object based on the environment
$request = new Request("POST", $uri, $headers, $cookies, $serverParams, $body, $uploadedFiles);
$reqBody = array(
"shop_name" => "STORE ABCE",
"sku" => "SKU-001"
);
$body->write(json_encode($reqBody));
// Add request data, if it exists
$request = $request->withParsedBody($reqBody);
// Set up a response object
$response = new Response();
$response = $this->app->process($request, $response);
self::assertEquals(400, $response->getStatusCode());
self::assertStringContainsString("Header: x-api-key cannot be empty", $response->getBody());
}
protected function tearDown(): void
{
$this->app = null;
}
}
the first assertion succeeds with a value of 400, but in the second assertion its fails, the string doesn't contain the value "Header: x-api-key cannot be empty" but instead this
Failed asserting that '{\n
"error": "AUTH_FAIL",\n
"errorDetail": "Header: Authorization, x-api-key required",\n
"status": 400,\n
"message": "BAD_REQUEST",\n
"data": ""\n
}' contains "Header: x-api-key cannot be empty".
the strange thing is that when I var_dump($request->getHeaders()) the headers value of the request I made it turns out to be there
array(3) {
["Content-Type"]=>
array(1) {
[0]=>
string(16) "application/json"
}
["Authorization"]=>
array(1) {
[0]=>
string(13) "client-apikey"
}
["x-api-key"]=>
array(1) {
[0]=>
string(0) ""
}
}
I've also tried testing my API endpoint using Postman, and the results are as expected
Request
curl --location --request POST 'http://localhost:8080/channel/shopify/v1/product/sync-by-sku' \
--header 'Authorization: client-apikey' \
--header 'x-api-key: 1293129382938' \
--header 'Content-Type: application/json' \
--header 'Cookie: PHPSESSID=tll8s24tp253rda1harv0koapi' \
--data-raw '{
"shop_name" : "STORE ABC",
"sku" : "SKU-991"
}'
Response
{
"error": "AUTH_FAIL",
"errorDetail": "Header: x-api-key cannot be empty",
"status": 400,
"message": "BAD_REQUEST",
"data": ""
}
Also I've read the answer from stakoverflow as described here Mock Slim endpoint POST requests with PHPUnit
But still I can't find the solution, the header is always presumed to be missing. I really appreciate the solution to this problem, thank you in advance
Upvotes: 0
Views: 299
Reputation: 39
finally after figuring out the structure and behavior of the Header and also Request in Slim 3, the Header class in Slim 3 always makes the key value to lower-case, I don't know what that means, but finally I need to adjust this behavior in my middleware, from which previously used $request->getHeaders() to $request->getHeaderLine() and also $request->hasHeader(), $request->getHeaders() made the header value upper-case and added HTTP_ to the front of the key
in my case this is the cause of the problem, because the request I use in the unit test must pass the lower-case value and don't have HTTP_ at the front of the key, so the middleware assumes that the key that should exist has never existed
Middleware Before
// Common Channel Auth with client-apikey
$app->add(function (Request $request, Response $response, callable $next) use ($container) {
$uri = $request->getUri();
$path = $uri->getPath();
$headers = $request->getHeaders();
$arrayPath = explode("/", $path);
if ($arrayPath[1] == "channel" && $arrayPath[3] == "tools")
{
return $next($request, $response);
}
elseif ($arrayPath[1] == "channel" && $arrayPath[4] != "webhook")
{
/** @var ClientRepository $clientRepository */
$clientRepository = $container->get("ClientRepository");
// Get Header With Name x-api-key & Authorization
if (isset($headers["HTTP_AUTHORIZATION"]) && isset($headers["HTTP_X_API_KEY"]))
{
if ($headers["HTTP_AUTHORIZATION"][0] == "client-apikey")
{
$reqClientApikey = $headers["HTTP_X_API_KEY"][0];
if (v::notBlank()->validate($reqClientApikey))
{
if ($clientRepository->findByClientApiKey($reqClientApikey))
{
return $next($request, $response);
Middleware After
// Common Channel Auth with client-apikey
$app->add(function (Request $request, Response $response, callable $next) use ($container) {
$uri = $request->getUri();
$path = $uri->getPath();
$arrayPath = explode("/", $path);
if ($arrayPath[1] == "channel" && $arrayPath[3] == "tools")
{
return $next($request, $response);
}
elseif ($arrayPath[1] == "channel" && $arrayPath[4] != "webhook")
{
/** @var ClientRepository $clientRepository */
$clientRepository = $container->get("ClientRepository");
// Using $request-hasHeader & $request->getHeaderLine instead of $headers["HTTP_AUTHORIZATION"]
if ($request->hasHeader("authorization") != null && $request->hasHeader("x-api-key") != null)
{
if ($request->getHeaderLine("authorization") == "client-apikey")
{
$reqClientApikey = $request->getHeaderLine("x-api-key");
if (v::notBlank()->validate($reqClientApikey))
{
if ($clientRepository->findByClientApiKey($reqClientApikey))
{
return $next($request, $response);
}
Unit Test
public function testPostSyncProductBySkuWithEmptyApikeyShouldReturnBadRequest()
{
// Create a mock environment for testing with
$environment = Environment::mock();
$uri = Uri::createFromString("/channel/shopify/v1/product/sync-by-sku");
$headers = new Headers([
"Content-Type" => "application/json",
"Authorization" => "client-apikey",
"x-api-key" => ""
]);
$cookies = [];
$serverParams = $environment->all();
$body = new RequestBody();
$uploadedFiles = UploadedFile::createFromEnvironment($environment);
// Set up a request object based on the environment
$request = new Request("POST", $uri, $headers, $cookies, $serverParams, $body, $uploadedFiles);
$reqBody = array(
"shop_name" => "STORE ABCE",
"sku" => "SKU-001"
);
$body->write(json_encode($reqBody));
// Add request data, if it exists
$request = $request->withParsedBody($reqBody);
// Set up a response object
$response = new Response();
$response = $this->app->process($request, $response);
self::assertEquals(400, $response->getStatusCode());
self::assertStringContainsString("Header: x-api-key cannot be empty", $response->getBody());
}
Once again, I hope this mistake I made will be a record for others, thank you
Upvotes: 0