Skip to content

Commit 01f2e3b

Browse files
committed
Add test coverage for HTTP methods, multipart bodies, content-type edge cases, job uniqueness, and response readability
- Test PUT, PATCH, DELETE methods are recorded correctly - Test multipart request bodies are recorded as <Multipart Body> - Test Content-Type with charset still records body - Test missing Content-Type returns unsupported content marker - Test job uniqueId is based on UUID and recording type - Test response remains fully readable after Barstool records it - Test streamed request bodies don't affect consumer code
1 parent 15b89b9 commit 01f2e3b

5 files changed

Lines changed: 243 additions & 0 deletions

File tree

tests/BarstoolTest.php

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,14 @@
1616
use function Pest\Laravel\assertDatabaseEmpty;
1717

1818
use Saloon\Exceptions\Request\FatalRequestException;
19+
use Saloon\Barstool\Tests\Fixtures\Requests\PutRequest;
1920
use Saloon\Barstool\Tests\Fixtures\Requests\PostRequest;
21+
use Saloon\Barstool\Tests\Fixtures\Requests\PatchRequest;
22+
use Saloon\Barstool\Tests\Fixtures\Requests\DeleteRequest;
2023
use Saloon\Barstool\Tests\Fixtures\Requests\GetFileRequest;
2124
use Saloon\Barstool\Tests\Fixtures\Requests\SoloUserRequest;
2225
use Saloon\Barstool\Tests\Fixtures\Connectors\RandomConnector;
26+
use Saloon\Barstool\Tests\Fixtures\Requests\MultipartPostRequest;
2327
use Saloon\Barstool\Tests\Fixtures\Requests\RequestWithConnector;
2428

2529
it('can be enabled', function () {
@@ -715,3 +719,158 @@
715719

716720
assertDatabaseCount('barstools', 1);
717721
});
722+
723+
it('correctly records multipart request bodies', function () {
724+
config()->set('barstool.enabled', true);
725+
726+
MockClient::global([
727+
MultipartPostRequest::class => MockResponse::make(
728+
body: ['success' => true],
729+
status: 200,
730+
headers: ['Content-Type' => 'application/json'],
731+
),
732+
]);
733+
734+
$connector = new RandomConnector;
735+
$request = new MultipartPostRequest;
736+
$request->body()->add('file', 'file-contents', 'document.txt');
737+
$response = $connector->send($request);
738+
739+
$barstool = Barstool::where('uuid', $response->getPendingRequest()->headers()->get('X-Barstool-UUID'))->sole();
740+
741+
expect($barstool)
742+
->request_class->toBe(MultipartPostRequest::class)
743+
->method->toBe('POST')
744+
->request_body->toBe('<Multipart Body>');
745+
});
746+
747+
it('correctly records PUT, PATCH and DELETE request methods', function () {
748+
config()->set('barstool.enabled', true);
749+
750+
MockClient::global([
751+
PutRequest::class => MockResponse::make(body: [], status: 200, headers: ['Content-Type' => 'application/json']),
752+
PatchRequest::class => MockResponse::make(body: [], status: 200, headers: ['Content-Type' => 'application/json']),
753+
DeleteRequest::class => MockResponse::make(body: [], status: 204, headers: ['Content-Type' => 'application/json']),
754+
]);
755+
756+
$connector = new RandomConnector;
757+
758+
$putResponse = $connector->send(new PutRequest);
759+
$patchResponse = $connector->send(new PatchRequest);
760+
$deleteResponse = $connector->send(new DeleteRequest);
761+
762+
$putBarstool = Barstool::where('uuid', $putResponse->getPendingRequest()->headers()->get('X-Barstool-UUID'))->sole();
763+
$patchBarstool = Barstool::where('uuid', $patchResponse->getPendingRequest()->headers()->get('X-Barstool-UUID'))->sole();
764+
$deleteBarstool = Barstool::where('uuid', $deleteResponse->getPendingRequest()->headers()->get('X-Barstool-UUID'))->sole();
765+
766+
expect($putBarstool)->method->toBe('PUT');
767+
expect($patchBarstool)->method->toBe('PATCH');
768+
expect($deleteBarstool)->method->toBe('DELETE');
769+
});
770+
771+
it('records response body when Content-Type includes charset', function () {
772+
config()->set('barstool.enabled', true);
773+
774+
MockClient::global([
775+
SoloUserRequest::class => MockResponse::make(
776+
body: ['name' => 'John Wayne'],
777+
status: 200,
778+
headers: ['Content-Type' => 'application/json; charset=utf-8'],
779+
),
780+
]);
781+
782+
$response = (new SoloUserRequest)->send();
783+
784+
$barstool = Barstool::where('uuid', $response->getPsrRequest()->getHeader('X-Barstool-UUID')[0])->sole();
785+
786+
expect($barstool)
787+
->response_body->toBe(json_encode(['name' => 'John Wayne']))
788+
->response_status->toBe(200);
789+
});
790+
791+
it('does not record response body when Content-Type is missing', function () {
792+
config()->set('barstool.enabled', true);
793+
794+
MockClient::global([
795+
SoloUserRequest::class => MockResponse::make(
796+
body: 'some response',
797+
status: 200,
798+
),
799+
]);
800+
801+
$response = (new SoloUserRequest)->send();
802+
803+
$barstool = Barstool::where('uuid', $response->getPsrRequest()->getHeader('X-Barstool-UUID')[0])->sole();
804+
805+
expect($barstool)->response_body->toBe('<Unsupported Barstool Response Content>');
806+
});
807+
808+
it('does not affect response readability after recording', function () {
809+
config()->set('barstool.enabled', true);
810+
811+
$expectedBody = ['data' => [['name' => 'John Wayne'], ['name' => 'Billy the Kid']]];
812+
813+
MockClient::global([
814+
SoloUserRequest::class => MockResponse::make(
815+
body: $expectedBody,
816+
status: 200,
817+
headers: ['Content-Type' => 'application/json'],
818+
),
819+
]);
820+
821+
$response = (new SoloUserRequest)->send();
822+
823+
// Barstool has already recorded the response via middleware at this point.
824+
// Verify the response is still fully readable for consumer code.
825+
expect($response->body())->toBe(json_encode($expectedBody));
826+
expect($response->json())->toBe($expectedBody);
827+
expect($response->json('data'))->toBe($expectedBody['data']);
828+
expect($response->status())->toBe(200);
829+
830+
// Reading multiple times should still work
831+
expect($response->body())->toBe(json_encode($expectedBody));
832+
});
833+
834+
it('does not affect streamed request body for consumer code', function () {
835+
config()->set('barstool.enabled', true);
836+
837+
MockClient::global([
838+
PostRequest::class => MockResponse::make(
839+
body: ['success' => true],
840+
status: 200,
841+
headers: ['Content-Type' => 'application/json'],
842+
),
843+
]);
844+
845+
$connector = new RandomConnector;
846+
$request = new PostRequest;
847+
$stream = fopen(__DIR__.'/yeehaw.txt', 'r');
848+
$request->body()->set($stream);
849+
$response = $connector->send($request);
850+
851+
// Barstool records '<Streamed Body>' without consuming the actual stream.
852+
// Verify the response is still fully readable.
853+
expect($response->json())->toBe(['success' => true]);
854+
expect($response->status())->toBe(200);
855+
856+
$barstool = Barstool::where('uuid', $response->getPendingRequest()->headers()->get('X-Barstool-UUID'))->sole();
857+
expect($barstool->request_body)->toBe('<Streamed Body>');
858+
});
859+
860+
it('generates unique job IDs based on UUID and recording type', function () {
861+
$requestJob = new RecordBarstoolJob(RecordingType::REQUEST, ['method' => 'GET'], 'test-uuid-123');
862+
$responseJob = new RecordBarstoolJob(RecordingType::RESPONSE, ['status' => 200], 'test-uuid-123');
863+
$fatalJob = new RecordBarstoolJob(RecordingType::FATAL, ['error' => 'fail'], 'test-uuid-123');
864+
865+
expect($requestJob->uniqueId())->toBe('test-uuid-123-request');
866+
expect($responseJob->uniqueId())->toBe('test-uuid-123-response');
867+
expect($fatalJob->uniqueId())->toBe('test-uuid-123-fatal');
868+
869+
// Same type + same UUID = same unique ID
870+
$duplicateJob = new RecordBarstoolJob(RecordingType::REQUEST, ['method' => 'POST'], 'test-uuid-123');
871+
expect($duplicateJob->uniqueId())->toBe($requestJob->uniqueId());
872+
873+
// Different UUID = different unique ID
874+
$differentUuidJob = new RecordBarstoolJob(RecordingType::REQUEST, ['method' => 'GET'], 'other-uuid-456');
875+
expect($differentUuidJob->uniqueId())->not->toBe($requestJob->uniqueId());
876+
});
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Saloon\Barstool\Tests\Fixtures\Requests;
6+
7+
use Saloon\Enums\Method;
8+
use Saloon\Http\Request;
9+
10+
class DeleteRequest extends Request
11+
{
12+
protected Method $method = Method::DELETE;
13+
14+
public function resolveEndpoint(): string
15+
{
16+
return 'user/1';
17+
}
18+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Saloon\Barstool\Tests\Fixtures\Requests;
6+
7+
use Saloon\Enums\Method;
8+
use Saloon\Http\Request;
9+
use Saloon\Contracts\Body\HasBody;
10+
use Saloon\Traits\Body\HasMultipartBody;
11+
12+
class MultipartPostRequest extends Request implements HasBody
13+
{
14+
use HasMultipartBody;
15+
16+
protected Method $method = Method::POST;
17+
18+
public function resolveEndpoint(): string
19+
{
20+
return 'upload';
21+
}
22+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Saloon\Barstool\Tests\Fixtures\Requests;
6+
7+
use Saloon\Enums\Method;
8+
use Saloon\Http\Request;
9+
use Saloon\Contracts\Body\HasBody;
10+
use Saloon\Traits\Body\HasJsonBody;
11+
12+
class PatchRequest extends Request implements HasBody
13+
{
14+
use HasJsonBody;
15+
16+
protected Method $method = Method::PATCH;
17+
18+
public function resolveEndpoint(): string
19+
{
20+
return 'user';
21+
}
22+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Saloon\Barstool\Tests\Fixtures\Requests;
6+
7+
use Saloon\Enums\Method;
8+
use Saloon\Http\Request;
9+
use Saloon\Contracts\Body\HasBody;
10+
use Saloon\Traits\Body\HasJsonBody;
11+
12+
class PutRequest extends Request implements HasBody
13+
{
14+
use HasJsonBody;
15+
16+
protected Method $method = Method::PUT;
17+
18+
public function resolveEndpoint(): string
19+
{
20+
return 'user';
21+
}
22+
}

0 commit comments

Comments
 (0)