Skip to content

Commit

Permalink
Added hasStarted, isExpired and isDepleted methods to the Promotion i…
Browse files Browse the repository at this point in the history
…nterface/class

- Improvements and fixes to validity checks
- Added test cases for validity checks
  • Loading branch information
fulopattila122 committed Aug 24, 2024
1 parent f626ba1 commit 92d69d0
Show file tree
Hide file tree
Showing 6 changed files with 300 additions and 2 deletions.
6 changes: 6 additions & 0 deletions src/Promotion/Contracts/Promotion.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ public static function findByCouponCode(string $couponCode): ?Promotion;

public function isValid(?\DateTimeInterface $at = null): bool;

public function hasStarted(?\DateTimeInterface $at = null): bool;

public function isExpired(?\DateTimeInterface $at = null): bool;

public function isDepleted(): bool;

public function isEligible(object $subject): bool;

public function isCouponBased(): bool;
Expand Down
37 changes: 35 additions & 2 deletions src/Promotion/Models/Promotion.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,44 @@ public function getActions(): Collection

public function isValid(?\DateTimeInterface $at = null): bool
{
if (null !== $this->usage_limit && $this->usage_count >= $this->usage_limit) {
return !$this->isDepleted() && $this->hasStarted() && !$this->isExpired($at);
}

public function hasStarted(?\DateTimeInterface $at = null): bool
{
if (null === $this->starts_at) {
return true;
}

$baseDate = $at ?? Carbon::now($this->starts_at->getTimezone());
if (!$baseDate instanceof Carbon) {
$baseDate = Carbon::instance($baseDate);
}

return $baseDate->isAfter($this->starts_at);
}

public function isExpired(?\DateTimeInterface $at = null): bool
{
if (null === $this->ends_at) {
return false;
}

$baseDate = $at ?? Carbon::now($this->ends_at->getTimezone());
if (!$baseDate instanceof Carbon) {
$baseDate = Carbon::instance($baseDate);
}

return $baseDate->isAfter($this->ends_at);
}

public function isDepleted(): bool
{
if (null === $this->usage_limit) {
return false;
}

return null === $this->ends_at || $this->ends_at->isAfter($at ?? Carbon::now($this->ends_at->getTimezone()));
return $this->usage_count >= $this->usage_limit;
}

public function isEligible(object $subject): bool
Expand Down
40 changes: 40 additions & 0 deletions src/Promotion/Tests/PromotionDepletionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

declare(strict_types=1);

namespace Vanilo\Promotion\Tests;

use Vanilo\Promotion\Models\Promotion;

class PromotionDepletionTest extends TestCase
{
/** @test */
public function it_is_not_depleted_if_the_usage_limit_is_null()
{
$this->assertFalse((new Promotion())->isDepleted());
}

/** @test */
public function it_is_depleted_if_the_usage_limit_is_zero()
{
$this->assertTrue((new Promotion(['usage_limit' => 0]))->isDepleted());
}

/** @test */
public function it_is_depleted_if_the_usage_limit_equals_the_usage_count()
{
$this->assertTrue((new Promotion(['usage_limit' => 1, 'usage_count' => 1]))->isDepleted());
}

/** @test */
public function it_is_depleted_if_the_usage_limit_is_smaller_than_the_usage_count()
{
$this->assertTrue((new Promotion(['usage_limit' => 1, 'usage_count' => 2]))->isDepleted());
}

/** @test */
public function it_is_not_depleted_if_the_usage_limit_is_greater_than_the_usage_count()
{
$this->assertFalse((new Promotion(['usage_limit' => 3, 'usage_count' => 2]))->isDepleted());
}
}
53 changes: 53 additions & 0 deletions src/Promotion/Tests/PromotionExpiryTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

declare(strict_types=1);

namespace Vanilo\Promotion\Tests;

use Illuminate\Support\Carbon;
use Vanilo\Promotion\Models\Promotion;

class PromotionExpiryTest extends TestCase
{
/** @test */
public function it_is_not_expired_if_the_end_date_is_in_the_future()
{
$this->assertFalse((new Promotion(['ends_at' => Carbon::now()->addDay()]))->isExpired());
}

/** @test */
public function it_is_not_expired_if_the_end_date_is_null()
{
$this->assertFalse((new Promotion())->isExpired());
}

/** @test */
public function it_is_expired_if_the_end_date_is_now()
{
$this->assertTrue((new Promotion(['ends_at' => new \DateTime()]))->isExpired());
}

/** @test */
public function it_is_expired_if_the_end_date_is_in_the_past()
{
$this->assertTrue((new Promotion(['ends_at' => Carbon::yesterday()]))->isExpired());
}

/** @test */
public function it_can_tell_if_it_will_be_expired_at_a_future_date()
{
$tomorrow = new \DateTime('+1day');
$this->assertTrue((new Promotion(['ends_at' => new \DateTime()]))->isExpired($tomorrow));
$this->assertFalse((new Promotion(['ends_at' => new \DateTime('+2day')]))->isExpired($tomorrow));
$this->assertFalse((new Promotion())->isExpired($tomorrow));
}

/** @test */
public function it_can_tell_if_it_was_expired_at_a_past_date()
{
$yesterday = new \DateTime('-1day');
$this->assertFalse((new Promotion(['ends_at' => new \DateTime()]))->isExpired($yesterday));
$this->assertTrue((new Promotion(['ends_at' => new \DateTime('-2day')]))->isExpired($yesterday));
$this->assertFalse((new Promotion())->isExpired($yesterday));
}
}
53 changes: 53 additions & 0 deletions src/Promotion/Tests/PromotionStartTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

declare(strict_types=1);

namespace Vanilo\Promotion\Tests;

use Illuminate\Support\Carbon;
use Vanilo\Promotion\Models\Promotion;

class PromotionStartTest extends TestCase
{
/** @test */
public function it_has_not_started_if_the_start_date_is_in_the_future()
{
$this->assertFalse((new Promotion(['starts_at' => Carbon::now()->addDay()]))->hasStarted());
}

/** @test */
public function it_has_started_if_the_start_date_is_null()
{
$this->assertTrue((new Promotion())->hasStarted());
}

/** @test */
public function it_has_started_if_the_start_date_is_now()
{
$this->assertTrue((new Promotion(['starts_at' => new \DateTime()]))->hasStarted());
}

/** @test */
public function it_has_started_if_the_start_date_is_in_the_past()
{
$this->assertTrue((new Promotion(['starts_at' => Carbon::yesterday()]))->hasStarted());
}

/** @test */
public function it_can_tell_if_it_will_be_started_at_a_future_date()
{
$tomorrow = new \DateTime('+1day');
$this->assertTrue((new Promotion(['starts_at' => new \DateTime()]))->hasStarted($tomorrow));
$this->assertFalse((new Promotion(['starts_at' => new \DateTime('+2day')]))->hasStarted($tomorrow));
$this->assertTrue((new Promotion())->hasStarted($tomorrow));
}

/** @test */
public function it_can_tell_if_it_was_started_at_a_past_date()
{
$yesterday = new \DateTime('-1day');
$this->assertFalse((new Promotion(['starts_at' => new \DateTime()]))->hasStarted($yesterday));
$this->assertTrue((new Promotion(['starts_at' => new \DateTime('-2day')]))->hasStarted($yesterday));
$this->assertTrue((new Promotion())->hasStarted($yesterday));
}
}
113 changes: 113 additions & 0 deletions src/Promotion/Tests/PromotionValidityTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<?php

declare(strict_types=1);

namespace Vanilo\Promotion\Tests;

use Carbon\Carbon;
use Vanilo\Promotion\Models\Promotion;

class PromotionValidityTest extends TestCase
{
/** @test */
public function it_is_not_valid_if_it_has_not_been_started_yet()
{
$this->assertFalse((new Promotion(['starts_at' => Carbon::tomorrow()]))->isValid());
}

/** @test */
public function it_is_not_valid_if_it_has_already_expired()
{
$this->assertFalse((new Promotion(['ends_at' => Carbon::tomorrow()]))->isValid());
}

/** @test */
public function it_is_not_valid_if_it_is_depleted()
{
$this->assertFalse((new Promotion(['usage_limit' => 1, 'usage_count' => 1]))->isValid());
}

/** @test */
public function it_is_not_valid_if_it_has_started_not_expired_but_is_depleted()
{
$promo = new Promotion([
'usage_limit' => 1,
'usage_count' => 1,
'starts_at' => Carbon::yesterday(),
'ends_at' => Carbon::tomorrow(),
]);

$this->assertFalse($promo->isValid());
}

/** @test */
public function it_is_not_valid_if_it_has_not_started_yet_has_no_expiry_and_is_not_depleted()
{
$promo = new Promotion([
'usage_limit' => 10,
'usage_count' => 1,
'starts_at' => Carbon::tomorrow(),
]);

$this->assertFalse($promo->isValid());
}

/** @test */
public function it_is_not_valid_if_it_has_no_start_date_has_expired_and_is_not_depleted()
{
$promo = new Promotion([
'usage_limit' => 10,
'usage_count' => 1,
'ends_at' => Carbon::yesterday(),
]);

$this->assertFalse($promo->isValid());
}

/** @test */
public function it_is_valid_if_it_has_no_start_date_has_not_expired_and_is_not_depleted()
{
$promo = new Promotion([
'usage_limit' => 10,
'usage_count' => 1,
'ends_at' => Carbon::tomorrow(),
]);

$this->assertTrue($promo->isValid());
}

/** @test */
public function it_is_valid_if_it_has_started_has_not_expired_and_is_not_depleted()
{
$promo = new Promotion([
'usage_limit' => 2,
'usage_count' => 1,
'starts_at' => Carbon::yesterday(),
'ends_at' => Carbon::tomorrow(),
]);

$this->assertTrue($promo->isValid());
}

/** @test */
public function it_is_valid_if_it_has_started_has_not_expired_and_is_unlimited()
{
$promo = new Promotion([
'usage_limit' => null,
'usage_count' => 10000,
'starts_at' => Carbon::yesterday(),
'ends_at' => Carbon::tomorrow(),
]);

$this->assertTrue($promo->isValid());
}

/** @test */
public function it_is_valid_if_it_has_neither_start_nor_end_date_and_is_unlimited()
{
$promo = new Promotion();

$this->assertTrue($promo->isValid());
}

}

0 comments on commit 92d69d0

Please sign in to comment.