|
16 | 16 | use function Pest\Laravel\assertDatabaseEmpty; |
17 | 17 |
|
18 | 18 | use Saloon\Exceptions\Request\FatalRequestException; |
| 19 | +use Saloon\Barstool\Tests\Fixtures\Requests\PutRequest; |
19 | 20 | use Saloon\Barstool\Tests\Fixtures\Requests\PostRequest; |
| 21 | +use Saloon\Barstool\Tests\Fixtures\Requests\PatchRequest; |
| 22 | +use Saloon\Barstool\Tests\Fixtures\Requests\DeleteRequest; |
20 | 23 | use Saloon\Barstool\Tests\Fixtures\Requests\GetFileRequest; |
21 | 24 | use Saloon\Barstool\Tests\Fixtures\Requests\SoloUserRequest; |
22 | 25 | use Saloon\Barstool\Tests\Fixtures\Connectors\RandomConnector; |
| 26 | +use Saloon\Barstool\Tests\Fixtures\Requests\MultipartPostRequest; |
23 | 27 | use Saloon\Barstool\Tests\Fixtures\Requests\RequestWithConnector; |
24 | 28 |
|
25 | 29 | it('can be enabled', function () { |
|
715 | 719 |
|
716 | 720 | assertDatabaseCount('barstools', 1); |
717 | 721 | }); |
| 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 | +}); |
0 commit comments