77use HiEvents \DomainObjects \EventDomainObject ;
88use HiEvents \DomainObjects \ProductDomainObject ;
99use HiEvents \DomainObjects \ProductPriceDomainObject ;
10+ use HiEvents \DomainObjects \PromoCodeDomainObject ;
1011use HiEvents \DomainObjects \Status \EventStatus ;
1112use HiEvents \Repository \Interfaces \EventRepositoryInterface ;
1213use HiEvents \Repository \Interfaces \PromoCodeRepositoryInterface ;
1920use Illuminate \Validation \ValidationException ;
2021use Mockery ;
2122use Mockery \MockInterface ;
23+ use Symfony \Component \HttpKernel \Exception \NotFoundHttpException ;
2224use Tests \TestCase ;
2325
2426class OrderCreateRequestValidationServiceTest extends TestCase
@@ -235,6 +237,180 @@ public function testUnrelatedOverReservedCapacityDoesNotBlockSelectedProduct():
235237 $ this ->assertTrue (true );
236238 }
237239
240+ public function testHiddenProductIsRejected (): void
241+ {
242+ $ eventId = 1 ;
243+ $ productId = 10 ;
244+ $ priceId = 101 ;
245+
246+ $ this ->setupMocks (
247+ eventId: $ eventId ,
248+ productId: $ productId ,
249+ priceIds: [$ priceId ],
250+ priceLabels: ['Hidden VIP ' ],
251+ availabilities: [
252+ ['price_id ' => $ priceId , 'quantity_available ' => 50 , 'quantity_reserved ' => 0 ],
253+ ],
254+ isHidden: true ,
255+ );
256+
257+ $ data = [
258+ 'products ' => [
259+ [
260+ 'product_id ' => $ productId ,
261+ 'quantities ' => [
262+ ['price_id ' => $ priceId , 'quantity ' => 1 ],
263+ ],
264+ ],
265+ ],
266+ ];
267+
268+ $ this ->expectException (NotFoundHttpException::class);
269+ $ this ->service ->validateRequestData ($ eventId , $ data );
270+ }
271+
272+ public function testProductHiddenWithoutPromoCodeIsRejectedWhenNoPromoCodeSupplied (): void
273+ {
274+ $ eventId = 1 ;
275+ $ productId = 10 ;
276+ $ priceId = 101 ;
277+
278+ $ this ->setupMocks (
279+ eventId: $ eventId ,
280+ productId: $ productId ,
281+ priceIds: [$ priceId ],
282+ priceLabels: ['Promo Only ' ],
283+ availabilities: [
284+ ['price_id ' => $ priceId , 'quantity_available ' => 50 , 'quantity_reserved ' => 0 ],
285+ ],
286+ isHiddenWithoutPromoCode: true ,
287+ );
288+
289+ $ data = [
290+ 'products ' => [
291+ [
292+ 'product_id ' => $ productId ,
293+ 'quantities ' => [
294+ ['price_id ' => $ priceId , 'quantity ' => 1 ],
295+ ],
296+ ],
297+ ],
298+ ];
299+
300+ $ this ->expectException (NotFoundHttpException::class);
301+ $ this ->service ->validateRequestData ($ eventId , $ data );
302+ }
303+
304+ public function testProductHiddenWithoutPromoCodeIsAllowedWithMatchingPromoCode (): void
305+ {
306+ $ eventId = 1 ;
307+ $ productId = 10 ;
308+ $ priceId = 101 ;
309+
310+ $ this ->setupMocks (
311+ eventId: $ eventId ,
312+ productId: $ productId ,
313+ priceIds: [$ priceId ],
314+ priceLabels: ['Promo Only ' ],
315+ availabilities: [
316+ ['price_id ' => $ priceId , 'quantity_available ' => 50 , 'quantity_reserved ' => 0 ],
317+ ],
318+ isHiddenWithoutPromoCode: true ,
319+ );
320+
321+ $ promoCode = Mockery::mock (PromoCodeDomainObject::class);
322+ $ promoCode ->shouldReceive ('isValid ' )->andReturn (true );
323+ $ promoCode ->shouldReceive ('appliesToProduct ' )->andReturn (true );
324+ $ this ->promoCodeRepository ->shouldReceive ('findFirstWhere ' )->andReturn ($ promoCode );
325+
326+ $ data = [
327+ 'promo_code ' => 'UNLOCK ' ,
328+ 'products ' => [
329+ [
330+ 'product_id ' => $ productId ,
331+ 'quantities ' => [
332+ ['price_id ' => $ priceId , 'quantity ' => 1 ],
333+ ],
334+ ],
335+ ],
336+ ];
337+
338+ $ this ->service ->validateRequestData ($ eventId , $ data );
339+ $ this ->assertTrue (true );
340+ }
341+
342+ public function testProductHiddenWithoutPromoCodeIsRejectedWhenPromoCodeDoesNotApply (): void
343+ {
344+ $ eventId = 1 ;
345+ $ productId = 10 ;
346+ $ priceId = 101 ;
347+
348+ $ this ->setupMocks (
349+ eventId: $ eventId ,
350+ productId: $ productId ,
351+ priceIds: [$ priceId ],
352+ priceLabels: ['Promo Only ' ],
353+ availabilities: [
354+ ['price_id ' => $ priceId , 'quantity_available ' => 50 , 'quantity_reserved ' => 0 ],
355+ ],
356+ isHiddenWithoutPromoCode: true ,
357+ );
358+
359+ $ promoCode = Mockery::mock (PromoCodeDomainObject::class);
360+ $ promoCode ->shouldReceive ('isValid ' )->andReturn (true );
361+ $ promoCode ->shouldReceive ('appliesToProduct ' )->andReturn (false );
362+ $ this ->promoCodeRepository ->shouldReceive ('findFirstWhere ' )->andReturn ($ promoCode );
363+
364+ $ data = [
365+ 'promo_code ' => 'WRONGPRODUCT ' ,
366+ 'products ' => [
367+ [
368+ 'product_id ' => $ productId ,
369+ 'quantities ' => [
370+ ['price_id ' => $ priceId , 'quantity ' => 1 ],
371+ ],
372+ ],
373+ ],
374+ ];
375+
376+ $ this ->expectException (NotFoundHttpException::class);
377+ $ this ->service ->validateRequestData ($ eventId , $ data );
378+ }
379+
380+ public function testHiddenPriceTierIsRejected (): void
381+ {
382+ $ eventId = 1 ;
383+ $ productId = 10 ;
384+ $ visiblePriceId = 101 ;
385+ $ hiddenPriceId = 102 ;
386+
387+ $ this ->setupMocks (
388+ eventId: $ eventId ,
389+ productId: $ productId ,
390+ priceIds: [$ visiblePriceId , $ hiddenPriceId ],
391+ priceLabels: ['General ' , 'Hidden VIP ' ],
392+ availabilities: [
393+ ['price_id ' => $ visiblePriceId , 'quantity_available ' => 50 , 'quantity_reserved ' => 0 ],
394+ ['price_id ' => $ hiddenPriceId , 'quantity_available ' => 50 , 'quantity_reserved ' => 0 ],
395+ ],
396+ hiddenPriceIds: [$ hiddenPriceId ],
397+ );
398+
399+ $ data = [
400+ 'products ' => [
401+ [
402+ 'product_id ' => $ productId ,
403+ 'quantities ' => [
404+ ['price_id ' => $ hiddenPriceId , 'quantity ' => 1 ],
405+ ],
406+ ],
407+ ],
408+ ];
409+
410+ $ this ->expectException (ValidationException::class);
411+ $ this ->service ->validateRequestData ($ eventId , $ data );
412+ }
413+
238414 private function setupMocks (
239415 int $ eventId ,
240416 int $ productId ,
@@ -243,6 +419,9 @@ private function setupMocks(
243419 array $ availabilities ,
244420 ?Collection $ capacities = null ,
245421 array $ extraAvailabilities = [],
422+ bool $ isHidden = false ,
423+ bool $ isHiddenWithoutPromoCode = false ,
424+ array $ hiddenPriceIds = [],
246425 ): void
247426 {
248427 $ event = Mockery::mock (EventDomainObject::class);
@@ -257,6 +436,7 @@ private function setupMocks(
257436 $ price = Mockery::mock (ProductPriceDomainObject::class);
258437 $ price ->shouldReceive ('getId ' )->andReturn ($ priceId );
259438 $ price ->shouldReceive ('getLabel ' )->andReturn ($ priceLabels [$ i ] ?? null );
439+ $ price ->shouldReceive ('getIsHidden ' )->andReturn (in_array ($ priceId , $ hiddenPriceIds , true ));
260440 $ productPrices ->push ($ price );
261441 }
262442
@@ -269,6 +449,8 @@ private function setupMocks(
269449 $ product ->shouldReceive ('isSoldOut ' )->andReturn (false );
270450 $ product ->shouldReceive ('getType ' )->andReturn (ProductPriceType::TIERED ->name );
271451 $ product ->shouldReceive ('getProductPrices ' )->andReturn ($ productPrices );
452+ $ product ->shouldReceive ('getIsHidden ' )->andReturn ($ isHidden );
453+ $ product ->shouldReceive ('getIsHiddenWithoutPromoCode ' )->andReturn ($ isHiddenWithoutPromoCode );
272454
273455 $ this ->productRepository ->shouldReceive ('loadRelation ' )->andReturnSelf ();
274456 $ this ->productRepository ->shouldReceive ('findWhereIn ' )->andReturn (new Collection ([$ product ]));
0 commit comments