From a650de79c1bef7b60a7ca7d51959bc2c25c41443 Mon Sep 17 00:00:00 2001 From: ncoronel Date: Wed, 26 Aug 2026 17:00:17 -0300 Subject: [PATCH] feat(purchase): add expiration time calculations to PurchaseResource and implement related tests --- .../Purchase/Resources/PurchaseResource.php | 8 ++- tests/Unit/Purchase/PurchaseResourceTest.php | 69 +++++++++++++++++++ 2 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 tests/Unit/Purchase/PurchaseResourceTest.php diff --git a/app/Domains/Purchase/Resources/PurchaseResource.php b/app/Domains/Purchase/Resources/PurchaseResource.php index 81d0ca3..a14a77e 100644 --- a/app/Domains/Purchase/Resources/PurchaseResource.php +++ b/app/Domains/Purchase/Resources/PurchaseResource.php @@ -17,6 +17,8 @@ class PurchaseResource extends JsonResource */ public function toArray(Request $request): array { + $serverTime = now(); + $expiresAt = $this->stockReservation?->expires_at; $items = $this->resource->relationLoaded('items') ? $this->resource->getRelation('items') : collect(); @@ -48,7 +50,11 @@ class PurchaseResource extends JsonResource 'created_at' => $this->created_at, 'status' => $this->status, 'payment_method' => $this->payment_method, - 'expires_at' => $this->stockReservation?->expires_at, + 'expires_at' => $expiresAt, + 'expires_in_seconds' => $expiresAt === null + ? null + : max(0, $expiresAt->getTimestamp() - $serverTime->getTimestamp()), + 'server_time' => $serverTime, 'dni' => $this->dni, 'transfer_payer_dni' => $this->transfer_payer_dni, 'telefono' => $this->telefono, diff --git a/tests/Unit/Purchase/PurchaseResourceTest.php b/tests/Unit/Purchase/PurchaseResourceTest.php new file mode 100644 index 0000000..a7f09f9 --- /dev/null +++ b/tests/Unit/Purchase/PurchaseResourceTest.php @@ -0,0 +1,69 @@ +startOfSecond(); + $this->travelTo($now); + $expiresAt = $now->copy()->addMinutes(12); + + $resource = $this->resourceFor(Purchase::STATUS_PENDING_PAYMENT, $expiresAt); + + $this->assertTrue($expiresAt->equalTo($resource['expires_at'])); + $this->assertSame(720, $resource['expires_in_seconds']); + $this->assertTrue($now->equalTo($resource['server_time'])); + + $this->travelBack(); + } + + public function test_it_clamps_an_overdue_checkout_to_zero_seconds(): void + { + $now = now()->startOfSecond(); + $this->travelTo($now); + + $resource = $this->resourceFor( + Purchase::STATUS_CREATED, + $now->copy()->subSecond(), + ); + + $this->assertSame(0, $resource['expires_in_seconds']); + + $this->travelBack(); + } + + public function test_it_exposes_null_expiration_after_the_purchase_enters_review(): void + { + $resource = $this->resourceFor( + Purchase::STATUS_IN_REVIEW, + null, + ); + + $this->assertNull($resource['expires_at']); + $this->assertNull($resource['expires_in_seconds']); + } + + /** @return array */ + private function resourceFor(string $status, ?Carbon $expiresAt): array + { + $purchase = (new Purchase)->forceFill([ + 'status' => $status, + 'total' => '0.00', + ]); + $purchase->setRelation('stockReservation', (new StockReservation)->forceFill([ + 'status' => StockReservation::STATUS_ACTIVE, + 'expires_at' => $expiresAt, + ])); + + return (new PurchaseResource($purchase))->toArray(Request::create('/')); + } +}