test: cover event variant replacements

This commit is contained in:
2026-09-14 14:11:24 -03:00
parent 2bd3bf9dc1
commit b4a7b80430
2 changed files with 144 additions and 0 deletions

View File

@@ -11,6 +11,7 @@ use App\Domains\Catalog\Models\Inventory;
use App\Domains\Catalog\Models\Variant;
use App\Domains\Event\Events\EventDateRescheduled;
use App\Domains\Event\Events\EventDateSuspended;
use App\Domains\Purchase\Services\Checkout\CatalogSelectionResolver;
use App\Domains\Tenant\Models\Tenant;
use App\Domains\Tenant\Models\WebsiteType;
use App\Domains\Ticket\Enums\ValidityTimeType;
@@ -20,6 +21,7 @@ use Database\Seeders\SocialMediaSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
use Laravel\Sanctum\Sanctum;
use Tests\TestCase;
@@ -247,6 +249,7 @@ class AdminAppEventControllerTest extends TestCase
'time_end' => '20:00',
]);
$variant = $this->createVariant($tenant, $original->id);
$variant->inventory()->update(['real_stock' => 5]);
$ticket = $this->createTicket($tenant, $admin, $variant);
Sanctum::actingAs($admin);
@@ -267,6 +270,31 @@ class AdminAppEventControllerTest extends TestCase
});
$this->assertDatabaseCount('event_dates', 2);
$variant->refresh();
$replacement = $variant->replacement()->firstOrFail();
$this->assertSame($original->id, $variant->event_date_id);
$this->assertSame($destination->id, $replacement->event_date_id);
$this->assertSame($variant->inventory_id, $replacement->inventory_id);
$this->assertNotNull($variant->sales_disabled_at);
$this->assertSame($replacement->id, $variant->replaced_by_variant_id);
$this->assertSame(
[$replacement->id],
$variant->catalogItem->fresh(['variants.inventory'])->visibleVariants()->modelKeys(),
);
$this->assertSame($variant->id, $ticket->fresh()->source_variant_id);
try {
app(CatalogSelectionResolver::class)->resolve(
$tenant,
$variant->catalog_item_id,
$variant->id,
'direct_items.0',
);
$this->fail('The historical variant should not be sellable.');
} catch (ValidationException $exception) {
$this->assertArrayHasKey('direct_items.0.variant_id', $exception->errors());
}
$this->assertSame('20 de Octubre 2027', $tenant->fresh()->event_date_text);
$this->getJson('/api/tenants/bootstrap?dominio=acme.test&path=%2F')
->assertOk()
@@ -287,6 +315,12 @@ class AdminAppEventControllerTest extends TestCase
])->assertOk()->assertJsonPath('data.status', 'rescheduled');
$this->assertDatabaseCount('event_dates', 3);
$replacement->refresh();
$latestReplacement = $replacement->replacement()->firstOrFail();
$this->assertSame($replacement->inventory_id, $latestReplacement->inventory_id);
$this->assertSame('2027-10-25', $latestReplacement->eventDate->date->format('Y-m-d'));
$this->assertFalse($replacement->isSellable());
$this->assertTrue($latestReplacement->isSellable());
$this->assertSame('25 de Octubre 2027', $tenant->fresh()->event_date_text);
$this->getJson('/api/tenants/bootstrap?dominio=acme.test&path=%2F')
->assertOk()
@@ -299,6 +333,41 @@ class AdminAppEventControllerTest extends TestCase
);
}
public function test_rescheduling_reuses_an_equivalent_destination_variant(): void
{
Event::fake([EventDateRescheduled::class]);
$tenant = $this->createActiveEvent($this->createTenant('acme'), 'Festival Acme');
$original = $tenant->eventDates()->create([
'date' => '2027-10-09',
'time_start' => '09:00',
'time_end' => '18:30',
]);
$destination = $tenant->eventDates()->create([
'date' => '2027-10-20',
'time_start' => '09:00',
'time_end' => '18:30',
]);
$historicalVariant = $this->createVariant($tenant, $original->id);
$destinationVariant = Variant::query()->create([
'catalog_item_id' => $historicalVariant->catalog_item_id,
'inventory_id' => Inventory::query()->create(['real_stock' => 5])->id,
'event_date_id' => $destination->id,
]);
Sanctum::actingAs($this->createAdminAppUser($tenant));
$this->postJson("/api/v1/adminapp/tenant/event-dates/{$original->id}/reschedule", [
'date' => '2027-10-20',
])->assertOk();
$this->assertSame(2, Variant::query()->count());
$this->assertSame(
$destinationVariant->id,
$historicalVariant->fresh()->replaced_by_variant_id,
);
$this->assertSame($original->id, $historicalVariant->fresh()->event_date_id);
$this->assertTrue($destinationVariant->fresh()->isSellable());
}
public function test_suspending_disables_only_tickets_without_another_usable_date(): void
{
Event::fake([EventDateSuspended::class]);
@@ -335,6 +404,8 @@ class AdminAppEventControllerTest extends TestCase
$this->assertNotNull($singleDateTicket->fresh()->disabled_at);
$this->assertNull($multipleDateTicket->fresh()->disabled_at);
$this->assertNotNull($singleDateVariant->fresh()->sales_disabled_at);
$this->assertNotNull($multipleDateVariant->fresh()->sales_disabled_at);
$this->assertSame('10 de Octubre 2027', $tenant->fresh()->event_date_text);
$this->getJson('/api/tenants/bootstrap?dominio=acme.test&path=%2F')
->assertOk()

View File

@@ -72,6 +72,79 @@ class StorePurchaseTest extends TestCase
$this->travelBack();
}
public function test_checkout_replaces_a_historical_cart_variant_without_duplicating_stock(): void
{
$tenant = $this->createTenant('sonder', 'Sonder', 'sonder.com.ar');
$user = User::factory()->create();
$historicalVariant = $this->createVariantForTenant('sonder', 10, '50.00');
$cart = Cart::query()->create([
'tenant_codigo' => $tenant->codigo,
'user_id' => $user->id,
'status' => Cart::STATUS_ACTIVE,
]);
$cart->addItem($historicalVariant->catalog_item_id, $historicalVariant->id, 2);
$replacement = Variant::query()->create([
'catalog_item_id' => $historicalVariant->catalog_item_id,
'inventory_id' => $historicalVariant->inventory_id,
'precio' => '50.00',
]);
$historicalVariant->update([
'replaced_by_variant_id' => $replacement->id,
'sales_disabled_at' => now(),
]);
$purchase = app(CheckoutService::class)->startCheckout($tenant, $user->id, [
'cart_id' => $cart->id,
]);
$this->assertDatabaseHas('carrito_items', [
'cart_id' => $cart->id,
'variant_id' => $replacement->id,
'cantidad' => 2,
]);
$this->assertDatabaseHas('compra_items', [
'compra_id' => $purchase->id,
'source_variant_id' => $replacement->id,
'cantidad' => 2,
]);
$this->assertDatabaseHas('inventories', [
'id' => $historicalVariant->inventory_id,
'real_stock' => 10,
'reserved_stock' => 2,
]);
$this->assertSame(8, $historicalVariant->catalogItem->fresh()->availableStock());
}
public function test_direct_checkout_rejects_a_historical_variant(): void
{
$tenant = $this->createTenant('sonder', 'Sonder', 'sonder.com.ar');
$user = User::factory()->create();
$historicalVariant = $this->createVariantForTenant('sonder', 10, '50.00');
$replacement = Variant::query()->create([
'catalog_item_id' => $historicalVariant->catalog_item_id,
'inventory_id' => $historicalVariant->inventory_id,
'precio' => '50.00',
]);
$historicalVariant->update([
'replaced_by_variant_id' => $replacement->id,
'sales_disabled_at' => now(),
]);
$this->actingAs($user, 'sanctum')
->postJson('/api/tenants/sonder/compras/start-checkout', [
'direct_items' => [[
'catalog_item_id' => $historicalVariant->catalog_item_id,
'variant_id' => $historicalVariant->id,
'cantidad' => 1,
]],
])
->assertUnprocessable()
->assertJsonValidationErrors('direct_items.0.variant_id');
$this->assertDatabaseCount('compras', 0);
}
public function test_checkout_cannot_replace_an_overdue_cart_reservation(): void
{
$tenant = $this->createTenant('sonder', 'Sonder', 'sonder.com.ar');