Skip to content

Commit b4b16a4

Browse files
committed
refactor: replace Mockery with Http facade in SearchDocs tests and update request verification logic
- Added `guzzlehttp/guzzle` as a dependency in `composer.json`. - Updated `MakesHttpRequests` to support `testing` environment for SSL verification. - Simplified and modernized tests with `Http::fake` and `Http::assertSent`.
1 parent d656a6f commit b4b16a4

File tree

3 files changed

+72
-118
lines changed

3 files changed

+72
-118
lines changed

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
],
2121
"require": {
2222
"php": "^8.1|^8.2",
23+
"guzzlehttp/guzzle": "^7.9",
2324
"illuminate/console": "^10.0|^11.0|^12.0",
2425
"illuminate/contracts": "^10.0|^11.0|^12.0",
2526
"illuminate/routing": "^10.0|^11.0|^12.0",

src/Concerns/MakesHttpRequests.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ public function client(): PendingRequest
1616
'User-Agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:140.0) Gecko/20100101 Firefox/140.0 Laravel Boost',
1717
]);
1818

19-
// Disable SSL verification for local development URLs
20-
if (app()->environment('local') || str_contains(config('boost.hosted.api_url', ''), '.test')) {
19+
// Disable SSL verification for local development URLs and testing
20+
if (app()->environment(['local', 'testing']) || str_contains(config('boost.hosted.api_url', ''), '.test')) {
2121
$client = $client->withoutVerifying();
2222
}
2323

tests/Feature/Mcp/Tools/SearchDocsTest.php

Lines changed: 69 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22

33
declare(strict_types=1);
44

5-
use Illuminate\Http\Client\PendingRequest;
6-
use Illuminate\Http\Client\Response;
5+
use Illuminate\Support\Facades\Http;
76
use Laravel\Boost\Mcp\Tools\SearchDocs;
87
use Laravel\Mcp\Server\Tools\ToolResult;
98
use Laravel\Roster\Enums\Packages;
@@ -20,34 +19,29 @@
2019
$roster = Mockery::mock(Roster::class);
2120
$roster->shouldReceive('packages')->andReturn($packages);
2221

23-
$mockResponse = Mockery::mock(Response::class);
24-
$mockResponse->shouldReceive('successful')->andReturn(true);
25-
$mockResponse->shouldReceive('json')->andReturn([
26-
'results' => [
27-
['content' => 'Laravel documentation content'],
28-
['content' => 'Pest documentation content'],
29-
],
22+
Http::fake([
23+
'https://boost.laravel.com/api/docs' => Http::response('Documentation search results', 200),
3024
]);
3125

32-
$mockClient = Mockery::mock(PendingRequest::class);
33-
$mockClient->shouldReceive('asJson')->andReturnSelf();
34-
$mockClient->shouldReceive('post')->andReturn($mockResponse);
35-
36-
$tool = Mockery::mock(SearchDocs::class, [$roster])->makePartial();
37-
$tool->shouldReceive('client')->andReturn($mockClient);
38-
39-
$result = $tool->handle(['queries' => 'authentication, testing']);
26+
$tool = new SearchDocs($roster);
27+
$result = $tool->handle(['queries' => 'authentication###testing']);
4028

4129
expect($result)->toBeInstanceOf(ToolResult::class);
4230

4331
$data = $result->toArray();
44-
expect($data['isError'])->toBeFalse();
45-
46-
$content = json_decode($data['content'][0]['text'], true);
47-
expect($content['knowledge_count'])->toBe(2);
48-
expect($content['knowledge'])->toContain('Laravel documentation content');
49-
expect($content['knowledge'])->toContain('Pest documentation content');
50-
expect($content['knowledge'])->toContain('---');
32+
expect($data['isError'])->toBeFalse()
33+
->and($data['content'][0]['text'])->toBe('Documentation search results');
34+
35+
Http::assertSent(function ($request) {
36+
return $request->url() === 'https://boost.laravel.com/api/docs' &&
37+
$request->data()['queries'] === ['authentication', 'testing'] &&
38+
$request->data()['packages'] === [
39+
['name' => 'laravel/framework', 'version' => '11.x'],
40+
['name' => 'pestphp/pest', 'version' => '2.x'],
41+
] &&
42+
$request->data()['token_limit'] === 10000 &&
43+
$request->data()['format'] === 'markdown';
44+
});
5145
});
5246

5347
test('it handles API error response', function () {
@@ -58,25 +52,18 @@
5852
$roster = Mockery::mock(Roster::class);
5953
$roster->shouldReceive('packages')->andReturn($packages);
6054

61-
$mockResponse = Mockery::mock(Response::class);
62-
$mockResponse->shouldReceive('successful')->andReturn(false);
63-
$mockResponse->shouldReceive('status')->andReturn(500);
64-
$mockResponse->shouldReceive('body')->andReturn('API Error');
65-
66-
$mockClient = Mockery::mock(PendingRequest::class);
67-
$mockClient->shouldReceive('asJson')->andReturnSelf();
68-
$mockClient->shouldReceive('post')->andReturn($mockResponse);
69-
70-
$tool = Mockery::mock(SearchDocs::class, [$roster])->makePartial();
71-
$tool->shouldReceive('client')->andReturn($mockClient);
55+
Http::fake([
56+
'https://boost.laravel.com/api/docs' => Http::response('API Error', 500),
57+
]);
7258

59+
$tool = new SearchDocs($roster);
7360
$result = $tool->handle(['queries' => 'authentication']);
7461

7562
expect($result)->toBeInstanceOf(ToolResult::class);
7663

7764
$data = $result->toArray();
78-
expect($data['isError'])->toBeTrue();
79-
expect($data['content'][0]['text'])->toBe('Failed to search documentation: API Error');
65+
expect($data['isError'])->toBeTrue()
66+
->and($data['content'][0]['text'])->toBe('Failed to search documentation: API Error');
8067
});
8168

8269
test('it filters empty queries', function () {
@@ -85,28 +72,24 @@
8572
$roster = Mockery::mock(Roster::class);
8673
$roster->shouldReceive('packages')->andReturn($packages);
8774

88-
$mockResponse = Mockery::mock(Response::class);
89-
$mockResponse->shouldReceive('successful')->andReturn(true);
90-
$mockResponse->shouldReceive('json')->andReturn(['results' => []]);
91-
92-
$mockClient = Mockery::mock(PendingRequest::class);
93-
$mockClient->shouldReceive('asJson')->andReturnSelf();
94-
$mockClient->shouldReceive('post')->withArgs(function ($url, $payload) {
95-
return $url === 'https://boost.laravel.com/api/docs' &&
96-
$payload['queries'] === ['test'] &&
97-
empty($payload['packages']) &&
98-
$payload['token_limit'] === 10000;
99-
})->andReturn($mockResponse);
100-
101-
$tool = Mockery::mock(SearchDocs::class, [$roster])->makePartial();
102-
$tool->shouldReceive('client')->andReturn($mockClient);
75+
Http::fake([
76+
'https://boost.laravel.com/api/docs' => Http::response('Empty results', 200),
77+
]);
10378

79+
$tool = new SearchDocs($roster);
10480
$result = $tool->handle(['queries' => 'test### ###*### ']);
10581

10682
expect($result)->toBeInstanceOf(ToolResult::class);
10783

10884
$data = $result->toArray();
10985
expect($data['isError'])->toBeFalse();
86+
87+
Http::assertSent(function ($request) {
88+
return $request->url() === 'https://boost.laravel.com/api/docs' &&
89+
$request->data()['queries'] === ['test'] &&
90+
empty($request->data()['packages']) &&
91+
$request->data()['token_limit'] === 10000;
92+
});
11093
});
11194

11295
test('it formats package data correctly', function () {
@@ -118,28 +101,21 @@
118101
$roster = Mockery::mock(Roster::class);
119102
$roster->shouldReceive('packages')->andReturn($packages);
120103

121-
$mockResponse = Mockery::mock(Response::class);
122-
$mockResponse->shouldReceive('successful')->andReturn(true);
123-
$mockResponse->shouldReceive('json')->andReturn(['results' => []]);
124-
125-
$mockClient = Mockery::mock(PendingRequest::class);
126-
$mockClient->shouldReceive('asJson')->andReturnSelf();
127-
$mockClient->shouldReceive('post')->with(
128-
'https://boost.laravel.com/api/docs',
129-
Mockery::on(function ($payload) {
130-
return $payload['packages'] === [
131-
['name' => 'laravel/framework', 'version' => '11.x'],
132-
['name' => 'livewire/livewire', 'version' => '3.x'],
133-
] && $payload['token_limit'] === 10000;
134-
})
135-
)->andReturn($mockResponse);
136-
137-
$tool = Mockery::mock(SearchDocs::class, [$roster])->makePartial();
138-
$tool->shouldReceive('client')->andReturn($mockClient);
104+
Http::fake([
105+
'https://boost.laravel.com/api/docs' => Http::response('Package data results', 200),
106+
]);
139107

108+
$tool = new SearchDocs($roster);
140109
$result = $tool->handle(['queries' => 'test']);
141110

142111
expect($result)->toBeInstanceOf(ToolResult::class);
112+
113+
Http::assertSent(function ($request) {
114+
return $request->data()['packages'] === [
115+
['name' => 'laravel/framework', 'version' => '11.x'],
116+
['name' => 'livewire/livewire', 'version' => '3.x'],
117+
] && $request->data()['token_limit'] === 10000;
118+
});
143119
});
144120

145121
test('it handles empty results', function () {
@@ -148,27 +124,18 @@
148124
$roster = Mockery::mock(Roster::class);
149125
$roster->shouldReceive('packages')->andReturn($packages);
150126

151-
$mockResponse = Mockery::mock(Response::class);
152-
$mockResponse->shouldReceive('successful')->andReturn(true);
153-
$mockResponse->shouldReceive('json')->andReturn(['results' => []]);
154-
155-
$mockClient = Mockery::mock(PendingRequest::class);
156-
$mockClient->shouldReceive('asJson')->andReturnSelf();
157-
$mockClient->shouldReceive('post')->andReturn($mockResponse);
158-
159-
$tool = Mockery::mock(SearchDocs::class, [$roster])->makePartial();
160-
$tool->shouldReceive('client')->andReturn($mockClient);
127+
Http::fake([
128+
'https://boost.laravel.com/api/docs' => Http::response('Empty response', 200),
129+
]);
161130

131+
$tool = new SearchDocs($roster);
162132
$result = $tool->handle(['queries' => 'nonexistent']);
163133

164134
expect($result)->toBeInstanceOf(ToolResult::class);
165135

166136
$data = $result->toArray();
167-
expect($data['isError'])->toBeFalse();
168-
169-
$content = json_decode($data['content'][0]['text'], true);
170-
expect($content['knowledge_count'])->toBe(0);
171-
expect($content['knowledge'])->toBe('');
137+
expect($data['isError'])->toBeFalse()
138+
->and($data['content'][0]['text'])->toBe('Empty response');
172139
});
173140

174141
test('it uses custom token_limit when provided', function () {
@@ -177,25 +144,18 @@
177144
$roster = Mockery::mock(Roster::class);
178145
$roster->shouldReceive('packages')->andReturn($packages);
179146

180-
$mockResponse = Mockery::mock(Response::class);
181-
$mockResponse->shouldReceive('successful')->andReturn(true);
182-
$mockResponse->shouldReceive('json')->andReturn(['results' => []]);
183-
184-
$mockClient = Mockery::mock(PendingRequest::class);
185-
$mockClient->shouldReceive('asJson')->andReturnSelf();
186-
$mockClient->shouldReceive('post')->with(
187-
'https://boost.laravel.com/api/docs',
188-
Mockery::on(function ($payload) {
189-
return $payload['token_limit'] === 5000;
190-
})
191-
)->andReturn($mockResponse);
192-
193-
$tool = Mockery::mock(SearchDocs::class, [$roster])->makePartial();
194-
$tool->shouldReceive('client')->andReturn($mockClient);
147+
Http::fake([
148+
'https://boost.laravel.com/api/docs' => Http::response('Custom token limit results', 200),
149+
]);
195150

151+
$tool = new SearchDocs($roster);
196152
$result = $tool->handle(['queries' => 'test', 'token_limit' => 5000]);
197153

198154
expect($result)->toBeInstanceOf(ToolResult::class);
155+
156+
Http::assertSent(function ($request) {
157+
return $request->data()['token_limit'] === 5000;
158+
});
199159
});
200160

201161
test('it caps token_limit at maximum of 1000000', function () {
@@ -204,23 +164,16 @@
204164
$roster = Mockery::mock(Roster::class);
205165
$roster->shouldReceive('packages')->andReturn($packages);
206166

207-
$mockResponse = Mockery::mock(Response::class);
208-
$mockResponse->shouldReceive('successful')->andReturn(true);
209-
$mockResponse->shouldReceive('json')->andReturn(['results' => []]);
210-
211-
$mockClient = Mockery::mock(PendingRequest::class);
212-
$mockClient->shouldReceive('asJson')->andReturnSelf();
213-
$mockClient->shouldReceive('post')->with(
214-
'https://boost.laravel.com/api/docs',
215-
Mockery::on(function ($payload) {
216-
return $payload['token_limit'] === 1000000; // Should be capped at 1M
217-
})
218-
)->andReturn($mockResponse);
219-
220-
$tool = Mockery::mock(SearchDocs::class, [$roster])->makePartial();
221-
$tool->shouldReceive('client')->andReturn($mockClient);
167+
Http::fake([
168+
'https://boost.laravel.com/api/docs' => Http::response('Capped token limit results', 200),
169+
]);
222170

223-
$result = $tool->handle(['queries' => 'test', 'token_limit' => 2000000]); // Request 2M but get capped at 1M
171+
$tool = new SearchDocs($roster);
172+
$result = $tool->handle(['queries' => 'test', 'token_limit' => 2000000]);
224173

225174
expect($result)->toBeInstanceOf(ToolResult::class);
175+
176+
Http::assertSent(function ($request) {
177+
return $request->data()['token_limit'] === 1000000;
178+
});
226179
});

0 commit comments

Comments
 (0)