From 55c8221b127d269c50676eaeeb878a906c666822 Mon Sep 17 00:00:00 2001 From: AHMED GAMAL <76778937+AHMED-GAMAL-AG@users.noreply.github.com> Date: Thu, 20 Feb 2025 13:42:13 +0200 Subject: [PATCH] Refactors model bindings to use config-based resolution Replaces direct model class dependencies with configurable model bindings to improve flexibility and maintainability. Now model classes can be swapped out via configuration without modifying core code. Key changes: - Replaces hardcoded model class references with config lookups - Adds validation for model instance types - Updates tests to use configured model classes - Maintains backward compatibility with existing model structure This improves package extensibility by allowing users to override default models through configuration rather than inheritance. --- .../factories/FeatureConsumptionFactory.php | 12 +- database/factories/FeatureFactory.php | 8 +- database/factories/PlanFactory.php | 8 +- database/factories/SubscriptionFactory.php | 11 +- .../factories/SubscriptionRenewalFactory.php | 10 +- ...2_01_235539_create_subscriptions_table.php | 3 +- ...0527_create_feature_consumptions_table.php | 2 +- ...206_create_subscription_renewals_table.php | 2 +- ...02_04_001622_create_feature_plan_table.php | 4 +- ...25_001622_create_feature_tickets_table.php | 2 +- src/Events/FeatureConsumed.php | 34 +++- src/Events/FeatureTicketCreated.php | 34 +++- src/Events/SubscriptionCanceled.php | 23 ++- src/Events/SubscriptionRenewed.php | 23 ++- src/Events/SubscriptionScheduled.php | 23 ++- src/Events/SubscriptionStarted.php | 22 ++- src/Events/SubscriptionSuppressed.php | 21 +- src/Models/Concerns/HasSubscriptions.php | 87 ++++++--- .../Concerns/ExpiresAndHasGraceDaysTest.php | 183 ++++++++---------- tests/Models/Concerns/ExpiresTest.php | 151 +++++++-------- .../Models/Concerns/HandlesRecurrenceTest.php | 44 +++-- .../Models/Concerns/HasSubscriptionsTest.php | 164 ++++++++-------- tests/Models/Concerns/StartsTest.php | 31 ++- tests/Models/Concerns/SuppressesTest.php | 31 ++- tests/Models/FeaturePlanTest.php | 21 +- tests/Models/FeatureTest.php | 21 +- tests/Models/PlanTest.php | 16 +- tests/Models/SubscriptionTest.php | 72 ++++--- tests/Scopes/ExpiringScopeTest.php | 71 ++++--- .../Scopes/ExpiringWithGraceDaysScopeTest.php | 119 +++++++----- tests/Scopes/StartingScopeTest.php | 83 +++++--- tests/Scopes/SuppressingScopeTest.php | 84 +++++--- 32 files changed, 815 insertions(+), 605 deletions(-) diff --git a/database/factories/FeatureConsumptionFactory.php b/database/factories/FeatureConsumptionFactory.php index ee1930f..3520959 100644 --- a/database/factories/FeatureConsumptionFactory.php +++ b/database/factories/FeatureConsumptionFactory.php @@ -2,13 +2,15 @@ namespace LucasDotVin\Soulbscription\Database\Factories; -use LucasDotVin\Soulbscription\Models\Feature; use Illuminate\Database\Eloquent\Factories\Factory; -use LucasDotVin\Soulbscription\Models\FeatureConsumption; - class FeatureConsumptionFactory extends Factory { - protected $model = FeatureConsumption::class; + protected $model; + + public function __construct() + { + $this->model = config('soulbscription.models.feature_consumption'); + } /** * Define the model's default state. @@ -18,7 +20,7 @@ class FeatureConsumptionFactory extends Factory public function definition() { return [ - 'feature_id' => Feature::factory(), + 'feature_id' => config('soulbscription.models.feature')::factory(), 'consumption' => $this->faker->randomFloat(), 'expired_at' => $this->faker->dateTime(), 'subscriber_id' => $this->faker->randomNumber(), diff --git a/database/factories/FeatureFactory.php b/database/factories/FeatureFactory.php index d6ad559..cadbeac 100644 --- a/database/factories/FeatureFactory.php +++ b/database/factories/FeatureFactory.php @@ -4,11 +4,15 @@ use LucasDotVin\Soulbscription\Enums\PeriodicityType; use Illuminate\Database\Eloquent\Factories\Factory; -use LucasDotVin\Soulbscription\Models\Feature; class FeatureFactory extends Factory { - protected $model = Feature::class; + protected $model; + + public function __construct() + { + $this->model = config('soulbscription.models.feature'); + } /** * Define the model's default state. diff --git a/database/factories/PlanFactory.php b/database/factories/PlanFactory.php index f391db9..5cb0ccd 100644 --- a/database/factories/PlanFactory.php +++ b/database/factories/PlanFactory.php @@ -4,11 +4,15 @@ use LucasDotVin\Soulbscription\Enums\PeriodicityType; use Illuminate\Database\Eloquent\Factories\Factory; -use LucasDotVin\Soulbscription\Models\Plan; class PlanFactory extends Factory { - protected $model = Plan::class; + protected $model; + + public function __construct() + { + $this->model = config('soulbscription.models.plan'); + } /** * Define the model's default state. diff --git a/database/factories/SubscriptionFactory.php b/database/factories/SubscriptionFactory.php index dd3e1a0..c796389 100644 --- a/database/factories/SubscriptionFactory.php +++ b/database/factories/SubscriptionFactory.php @@ -2,13 +2,16 @@ namespace LucasDotVin\Soulbscription\Database\Factories; -use LucasDotVin\Soulbscription\Models\Plan; use Illuminate\Database\Eloquent\Factories\Factory; -use LucasDotVin\Soulbscription\Models\Subscription; class SubscriptionFactory extends Factory { - protected $model = Subscription::class; + protected $model; + + public function __construct() + { + $this->model = config('soulbscription.models.subscription'); + } /** * Define the model's default state. @@ -18,7 +21,7 @@ class SubscriptionFactory extends Factory public function definition() { return [ - 'plan_id' => Plan::factory(), + 'plan_id' => config('soulbscription.models.plan')::factory(), 'canceled_at' => null, 'started_at' => $this->faker->dateTime(), 'suppressed_at' => null, diff --git a/database/factories/SubscriptionRenewalFactory.php b/database/factories/SubscriptionRenewalFactory.php index a54c207..06d4beb 100644 --- a/database/factories/SubscriptionRenewalFactory.php +++ b/database/factories/SubscriptionRenewalFactory.php @@ -2,12 +2,16 @@ namespace LucasDotVin\Soulbscription\Database\Factories; -use LucasDotVin\Soulbscription\Models\{Subscription, SubscriptionRenewal}; use Illuminate\Database\Eloquent\Factories\Factory; class SubscriptionRenewalFactory extends Factory { - protected $model = SubscriptionRenewal::class; + protected $model; + + public function __construct() + { + $this->model = config('soulbscription.models.subscription_renewal'); + } /** * Define the model's default state. @@ -17,7 +21,7 @@ class SubscriptionRenewalFactory extends Factory public function definition() { return [ - 'subscription_id' => Subscription::factory(), + 'subscription_id' => config('soulbscription.models.subscription')::factory(), 'overdue' => $this->faker->boolean(), 'renewal' => $this->faker->boolean(), ]; diff --git a/database/migrations/2022_02_01_235539_create_subscriptions_table.php b/database/migrations/2022_02_01_235539_create_subscriptions_table.php index 5cf7c64..6763b2c 100644 --- a/database/migrations/2022_02_01_235539_create_subscriptions_table.php +++ b/database/migrations/2022_02_01_235539_create_subscriptions_table.php @@ -2,7 +2,6 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; -use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Schema; return new class() extends Migration { @@ -15,7 +14,7 @@ public function up() { Schema::create('subscriptions', function (Blueprint $table) { $table->id(); - $table->foreignIdFor(\LucasDotVin\Soulbscription\Models\Plan::class); + $table->foreignIdFor(config('soulbscription.models.plan')::class); $table->timestamp('canceled_at')->nullable(); $table->timestamp('expired_at')->nullable(); $table->timestamp('grace_days_ended_at')->nullable(); diff --git a/database/migrations/2022_02_02_000527_create_feature_consumptions_table.php b/database/migrations/2022_02_02_000527_create_feature_consumptions_table.php index 526eb92..bf5b2d1 100644 --- a/database/migrations/2022_02_02_000527_create_feature_consumptions_table.php +++ b/database/migrations/2022_02_02_000527_create_feature_consumptions_table.php @@ -16,7 +16,7 @@ public function up() $table->id(); $table->decimal('consumption')->unsigned()->nullable(); $table->timestamp('expired_at')->nullable(); - $table->foreignIdFor(\LucasDotVin\Soulbscription\Models\Feature::class)->constrained()->cascadeOnDelete(); + $table->foreignIdFor(config('soulbscription.models.feature')::class)->constrained()->cascadeOnDelete(); $table->timestamps(); if (config('soulbscription.models.subscriber.uses_uuid')) { diff --git a/database/migrations/2022_02_03_001206_create_subscription_renewals_table.php b/database/migrations/2022_02_03_001206_create_subscription_renewals_table.php index beeaa12..dc13026 100644 --- a/database/migrations/2022_02_03_001206_create_subscription_renewals_table.php +++ b/database/migrations/2022_02_03_001206_create_subscription_renewals_table.php @@ -16,7 +16,7 @@ public function up() $table->id(); $table->boolean('overdue'); $table->boolean('renewal'); - $table->foreignIdFor(\LucasDotVin\Soulbscription\Models\Subscription::class); + $table->foreignIdFor(config('soulbscription.models.subscription')::class); $table->timestamps(); }); } diff --git a/database/migrations/2022_02_04_001622_create_feature_plan_table.php b/database/migrations/2022_02_04_001622_create_feature_plan_table.php index c86f672..17c95fd 100644 --- a/database/migrations/2022_02_04_001622_create_feature_plan_table.php +++ b/database/migrations/2022_02_04_001622_create_feature_plan_table.php @@ -15,8 +15,8 @@ public function up() Schema::create('feature_plan', function (Blueprint $table) { $table->id(); $table->decimal('charges')->nullable(); - $table->foreignIdFor(\LucasDotVin\Soulbscription\Models\Feature::class)->constrained()->cascadeOnDelete(); - $table->foreignIdFor(\LucasDotVin\Soulbscription\Models\Plan::class)->constrained()->cascadeOnDelete(); + $table->foreignIdFor(config('soulbscription.models.plan')::class)->constrained()->cascadeOnDelete(); + $table->foreignIdFor(config('soulbscription.models.feature')::class)->constrained()->cascadeOnDelete(); $table->timestamps(); }); } diff --git a/database/migrations/2022_02_25_001622_create_feature_tickets_table.php b/database/migrations/2022_02_25_001622_create_feature_tickets_table.php index f384584..6fac6b1 100644 --- a/database/migrations/2022_02_25_001622_create_feature_tickets_table.php +++ b/database/migrations/2022_02_25_001622_create_feature_tickets_table.php @@ -16,7 +16,7 @@ public function up() $table->id(); $table->decimal('charges')->nullable(); $table->timestamp('expired_at')->nullable(); - $table->foreignIdFor(\LucasDotVin\Soulbscription\Models\Feature::class)->constrained()->cascadeOnDelete(); + $table->foreignIdFor(config('soulbscription.models.feature')::class)->constrained()->cascadeOnDelete(); $table->timestamps(); if (config('soulbscription.models.subscriber.uses_uuid')) { diff --git a/src/Events/FeatureConsumed.php b/src/Events/FeatureConsumed.php index def4b24..374f2e8 100644 --- a/src/Events/FeatureConsumed.php +++ b/src/Events/FeatureConsumed.php @@ -2,23 +2,39 @@ namespace LucasDotVin\Soulbscription\Events; -use Illuminate\Broadcasting\InteractsWithSockets; -use Illuminate\Foundation\Events\Dispatchable; +use InvalidArgumentException; use Illuminate\Queue\SerializesModels; -use LucasDotVin\Soulbscription\Models\Feature; -use LucasDotVin\Soulbscription\Models\FeatureConsumption; +use Illuminate\Foundation\Events\Dispatchable; +use Illuminate\Broadcasting\InteractsWithSockets; class FeatureConsumed { use Dispatchable; - use InteractsWithSockets; use SerializesModels; + use InteractsWithSockets; + + public mixed $feature; + public mixed $subscriber; + public mixed $featureConsumption; public function __construct( - public $subscriber, - public Feature $feature, - public FeatureConsumption $featureConsumption, + $subscriber, + mixed $feature, + mixed $featureConsumption ) { - // + $featureClass = config('soulbscription.models.feature'); + $featureConsumptionClass = config('soulbscription.models.feature_consumption'); + + throw_if(!($feature instanceof $featureClass), new InvalidArgumentException( + "Feature must be an instance of $featureClass." + )); + + throw_if(!($featureConsumption instanceof $featureConsumptionClass), new InvalidArgumentException( + "FeatureConsumption must be an instance of $featureConsumptionClass." + )); + + $this->feature = $feature; + $this->subscriber = $subscriber; + $this->featureConsumption = $featureConsumption; } } diff --git a/src/Events/FeatureTicketCreated.php b/src/Events/FeatureTicketCreated.php index 61fdb61..5124152 100644 --- a/src/Events/FeatureTicketCreated.php +++ b/src/Events/FeatureTicketCreated.php @@ -2,23 +2,39 @@ namespace LucasDotVin\Soulbscription\Events; -use Illuminate\Broadcasting\InteractsWithSockets; -use Illuminate\Foundation\Events\Dispatchable; +use InvalidArgumentException; use Illuminate\Queue\SerializesModels; -use LucasDotVin\Soulbscription\Models\Feature; -use LucasDotVin\Soulbscription\Models\FeatureTicket; +use Illuminate\Foundation\Events\Dispatchable; +use Illuminate\Broadcasting\InteractsWithSockets; class FeatureTicketCreated { use Dispatchable; - use InteractsWithSockets; use SerializesModels; + use InteractsWithSockets; + + public mixed $feature; + public mixed $subscriber; + public mixed $featureTicket; public function __construct( - public $subscriber, - public Feature $feature, - public FeatureTicket $featureTicket, + $subscriber, + mixed $feature, + mixed $featureTicket ) { - // + $featureClass = config('soulbscription.models.feature'); + $featureTicketClass = config('soulbscription.models.feature_ticket'); + + throw_if(!($feature instanceof $featureClass), new InvalidArgumentException( + "Feature must be an instance of $featureClass." + )); + + throw_if(!($featureTicket instanceof $featureTicketClass), new InvalidArgumentException( + "FeatureTicket must be an instance of $featureTicketClass." + )); + + $this->feature = $feature; + $this->subscriber = $subscriber; + $this->featureTicket = $featureTicket; } } diff --git a/src/Events/SubscriptionCanceled.php b/src/Events/SubscriptionCanceled.php index 7ac3348..f1c29a4 100644 --- a/src/Events/SubscriptionCanceled.php +++ b/src/Events/SubscriptionCanceled.php @@ -2,20 +2,27 @@ namespace LucasDotVin\Soulbscription\Events; -use Illuminate\Broadcasting\InteractsWithSockets; -use Illuminate\Foundation\Events\Dispatchable; +use InvalidArgumentException; use Illuminate\Queue\SerializesModels; -use LucasDotVin\Soulbscription\Models\Subscription; +use Illuminate\Foundation\Events\Dispatchable; +use Illuminate\Broadcasting\InteractsWithSockets; class SubscriptionCanceled { use Dispatchable; - use InteractsWithSockets; use SerializesModels; + use InteractsWithSockets; + + public mixed $subscription; + + public function __construct(mixed $subscription) + { + $subscriptionClass = config('soulbscription.models.subscription'); + + throw_if(!($subscription instanceof $subscriptionClass), new InvalidArgumentException( + "Subscription must be an instance of $subscriptionClass." + )); - public function __construct( - public Subscription $subscription, - ) { - // + $this->subscription = $subscription; } } diff --git a/src/Events/SubscriptionRenewed.php b/src/Events/SubscriptionRenewed.php index d0dcd89..5ab9da7 100644 --- a/src/Events/SubscriptionRenewed.php +++ b/src/Events/SubscriptionRenewed.php @@ -2,20 +2,27 @@ namespace LucasDotVin\Soulbscription\Events; -use Illuminate\Broadcasting\InteractsWithSockets; -use Illuminate\Foundation\Events\Dispatchable; +use InvalidArgumentException; use Illuminate\Queue\SerializesModels; -use LucasDotVin\Soulbscription\Models\Subscription; +use Illuminate\Foundation\Events\Dispatchable; +use Illuminate\Broadcasting\InteractsWithSockets; class SubscriptionRenewed { use Dispatchable; - use InteractsWithSockets; use SerializesModels; + use InteractsWithSockets; + + public mixed $subscription; + + public function __construct(mixed $subscription) + { + $subscriptionClass = config('soulbscription.models.subscription'); + + throw_if(!($subscription instanceof $subscriptionClass), new InvalidArgumentException( + "Subscription must be an instance of $subscriptionClass." + )); - public function __construct( - public Subscription $subscription, - ) { - // + $this->subscription = $subscription; } } diff --git a/src/Events/SubscriptionScheduled.php b/src/Events/SubscriptionScheduled.php index 591f329..462231c 100644 --- a/src/Events/SubscriptionScheduled.php +++ b/src/Events/SubscriptionScheduled.php @@ -2,20 +2,27 @@ namespace LucasDotVin\Soulbscription\Events; -use Illuminate\Broadcasting\InteractsWithSockets; -use Illuminate\Foundation\Events\Dispatchable; +use InvalidArgumentException; use Illuminate\Queue\SerializesModels; -use LucasDotVin\Soulbscription\Models\Subscription; +use Illuminate\Foundation\Events\Dispatchable; +use Illuminate\Broadcasting\InteractsWithSockets; class SubscriptionScheduled { use Dispatchable; - use InteractsWithSockets; use SerializesModels; + use InteractsWithSockets; + + public mixed $subscription; + + public function __construct(mixed $subscription) + { + $subscriptionClass = config('soulbscription.models.subscription'); + + throw_if(!($subscription instanceof $subscriptionClass), new InvalidArgumentException( + "Subscription must be an instance of $subscriptionClass." + )); - public function __construct( - public Subscription $subscription, - ) { - // + $this->subscription = $subscription; } } diff --git a/src/Events/SubscriptionStarted.php b/src/Events/SubscriptionStarted.php index 4333315..0b6ae5c 100644 --- a/src/Events/SubscriptionStarted.php +++ b/src/Events/SubscriptionStarted.php @@ -2,20 +2,26 @@ namespace LucasDotVin\Soulbscription\Events; -use Illuminate\Broadcasting\InteractsWithSockets; -use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Queue\SerializesModels; -use LucasDotVin\Soulbscription\Models\Subscription; +use Illuminate\Foundation\Events\Dispatchable; +use Illuminate\Broadcasting\InteractsWithSockets; class SubscriptionStarted { use Dispatchable; - use InteractsWithSockets; use SerializesModels; + use InteractsWithSockets; + + public mixed $subscription; + + public function __construct(mixed $subscription) + { + $subscriptionClass = config('soulbscription.models.subscription'); + + throw_if(!($subscription instanceof $subscriptionClass), new InvalidArgumentException( + "Subscription must be an instance of $subscriptionClass." + )); - public function __construct( - public Subscription $subscription, - ) { - // + $this->subscription = $subscription; } } diff --git a/src/Events/SubscriptionSuppressed.php b/src/Events/SubscriptionSuppressed.php index bfb93bc..63ed47a 100644 --- a/src/Events/SubscriptionSuppressed.php +++ b/src/Events/SubscriptionSuppressed.php @@ -2,10 +2,10 @@ namespace LucasDotVin\Soulbscription\Events; -use Illuminate\Broadcasting\InteractsWithSockets; -use Illuminate\Foundation\Events\Dispatchable; +use InvalidArgumentException; use Illuminate\Queue\SerializesModels; -use LucasDotVin\Soulbscription\Models\Subscription; +use Illuminate\Foundation\Events\Dispatchable; +use Illuminate\Broadcasting\InteractsWithSockets; class SubscriptionSuppressed { @@ -13,9 +13,16 @@ class SubscriptionSuppressed use InteractsWithSockets; use SerializesModels; - public function __construct( - public Subscription $subscription, - ) { - // + public mixed $subscription; + + public function __construct(mixed $subscription) + { + $subscriptionClass = config('soulbscription.models.subscription'); + + throw_if(!($subscription instanceof $subscriptionClass), new InvalidArgumentException( + "Subscription must be an instance of $subscriptionClass." + )); + + $this->subscription = $subscription; } } diff --git a/src/Models/Concerns/HasSubscriptions.php b/src/Models/Concerns/HasSubscriptions.php index 14b3fe1..fe52bf1 100644 --- a/src/Models/Concerns/HasSubscriptions.php +++ b/src/Models/Concerns/HasSubscriptions.php @@ -2,21 +2,17 @@ namespace LucasDotVin\Soulbscription\Models\Concerns; +use LogicException; +use OverflowException; +use OutOfBoundsException; +use InvalidArgumentException; +use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Collection; -use Illuminate\Database\Eloquent\Model; -use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Database\Eloquent\Relations\HasMany; -use InvalidArgumentException; -use LogicException; use LucasDotVin\Soulbscription\Events\FeatureConsumed; +use Illuminate\Database\Eloquent\ModelNotFoundException; use LucasDotVin\Soulbscription\Events\FeatureTicketCreated; -use LucasDotVin\Soulbscription\Models\Feature; -use LucasDotVin\Soulbscription\Models\FeatureTicket; -use LucasDotVin\Soulbscription\Models\Plan; -use LucasDotVin\Soulbscription\Models\Subscription; -use OutOfBoundsException; -use OverflowException; trait HasSubscriptions { @@ -117,8 +113,14 @@ public function setConsumedQuota($featureName, float $consumption) event(new FeatureConsumed($this, $feature, $featureConsumption)); } - public function subscribeTo(Plan $plan, $expiration = null, $startDate = null): Subscription + public function subscribeTo(mixed $plan, $expiration = null, $startDate = null): mixed { + $planClass = config('soulbscription.models.plan'); + + throw_if(!($plan instanceof $planClass), new InvalidArgumentException( + "Plan must be an instance of $planClass." + )); + if ($plan->periodicity) { $expiration = $expiration ?? $plan->calculateNextRecurrenceEnd($startDate); @@ -140,30 +142,42 @@ public function subscribeTo(Plan $plan, $expiration = null, $startDate = null): ->start($startDate); } - public function hasSubscriptionTo(Plan $plan): bool + public function hasSubscriptionTo(mixed $plan): bool { + $planClass = config('soulbscription.models.plan'); + + throw_if(!($plan instanceof $planClass), new InvalidArgumentException( + "Plan must be an instance of $planClass." + )); + return $this->subscription() ->where('plan_id', $plan->id) ->exists(); } - public function isSubscribedTo(Plan $plan): bool + public function isSubscribedTo(mixed $plan): bool { return $this->hasSubscriptionTo($plan); } - public function missingSubscriptionTo(Plan $plan): bool + public function missingSubscriptionTo(mixed $plan): bool { return ! $this->hasSubscriptionTo($plan); } - public function isNotSubscribedTo(Plan $plan): bool + public function isNotSubscribedTo(mixed $plan): bool { return ! $this->isSubscribedTo($plan); } - public function switchTo(Plan $plan, $expiration = null, $immediately = true): Subscription + public function switchTo(mixed $plan, $expiration = null, $immediately = true): mixed { + $planClass = config('soulbscription.models.plan'); + + throw_if(!($plan instanceof $planClass), new InvalidArgumentException( + "Plan must be an instance of $planClass." + )); + if ($immediately) { $this->subscription ->markAsSwitched() @@ -178,23 +192,22 @@ public function switchTo(Plan $plan, $expiration = null, $immediately = true): S ->save(); $startDate = $this->subscription->expired_at; - $newSubscription = $this->subscribeTo($plan, startDate: $startDate); - - return $newSubscription; + return $this->subscribeTo($plan, startDate: $startDate); } + /** * @throws LogicException * @throws ModelNotFoundException */ - public function giveTicketFor($featureName, $expiration = null, ?float $charges = null): FeatureTicket + public function giveTicketFor($featureName, $expiration = null, ?float $charges = null): mixed { throw_unless( config('soulbscription.feature_tickets'), new LogicException('The tickets are not enabled in the configs.'), ); - $feature = Feature::whereName($featureName)->firstOrFail(); + $feature = config('soulbscription.models.feature')::whereName($featureName)->firstOrFail(); $featureTicket = $this->featureTickets() ->make([ @@ -286,8 +299,14 @@ public function getTotalCharges($featureName): float return $subscriptionCharges + $ticketCharges; } - protected function consumeNotQuotaFeature(Feature $feature, ?float $consumption = null) + protected function consumeNotQuotaFeature(mixed $feature, ?float $consumption = null) { + $featureClass = config('soulbscription.models.feature'); + + throw_if(!($feature instanceof $featureClass), new InvalidArgumentException( + "Feature must be an instance of $featureClass." + )); + $consumptionExpiration = $feature->consumable ? $feature->calculateNextRecurrenceEnd($this->subscription->started_at) : null; @@ -305,8 +324,15 @@ protected function consumeNotQuotaFeature(Feature $feature, ?float $consumption return $featureConsumption; } - protected function consumeQuotaFeature(Feature $feature, float $consumption) + + protected function consumeQuotaFeature(mixed $feature, float $consumption) { + $featureClass = config('soulbscription.models.feature'); + + throw_if(!($feature instanceof $featureClass), new InvalidArgumentException( + "Feature must be an instance of $featureClass." + )); + $featureConsumption = $this->featureConsumptions() ->whereFeatureId($feature->id) ->firstOrNew(); @@ -346,13 +372,16 @@ protected function getTicketChargesForAFeature(Model $feature): float ->sum('charges'); } - public function getFeature(string $featureName): ?Feature + public function getFeature(string $featureName): mixed { + $featureClass = config('soulbscription.models.feature'); + $feature = $this->features->firstWhere('name', $featureName); - return $feature; + return $feature instanceof $featureClass ? $feature : null; } + public function getFeaturesAttribute(): Collection { if (! is_null($this->loadedFeatures)) { @@ -386,12 +415,12 @@ protected function loadTicketFeatures(): Collection return $this->loadedTicketFeatures; } - return $this->loadedTicketFeatures = Feature::with([ - 'tickets' => fn (HasMany $query) => $query->withoutExpired()->whereMorphedTo('subscriber', $this), - ]) + return $this->loadedTicketFeatures = config('soulbscription.models.feature')::with([ + 'tickets' => fn(HasMany $query) => $query->withoutExpired()->whereMorphedTo('subscriber', $this), + ]) ->whereHas( 'tickets', - fn (Builder $query) => $query->withoutExpired()->whereMorphedTo('subscriber', $this), + fn(Builder $query) => $query->withoutExpired()->whereMorphedTo('subscriber', $this), ) ->get(); } diff --git a/tests/Models/Concerns/ExpiresAndHasGraceDaysTest.php b/tests/Models/Concerns/ExpiresAndHasGraceDaysTest.php index 4e6d17b..1596638 100644 --- a/tests/Models/Concerns/ExpiresAndHasGraceDaysTest.php +++ b/tests/Models/Concerns/ExpiresAndHasGraceDaysTest.php @@ -2,23 +2,36 @@ namespace Tests\Feature\Models\Concerns; -use Illuminate\Foundation\Testing\RefreshDatabase; +use Tests\TestCase; +use InvalidArgumentException; +use Illuminate\Database\Eloquent\Model; use Illuminate\Foundation\Testing\WithFaker; +use Illuminate\Foundation\Testing\RefreshDatabase; use LucasDotVin\Soulbscription\Models\Concerns\ExpiresAndHasGraceDays; use LucasDotVin\Soulbscription\Models\Scopes\ExpiringWithGraceDaysScope; -use LucasDotVin\Soulbscription\Models\Subscription; -use Tests\TestCase; class ExpiresAndHasGraceDaysTest extends TestCase { - use RefreshDatabase; use WithFaker; + use RefreshDatabase; - public const MODEL = Subscription::class; + public const MODEL = 'soulbscription.models.subscription'; + + protected function getModelClass() + { + $modelClass = config(self::MODEL); + + throw_if(!is_a($modelClass, Model::class, true), new InvalidArgumentException( + "Configured subscription model must be a subclass of " . Model::class + )); + + return $modelClass; + } public function testTraitAppliesScope() { - $model = self::MODEL::factory()->create(); + $modelClass = $this->getModelClass(); + $model = $modelClass::factory()->create(); $this->assertArrayHasKey(ExpiresAndHasGraceDays::class, class_uses_recursive($model)); $this->assertArrayHasKey(ExpiringWithGraceDaysScope::class, $model->getGlobalScopes()); @@ -26,25 +39,19 @@ public function testTraitAppliesScope() public function testModelReturnsExpiredStatus() { - $expiredModel = self::MODEL::factory() - ->expired() - ->create(); - - $expiredModelWithFutureGraceDays = self::MODEL::factory() - ->expired() - ->create([ - 'grace_days_ended_at' => now()->addDay(), - ]); - - $expiredModelWithPastGraceDays = self::MODEL::factory() - ->expired() - ->create([ - 'grace_days_ended_at' => now()->subDay(), - ]); - - $notExpiredModel = self::MODEL::factory() - ->notExpired() - ->create(); + $modelClass = $this->getModelClass(); + + $expiredModel = $modelClass::factory()->expired()->create(); + + $expiredModelWithFutureGraceDays = $modelClass::factory()->expired()->create([ + 'grace_days_ended_at' => now()->addDay(), + ]); + + $expiredModelWithPastGraceDays = $modelClass::factory()->expired()->create([ + 'grace_days_ended_at' => now()->subDay(), + ]); + + $notExpiredModel = $modelClass::factory()->notExpired()->create(); $this->assertTrue($expiredModel->expired()); $this->assertFalse($expiredModelWithFutureGraceDays->expired()); @@ -54,31 +61,23 @@ public function testModelReturnsExpiredStatus() public function testModelReturnsNotExpiredStatus() { - $expiredModel = self::MODEL::factory() - ->expired() - ->create(); - - $modelWithNullExpiredAt = self::MODEL::factory() - ->expired() - ->create([ - 'expired_at' => null, - ]); - - $expiredModelWithFutureGraceDays = self::MODEL::factory() - ->expired() - ->create([ - 'grace_days_ended_at' => now()->addDay(), - ]); - - $expiredModelWithPastGraceDays = self::MODEL::factory() - ->expired() - ->create([ - 'grace_days_ended_at' => now()->subDay(), - ]); - - $notExpiredModel = self::MODEL::factory() - ->notExpired() - ->create(); + $modelClass = $this->getModelClass(); + + $expiredModel = $modelClass::factory()->expired()->create(); + + $modelWithNullExpiredAt = $modelClass::factory()->expired()->create([ + 'expired_at' => null, + ]); + + $expiredModelWithFutureGraceDays = $modelClass::factory()->expired()->create([ + 'grace_days_ended_at' => now()->addDay(), + ]); + + $expiredModelWithPastGraceDays = $modelClass::factory()->expired()->create([ + 'grace_days_ended_at' => now()->subDay(), + ]); + + $notExpiredModel = $modelClass::factory()->notExpired()->create(); $this->assertFalse($expiredModel->notExpired()); $this->assertTrue($expiredModelWithFutureGraceDays->notExpired()); @@ -89,31 +88,23 @@ public function testModelReturnsNotExpiredStatus() public function testModelReturnsIfItHasExpired() { - $expiredModel = self::MODEL::factory() - ->expired() - ->create(); - - $modelWithNullExpiredAt = self::MODEL::factory() - ->expired() - ->create([ - 'expired_at' => null, - ]); - - $expiredModelWithFutureGraceDays = self::MODEL::factory() - ->expired() - ->create([ - 'grace_days_ended_at' => now()->addDay(), - ]); - - $expiredModelWithPastGraceDays = self::MODEL::factory() - ->expired() - ->create([ - 'grace_days_ended_at' => now()->subDay(), - ]); - - $notExpiredModel = self::MODEL::factory() - ->notExpired() - ->create(); + $modelClass = $this->getModelClass(); + + $expiredModel = $modelClass::factory()->expired()->create(); + + $modelWithNullExpiredAt = $modelClass::factory()->expired()->create([ + 'expired_at' => null, + ]); + + $expiredModelWithFutureGraceDays = $modelClass::factory()->expired()->create([ + 'grace_days_ended_at' => now()->addDay(), + ]); + + $expiredModelWithPastGraceDays = $modelClass::factory()->expired()->create([ + 'grace_days_ended_at' => now()->subDay(), + ]); + + $notExpiredModel = $modelClass::factory()->notExpired()->create(); $this->assertTrue($expiredModel->hasExpired()); $this->assertFalse($expiredModelWithFutureGraceDays->hasExpired()); @@ -124,31 +115,23 @@ public function testModelReturnsIfItHasExpired() public function testModelReturnsIfItHasNotExpired() { - $expiredModel = self::MODEL::factory() - ->expired() - ->create(); - - $modelWithNullExpiredAt = self::MODEL::factory() - ->expired() - ->create([ - 'expired_at' => null, - ]); - - $expiredModelWithFutureGraceDays = self::MODEL::factory() - ->expired() - ->create([ - 'grace_days_ended_at' => now()->addDay(), - ]); - - $expiredModelWithPastGraceDays = self::MODEL::factory() - ->expired() - ->create([ - 'grace_days_ended_at' => now()->subDay(), - ]); - - $notExpiredModel = self::MODEL::factory() - ->notExpired() - ->create(); + $modelClass = $this->getModelClass(); + + $expiredModel = $modelClass::factory()->expired()->create(); + + $modelWithNullExpiredAt = $modelClass::factory()->expired()->create([ + 'expired_at' => null, + ]); + + $expiredModelWithFutureGraceDays = $modelClass::factory()->expired()->create([ + 'grace_days_ended_at' => now()->addDay(), + ]); + + $expiredModelWithPastGraceDays = $modelClass::factory()->expired()->create([ + 'grace_days_ended_at' => now()->subDay(), + ]); + + $notExpiredModel = $modelClass::factory()->notExpired()->create(); $this->assertFalse($expiredModel->hasNotExpired()); $this->assertTrue($expiredModelWithFutureGraceDays->hasNotExpired()); diff --git a/tests/Models/Concerns/ExpiresTest.php b/tests/Models/Concerns/ExpiresTest.php index 16a6837..29aee5f 100644 --- a/tests/Models/Concerns/ExpiresTest.php +++ b/tests/Models/Concerns/ExpiresTest.php @@ -2,125 +2,120 @@ namespace Tests\Feature\Models\Concerns; -use Illuminate\Foundation\Testing\RefreshDatabase; -use Illuminate\Foundation\Testing\WithFaker; -use LucasDotVin\Soulbscription\Models\FeatureConsumption; use Tests\TestCase; +use InvalidArgumentException; +use Illuminate\Support\Carbon; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Foundation\Testing\WithFaker; +use Illuminate\Foundation\Testing\RefreshDatabase; +use LucasDotVin\Soulbscription\Enums\PeriodicityType; -class ExpiresTest extends TestCase +class HandlesRecurrenceTest extends TestCase { use RefreshDatabase; use WithFaker; - public const MODEL = FeatureConsumption::class; + public const MODEL = 'soulbscription.models.plan'; - public function testExpiredModelsAreNotReturnedByDefault() + protected function getModelClass() { - $expiredModelsCount = $this->faker()->randomDigitNotNull(); - self::MODEL::factory()->count($expiredModelsCount)->create([ - 'expired_at' => now()->subDay(), - ]); + $modelClass = config(self::MODEL); - $unexpiredModelsCount = $this->faker()->randomDigitNotNull(); - $unexpiredModels = self::MODEL::factory()->count($unexpiredModelsCount)->create([ - 'expired_at' => now()->addDay(), - ]); + throw_if(!is_a($modelClass, Model::class, true), new InvalidArgumentException( + "Configured plan model must be a subclass of " . Model::class + )); - $returnedSubscriptions = self::MODEL::all(); - - $this->assertEqualsCanonicalizing( - $unexpiredModels->pluck('id')->toArray(), - $returnedSubscriptions->pluck('id')->toArray(), - ); + return $modelClass; } - public function testExpiredModelsAreReturnedWhenCallingMethodWithExpired() + public function testModelCalculateYearlyExpiration() { - $expiredModelsCount = $this->faker()->randomDigitNotNull(); - $expiredModels = self::MODEL::factory()->count($expiredModelsCount)->create([ - 'expired_at' => now()->subDay(), - ]); + Carbon::setTestNow(now()); - $unexpiredModelsCount = $this->faker()->randomDigitNotNull(); - $unexpiredModels = self::MODEL::factory()->count($unexpiredModelsCount)->create([ - 'expired_at' => now()->addDay(), + $years = $this->faker->randomDigitNotNull(); + $modelClass = $this->getModelClass(); + $plan = $modelClass::factory()->create([ + 'periodicity_type' => PeriodicityType::Year, + 'periodicity' => $years, ]); - $expectedSubscriptions = $expiredModels->concat($unexpiredModels); + $this->assertEquals(now()->addYears($years), $plan->calculateNextRecurrenceEnd()); + } - $returnedSubscriptions = self::MODEL::withExpired()->get(); + public function testModelCalculateMonthlyExpiration() + { + Carbon::setTestNow(now()); - $this->assertEqualsCanonicalizing( - $expectedSubscriptions->pluck('id')->toArray(), - $returnedSubscriptions->pluck('id')->toArray(), - ); + $months = $this->faker->randomDigitNotNull(); + $modelClass = $this->getModelClass(); + $plan = $modelClass::factory()->create([ + 'periodicity_type' => PeriodicityType::Month, + 'periodicity' => $months, + ]); + + $this->assertEquals(now()->addMonths($months), $plan->calculateNextRecurrenceEnd()); } - public function testOnlyExpiredModelsAreReturnedWhenCallingMethodOnlyExpired() + public function testModelCalculateWeeklyExpiration() { - $expiredModelsCount = $this->faker()->randomDigitNotNull(); - $expiredModels = self::MODEL::factory()->count($expiredModelsCount)->create([ - 'expired_at' => now()->subDay(), - ]); + Carbon::setTestNow(now()); - $unexpiredModelsCount = $this->faker()->randomDigitNotNull(); - self::MODEL::factory()->count($unexpiredModelsCount)->create([ - 'expired_at' => now()->addDay(), + $weeks = $this->faker->randomDigitNotNull(); + $modelClass = $this->getModelClass(); + $plan = $modelClass::factory()->create([ + 'periodicity_type' => PeriodicityType::Week, + 'periodicity' => $weeks, ]); - $returnedSubscriptions = self::MODEL::onlyExpired()->get(); - - $this->assertEqualsCanonicalizing( - $expiredModels->pluck('id')->toArray(), - $returnedSubscriptions->pluck('id')->toArray(), - ); + $this->assertEquals(now()->addWeeks($weeks), $plan->calculateNextRecurrenceEnd()); } - public function testModelReturnsExpiredStatus() + public function testModelCalculateDailyExpiration() { - $expiredModel = self::MODEL::factory() - ->expired() - ->create(); + Carbon::setTestNow(now()); - $notExpiredModel = self::MODEL::factory() - ->notExpired() - ->create(); + $days = $this->faker->randomDigitNotNull(); + $modelClass = $this->getModelClass(); + $plan = $modelClass::factory()->create([ + 'periodicity_type' => PeriodicityType::Day, + 'periodicity' => $days, + ]); - $this->assertTrue($expiredModel->expired()); - $this->assertFalse($notExpiredModel->expired()); + $this->assertEquals(now()->addDays($days), $plan->calculateNextRecurrenceEnd()); } - public function testModelReturnsNotExpiredStatus() + public function testModelCalculateExpirationWithADifferentStart() { - $expiredModel = self::MODEL::factory() - ->expired() - ->create(); + Carbon::setTestNow(now()); - $notExpiredModel = self::MODEL::factory() - ->notExpired() - ->create(); + $weeks = $this->faker->randomDigitNotNull(); + $modelClass = $this->getModelClass(); + $plan = $modelClass::factory()->create([ + 'periodicity_type' => PeriodicityType::Week, + 'periodicity' => $weeks, + ]); - $this->assertFalse($expiredModel->notExpired()); - $this->assertTrue($notExpiredModel->notExpired()); + $start = now()->subDay(); + + $this->assertEquals($start->copy()->addWeeks($weeks), $plan->calculateNextRecurrenceEnd($start)); } - public function testModelsWithoutExpirationDateAreReturnedByDefault() + public function testModelCalculateExpirationWithADifferentStartAsString() { - $expiredModelsCount = $this->faker()->randomDigitNotNull(); - self::MODEL::factory()->count($expiredModelsCount)->create([ - 'expired_at' => now()->subDay(), - ]); + Carbon::setTestNow(today()); - $unexpiredModelsCount = $this->faker()->randomDigitNotNull(); - $unexpiredModels = self::MODEL::factory()->count($unexpiredModelsCount)->create([ - 'expired_at' => null, + $weeks = $this->faker->randomDigitNotNull(); + $modelClass = $this->getModelClass(); + $plan = $modelClass::factory()->create([ + 'periodicity_type' => PeriodicityType::Week, + 'periodicity' => $weeks, ]); - $returnedSubscriptions = self::MODEL::all(); + $start = today()->subDay(); - $this->assertEqualsCanonicalizing( - $unexpiredModels->pluck('id')->toArray(), - $returnedSubscriptions->pluck('id')->toArray(), + $this->assertEquals( + $start->copy()->addWeeks($weeks), + $plan->calculateNextRecurrenceEnd($start->toDateTimeString()), ); } } diff --git a/tests/Models/Concerns/HandlesRecurrenceTest.php b/tests/Models/Concerns/HandlesRecurrenceTest.php index 3c299b9..f3de51a 100644 --- a/tests/Models/Concerns/HandlesRecurrenceTest.php +++ b/tests/Models/Concerns/HandlesRecurrenceTest.php @@ -2,26 +2,39 @@ namespace Tests\Feature\Models\Concerns; -use Illuminate\Foundation\Testing\RefreshDatabase; -use Illuminate\Foundation\Testing\WithFaker; +use Tests\TestCase; +use InvalidArgumentException; use Illuminate\Support\Carbon; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Foundation\Testing\WithFaker; +use Illuminate\Foundation\Testing\RefreshDatabase; use LucasDotVin\Soulbscription\Enums\PeriodicityType; -use LucasDotVin\Soulbscription\Models\Plan; -use Tests\TestCase; class HandlesRecurrenceTest extends TestCase { - use RefreshDatabase; use WithFaker; + use RefreshDatabase; + + public const MODEL = 'soulbscription.models.plan'; + + protected function getModelClass() + { + $modelClass = config(self::MODEL); + + throw_if(!is_a($modelClass, Model::class, true), new InvalidArgumentException( + "Configured plan model must be a subclass of " . Model::class + )); - public const MODEL = Plan::class; + return $modelClass; + } public function testModelCalculateYearlyExpiration() { Carbon::setTestNow(now()); $years = $this->faker->randomDigitNotNull(); - $plan = self::MODEL::factory()->create([ + $modelClass = $this->getModelClass(); + $plan = $modelClass::factory()->create([ 'periodicity_type' => PeriodicityType::Year, 'periodicity' => $years, ]); @@ -34,7 +47,8 @@ public function testModelCalculateMonthlyExpiration() Carbon::setTestNow(now()); $months = $this->faker->randomDigitNotNull(); - $plan = self::MODEL::factory()->create([ + $modelClass = $this->getModelClass(); + $plan = $modelClass::factory()->create([ 'periodicity_type' => PeriodicityType::Month, 'periodicity' => $months, ]); @@ -47,7 +61,8 @@ public function testModelCalculateWeeklyExpiration() Carbon::setTestNow(now()); $weeks = $this->faker->randomDigitNotNull(); - $plan = self::MODEL::factory()->create([ + $modelClass = $this->getModelClass(); + $plan = $modelClass::factory()->create([ 'periodicity_type' => PeriodicityType::Week, 'periodicity' => $weeks, ]); @@ -60,7 +75,8 @@ public function testModelCalculateDailyExpiration() Carbon::setTestNow(now()); $days = $this->faker->randomDigitNotNull(); - $plan = self::MODEL::factory()->create([ + $modelClass = $this->getModelClass(); + $plan = $modelClass::factory()->create([ 'periodicity_type' => PeriodicityType::Day, 'periodicity' => $days, ]); @@ -73,7 +89,8 @@ public function testModelCalculateExpirationWithADifferentStart() Carbon::setTestNow(now()); $weeks = $this->faker->randomDigitNotNull(); - $plan = self::MODEL::factory()->create([ + $modelClass = $this->getModelClass(); + $plan = $modelClass::factory()->create([ 'periodicity_type' => PeriodicityType::Week, 'periodicity' => $weeks, ]); @@ -88,7 +105,8 @@ public function testModelCalculateExpirationWithADifferentStartAsString() Carbon::setTestNow(today()); $weeks = $this->faker->randomDigitNotNull(); - $plan = self::MODEL::factory()->create([ + $modelClass = $this->getModelClass(); + $plan = $modelClass::factory()->create([ 'periodicity_type' => PeriodicityType::Week, 'periodicity' => $weeks, ]); @@ -97,7 +115,7 @@ public function testModelCalculateExpirationWithADifferentStartAsString() $this->assertEquals( $start->copy()->addWeeks($weeks), - $plan->calculateNextRecurrenceEnd($start->toDateTimeString()), + $plan->calculateNextRecurrenceEnd($start->toDateTimeString()) ); } } diff --git a/tests/Models/Concerns/HasSubscriptionsTest.php b/tests/Models/Concerns/HasSubscriptionsTest.php index 29cc427..4b98d50 100644 --- a/tests/Models/Concerns/HasSubscriptionsTest.php +++ b/tests/Models/Concerns/HasSubscriptionsTest.php @@ -2,36 +2,34 @@ namespace Tests\Feature\Models\Concerns; -use Illuminate\Database\Eloquent\ModelNotFoundException; -use Illuminate\Foundation\Testing\RefreshDatabase; -use Illuminate\Foundation\Testing\WithFaker; +use Tests\TestCase; +use Tests\Mocks\Models\User; + +use LogicException; +use OverflowException; +use OutOfBoundsException; +use InvalidArgumentException; use Illuminate\Support\Carbon; use Illuminate\Support\Facades\Event; -use InvalidArgumentException; -use LogicException; +use Illuminate\Foundation\Testing\WithFaker; +use Illuminate\Foundation\Testing\RefreshDatabase; use LucasDotVin\Soulbscription\Events\FeatureConsumed; +use Illuminate\Database\Eloquent\ModelNotFoundException; +use LucasDotVin\Soulbscription\Events\SubscriptionStarted; use LucasDotVin\Soulbscription\Events\FeatureTicketCreated; use LucasDotVin\Soulbscription\Events\SubscriptionScheduled; -use LucasDotVin\Soulbscription\Events\SubscriptionStarted; use LucasDotVin\Soulbscription\Events\SubscriptionSuppressed; -use LucasDotVin\Soulbscription\Models\Feature; -use LucasDotVin\Soulbscription\Models\FeatureConsumption; -use LucasDotVin\Soulbscription\Models\Plan; -use LucasDotVin\Soulbscription\Models\Subscription; -use LucasDotVin\Soulbscription\Models\SubscriptionRenewal; -use OutOfBoundsException; -use OverflowException; -use Tests\Mocks\Models\User; -use Tests\TestCase; + + class HasSubscriptionsTest extends TestCase { - use RefreshDatabase; use WithFaker; + use RefreshDatabase; public function testModelCanSubscribeToAPlan() { - $plan = Plan::factory()->createOne(); + $plan = config('soulbscription.models.plan')::factory()->createOne(); $subscriber = User::factory()->createOne(); Event::fake(); @@ -52,7 +50,7 @@ public function testModelCanSubscribeToAPlan() public function testModelDefinesGraceDaysEnd() { - $plan = Plan::factory() + $plan = config('soulbscription.models.plan')::factory() ->withGraceDays() ->createOne(); @@ -68,8 +66,8 @@ public function testModelCanSwitchToAPlan() { Carbon::setTestNow(now()); - $oldPlan = Plan::factory()->createOne(); - $newPlan = Plan::factory()->createOne(); + $oldPlan = config('soulbscription.models.plan')::factory()->createOne(); + $newPlan = config('soulbscription.models.plan')::factory()->createOne(); $subscriber = User::factory()->createOne(); $oldSubscription = $subscriber->subscribeTo($oldPlan); @@ -100,8 +98,8 @@ public function testModelCanScheduleSwitchToAPlan() { Carbon::setTestNow(now()); - $oldPlan = Plan::factory()->createOne(); - $newPlan = Plan::factory()->createOne(); + $oldPlan = config('soulbscription.models.plan')::factory()->createOne(); + $newPlan = config('soulbscription.models.plan')::factory()->createOne(); $subscriber = User::factory()->createOne(); $oldSubscription = $subscriber->subscribeTo($oldPlan); @@ -128,8 +126,8 @@ public function testModelCanScheduleSwitchToAPlan() public function testModelGetNewSubscriptionAfterSwitching() { - $oldPlan = Plan::factory()->createOne(); - $newPlan = Plan::factory()->createOne(); + $oldPlan = config('soulbscription.models.plan')::factory()->createOne(); + $newPlan = config('soulbscription.models.plan')::factory()->createOne(); $subscriber = User::factory()->createOne(); $subscriber->subscribeTo($oldPlan, startDate: now()->subDay()); @@ -143,8 +141,8 @@ public function testModelGetCurrentSubscriptionAfterScheduleASwitch() { Carbon::setTestNow(now()); - $oldPlan = Plan::factory()->createOne(); - $newPlan = Plan::factory()->createOne(); + $oldPlan = config('soulbscription.models.plan')::factory()->createOne(); + $newPlan = config('soulbscription.models.plan')::factory()->createOne(); $subscriber = User::factory()->createOne(); $oldSubscription = $subscriber->subscribeTo($oldPlan); @@ -159,8 +157,8 @@ public function testModelCanConsumeAFeature() $charges = $this->faker->numberBetween(5, 10); $consumption = $this->faker->numberBetween(1, $charges); - $plan = Plan::factory()->createOne(); - $feature = Feature::factory()->consumable()->createOne(); + $plan = config('soulbscription.models.plan')::factory()->createOne(); + $feature = config('soulbscription.models.feature')::factory()->consumable()->createOne(); $feature->plans()->attach($plan, [ 'charges' => $charges, ]); @@ -184,8 +182,8 @@ public function testModelCanConsumeAFeature() public function testModelCanConsumeANotConsumableFeatureIfItIsAvailable() { - $plan = Plan::factory()->createOne(); - $feature = Feature::factory()->notConsumable()->createOne(); + $plan = config('soulbscription.models.plan')::factory()->createOne(); + $feature = config('soulbscription.models.feature')::factory()->notConsumable()->createOne(); $feature->plans()->attach($plan); $subscriber = User::factory()->createOne(); @@ -205,8 +203,8 @@ public function testModelCantConsumeAnUnavailableFeature() $charges = $this->faker->numberBetween(5, 10); $consumption = $this->faker->numberBetween(1, $charges); - $plan = Plan::factory()->createOne(); - $feature = Feature::factory()->consumable()->createOne(); + $plan = config('soulbscription.models.plan')::factory()->createOne(); + $feature = config('soulbscription.models.feature')::factory()->consumable()->createOne(); $feature->plans()->attach($plan, [ 'charges' => $charges, ]); @@ -231,8 +229,8 @@ public function testModelCantConsumeAFeatureBeyondItsCharges() $charges = $this->faker->numberBetween(5, 10); $consumption = $charges + 1; - $plan = Plan::factory()->createOne(); - $feature = Feature::factory()->consumable()->createOne(); + $plan = config('soulbscription.models.plan')::factory()->createOne(); + $feature = config('soulbscription.models.feature')::factory()->consumable()->createOne(); $feature->plans()->attach($plan, [ 'charges' => $charges, ]); @@ -257,8 +255,8 @@ public function testModelCanConsumeSomeAmountOfAConsumableFeature() $charges = $this->faker->numberBetween(5, 10); $consumption = $this->faker->numberBetween(1, $charges); - $plan = Plan::factory()->createOne(); - $feature = Feature::factory()->consumable()->createOne(); + $plan = config('soulbscription.models.plan')::factory()->createOne(); + $feature = config('soulbscription.models.feature')::factory()->consumable()->createOne(); $feature->plans()->attach($plan, [ 'charges' => $charges, ]); @@ -276,8 +274,8 @@ public function testModelCantConsumeSomeAmountOfAConsumableFeatureFromAnExpiredS $charges = $this->faker->numberBetween(5, 10); $consumption = $this->faker->numberBetween(1, $charges); - $plan = Plan::factory()->createOne(); - $feature = Feature::factory()->consumable()->createOne(); + $plan = config('soulbscription.models.plan')::factory()->createOne(); + $feature = config('soulbscription.models.feature')::factory()->consumable()->createOne(); $feature->plans()->attach($plan, [ 'charges' => $charges, ]); @@ -295,8 +293,8 @@ public function testModelCantConsumeSomeAmountOfAConsumableFeature() $charges = $this->faker->numberBetween(5, 10); $consumption = $charges + 1; - $plan = Plan::factory()->createOne(); - $feature = Feature::factory()->consumable()->createOne(); + $plan = config('soulbscription.models.plan')::factory()->createOne(); + $feature = config('soulbscription.models.feature')::factory()->consumable()->createOne(); $feature->plans()->attach($plan, [ 'charges' => $charges, ]); @@ -314,8 +312,8 @@ public function testModelCanConsumeSomeAmountOfAConsumableFeatureIfItsConsumptio $charges = $this->faker->numberBetween(5, 10); $consumption = $this->faker->numberBetween(1, $charges); - $plan = Plan::factory()->createOne(); - $feature = Feature::factory()->consumable()->createOne(); + $plan = config('soulbscription.models.plan')::factory()->createOne(); + $feature = config('soulbscription.models.feature')::factory()->consumable()->createOne(); $feature->plans()->attach($plan, [ 'charges' => $charges, ]); @@ -323,7 +321,7 @@ public function testModelCanConsumeSomeAmountOfAConsumableFeatureIfItsConsumptio $subscriber = User::factory()->createOne(); $subscriber->subscribeTo($plan); - FeatureConsumption::factory() + config('soulbscription.models.feature_consumption')::factory() ->for($feature) ->for($subscriber, 'subscriber') ->createOne([ @@ -339,12 +337,12 @@ public function testModelCanConsumeSomeAmountOfAConsumableFeatureIfItsConsumptio public function testModelHasSubscriptionRenewals() { $subscriber = User::factory()->createOne(); - $subscription = Subscription::factory() + $subscription = config('soulbscription.models.subscription')::factory() ->for($subscriber, 'subscriber') ->createOne(); $renewalsCount = $this->faker->randomDigitNotNull(); - $renewals = SubscriptionRenewal::factory() + $renewals = config('soulbscription.models.subscription_renewal')::factory() ->times($renewalsCount) ->for($subscription) ->createOne(); @@ -357,7 +355,7 @@ public function testModelHasSubscriptionRenewals() public function testModelHasFeatureTickets() { - $feature = Feature::factory()->consumable()->createOne(); + $feature = config('soulbscription.models.feature')::factory()->consumable()->createOne(); $subscriber = User::factory()->createOne(); @@ -375,7 +373,7 @@ public function testModelHasFeatureTickets() public function testModelFeatureTicketsGetsOnlyNotExpired() { - $feature = Feature::factory()->consumable()->createOne(); + $feature = config('soulbscription.models.feature')::factory()->consumable()->createOne(); $subscriber = User::factory()->createOne(); @@ -408,7 +406,7 @@ public function testModelFeatureTicketsGetsOnlyNotExpired() public function testModelGetFeaturesFromTickets() { - $feature = Feature::factory()->consumable()->createOne(); + $feature = config('soulbscription.models.feature')::factory()->consumable()->createOne(); $subscriber = User::factory()->createOne(); @@ -429,7 +427,7 @@ public function testModelGetFeaturesFromTickets() public function testModelGetFeaturesFromNonExpirableTickets() { - $feature = Feature::factory()->consumable()->createOne(); + $feature = config('soulbscription.models.feature')::factory()->consumable()->createOne(); $subscriber = User::factory()->createOne(); @@ -453,7 +451,7 @@ public function testModelCanConsumeSomeAmountOfAConsumableFeatureFromATicket() $charges = $this->faker->numberBetween(5, 10); $consumption = $this->faker->numberBetween(1, $charges); - $feature = Feature::factory()->consumable()->createOne(); + $feature = config('soulbscription.models.feature')::factory()->consumable()->createOne(); $subscriber = User::factory()->createOne(); $ticket = $subscriber->featureTickets()->make([ @@ -476,9 +474,9 @@ public function testModelCanRetrieveTotalChargesForAFeatureConsideringTickets() $subscriptionFeatureCharges = $this->faker->numberBetween(5, 10); $ticketFeatureCharges = $this->faker->numberBetween(5, 10); - $feature = Feature::factory()->consumable()->createOne(); + $feature = config('soulbscription.models.feature')::factory()->consumable()->createOne(); - $plan = Plan::factory()->createOne(); + $plan = config('soulbscription.models.plan')::factory()->createOne(); $feature->plans()->attach($plan, [ 'charges' => $subscriptionFeatureCharges, ]); @@ -503,7 +501,7 @@ public function testModelCanRetrieveTotalChargesForAFeatureConsideringTickets() public function testModelCanConsumeANotConsumableFeatureFromATicket() { - $feature = Feature::factory()->notConsumable()->createOne(); + $feature = config('soulbscription.models.feature')::factory()->notConsumable()->createOne(); $subscriber = User::factory()->createOne(); $ticket = $subscriber->featureTickets()->make([ @@ -524,8 +522,8 @@ public function testModelCanRetrieveTotalConsumptionsForAFeature() { $consumption = $this->faker->randomDigitNotNull(); - $plan = Plan::factory()->createOne(); - $feature = Feature::factory()->consumable()->createOne(); + $plan = config('soulbscription.models.plan')::factory()->createOne(); + $feature = config('soulbscription.models.feature')::factory()->consumable()->createOne(); $feature->plans()->attach($plan); $subscriber = User::factory()->createOne(); @@ -551,8 +549,8 @@ public function testModelCanRetrieveRemainingChargesForAFeature() $charges = $this->faker->numberBetween(6, 10); $consumption = $this->faker->numberBetween(1, 5); - $plan = Plan::factory()->createOne(); - $feature = Feature::factory()->consumable()->createOne(); + $plan = config('soulbscription.models.plan')::factory()->createOne(); + $feature = config('soulbscription.models.feature')::factory()->consumable()->createOne(); $feature->plans()->attach($plan, [ 'charges' => $charges, ]); @@ -577,10 +575,10 @@ public function testModelCanRetrieveRemainingChargesForAFeature() public function testModelCantUseChargesFromExpiredTickets() { - $feature = Feature::factory()->consumable()->createOne(); + $feature = config('soulbscription.models.feature')::factory()->consumable()->createOne(); $subscriber = User::factory()->createOne(); - $plan = Plan::factory()->createOne(); + $plan = config('soulbscription.models.plan')::factory()->createOne(); $subscriber->subscribeTo($plan); $subscriptionFeatureCharges = $this->faker->numberBetween(5, 10); @@ -615,10 +613,10 @@ public function testModelCantUseChargesFromExpiredTickets() public function testItIgnoresTicketsWhenItIsDisabled() { - $feature = Feature::factory()->consumable()->createOne(); + $feature = config('soulbscription.models.feature')::factory()->consumable()->createOne(); $subscriber = User::factory()->createOne(); - $plan = Plan::factory()->createOne(); + $plan = config('soulbscription.models.plan')::factory()->createOne(); $plan->features()->attach($feature); $subscriber->subscribeTo($plan); @@ -644,7 +642,7 @@ public function testItCanCreateATicket() $charges = $this->faker->randomDigitNotNull(); $expiration = $this->faker->dateTime(); - $feature = Feature::factory()->consumable()->createOne(); + $feature = config('soulbscription.models.feature')::factory()->consumable()->createOne(); $subscriber = User::factory()->createOne(); @@ -663,7 +661,7 @@ public function testItCanCreateANonExpirableTicket() { $charges = $this->faker->randomDigitNotNull(); - $feature = Feature::factory()->consumable()->createOne(); + $feature = config('soulbscription.models.feature')::factory()->consumable()->createOne(); $subscriber = User::factory()->createOne(); @@ -683,7 +681,7 @@ public function testItFiresEventWhenCreatingATicket() $charges = $this->faker->randomDigitNotNull(); $expiration = $this->faker->dateTime(); - $feature = Feature::factory()->consumable()->createOne(); + $feature = config('soulbscription.models.feature')::factory()->consumable()->createOne(); $subscriber = User::factory()->createOne(); @@ -717,7 +715,7 @@ public function testItRaisesAnExceptionWhenCreatingATicketDespiteItIsDisabled() $charges = $this->faker->randomDigitNotNull(); $expiration = $this->faker->dateTime(); - $feature = Feature::factory()->consumable()->createOne(); + $feature = config('soulbscription.models.feature')::factory()->consumable()->createOne(); $subscriber = User::factory()->createOne(); @@ -734,8 +732,8 @@ public function testItCreateANotExpirableConsumptionForQuotaFeatures() $charges = $this->faker->numberBetween(5, 10); $consumption = $this->faker->numberBetween(1, $charges); - $plan = Plan::factory()->createOne(); - $feature = Feature::factory()->quota()->createOne(); + $plan = config('soulbscription.models.plan')::factory()->createOne(); + $feature = config('soulbscription.models.feature')::factory()->quota()->createOne(); $feature->plans()->attach($plan, [ 'charges' => $charges, ]); @@ -758,8 +756,8 @@ public function testItDoesNotCreateNewConsumptionsForQuoeFeatures() $charges = $this->faker->numberBetween(5, 10); $consumption = $this->faker->numberBetween(1, $charges / 2); - $plan = Plan::factory()->createOne(); - $feature = Feature::factory()->quota()->createOne(); + $plan = config('soulbscription.models.plan')::factory()->createOne(); + $feature = config('soulbscription.models.feature')::factory()->quota()->createOne(); $feature->plans()->attach($plan, [ 'charges' => $charges, ]); @@ -784,8 +782,8 @@ public function testItCanSetQuotaFeatureConsumption() $charges = $this->faker->numberBetween(5, 10); $consumption = $this->faker->numberBetween(1, $charges / 2); - $plan = Plan::factory()->createOne(); - $feature = Feature::factory()->quota()->createOne(); + $plan = config('soulbscription.models.plan')::factory()->createOne(); + $feature = config('soulbscription.models.feature')::factory()->quota()->createOne(); $feature->plans()->attach($plan, [ 'charges' => $charges, ]); @@ -810,8 +808,8 @@ public function testItRaisesAnExceptionWhenSettingConsumedQuotaForANotQuotaFeatu $charges = $this->faker->numberBetween(5, 10); $consumption = $this->faker->numberBetween(1, $charges / 2); - $plan = Plan::factory()->createOne(); - $feature = Feature::factory()->notQuota()->createOne(); + $plan = config('soulbscription.models.plan')::factory()->createOne(); + $feature = config('soulbscription.models.feature')::factory()->notQuota()->createOne(); $feature->plans()->attach($plan, [ 'charges' => $charges, ]); @@ -827,7 +825,7 @@ public function testItRaisesAnExceptionWhenSettingConsumedQuotaForANotQuotaFeatu public function testItChecksIfTheUserHasSubscriptionToAPlan() { - $plan = Plan::factory()->createOne(); + $plan = config('soulbscription.models.plan')::factory()->createOne(); $subscriber = User::factory()->createOne(); $subscriber->subscribeTo($plan); @@ -841,7 +839,7 @@ public function testItChecksIfTheUserHasSubscriptionToAPlan() public function testItChecksIfTheUserDoesNotHaveSubscriptionToAPlan() { - $plan = Plan::factory()->createOne(); + $plan = config('soulbscription.models.plan')::factory()->createOne(); $subscriber = User::factory()->createOne(); $subscriber->subscribeTo($plan); @@ -855,7 +853,7 @@ public function testItChecksIfTheUserDoesNotHaveSubscriptionToAPlan() public function testItReturnsTheLastSubscriptionWhenRetrievingExpired() { - $plan = Plan::factory()->createOne(); + $plan = config('soulbscription.models.plan')::factory()->createOne(); $subscriber = User::factory()->createOne(); $subscriber->subscribeTo($plan, now()->subDay(), now()->subDay()); @@ -871,8 +869,8 @@ public function testItCanConsumeAFeatureAfterItsChargesIfThisFeatureIsPostpaid() $charges = $this->faker->numberBetween(5, 10); $consumption = $this->faker->numberBetween(1, $charges * 2); - $plan = Plan::factory()->createOne(); - $feature = Feature::factory()->postpaid()->createOne(); + $plan = config('soulbscription.models.plan')::factory()->createOne(); + $feature = config('soulbscription.models.feature')::factory()->postpaid()->createOne(); $feature->plans()->attach($plan, [ 'charges' => $charges, ]); @@ -894,8 +892,8 @@ public function testItDoesNotReturnNegativeChargesForFeatures() $charges = $this->faker->numberBetween(5, 10); $consumption = $this->faker->numberBetween($charges + 1, $charges * 2); - $plan = Plan::factory()->createOne(); - $feature = Feature::factory()->postpaid()->createOne(); + $plan = config('soulbscription.models.plan')::factory()->createOne(); + $feature = config('soulbscription.models.feature')::factory()->postpaid()->createOne(); $feature->plans()->attach($plan, [ 'charges' => $charges, ]); @@ -913,8 +911,8 @@ public function testItReturnsNegativeBalanceForFeatures() $charges = $this->faker->numberBetween(5, 10); $consumption = $this->faker->numberBetween($charges + 1, $charges * 2); - $plan = Plan::factory()->createOne(); - $feature = Feature::factory()->postpaid()->createOne(); + $plan = config('soulbscription.models.plan')::factory()->createOne(); + $feature = config('soulbscription.models.feature')::factory()->postpaid()->createOne(); $feature->plans()->attach($plan, [ 'charges' => $charges, ]); @@ -933,7 +931,7 @@ public function testItReturnsRemainingChargesOnlyForTheGivenUser() $charges = $this->faker->numberBetween(5, 10); - $feature = Feature::factory()->createOne(); + $feature = config('soulbscription.models.feature')::factory()->createOne(); $subscriber = User::factory()->createOne(); $subscriber->giveTicketFor($feature->name, null, $charges); diff --git a/tests/Models/Concerns/StartsTest.php b/tests/Models/Concerns/StartsTest.php index 3f58623..2885dcf 100644 --- a/tests/Models/Concerns/StartsTest.php +++ b/tests/Models/Concerns/StartsTest.php @@ -2,21 +2,34 @@ namespace Tests\Feature\Models\Concerns; -use Illuminate\Foundation\Testing\RefreshDatabase; -use Illuminate\Foundation\Testing\WithFaker; -use LucasDotVin\Soulbscription\Models\Subscription; use Tests\TestCase; +use InvalidArgumentException; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Foundation\Testing\WithFaker; +use Illuminate\Foundation\Testing\RefreshDatabase; class StartsTest extends TestCase { - use RefreshDatabase; use WithFaker; + use RefreshDatabase; - public const MODEL = Subscription::class; + public const MODEL = 'soulbscription.models.subscription'; + + protected function getModelClass() + { + $modelClass = config(self::MODEL); + + throw_if(!is_a($modelClass, Model::class, true), new InvalidArgumentException( + "Configured subscription model must be a subclass of " . Model::class + )); + + return $modelClass; + } public function testModelReturnsStartedWhenStartedAtIsOnThePast() { - $model = self::MODEL::factory()->make([ + $modelClass = $this->getModelClass(); + $model = $modelClass::factory()->make([ 'started_at' => now()->subDay(), ]); @@ -26,7 +39,8 @@ public function testModelReturnsStartedWhenStartedAtIsOnThePast() public function testModelReturnsNotStartedWhenStartedAtIsOnTheFuture() { - $model = self::MODEL::factory()->make([ + $modelClass = $this->getModelClass(); + $model = $modelClass::factory()->make([ 'started_at' => now()->addDay(), ]); @@ -36,7 +50,8 @@ public function testModelReturnsNotStartedWhenStartedAtIsOnTheFuture() public function testModelReturnsNotStartedWhenStartedAtIsNull() { - $model = self::MODEL::factory()->make(); + $modelClass = $this->getModelClass(); + $model = $modelClass::factory()->make(); $model->started_at = null; $this->assertFalse($model->started()); diff --git a/tests/Models/Concerns/SuppressesTest.php b/tests/Models/Concerns/SuppressesTest.php index 3d19ee4..31a6f3d 100644 --- a/tests/Models/Concerns/SuppressesTest.php +++ b/tests/Models/Concerns/SuppressesTest.php @@ -2,21 +2,34 @@ namespace Tests\Feature\Models\Concerns; -use Illuminate\Foundation\Testing\RefreshDatabase; -use Illuminate\Foundation\Testing\WithFaker; -use LucasDotVin\Soulbscription\Models\Subscription; use Tests\TestCase; +use InvalidArgumentException; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Foundation\Testing\WithFaker; +use Illuminate\Foundation\Testing\RefreshDatabase; class SuppressesTest extends TestCase { - use RefreshDatabase; use WithFaker; + use RefreshDatabase; - public const MODEL = Subscription::class; + public const MODEL = 'soulbscription.models.subscription'; + + protected function getModelClass() + { + $modelClass = config(self::MODEL); + + throw_if(!is_a($modelClass, Model::class, true), new InvalidArgumentException( + "Configured subscription model must be a subclass of " . Model::class + )); + + return $modelClass; + } public function testModelReturnsSuppressedWhenSuppressedAtIsOnThePast() { - $model = self::MODEL::factory()->make([ + $modelClass = $this->getModelClass(); + $model = $modelClass::factory()->make([ 'suppressed_at' => now()->subDay(), ]); @@ -26,7 +39,8 @@ public function testModelReturnsSuppressedWhenSuppressedAtIsOnThePast() public function testModelReturnsNotSuppressedWhenSuppressedAtIsOnTheFuture() { - $model = self::MODEL::factory()->make([ + $modelClass = $this->getModelClass(); + $model = $modelClass::factory()->make([ 'suppressed_at' => now()->addDay(), ]); @@ -36,7 +50,8 @@ public function testModelReturnsNotSuppressedWhenSuppressedAtIsOnTheFuture() public function testModelReturnsNotSuppressedWhenSuppressedAtIsNull() { - $model = self::MODEL::factory()->make(); + $modelClass = $this->getModelClass(); + $model = $modelClass::factory()->make(); $model->suppressed_at = null; $this->assertFalse($model->suppressed()); diff --git a/tests/Models/FeaturePlanTest.php b/tests/Models/FeaturePlanTest.php index f2c4847..ae7bad8 100644 --- a/tests/Models/FeaturePlanTest.php +++ b/tests/Models/FeaturePlanTest.php @@ -2,40 +2,37 @@ namespace Tests\Feature\Models; -use Illuminate\Foundation\Testing\RefreshDatabase; -use Illuminate\Foundation\Testing\WithFaker; -use LucasDotVin\Soulbscription\Models\Feature; -use LucasDotVin\Soulbscription\Models\FeaturePlan; -use LucasDotVin\Soulbscription\Models\Plan; use Tests\TestCase; +use Illuminate\Foundation\Testing\WithFaker; +use Illuminate\Foundation\Testing\RefreshDatabase; class FeaturePlanTest extends TestCase { - use RefreshDatabase; use WithFaker; + use RefreshDatabase; public function testModelCanRetrievePlan() { - $feature = Feature::factory() + $feature = config('soulbscription.models.feature')::factory() ->create(); - $plan = Plan::factory()->create(); + $plan = config('soulbscription.models.plan')::factory()->create(); $plan->features()->attach($feature); - $featurePlanPivot = FeaturePlan::first(); + $featurePlanPivot = config('soulbscription.models.feature_plan')::first(); $this->assertEquals($plan->id, $featurePlanPivot->plan->id); } public function testModelCanRetrieveFeature() { - $feature = Feature::factory() + $feature = config('soulbscription.models.feature')::factory() ->create(); - $plan = Plan::factory()->create(); + $plan = config('soulbscription.models.plan')::factory()->create(); $plan->features()->attach($feature); - $featurePlanPivot = FeaturePlan::first(); + $featurePlanPivot = config('soulbscription.models.feature_plan')::first(); $this->assertEquals($feature->id, $featurePlanPivot->feature->id); } diff --git a/tests/Models/FeatureTest.php b/tests/Models/FeatureTest.php index 680e1fc..11a30ae 100644 --- a/tests/Models/FeatureTest.php +++ b/tests/Models/FeatureTest.php @@ -2,12 +2,11 @@ namespace Tests\Feature\Models; -use Illuminate\Foundation\Testing\RefreshDatabase; -use Illuminate\Foundation\Testing\WithFaker; +use Tests\TestCase; use Illuminate\Support\Carbon; +use Illuminate\Foundation\Testing\WithFaker; +use Illuminate\Foundation\Testing\RefreshDatabase; use LucasDotVin\Soulbscription\Enums\PeriodicityType; -use LucasDotVin\Soulbscription\Models\Feature; -use Tests\TestCase; class FeatureTest extends TestCase { @@ -19,7 +18,7 @@ public function testModelCalculateYearlyExpiration() Carbon::setTestNow(now()); $years = $this->faker->randomDigitNotNull(); - $feature = Feature::factory()->create([ + $feature = config('soulbscription.models.feature')::factory()->create([ 'periodicity_type' => PeriodicityType::Year, 'periodicity' => $years, ]); @@ -32,7 +31,7 @@ public function testModelCalculateMonthlyExpiration() Carbon::setTestNow(now()); $months = $this->faker->randomDigitNotNull(); - $feature = Feature::factory()->create([ + $feature = config('soulbscription.models.feature')::factory()->create([ 'periodicity_type' => PeriodicityType::Month, 'periodicity' => $months, ]); @@ -45,7 +44,7 @@ public function testModelCalculateWeeklyExpiration() Carbon::setTestNow(now()); $weeks = $this->faker->randomDigitNotNull(); - $feature = Feature::factory()->create([ + $feature = config('soulbscription.models.feature')::factory()->create([ 'periodicity_type' => PeriodicityType::Week, 'periodicity' => $weeks, ]); @@ -58,7 +57,7 @@ public function testModelCalculateDailyExpiration() Carbon::setTestNow(now()); $days = $this->faker->randomDigitNotNull(); - $feature = Feature::factory()->create([ + $feature = config('soulbscription.models.feature')::factory()->create([ 'periodicity_type' => PeriodicityType::Day, 'periodicity' => $days, ]); @@ -70,7 +69,7 @@ public function testModelcalculateNextRecurrenceEndConsideringRecurrences() { Carbon::setTestNow(now()); - $feature = Feature::factory()->create([ + $feature = config('soulbscription.models.feature')::factory()->create([ 'periodicity_type' => PeriodicityType::Week, 'periodicity' => 1, ]); @@ -82,11 +81,11 @@ public function testModelcalculateNextRecurrenceEndConsideringRecurrences() public function testModelIsNotQuotaByDefault() { - $creationPayload = Feature::factory()->raw(); + $creationPayload = config('soulbscription.models.feature')::factory()->raw(); unset($creationPayload['quota']); - $feature = Feature::create($creationPayload); + $feature = config('soulbscription.models.feature')::create($creationPayload); $this->assertDatabaseHas('features', [ 'id' => $feature->id, diff --git a/tests/Models/PlanTest.php b/tests/Models/PlanTest.php index ea543f6..2a22743 100644 --- a/tests/Models/PlanTest.php +++ b/tests/Models/PlanTest.php @@ -2,13 +2,11 @@ namespace Tests\Feature\Models; -use Illuminate\Foundation\Testing\RefreshDatabase; -use Illuminate\Foundation\Testing\WithFaker; +use Tests\TestCase; use Illuminate\Support\Carbon; +use Illuminate\Foundation\Testing\WithFaker; +use Illuminate\Foundation\Testing\RefreshDatabase; use LucasDotVin\Soulbscription\Enums\PeriodicityType; -use LucasDotVin\Soulbscription\Models\Plan; -use LucasDotVin\Soulbscription\Models\Subscription; -use Tests\TestCase; class PlanTest extends TestCase { @@ -21,7 +19,7 @@ public function testModelCancalculateGraceDaysEnd() $days = $this->faker->randomDigitNotNull(); $graceDays = $this->faker->randomDigitNotNull(); - $plan = Plan::factory()->create([ + $plan = config('soulbscription.models.plan')::factory()->create([ 'grace_days' => $graceDays, 'periodicity_type' => PeriodicityType::Day, 'periodicity' => $days, @@ -35,10 +33,10 @@ public function testModelCancalculateGraceDaysEnd() public function testModelCanRetrieveSubscriptions() { - $plan = Plan::factory() + $plan = config('soulbscription.models.plan')::factory() ->create(); - $subscriptions = Subscription::factory() + $subscriptions = config('soulbscription.models.subscription')::factory() ->for($plan) ->count($subscriptionsCount = $this->faker->randomDigitNotNull()) ->started() @@ -54,7 +52,7 @@ public function testModelCanRetrieveSubscriptions() public function testPlanCanBeCreatedWithoutPeriodicity() { - $plan = Plan::factory() + $plan = config('soulbscription.models.plan')::factory() ->create([ 'periodicity' => null, 'periodicity_type' => null, diff --git a/tests/Models/SubscriptionTest.php b/tests/Models/SubscriptionTest.php index e2cc4bd..4d2b97f 100644 --- a/tests/Models/SubscriptionTest.php +++ b/tests/Models/SubscriptionTest.php @@ -2,31 +2,29 @@ namespace Tests\Feature\Models; -use Illuminate\Foundation\Testing\RefreshDatabase; -use Illuminate\Foundation\Testing\WithFaker; +use Tests\TestCase; +use Tests\Mocks\Models\User; use Illuminate\Support\Carbon; use Illuminate\Support\Facades\Event; +use Illuminate\Foundation\Testing\WithFaker; +use Illuminate\Foundation\Testing\RefreshDatabase; use LucasDotVin\Soulbscription\Events\SubscriptionCanceled; use LucasDotVin\Soulbscription\Events\SubscriptionRenewed; use LucasDotVin\Soulbscription\Events\SubscriptionStarted; use LucasDotVin\Soulbscription\Events\SubscriptionSuppressed; -use LucasDotVin\Soulbscription\Models\Plan; -use LucasDotVin\Soulbscription\Models\Subscription; -use Tests\Mocks\Models\User; -use Tests\TestCase; class SubscriptionTest extends TestCase { - use RefreshDatabase; use WithFaker; + use RefreshDatabase; public function testModelRenews() { Carbon::setTestNow(now()); - $plan = Plan::factory()->create(); + $plan = config('soulbscription.models.plan')::factory()->create(); $subscriber = User::factory()->create(); - $subscription = Subscription::factory() + $subscription = config('soulbscription.models.subscription')::factory() ->for($plan) ->for($subscriber, 'subscriber') ->create([ @@ -53,9 +51,9 @@ public function testModelRenewsBasedOnCurrentDateIfOverdue() { Carbon::setTestNow(now()); - $plan = Plan::factory()->create(); + $plan = config('soulbscription.models.plan')::factory()->create(); $subscriber = User::factory()->create(); - $subscription = Subscription::factory() + $subscription = config('soulbscription.models.subscription')::factory() ->for($plan) ->for($subscriber, 'subscriber') ->create([ @@ -82,9 +80,9 @@ public function testModelCanCancel() { Carbon::setTestNow(now()); - $plan = Plan::factory()->create(); + $plan = config('soulbscription.models.plan')::factory()->create(); $subscriber = User::factory()->create(); - $subscription = Subscription::factory() + $subscription = config('soulbscription.models.subscription')::factory() ->for($plan) ->for($subscriber, 'subscriber') ->notStarted() @@ -106,9 +104,9 @@ public function testModelCanStart() { Carbon::setTestNow(now()); - $plan = Plan::factory()->create(); + $plan = config('soulbscription.models.plan')::factory()->create(); $subscriber = User::factory()->create(); - $subscription = Subscription::factory() + $subscription = config('soulbscription.models.subscription')::factory() ->for($plan) ->for($subscriber, 'subscriber') ->notStarted() @@ -130,9 +128,9 @@ public function testModelCanSuppress() { Carbon::setTestNow(now()); - $plan = Plan::factory()->create(); + $plan = config('soulbscription.models.plan')::factory()->create(); $subscriber = User::factory()->create(); - $subscription = Subscription::factory() + $subscription = config('soulbscription.models.subscription')::factory() ->for($plan) ->for($subscriber, 'subscriber') ->create(); @@ -151,9 +149,9 @@ public function testModelCanSuppress() public function testModelCanMarkAsSwitched() { - $plan = Plan::factory()->create(); + $plan = config('soulbscription.models.plan')::factory()->create(); $subscriber = User::factory()->create(); - $subscription = Subscription::factory() + $subscription = config('soulbscription.models.subscription')::factory() ->for($plan) ->for($subscriber, 'subscriber') ->create(); @@ -170,7 +168,7 @@ public function testModelCanMarkAsSwitched() public function testModelRegistersRenewal() { $subscriber = User::factory()->create(); - $subscription = Subscription::factory() + $subscription = config('soulbscription.models.subscription')::factory() ->for($subscriber, 'subscriber') ->create(); @@ -186,7 +184,7 @@ public function testModelRegistersRenewal() public function testModelRegistersOverdue() { $subscriber = User::factory()->create(); - $subscription = Subscription::factory() + $subscription = config('soulbscription.models.subscription')::factory() ->for($subscriber, 'subscriber') ->create([ 'expired_at' => now()->subDay(), @@ -204,7 +202,7 @@ public function testModelRegistersOverdue() public function testModelConsidersGraceDaysOnOverdue() { $subscriber = User::factory()->create(); - $subscription = Subscription::factory() + $subscription = config('soulbscription.models.subscription')::factory() ->for($subscriber, 'subscriber') ->create([ 'grace_days_ended_at' => now()->addDay(), @@ -222,21 +220,21 @@ public function testModelConsidersGraceDaysOnOverdue() public function testModelReturnsNotStartedSubscriptionsInNotActiveScope() { - Subscription::factory() + config('soulbscription.models.subscription')::factory() ->count($this->faker()->randomDigitNotNull()) ->started() ->notExpired() ->notSuppressed() ->create(); - $notStartedSubscription = Subscription::factory() + $notStartedSubscription = config('soulbscription.models.subscription')::factory() ->count($notStartedSubscriptionCount = $this->faker()->randomDigitNotNull()) ->notStarted() ->notExpired() ->notSuppressed() ->create(); - $returnedSubscriptions = Subscription::notActive()->get(); + $returnedSubscriptions = config('soulbscription.models.subscription')::notActive()->get(); $this->assertCount($notStartedSubscriptionCount, $returnedSubscriptions); $notStartedSubscription->each( @@ -246,21 +244,21 @@ public function testModelReturnsNotStartedSubscriptionsInNotActiveScope() public function testModelReturnsExpiredSubscriptionsInNotActiveScope() { - Subscription::factory() + config('soulbscription.models.subscription')::factory() ->count($this->faker()->randomDigitNotNull()) ->started() ->notExpired() ->notSuppressed() ->create(); - $expiredSubscription = Subscription::factory() + $expiredSubscription = config('soulbscription.models.subscription')::factory() ->count($expiredSubscriptionCount = $this->faker()->randomDigitNotNull()) ->started() ->expired() ->notSuppressed() ->create(); - $returnedSubscriptions = Subscription::notActive()->get(); + $returnedSubscriptions = config('soulbscription.models.subscription')::notActive()->get(); $this->assertCount($expiredSubscriptionCount, $returnedSubscriptions); $expiredSubscription->each( @@ -270,21 +268,21 @@ public function testModelReturnsExpiredSubscriptionsInNotActiveScope() public function testModelReturnsSuppressedSubscriptionsInNotActiveScope() { - Subscription::factory() + config('soulbscription.models.subscription')::factory() ->count($this->faker()->randomDigitNotNull()) ->started() ->notExpired() ->notSuppressed() ->create(); - $suppressedSubscription = Subscription::factory() + $suppressedSubscription = config('soulbscription.models.subscription')::factory() ->count($suppressedSubscriptionCount = $this->faker()->randomDigitNotNull()) ->started() ->notExpired() ->suppressed() ->create(); - $returnedSubscriptions = Subscription::notActive()->get(); + $returnedSubscriptions = config('soulbscription.models.subscription')::notActive()->get(); $this->assertCount($suppressedSubscriptionCount, $returnedSubscriptions); $suppressedSubscription->each( @@ -294,7 +292,7 @@ public function testModelReturnsSuppressedSubscriptionsInNotActiveScope() public function testModelReturnsOnlyCanceledSubscriptionsWithTheScope() { - Subscription::factory() + config('soulbscription.models.subscription')::factory() ->count($this->faker()->randomDigitNotNull()) ->started() ->notExpired() @@ -302,7 +300,7 @@ public function testModelReturnsOnlyCanceledSubscriptionsWithTheScope() ->notCanceled() ->create(); - $canceledSubscription = Subscription::factory() + $canceledSubscription = config('soulbscription.models.subscription')::factory() ->count($canceledSubscriptionCount = $this->faker()->randomDigitNotNull()) ->started() ->notExpired() @@ -310,7 +308,7 @@ public function testModelReturnsOnlyCanceledSubscriptionsWithTheScope() ->canceled() ->create(); - $returnedSubscriptions = Subscription::canceled()->get(); + $returnedSubscriptions = config('soulbscription.models.subscription')::canceled()->get(); $this->assertCount($canceledSubscriptionCount, $returnedSubscriptions); $canceledSubscription->each( @@ -320,7 +318,7 @@ public function testModelReturnsOnlyCanceledSubscriptionsWithTheScope() public function testModelReturnsOnlyNotCanceledSubscriptionsWithTheScope() { - Subscription::factory() + config('soulbscription.models.subscription')::factory() ->count($this->faker()->randomDigitNotNull()) ->started() ->notExpired() @@ -328,7 +326,7 @@ public function testModelReturnsOnlyNotCanceledSubscriptionsWithTheScope() ->canceled() ->create(); - $notCanceledSubscription = Subscription::factory() + $notCanceledSubscription = config('soulbscription.models.subscription')::factory() ->count($notCanceledSubscriptionCount = $this->faker()->randomDigitNotNull()) ->started() ->notExpired() @@ -336,7 +334,7 @@ public function testModelReturnsOnlyNotCanceledSubscriptionsWithTheScope() ->notCanceled() ->create(); - $returnedSubscriptions = Subscription::notCanceled()->get(); + $returnedSubscriptions = config('soulbscription.models.subscription')::notCanceled()->get(); $this->assertCount($notCanceledSubscriptionCount, $returnedSubscriptions); $notCanceledSubscription->each( diff --git a/tests/Scopes/ExpiringScopeTest.php b/tests/Scopes/ExpiringScopeTest.php index 2b38dc1..e065171 100644 --- a/tests/Scopes/ExpiringScopeTest.php +++ b/tests/Scopes/ExpiringScopeTest.php @@ -2,31 +2,45 @@ namespace Tests\Feature\Models; -use Illuminate\Foundation\Testing\RefreshDatabase; -use Illuminate\Foundation\Testing\WithFaker; -use LucasDotVin\Soulbscription\Models\FeatureConsumption; use Tests\TestCase; +use InvalidArgumentException; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Foundation\Testing\WithFaker; +use Illuminate\Foundation\Testing\RefreshDatabase; class ExpiringScopeTest extends TestCase { - use RefreshDatabase; use WithFaker; + use RefreshDatabase; + + public const MODEL = 'soulbscription.models.feature_consumption'; + + protected function getModelClass() + { + $modelClass = config(self::MODEL); + + throw_if(!is_a($modelClass, Model::class, true), new InvalidArgumentException( + "Configured feature consumption model must be a subclass of " . Model::class + )); - public const MODEL = FeatureConsumption::class; + return $modelClass; + } public function testExpiredModelsAreNotReturnedByDefault() { - $expiredModelsCount = $this->faker()->randomDigitNotNull(); - self::MODEL::factory()->count($expiredModelsCount)->create([ + $modelClass = $this->getModelClass(); + $expiredModelsCount = $this->faker->randomDigitNotNull(); + + $modelClass::factory()->count($expiredModelsCount)->create([ 'expired_at' => now()->subDay(), ]); - $unexpiredModelsCount = $this->faker()->randomDigitNotNull(); - $unexpiredModels = self::MODEL::factory()->count($unexpiredModelsCount)->create([ + $unexpiredModelsCount = $this->faker->randomDigitNotNull(); + $unexpiredModels = $modelClass::factory()->count($unexpiredModelsCount)->create([ 'expired_at' => now()->addDay(), ]); - $returnedFeatureConsumptions = self::MODEL::all(); + $returnedFeatureConsumptions = $modelClass::all(); $this->assertEqualsCanonicalizing( $unexpiredModels->pluck('id')->toArray(), @@ -36,19 +50,20 @@ public function testExpiredModelsAreNotReturnedByDefault() public function testExpiredModelsAreReturnedWhenCallingMethodWithExpired() { - $expiredModelsCount = $this->faker()->randomDigitNotNull(); - $expiredModels = self::MODEL::factory()->count($expiredModelsCount)->create([ + $modelClass = $this->getModelClass(); + $expiredModelsCount = $this->faker->randomDigitNotNull(); + + $expiredModels = $modelClass::factory()->count($expiredModelsCount)->create([ 'expired_at' => now()->subDay(), ]); - $unexpiredModelsCount = $this->faker()->randomDigitNotNull(); - $unexpiredModels = self::MODEL::factory()->count($unexpiredModelsCount)->create([ + $unexpiredModelsCount = $this->faker->randomDigitNotNull(); + $unexpiredModels = $modelClass::factory()->count($unexpiredModelsCount)->create([ 'expired_at' => now()->addDay(), ]); $expectedFeatureConsumptions = $expiredModels->concat($unexpiredModels); - - $returnedFeatureConsumptions = self::MODEL::withExpired()->get(); + $returnedFeatureConsumptions = $modelClass::withExpired()->get(); $this->assertEqualsCanonicalizing( $expectedFeatureConsumptions->pluck('id')->toArray(), @@ -58,17 +73,19 @@ public function testExpiredModelsAreReturnedWhenCallingMethodWithExpired() public function testExpiredModelsAreNotReturnedWhenCallingMethodWithExpiredAndPassingFalse() { - $expiredModelsCount = $this->faker()->randomDigitNotNull(); - self::MODEL::factory()->count($expiredModelsCount)->create([ + $modelClass = $this->getModelClass(); + $expiredModelsCount = $this->faker->randomDigitNotNull(); + + $modelClass::factory()->count($expiredModelsCount)->create([ 'expired_at' => now()->subDay(), ]); - $unexpiredModelsCount = $this->faker()->randomDigitNotNull(); - $unexpiredModels = self::MODEL::factory()->count($unexpiredModelsCount)->create([ + $unexpiredModelsCount = $this->faker->randomDigitNotNull(); + $unexpiredModels = $modelClass::factory()->count($unexpiredModelsCount)->create([ 'expired_at' => now()->addDay(), ]); - $returnedFeatureConsumptions = self::MODEL::withExpired(false)->get(); + $returnedFeatureConsumptions = $modelClass::withExpired(false)->get(); $this->assertEqualsCanonicalizing( $unexpiredModels->pluck('id')->toArray(), @@ -78,17 +95,19 @@ public function testExpiredModelsAreNotReturnedWhenCallingMethodWithExpiredAndPa public function testOnlyExpiredModelsAreReturnedWhenCallingMethodOnlyExpired() { - $expiredModelsCount = $this->faker()->randomDigitNotNull(); - $expiredModels = self::MODEL::factory()->count($expiredModelsCount)->create([ + $modelClass = $this->getModelClass(); + $expiredModelsCount = $this->faker->randomDigitNotNull(); + + $expiredModels = $modelClass::factory()->count($expiredModelsCount)->create([ 'expired_at' => now()->subDay(), ]); - $unexpiredModelsCount = $this->faker()->randomDigitNotNull(); - self::MODEL::factory()->count($unexpiredModelsCount)->create([ + $unexpiredModelsCount = $this->faker->randomDigitNotNull(); + $modelClass::factory()->count($unexpiredModelsCount)->create([ 'expired_at' => now()->addDay(), ]); - $returnedFeatureConsumptions = self::MODEL::onlyExpired()->get(); + $returnedFeatureConsumptions = $modelClass::onlyExpired()->get(); $this->assertEqualsCanonicalizing( $expiredModels->pluck('id')->toArray(), diff --git a/tests/Scopes/ExpiringWithGraceDaysScopeTest.php b/tests/Scopes/ExpiringWithGraceDaysScopeTest.php index c49d2ec..bdace9c 100644 --- a/tests/Scopes/ExpiringWithGraceDaysScopeTest.php +++ b/tests/Scopes/ExpiringWithGraceDaysScopeTest.php @@ -2,38 +2,51 @@ namespace Tests\Feature\Models; -use Illuminate\Foundation\Testing\RefreshDatabase; -use Illuminate\Foundation\Testing\WithFaker; -use LucasDotVin\Soulbscription\Models\Subscription; use Tests\TestCase; +use InvalidArgumentException; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Foundation\Testing\WithFaker; +use Illuminate\Foundation\Testing\RefreshDatabase; class ExpiringWithGraceDaysScopeTest extends TestCase { - use RefreshDatabase; use WithFaker; + use RefreshDatabase; + + public const MODEL = 'soulbscription.models.subscription'; + + protected function getModelClass() + { + $modelClass = config(self::MODEL); - public const MODEL = Subscription::class; + throw_if(!is_a($modelClass, Model::class, true), new InvalidArgumentException( + "Configured subscription model must be a subclass of " . Model::class + )); + + return $modelClass; + } public function testExpiredModelsAreNotReturnedByDefault() { - $expiredModelsCount = $this->faker()->randomDigitNotNull(); - self::MODEL::factory()->count($expiredModelsCount)->create([ + $modelClass = $this->getModelClass(); + $expiredModelsCount = $this->faker->randomDigitNotNull(); + + $modelClass::factory()->count($expiredModelsCount)->create([ 'expired_at' => now()->subDay(), ]); - $unexpiredModelsCount = $this->faker()->randomDigitNotNull(); - $unexpiredModels = self::MODEL::factory()->count($unexpiredModelsCount)->create([ + $unexpiredModelsCount = $this->faker->randomDigitNotNull(); + $unexpiredModels = $modelClass::factory()->count($unexpiredModelsCount)->create([ 'expired_at' => now()->addDay(), ]); - $modelsWithNullExpiredAtCount = $this->faker()->randomDigitNotNull(); - $modelsWithNullExpired = self::MODEL::factory()->count($modelsWithNullExpiredAtCount)->create([ + $modelsWithNullExpiredAtCount = $this->faker->randomDigitNotNull(); + $modelsWithNullExpired = $modelClass::factory()->count($modelsWithNullExpiredAtCount)->create([ 'expired_at' => null, ]); $expectedSubscriptions = $unexpiredModels->concat($modelsWithNullExpired); - - $returnedSubscriptions = self::MODEL::all(); + $returnedSubscriptions = $modelClass::all(); $this->assertEqualsCanonicalizing( $expectedSubscriptions->pluck('id')->toArray(), @@ -43,26 +56,28 @@ public function testExpiredModelsAreNotReturnedByDefault() public function testExpiredModelsWithGraceDaysAreReturnedByDefault() { - $expiredModelsWithoutGraceDaysCount = $this->faker()->randomDigitNotNull(); - self::MODEL::factory()->count($expiredModelsWithoutGraceDaysCount)->create([ + $modelClass = $this->getModelClass(); + $expiredModelsWithoutGraceDaysCount = $this->faker->randomDigitNotNull(); + + $modelClass::factory()->count($expiredModelsWithoutGraceDaysCount)->create([ 'expired_at' => now()->subDay(), 'grace_days_ended_at' => null, ]); - $expiredModelsWithPastGraceDaysCount = $this->faker()->randomDigitNotNull(); - self::MODEL::factory()->count($expiredModelsWithPastGraceDaysCount)->create([ + $expiredModelsWithPastGraceDaysCount = $this->faker->randomDigitNotNull(); + $modelClass::factory()->count($expiredModelsWithPastGraceDaysCount)->create([ 'expired_at' => now()->subDay(), 'grace_days_ended_at' => now()->subDay(), ]); - $expiredModelsWithFutureGraceDaysCount = $this->faker()->randomDigitNotNull(); - $expiredModelsWithFutureGraceDays = self::MODEL::factory() + $expiredModelsWithFutureGraceDaysCount = $this->faker->randomDigitNotNull(); + $expiredModelsWithFutureGraceDays = $modelClass::factory() ->count($expiredModelsWithFutureGraceDaysCount)->create([ 'expired_at' => now()->subDay(), 'grace_days_ended_at' => now()->addDay(), ]); - $returnedSubscriptions = self::MODEL::all(); + $returnedSubscriptions = $modelClass::all(); $this->assertEqualsCanonicalizing( $expiredModelsWithFutureGraceDays->pluck('id'), @@ -72,25 +87,27 @@ public function testExpiredModelsWithGraceDaysAreReturnedByDefault() public function testExpiredModelsAreReturnedWhenCallingMethodWithExpired() { - $expiredModelsCount = $this->faker()->randomDigitNotNull(); - $expiredModels = self::MODEL::factory()->count($expiredModelsCount)->create([ + $modelClass = $this->getModelClass(); + $expiredModelsCount = $this->faker->randomDigitNotNull(); + + $expiredModels = $modelClass::factory()->count($expiredModelsCount)->create([ 'expired_at' => now()->subDay(), ]); - $unexpiredModelsCount = $this->faker()->randomDigitNotNull(); - $unexpiredModels = self::MODEL::factory()->count($unexpiredModelsCount)->create([ + $unexpiredModelsCount = $this->faker->randomDigitNotNull(); + $unexpiredModels = $modelClass::factory()->count($unexpiredModelsCount)->create([ 'expired_at' => now()->addDay(), ]); - $expiredModelsWithFutureGraceDays = self::MODEL::factory() - ->count($this->faker()->randomDigitNotNull()) + $expiredModelsWithFutureGraceDays = $modelClass::factory() + ->count($this->faker->randomDigitNotNull()) ->create([ 'expired_at' => now()->subDay(), 'grace_days_ended_at' => now()->addDay(), ]); - $modelsWithNullExpiredAtCount = $this->faker()->randomDigitNotNull(); - $modelsWithNullExpired = self::MODEL::factory()->count($modelsWithNullExpiredAtCount)->create([ + $modelsWithNullExpiredAtCount = $this->faker->randomDigitNotNull(); + $modelsWithNullExpired = $modelClass::factory()->count($modelsWithNullExpiredAtCount)->create([ 'expired_at' => null, ]); @@ -98,7 +115,7 @@ public function testExpiredModelsAreReturnedWhenCallingMethodWithExpired() ->concat($expiredModelsWithFutureGraceDays) ->concat($modelsWithNullExpired); - $returnedSubscriptions = self::MODEL::withExpired()->get(); + $returnedSubscriptions = $modelClass::withExpired()->get(); $this->assertEqualsCanonicalizing( $expectedSubscriptions->pluck('id')->toArray(), @@ -108,32 +125,34 @@ public function testExpiredModelsAreReturnedWhenCallingMethodWithExpired() public function testExpiredModelsAreNotReturnedWhenCallingMethodWithExpiredAndPassingFalse() { - $expiredModelsCount = $this->faker()->randomDigitNotNull(); - self::MODEL::factory()->count($expiredModelsCount)->create([ + $modelClass = $this->getModelClass(); + $expiredModelsCount = $this->faker->randomDigitNotNull(); + + $modelClass::factory()->count($expiredModelsCount)->create([ 'expired_at' => now()->subDay(), ]); - $unexpiredModelsCount = $this->faker()->randomDigitNotNull(); - $unexpiredModels = self::MODEL::factory()->count($unexpiredModelsCount)->create([ + $unexpiredModelsCount = $this->faker->randomDigitNotNull(); + $unexpiredModels = $modelClass::factory()->count($unexpiredModelsCount)->create([ 'expired_at' => now()->addDay(), ]); - $expiredModelsWithFutureGraceDays = self::MODEL::factory() - ->count($this->faker()->randomDigitNotNull()) + $expiredModelsWithFutureGraceDays = $modelClass::factory() + ->count($this->faker->randomDigitNotNull()) ->create([ 'expired_at' => now()->subDay(), 'grace_days_ended_at' => now()->addDay(), ]); - $modelsWithNullExpiredAtCount = $this->faker()->randomDigitNotNull(); - $modelsWithNullExpired = self::MODEL::factory()->count($modelsWithNullExpiredAtCount)->create([ + $modelsWithNullExpiredAtCount = $this->faker->randomDigitNotNull(); + $modelsWithNullExpired = $modelClass::factory()->count($modelsWithNullExpiredAtCount)->create([ 'expired_at' => null, ]); $expectedSubscriptions = $unexpiredModels->concat($expiredModelsWithFutureGraceDays) ->concat($modelsWithNullExpired); - $returnedSubscriptions = self::MODEL::withExpired(false)->get(); + $returnedSubscriptions = $modelClass::withExpired(false)->get(); $this->assertEqualsCanonicalizing( $expectedSubscriptions->pluck('id')->toArray(), @@ -143,30 +162,32 @@ public function testExpiredModelsAreNotReturnedWhenCallingMethodWithExpiredAndPa public function testOnlyExpiredModelsAreReturnedWhenCallingMethodOnlyExpired() { - $expiredModelsCount = $this->faker()->randomDigitNotNull(); - $expiredModels = self::MODEL::factory()->count($expiredModelsCount)->create([ + $modelClass = $this->getModelClass(); + $expiredModelsCount = $this->faker->randomDigitNotNull(); + + $expiredModels = $modelClass::factory()->count($expiredModelsCount)->create([ 'expired_at' => now()->subDay(), ]); - $unexpiredModelsCount = $this->faker()->randomDigitNotNull(); - self::MODEL::factory()->count($unexpiredModelsCount)->create([ + $unexpiredModelsCount = $this->faker->randomDigitNotNull(); + $modelClass::factory()->count($unexpiredModelsCount)->create([ 'expired_at' => now()->addDay(), ]); - $modelsWithNullExpiredAtCount = $this->faker()->randomDigitNotNull(); - self::MODEL::factory()->count($modelsWithNullExpiredAtCount)->create([ + $modelsWithNullExpiredAtCount = $this->faker->randomDigitNotNull(); + $modelClass::factory()->count($modelsWithNullExpiredAtCount)->create([ 'expired_at' => null, ]); - $expiredModelsWithPastGraceDays = self::MODEL::factory() - ->count($this->faker()->randomDigitNotNull()) + $expiredModelsWithPastGraceDays = $modelClass::factory() + ->count($this->faker->randomDigitNotNull()) ->create([ 'expired_at' => now()->subDay(), 'grace_days_ended_at' => now()->subDay(), ]); - $expiredModelsWithNullGraceDays = self::MODEL::factory() - ->count($this->faker()->randomDigitNotNull()) + $expiredModelsWithNullGraceDays = $modelClass::factory() + ->count($this->faker->randomDigitNotNull()) ->create([ 'expired_at' => now()->subDay(), 'grace_days_ended_at' => null, @@ -175,7 +196,7 @@ public function testOnlyExpiredModelsAreReturnedWhenCallingMethodOnlyExpired() $expectedSubscriptions = $expiredModels->concat($expiredModelsWithNullGraceDays) ->concat($expiredModelsWithPastGraceDays); - $returnedSubscriptions = self::MODEL::onlyExpired()->get(); + $returnedSubscriptions = $modelClass::onlyExpired()->get(); $this->assertEqualsCanonicalizing( $expectedSubscriptions->pluck('id')->toArray(), diff --git a/tests/Scopes/StartingScopeTest.php b/tests/Scopes/StartingScopeTest.php index 4d7fe44..98f74d7 100644 --- a/tests/Scopes/StartingScopeTest.php +++ b/tests/Scopes/StartingScopeTest.php @@ -2,33 +2,47 @@ namespace Tests\Feature\Models; -use Illuminate\Foundation\Testing\RefreshDatabase; -use Illuminate\Foundation\Testing\WithFaker; -use LucasDotVin\Soulbscription\Models\Subscription; use Tests\TestCase; +use InvalidArgumentException; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Foundation\Testing\WithFaker; +use Illuminate\Foundation\Testing\RefreshDatabase; class StartingScopeTest extends TestCase { - use RefreshDatabase; use WithFaker; + use RefreshDatabase; + + public const MODEL = 'soulbscription.models.subscription'; + + protected function getModelClass() + { + $modelClass = config(self::MODEL); - public const MODEL = Subscription::class; + throw_if(!is_a($modelClass, Model::class, true), new InvalidArgumentException( + "Configured subscription model must be a subclass of " . Model::class + )); + + return $modelClass; + } public function testNotStartedModelsAreNotReturnedByDefault() { - $startedModelsCount = $this->faker()->randomDigitNotNull(); - $startedModels = self::MODEL::factory()->count($startedModelsCount)->create([ + $modelClass = $this->getModelClass(); + $startedModelsCount = $this->faker->randomDigitNotNull(); + + $startedModels = $modelClass::factory()->count($startedModelsCount)->create([ 'expired_at' => now()->addDay(), 'started_at' => now()->subDay(), ]); - $notStartedModelsCount = $this->faker()->randomDigitNotNull(); - self::MODEL::factory()->count($notStartedModelsCount)->create([ + $notStartedModelsCount = $this->faker->randomDigitNotNull(); + $modelClass::factory()->count($notStartedModelsCount)->create([ 'expired_at' => now()->addDay(), 'started_at' => now()->addDay(), ]); - $returnedSubscriptions = self::MODEL::all(); + $returnedSubscriptions = $modelClass::all(); $this->assertEqualsCanonicalizing( $startedModels->pluck('id')->toArray(), @@ -38,19 +52,21 @@ public function testNotStartedModelsAreNotReturnedByDefault() public function testNotStartedModelsAreNotReturnedWhenCallingWithoutNotStarted() { - $startedModelsCount = $this->faker()->randomDigitNotNull(); - $startedModels = self::MODEL::factory()->count($startedModelsCount)->create([ + $modelClass = $this->getModelClass(); + $startedModelsCount = $this->faker->randomDigitNotNull(); + + $startedModels = $modelClass::factory()->count($startedModelsCount)->create([ 'expired_at' => now()->addDay(), 'started_at' => now()->subDay(), ]); - $notStartedModelsCount = $this->faker()->randomDigitNotNull(); - self::MODEL::factory()->count($notStartedModelsCount)->create([ + $notStartedModelsCount = $this->faker->randomDigitNotNull(); + $modelClass::factory()->count($notStartedModelsCount)->create([ 'expired_at' => now()->addDay(), 'started_at' => now()->addDay(), ]); - $returnedSubscriptions = self::MODEL::withoutNotStarted()->get(); + $returnedSubscriptions = $modelClass::withoutNotStarted()->get(); $this->assertEqualsCanonicalizing( $startedModels->pluck('id')->toArray(), @@ -60,21 +76,22 @@ public function testNotStartedModelsAreNotReturnedWhenCallingWithoutNotStarted() public function testStartedModelsAreReturnedWhenCallingMethodWithNotStarted() { - $startedModelsCount = $this->faker()->randomDigitNotNull(); - $startedModels = self::MODEL::factory()->count($startedModelsCount)->create([ + $modelClass = $this->getModelClass(); + $startedModelsCount = $this->faker->randomDigitNotNull(); + + $startedModels = $modelClass::factory()->count($startedModelsCount)->create([ 'expired_at' => now()->addDay(), 'started_at' => now()->subDay(), ]); - $notStartedModelsCount = $this->faker()->randomDigitNotNull(); - $notStartedModels = self::MODEL::factory()->count($notStartedModelsCount)->create([ + $notStartedModelsCount = $this->faker->randomDigitNotNull(); + $notStartedModels = $modelClass::factory()->count($notStartedModelsCount)->create([ 'expired_at' => now()->addDay(), 'started_at' => now()->addDay(), ]); $expectedSubscriptions = $startedModels->concat($notStartedModels); - - $returnedSubscriptions = self::MODEL::withNotStarted()->get(); + $returnedSubscriptions = $modelClass::withNotStarted()->get(); $this->assertEqualsCanonicalizing( $expectedSubscriptions->pluck('id')->toArray(), @@ -84,19 +101,21 @@ public function testStartedModelsAreReturnedWhenCallingMethodWithNotStarted() public function testNotStartedModelsAreReturnedWhenCallingMethodWithNotStartedAndPassingAFalse() { - $startedModelsCount = $this->faker()->randomDigitNotNull(); - $startedModels = self::MODEL::factory()->count($startedModelsCount)->create([ + $modelClass = $this->getModelClass(); + $startedModelsCount = $this->faker->randomDigitNotNull(); + + $startedModels = $modelClass::factory()->count($startedModelsCount)->create([ 'expired_at' => now()->addDay(), 'started_at' => now()->subDay(), ]); - $notStartedModelsCount = $this->faker()->randomDigitNotNull(); - self::MODEL::factory()->count($notStartedModelsCount)->create([ + $notStartedModelsCount = $this->faker->randomDigitNotNull(); + $modelClass::factory()->count($notStartedModelsCount)->create([ 'expired_at' => now()->addDay(), 'started_at' => now()->addDay(), ]); - $returnedSubscriptions = self::MODEL::withNotStarted(false)->get(); + $returnedSubscriptions = $modelClass::withNotStarted(false)->get(); $this->assertEqualsCanonicalizing( $startedModels->pluck('id')->toArray(), @@ -106,19 +125,21 @@ public function testNotStartedModelsAreReturnedWhenCallingMethodWithNotStartedAn public function testOnlyStartedModelsAreReturnedWhenCallingMethodOnlyNotStarted() { - $startedModelsCount = $this->faker()->randomDigitNotNull(); - self::MODEL::factory()->count($startedModelsCount)->create([ + $modelClass = $this->getModelClass(); + $startedModelsCount = $this->faker->randomDigitNotNull(); + + $modelClass::factory()->count($startedModelsCount)->create([ 'expired_at' => now()->addDay(), 'started_at' => now()->subDay(), ]); - $notStartedModelsCount = $this->faker()->randomDigitNotNull(); - $notStartedModels = self::MODEL::factory()->count($notStartedModelsCount)->create([ + $notStartedModelsCount = $this->faker->randomDigitNotNull(); + $notStartedModels = $modelClass::factory()->count($notStartedModelsCount)->create([ 'expired_at' => now()->addDay(), 'started_at' => now()->addDay(), ]); - $returnedSubscriptions = self::MODEL::onlyNotStarted()->get(); + $returnedSubscriptions = $modelClass::onlyNotStarted()->get(); $this->assertEqualsCanonicalizing( $notStartedModels->pluck('id')->toArray(), diff --git a/tests/Scopes/SuppressingScopeTest.php b/tests/Scopes/SuppressingScopeTest.php index 810c2e1..25c5977 100644 --- a/tests/Scopes/SuppressingScopeTest.php +++ b/tests/Scopes/SuppressingScopeTest.php @@ -2,37 +2,51 @@ namespace Tests\Feature\Models; -use Illuminate\Foundation\Testing\RefreshDatabase; -use Illuminate\Foundation\Testing\WithFaker; -use LucasDotVin\Soulbscription\Models\Subscription; use Tests\TestCase; +use InvalidArgumentException; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Foundation\Testing\WithFaker; +use Illuminate\Foundation\Testing\RefreshDatabase; class SuppressingScopeTest extends TestCase { - use RefreshDatabase; use WithFaker; + use RefreshDatabase; + + public const MODEL = 'soulbscription.models.subscription'; + + protected function getModelClass() + { + $modelClass = config(self::MODEL); - public const MODEL = Subscription::class; + throw_if(!is_a($modelClass, Model::class, true), new InvalidArgumentException( + "Configured subscription model must be a subclass of " . Model::class + )); + + return $modelClass; + } public function testSuppressedModelsAreNotReturnedByDefault() { - $suppressedModelsCount = $this->faker()->randomDigitNotNull(); - self::MODEL::factory() + $modelClass = $this->getModelClass(); + $suppressedModelsCount = $this->faker->randomDigitNotNull(); + + $modelClass::factory() ->count($suppressedModelsCount) ->suppressed() ->notExpired() ->started() ->create(); - $notSuppressedModelsCount = $this->faker()->randomDigitNotNull(); - $notSuppressedModels = self::MODEL::factory() + $notSuppressedModelsCount = $this->faker->randomDigitNotNull(); + $notSuppressedModels = $modelClass::factory() ->count($notSuppressedModelsCount) ->notSuppressed() ->notExpired() ->started() ->create(); - $returnedSubscriptions = self::MODEL::all(); + $returnedSubscriptions = $modelClass::all(); $this->assertEqualsCanonicalizing( $notSuppressedModels->pluck('id')->toArray(), @@ -42,23 +56,25 @@ public function testSuppressedModelsAreNotReturnedByDefault() public function testSuppressedModelsAreNotReturnedWhenCallingWithoutNotSuppressed() { - $suppressedModelsCount = $this->faker()->randomDigitNotNull(); - self::MODEL::factory() + $modelClass = $this->getModelClass(); + $suppressedModelsCount = $this->faker->randomDigitNotNull(); + + $modelClass::factory() ->count($suppressedModelsCount) ->suppressed() ->notExpired() ->started() ->create(); - $notSuppressedModelsCount = $this->faker()->randomDigitNotNull(); - $notSuppressedModels = self::MODEL::factory() + $notSuppressedModelsCount = $this->faker->randomDigitNotNull(); + $notSuppressedModels = $modelClass::factory() ->count($notSuppressedModelsCount) ->notSuppressed() ->notExpired() ->started() ->create(); - $returnedSubscriptions = self::MODEL::withoutSuppressed()->get(); + $returnedSubscriptions = $modelClass::withoutSuppressed()->get(); $this->assertEqualsCanonicalizing( $notSuppressedModels->pluck('id')->toArray(), @@ -68,16 +84,18 @@ public function testSuppressedModelsAreNotReturnedWhenCallingWithoutNotSuppresse public function testSuppressedModelsAreReturnedWhenCallingMethodWithNotSuppressed() { - $suppressedModelsCount = $this->faker()->randomDigitNotNull(); - $suppressedModels = self::MODEL::factory() + $modelClass = $this->getModelClass(); + $suppressedModelsCount = $this->faker->randomDigitNotNull(); + + $suppressedModels = $modelClass::factory() ->count($suppressedModelsCount) ->suppressed() ->notExpired() ->started() ->create(); - $notSuppressedModelsCount = $this->faker()->randomDigitNotNull(); - $notSuppressedModels = self::MODEL::factory() + $notSuppressedModelsCount = $this->faker->randomDigitNotNull(); + $notSuppressedModels = $modelClass::factory() ->count($notSuppressedModelsCount) ->notSuppressed() ->notExpired() @@ -85,8 +103,7 @@ public function testSuppressedModelsAreReturnedWhenCallingMethodWithNotSuppresse ->create(); $expectedSubscriptions = $suppressedModels->concat($notSuppressedModels); - - $returnedSubscriptions = self::MODEL::withSuppressed()->get(); + $returnedSubscriptions = $modelClass::withSuppressed()->get(); $this->assertEqualsCanonicalizing( $expectedSubscriptions->pluck('id')->toArray(), @@ -96,23 +113,25 @@ public function testSuppressedModelsAreReturnedWhenCallingMethodWithNotSuppresse public function testSuppressedModelsAreReturnedWhenCallingMethodWithNotSuppressedAndPassingAFalse() { - $suppressedModelsCount = $this->faker()->randomDigitNotNull(); - self::MODEL::factory() + $modelClass = $this->getModelClass(); + $suppressedModelsCount = $this->faker->randomDigitNotNull(); + + $modelClass::factory() ->count($suppressedModelsCount) ->suppressed() ->notExpired() ->started() ->create(); - $notSuppressedModelsCount = $this->faker()->randomDigitNotNull(); - $notSuppressedModels = self::MODEL::factory() + $notSuppressedModelsCount = $this->faker->randomDigitNotNull(); + $notSuppressedModels = $modelClass::factory() ->count($notSuppressedModelsCount) ->notSuppressed() ->notExpired() ->started() ->create(); - $returnedSubscriptions = self::MODEL::withSuppressed(false)->get(); + $returnedSubscriptions = $modelClass::withSuppressed(false)->get(); $this->assertEqualsCanonicalizing( $notSuppressedModels->pluck('id')->toArray(), @@ -122,23 +141,26 @@ public function testSuppressedModelsAreReturnedWhenCallingMethodWithNotSuppresse public function testOnlySuppressedModelsAreReturnedWhenCallingMethodOnlyNotSuppressed() { - $suppressedModelsCount = $this->faker()->randomDigitNotNull(); - $suppressedModels = self::MODEL::factory() + $modelClass = $this->getModelClass(); + $suppressedModelsCount = $this->faker->randomDigitNotNull(); + + $suppressedModels = $modelClass::factory() ->count($suppressedModelsCount) ->suppressed() ->notExpired() ->started() ->create(); - $notSuppressedModelsCount = $this->faker()->randomDigitNotNull(); - self::MODEL::factory() + $notSuppressedModelsCount = $this->faker->randomDigitNotNull(); + + $modelClass::factory() ->count($notSuppressedModelsCount) ->notSuppressed() ->notExpired() ->started() ->create(); - $returnedSubscriptions = self::MODEL::onlySuppressed()->get(); + $returnedSubscriptions = $modelClass::onlySuppressed()->get(); $this->assertEqualsCanonicalizing( $suppressedModels->pluck('id')->toArray(),