1+ <?php
2+
3+ namespace Tests \Feature ;
4+
5+ use App \Models \Product ;
6+ use App \Models \Category ;
7+ use App \Models \User ;
8+ use Illuminate \Foundation \Testing \RefreshDatabase ;
9+ use Tests \TestCase ;
10+
11+ class SearchApiTest extends TestCase
12+ {
13+ use RefreshDatabase;
14+
15+ protected $ user ;
16+ protected $ electronicsCategory ;
17+ protected $ booksCategory ;
18+ protected $ laptop ;
19+ protected $ phone ;
20+ protected $ novel ;
21+
22+ protected function setUp (): void
23+ {
24+ parent ::setUp ();
25+
26+ $ this ->user = User::factory ()->create ();
27+
28+ // Create test categories
29+ $ this ->electronicsCategory = Category::factory ()->create ([
30+ 'name ' => 'Electronics ' ,
31+ 'slug ' => 'electronics ' ,
32+ ]);
33+
34+ $ this ->booksCategory = Category::factory ()->create ([
35+ 'name ' => 'Books ' ,
36+ 'slug ' => 'books ' ,
37+ ]);
38+
39+ // Create test products
40+ $ this ->laptop = Product::factory ()->create ([
41+ 'name ' => 'Gaming Laptop ' ,
42+ 'slug ' => 'gaming-laptop ' ,
43+ 'description ' => 'High-performance gaming laptop with RGB keyboard ' ,
44+ 'price ' => 1299.99 ,
45+ 'is_active ' => true ,
46+ 'category_id ' => $ this ->electronicsCategory ->id ,
47+ ]);
48+
49+ $ this ->phone = Product::factory ()->create ([
50+ 'name ' => 'Smartphone Pro ' ,
51+ 'slug ' => 'smartphone-pro ' ,
52+ 'description ' => 'Latest smartphone with advanced camera features ' ,
53+ 'price ' => 899.99 ,
54+ 'is_active ' => true ,
55+ 'category_id ' => $ this ->electronicsCategory ->id ,
56+ ]);
57+
58+ $ this ->novel = Product::factory ()->create ([
59+ 'name ' => 'Science Fiction Novel ' ,
60+ 'slug ' => 'science-fiction-novel ' ,
61+ 'description ' => 'Epic space adventure novel ' ,
62+ 'price ' => 19.99 ,
63+ 'is_active ' => true ,
64+ 'category_id ' => $ this ->booksCategory ->id ,
65+ ]);
66+ }
67+
68+ /** @test */
69+ public function it_can_search_products_by_name ()
70+ {
71+ $ response = $ this ->getJson ('/api/search?q=laptop ' );
72+
73+ $ response ->assertStatus (200 )
74+ ->assertJson ([
75+ 'success ' => true ,
76+ ])
77+ ->assertJsonStructure ([
78+ 'success ' ,
79+ 'data ' => [
80+ 'products ' => [
81+ '* ' => [
82+ 'id ' ,
83+ 'name ' ,
84+ 'slug ' ,
85+ 'price ' ,
86+ 'category '
87+ ]
88+ ],
89+ 'categories ' => [
90+ '* ' => [
91+ 'id ' ,
92+ 'name ' ,
93+ 'slug '
94+ ]
95+ ],
96+ 'total ' ,
97+ 'per_page ' ,
98+ 'current_page ' ,
99+ 'last_page '
100+ ],
101+ 'message '
102+ ]);
103+
104+ $ products = $ response ->json ('data.products ' );
105+ $ this ->assertGreaterThan (0 , count ($ products ));
106+ $ this ->assertStringContainsString ('Laptop ' , $ products [0 ]['name ' ]);
107+ }
108+
109+ /** @test */
110+ public function it_can_search_products_by_category ()
111+ {
112+ $ response = $ this ->getJson ('/api/search?category=electronics ' );
113+
114+ $ response ->assertStatus (200 );
115+
116+ $ products = $ response ->json ('data.products ' );
117+ foreach ($ products as $ product ) {
118+ $ this ->assertEquals ('electronics ' , $ product ['category ' ]['slug ' ]);
119+ }
120+ }
121+
122+ /** @test */
123+ public function it_can_filter_search_by_price_range ()
124+ {
125+ $ response = $ this ->getJson ('/api/search?min_price=800&max_price=1000 ' );
126+
127+ $ response ->assertStatus (200 );
128+
129+ $ products = $ response ->json ('data.products ' );
130+ foreach ($ products as $ product ) {
131+ $ this ->assertGreaterThanOrEqual (800 , $ product ['price ' ]);
132+ $ this ->assertLessThanOrEqual (1000 , $ product ['price ' ]);
133+ }
134+ }
135+
136+ /** @test */
137+ public function it_returns_empty_results_for_no_matches ()
138+ {
139+ $ response = $ this ->getJson ('/api/search?q=nonexistentproduct ' );
140+
141+ $ response ->assertStatus (200 );
142+
143+ $ products = $ response ->json ('data.products ' );
144+ $ this ->assertCount (0 , $ products );
145+ }
146+
147+ /** @test */
148+ public function it_validates_search_query_length ()
149+ {
150+ $ response = $ this ->getJson ('/api/search?q=a ' );
151+
152+ $ response ->assertStatus (422 )
153+ ->assertJsonValidationErrors (['q ' ]);
154+ }
155+
156+ /** @test */
157+ public function it_validates_price_range_parameters ()
158+ {
159+ $ response = $ this ->getJson ('/api/search?min_price=-10 ' );
160+
161+ $ response ->assertStatus (422 )
162+ ->assertJsonValidationErrors (['min_price ' ]);
163+ }
164+
165+ /** @test */
166+ public function it_can_search_categories ()
167+ {
168+ $ response = $ this ->getJson ('/api/search?q=electro ' );
169+
170+ $ response ->assertStatus (200 );
171+
172+ $ categories = $ response ->json ('data.categories ' );
173+ $ this ->assertGreaterThan (0 , count ($ categories ));
174+
175+ $ electronicsFound = false ;
176+ foreach ($ categories as $ category ) {
177+ if ($ category ['slug ' ] === 'electronics ' ) {
178+ $ electronicsFound = true ;
179+ break ;
180+ }
181+ }
182+ $ this ->assertTrue ($ electronicsFound );
183+ }
184+
185+ /** @test */
186+ public function it_searches_in_category_names_and_descriptions ()
187+ {
188+ $ response = $ this ->getJson ('/api/search?q=books ' );
189+
190+ $ response ->assertStatus (200 );
191+
192+ $ categories = $ response ->json ('data.categories ' );
193+ $ this ->assertGreaterThan (0 , count ($ categories ));
194+ }
195+
196+ /** @test */
197+ public function it_paginates_search_results_correctly ()
198+ {
199+ // Create more products for pagination testing
200+ for ($ i = 0 ; $ i < 25 ; $ i ++) {
201+ Product::factory ()->create ([
202+ 'name ' => "Product {$ i }" ,
203+ 'price ' => 10.00 + $ i ,
204+ 'is_active ' => true ,
205+ 'category_id ' => $ this ->electronicsCategory ->id ,
206+ ]);
207+ }
208+
209+ $ response = $ this ->getJson ('/api/search?q=Product&per_page=10&page=1 ' );
210+
211+ $ response ->assertStatus (200 );
212+
213+ $ data = $ response ->json ('data ' );
214+ $ this ->assertEquals (10 , $ data ['per_page ' ]);
215+ $ this ->assertEquals (1 , $ data ['current_page ' ]);
216+ $ this ->assertLessThanOrEqual (10 , count ($ data ['products ' ]));
217+ }
218+
219+ /** @test */
220+ public function it_handles_page_parameter_correctly ()
221+ {
222+ $ response = $ this ->getJson ('/api/search?q=Product&per_page=5&page=2 ' );
223+
224+ $ response ->assertStatus (200 );
225+
226+ $ data = $ response ->json ('data ' );
227+ $ this ->assertEquals (2 , $ data ['current_page ' ]);
228+ $ this ->assertEquals (5 , $ data ['per_page ' ]);
229+ }
230+
231+ /** @test */
232+ public function it_includes_search_metadata_in_response ()
233+ {
234+ $ response = $ this ->getJson ('/api/search?q=laptop ' );
235+
236+ $ response ->assertStatus (200 );
237+
238+ $ data = $ response ->json ('data ' );
239+ $ this ->assertArrayHasKey ('total ' , $ data );
240+ $ this ->assertArrayHasKey ('per_page ' , $ data );
241+ $ this ->assertArrayHasKey ('current_page ' , $ data );
242+ $ this ->assertArrayHasKey ('last_page ' , $ data );
243+ }
244+
245+ /** @test */
246+ public function it_handles_large_result_sets_efficiently ()
247+ {
248+ // Create many products
249+ for ($ i = 0 ; $ i < 100 ; $ i ++) {
250+ Product::factory ()->create ([
251+ 'name ' => "Searchable Product {$ i }" ,
252+ 'is_active ' => true ,
253+ 'category_id ' => $ this ->electronicsCategory ->id ,
254+ ]);
255+ }
256+
257+ $ startTime = microtime (true );
258+ $ response = $ this ->getJson ('/api/search?q=Searchable ' );
259+ $ endTime = microtime (true );
260+
261+ $ response ->assertStatus (200 );
262+ $ responseTime = ($ endTime - $ startTime ) * 1000 ; // Convert to milliseconds
263+
264+ // Should respond within reasonable time (less than 1 second)
265+ $ this ->assertLessThan (1000 , $ responseTime );
266+ }
267+
268+ /** @test */
269+ public function it_handles_malformed_search_requests_gracefully ()
270+ {
271+ $ response = $ this ->getJson ('/api/search ' );
272+
273+ // Should either return empty results or require query parameter
274+ $ this ->assertContains ($ response ->status (), [200 , 422 ]);
275+ }
276+
277+ /** @test */
278+ public function it_handles_database_connection_issues_gracefully ()
279+ {
280+ // This would require mocking database failures
281+ // For now, we'll test with invalid parameters
282+ $ response = $ this ->getJson ('/api/search?q= ' . str_repeat ('a ' , 1000 ));
283+
284+ $ response ->assertStatus (422 );
285+ }
286+ }
0 commit comments