seed(AuthorizationSeeder::class); WebsiteType::query()->create([ 'codigo' => 'onticket', 'nombre' => 'OnTicket', ]); } public function test_authentication_is_required(): void { $this->postJson('/api/v1/adminapp/tenant/accommodations', ['variants' => []]) ->assertUnauthorized(); } public function test_it_creates_accommodation_variants_and_adds_type_options(): void { [$tenant, $attribute] = $this->configuredTenant(); Sanctum::actingAs($this->createAdminAppUser($tenant)); $this->postJson('/api/v1/adminapp/tenant/accommodations', [ 'variants' => [ $this->variantPayload('Casa Rodante Familiar', 'Parcela grande', 25, 45000), $this->variantPayload('Carpa', null, 100, 35000), ], ]) ->assertOk() ->assertJsonPath('data.name', 'Alojamiento') ->assertJsonCount(2, 'data.variants') ->assertJsonPath('data.variants.0.title', 'Casa Rodante Familiar') ->assertJsonPath('data.variants.0.value', 'casa_rodante_familiar') ->assertJsonPath('data.variants.0.description', 'Parcela grande') ->assertJsonPath('data.variants.0.stock', 25) ->assertJsonPath('data.variants.0.price', '45000.00'); $this->getJson('/api/v1/adminapp/tenant/accommodations') ->assertOk() ->assertJsonPath('data.name', 'Alojamiento') ->assertJsonCount(2, 'data.variants') ->assertJsonPath('data.variants.0.title', 'Casa Rodante Familiar') ->assertJsonPath('data.variants.1.title', 'Carpa'); $this->assertDatabaseHas('attribute_options', [ 'attribute_id' => $attribute->id, 'value' => 'casa_rodante_familiar', 'label' => 'Casa Rodante Familiar', ]); $this->assertDatabaseHas('attribute_options', [ 'attribute_id' => $attribute->id, 'value' => 'carpa', 'label' => 'Carpa', ]); $this->assertDatabaseCount('catalog_items', 1); $this->assertTrue(CatalogItem::query()->sole()->has_tickets); $this->assertDatabaseCount('variantes', 2); $this->assertDatabaseCount('inventories', 2); $this->assertDatabaseCount('variant_values', 2); } public function test_it_updates_variants_with_an_id_and_creates_variants_without_one(): void { [$tenant, $attribute] = $this->configuredTenant(); Sanctum::actingAs($this->createAdminAppUser($tenant)); $created = $this->postJson('/api/v1/adminapp/tenant/accommodations', [ 'variants' => [ $this->variantPayload('Casa Rodante', 'Descripción inicial', 20, 40000), ], ])->assertOk(); $variantId = $created->json('data.variants.0.id'); $updated = $this->variantPayload('Casa Rodante Premium', 'Con electricidad', 15, 50000); $updated['id'] = $variantId; $this->postJson('/api/v1/adminapp/tenant/accommodations', [ 'variants' => [ $updated, $this->variantPayload('Motor Home', null, 10, 60000), ], ]) ->assertOk() ->assertJsonCount(2, 'data.variants') ->assertJsonPath('data.variants.0.id', $variantId) ->assertJsonPath('data.variants.0.title', 'Casa Rodante Premium') ->assertJsonPath('data.variants.0.value', 'casa_rodante_premium') ->assertJsonPath('data.variants.0.stock', 15) ->assertJsonPath('data.variants.1.value', 'motor_home'); $this->assertDatabaseMissing('attribute_options', [ 'attribute_id' => $attribute->id, 'value' => 'casa_rodante', ]); $this->assertDatabaseHas('attribute_options', [ 'attribute_id' => $attribute->id, 'value' => 'casa_rodante_premium', 'label' => 'Casa Rodante Premium', ]); $this->assertDatabaseHas('variant_values', [ 'variant_id' => $variantId, 'value' => 'casa_rodante_premium', ]); $this->assertDatabaseCount('variantes', 2); } public function test_it_deletes_an_accommodation_and_its_empty_product(): void { [$tenant] = $this->configuredTenant(); Sanctum::actingAs($this->createAdminAppUser($tenant)); $accommodationId = $this->postJson('/api/v1/adminapp/tenant/accommodations', [ 'variants' => [ $this->variantPayload('Carpa', null, 100, 35000), ], ])->assertOk()->json('data.variants.0.id'); $this->deleteJson("/api/v1/adminapp/tenant/accommodations/{$accommodationId}") ->assertNoContent(); $this->assertDatabaseCount('catalog_items', 0); $this->assertDatabaseCount('variantes', 0); $this->assertDatabaseCount('inventories', 0); } public function test_it_rejects_duplicate_normalized_titles(): void { [$tenant] = $this->configuredTenant(); Sanctum::actingAs($this->createAdminAppUser($tenant)); $this->postJson('/api/v1/adminapp/tenant/accommodations', [ 'variants' => [ $this->variantPayload('Casa Rodante', null, 10, 40000), $this->variantPayload(' CASA RODANTE ', null, 10, 40000), ], ]) ->assertUnprocessable() ->assertJsonValidationErrors('variants.1.title'); $this->assertDatabaseCount('catalog_items', 0); $this->assertDatabaseCount('attribute_options', 0); } public function test_the_tenant_must_have_the_accommodations_menu(): void { [$tenant] = $this->configuredTenant(withMenu: false); Sanctum::actingAs($this->createAdminAppUser($tenant)); $this->postJson('/api/v1/adminapp/tenant/accommodations', [ 'variants' => [ $this->variantPayload('Carpa', null, 10, 35000), ], ])->assertNotFound(); $this->assertDatabaseCount('catalog_items', 0); } public function test_a_different_tenant_can_use_the_endpoint_when_it_has_the_menu(): void { [$tenant] = $this->configuredTenant('another_tenant'); Sanctum::actingAs($this->createAdminAppUser($tenant)); $this->postJson('/api/v1/adminapp/tenant/accommodations', [ 'variants' => [ $this->variantPayload('Dormitorio Compartido', null, 30, 25000), ], ]) ->assertOk() ->assertJsonPath('data.variants.0.value', 'dormitorio_compartido'); } /** @return array{Tenant, Attribute} */ private function configuredTenant( string $tenantCode = 'fiesta_futbol_infantil', bool $withMenu = true, ): array { $headerLogo = Attachment::query()->create([ 'path' => 'test/header.png', 'filename' => 'header.png', 'type' => AttachmentType::Image, 'mime_type' => 'image/png', ]); $footerLogo = Attachment::query()->create([ 'path' => 'test/footer.png', 'filename' => 'footer.png', 'type' => AttachmentType::Image, 'mime_type' => 'image/png', ]); $tenant = Tenant::query()->create([ 'codigo' => $tenantCode, 'nombre' => 'Fiesta Fútbol Infantil', 'dominio' => 'fiesta.test', 'primary_color' => '#112233', 'secondary_color' => '#445566', 'danger_color' => '#cc0000', 'header_bg_color' => '#ffffff', 'footer_bg_color' => '#111111', 'header_logo_id' => $headerLogo->id, 'footer_logo_id' => $footerLogo->id, 'website_type_code' => 'onticket', ]); $attribute = Attribute::query()->create([ 'tenant_codigo' => $tenant->codigo, 'codigo' => 'tipo_alojamiento', 'nombre' => 'TipoAlojamiento', 'type' => FieldType::Select, 'is_required' => true, ]); if ($withMenu) { $menu = Menu::query()->firstOrCreate( ['code' => 'adminapp.fiesta-futbol-infantil.alojamientos'], ['label' => 'Alojamientos', 'route' => '/admin/alojamientos'], ); $tenant->menues()->syncWithoutDetaching([$menu->code]); } return [$tenant, $attribute]; } /** @return array */ private function variantPayload( string $title, ?string $description, int $stock, float $price, ): array { return [ 'title' => $title, 'description' => $description, 'stock' => $stock, 'price' => $price, ]; } private function createAdminAppUser(Tenant $tenant): User { return User::factory()->create([ 'rol_codigo' => RoleCode::AdminApp->value, 'tenant_codigo' => $tenant->codigo, ]); } }