Skip to content

Commit

Permalink
Add HTTP Request class and Unit Tests
Browse files Browse the repository at this point in the history
- Implemented `Request` class under the `Atproto\HTTP` namespace with methods to manage URL, path, method, headers, parameters, and query parameters.
- Added flexibility in setting and retrieving values for headers, parameters, and query parameters.
- Provided encoding for headers and parameters as arrays or JSON.
- Created `RequestTest` class under `Tests\Unit\HTTP` to ensure proper functionality:
  - Added tests for setting and retrieving values via the methods.
  - Verified instance return consistency when setting values.
  - Tested encoding output for headers, parameters, and query parameters.
  • Loading branch information
shahmal1yev committed Sep 11, 2024
1 parent 73358db commit 16b2279
Show file tree
Hide file tree
Showing 2 changed files with 323 additions and 0 deletions.
128 changes: 128 additions & 0 deletions src/HTTP/Request.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
<?php

namespace Atproto\HTTP;

class Request
{
protected string $url = '';
protected string $path = '';
protected string $method = 'GET';
protected array $headers = [];
protected array $parameters = [];
protected array $queryParameters = [];

public function url(string $url = null)
{
if (is_null($url)) {
return $this->url;
}

$this->url = $url;

return $this;
}

public function path(string $path = null)
{
if (is_null($path)) {
return $this->path;
}

$this->path = $path;

return $this;
}

public function method(string $method = null)
{
if (is_null($method)) {
return $this->method;
}

$this->method = $method;

return $this;
}

public function header(string $name, string $value = null)
{
if (is_null($value)) {
return $this->headers[$name] ?? null;
}

$this->headers[$name] = $value;

return $this;
}

public function parameter(string $name, string $value = null)
{
if (is_null($value)) {
return $this->parameters[$name] ?? null;
}

$this->parameters[$name] = $value;

return $this;
}

public function queryParameter(string $name, string $value = null)
{
if (is_null($value)) {
return $this->queryParameters[$name] ?? null;
}

$this->queryParameters[$name] = $value;

return $this;
}

public function headers($headers = null)
{
if (is_bool($headers) && $headers) {
return array_map(
fn ($name, $value) => "$name: $value",
array_keys($this->headers),
array_values($this->headers)
);
}

if (is_null($headers)) {
return $this->headers;
}

$this->headers = $headers;

return $this;
}

public function parameters($parameters = null)
{
if (is_bool($parameters) && $parameters) {
return json_encode($this->parameters);
}

if (is_null($parameters)) {
return $this->parameters;
}

$this->parameters = $parameters;

return $this;
}

public function queryParameters($queryParameters = null)
{
if (is_bool($queryParameters) && $queryParameters) {
return http_build_query($this->queryParameters);
}

if (is_null($queryParameters)) {
return $this->queryParameters;
}

$this->queryParameters = $queryParameters;

return $this;
}
}
195 changes: 195 additions & 0 deletions tests/Unit/HTTP/RequestTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
<?php

namespace Tests\Unit\HTTP;

use ArgumentCountError;
use Atproto\HTTP\Request;
use Faker\Factory;
use Faker\Generator;
use PHPUnit\Framework\TestCase;
use ReflectionClass;
use ReflectionProperty;
use TypeError;

class RequestTest extends TestCase
{
protected Request $request;
protected Generator $faker;

protected function setUp(): void
{
$this->request = new Request();
$this->faker = Factory::create();
}

/** @dataProvider methodProvider */
public function testMethodSetsCorrectValue(string $method, string $property): void
{
$key = $this->faker->word;
$expected = $this->faker->word;

try {
$reflectedProperty = $this->getPropertyValue($property);

$value = (is_array($reflectedProperty))
? [$key => $expected]
: $expected;

$this->request->$method($value);
} catch (TypeError $e) {
$this->request->$method($key, $expected);
}

$actual = $this->getPropertyValue($property);

if (is_array($actual)) {
$this->assertArrayHasKey($key, $actual);
$actual = $actual[$key];
}

$this->assertSame($expected, $actual);
}

/** @dataProvider methodProvider */
public function testMethodReturnsCorrectValue(string $method, string $property): void
{
$key = $this->faker->word;
$expected = $this->faker->word;

$propertyValue = $this->getPropertyValue($property);

if (is_array($propertyValue)) {
$expected = [$key => $expected];
}

$this->setPropertyValue($property, $expected);

try {
$actual = $this->request->$method();
} catch (ArgumentCountError $e) {
$actual = $this->request->$method($key);
$expected = current($expected);
}

$this->assertSame($expected, $actual);
}

/** @dataProvider methodProvider */
public function testMethodReturnsSameInstanceWhenSettingValue(string $method, string $property): void
{
$propertyValue = $this->getPropertyValue($property);

$value = is_array($propertyValue)
? [$this->faker->word]
: $this->faker->word;

try {
$actual = $this->request->$method($value);
} catch (TypeError $e) {
$actual = $this->request->$method($this->faker->word, $this->faker->word);
}

$this->assertInstanceOf(Request::class, $actual);
$this->assertSame($this->request, $actual);
}

/** @dataProvider encodableProvider */
public function testMethodsReturnEncodableContentCorrectly(string $property, string $method, string $verifier): void
{
$content = $this->randomArray();

$this->setPropertyValue($property, $content);

$expected = $content;
$actual = $this->request->$method(true);

$this->$verifier($expected, $actual);
}

public function encodableProvider(): array
{
return [
['headers', 'headers', 'assertHeadersEncodedCorrectly'],
['parameters', 'parameters', 'assertParametersEncodedCorrectly'],
['queryParameters', 'queryParameters', 'assertQueryParametersEncodedCorrectly'],
];
}

public function methodProvider(): array
{
# [method, property]

return [
['url', 'url'],
['path', 'path'],
['method', 'method'],
['header', 'headers'],
['headers', 'headers'],
['parameter', 'parameters'],
['parameters', 'parameters'],
['queryParameter', 'queryParameters'],
['queryParameters', 'queryParameters'],
];
}

protected function assertHeadersEncodedCorrectly(array $expected, array $actual): void
{
$expected = array_map(fn ($key, $value) => "$key: $value", array_keys($expected), array_values($expected));

$this->assertEquals($expected, $actual);
$this->assertIsArray($actual);
}

protected function assertParametersEncodedCorrectly(array $expected, string $actual): void
{
$expected = json_encode($expected);

$this->assertSame($expected, $actual);
$this->assertIsString($actual);
}

protected function assertQueryParametersEncodedCorrectly(array $expected, string $actual): void
{
$expected = http_build_query($expected);

$this->assertSame($expected, $actual);
$this->assertIsString($actual);
}

protected function randomArray(int $count = null): array
{
$count = $count ?: $this->faker->numberBetween(1, 100);

$keys = [];
$values = [];

for ($i = 0; $i < $count; $i++) {
$keys[] = $this->faker->word;
$values[] = $this->faker->word;
}

return array_combine(
$keys,
$values
);
}

protected function property(string $name): ReflectionProperty
{
$reflection = new ReflectionClass($this->request);
$property = $reflection->getProperty($name);
$property->setAccessible(true);

return $property;
}

protected function getPropertyValue(string $propertyName)
{
return $this->property($propertyName)->getValue($this->request);
}

protected function setPropertyValue(string $propertyName, $value): void
{
$this->property($propertyName)->setValue($this->request, $value);
}
}

0 comments on commit 16b2279

Please sign in to comment.