- Updated tests for Accommodation, Entry, Food, Merchandise, and Sale controllers to use soft deletes for variants and ensure proper inventory counts. - Enhanced ticket generation logic to resolve validity from soft-deleted catalog sources. - Introduced a new TicketValidityResolver service to manage ticket validity based on event dates and variant definitions. - Removed unnecessary database assertions and improved the clarity of validity checks in tests. - Added comprehensive tests for the new TicketValidityResolver service, ensuring correct handling of event dates and multi-select options. - Cleaned up unused code and assertions in existing tests for better maintainability.
210 lines
7.7 KiB
PHP
210 lines
7.7 KiB
PHP
<?php
|
|
|
|
namespace Tests\Unit\Ticket;
|
|
|
|
use App\Domains\Catalog\Models\Attribute;
|
|
use App\Domains\Catalog\Models\AttributeOption;
|
|
use App\Domains\Catalog\Models\ItemAttribute;
|
|
use App\Domains\Catalog\Models\Variant;
|
|
use App\Domains\Catalog\Models\VariantDefinition;
|
|
use App\Domains\Event\Models\EventDate;
|
|
use App\Domains\Shared\Enums\FieldType;
|
|
use App\Domains\Ticket\Enums\ValidityTimeType;
|
|
use App\Domains\Ticket\Models\ValidityTime;
|
|
use App\Domains\Ticket\Services\TicketValidityResolver;
|
|
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
|
|
use Illuminate\Support\Carbon;
|
|
use Tests\TestCase;
|
|
|
|
class TicketValidityResolverTest extends TestCase
|
|
{
|
|
private TicketValidityResolver $resolver;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
$this->resolver = new TicketValidityResolver;
|
|
}
|
|
|
|
public function test_event_dates_and_multi_select_options_expand_to_or_groups(): void
|
|
{
|
|
$dates = new EloquentCollection([
|
|
$this->eventDate('2026-08-20'),
|
|
$this->eventDate('2026-08-21'),
|
|
]);
|
|
[$itemAttribute, $definitions] = $this->attributeDimension(
|
|
allowMultiSelect: true,
|
|
options: [
|
|
'Desayuno' => $this->timeWindow('07:00:00', '12:00:00'),
|
|
'Cena' => $this->timeWindow('20:00:00', '24:00:00'),
|
|
],
|
|
);
|
|
$variant = $this->variant($dates, $definitions);
|
|
|
|
$validity = $this->resolver->resolveVariant($variant);
|
|
|
|
$this->assertTrue($validity->isResolvable);
|
|
$this->assertFalse($validity->isUnrestricted);
|
|
$this->assertCount(4, $validity->groups);
|
|
$this->assertTrue($validity->groups->every(
|
|
fn ($group): bool => $group->validityTimes->count() === 2
|
|
));
|
|
$this->assertSame($itemAttribute, $definitions->first()->itemAttribute);
|
|
}
|
|
|
|
public function test_different_temporal_attributes_are_anded_in_the_same_group(): void
|
|
{
|
|
[, $scheduleDefinitions] = $this->attributeDimension(
|
|
allowMultiSelect: false,
|
|
options: ['Almuerzo' => $this->timeWindow('12:00:00', '15:00:00')],
|
|
);
|
|
[, $admissionDefinitions] = $this->attributeDimension(
|
|
allowMultiSelect: false,
|
|
options: ['Ingreso' => $this->timeWindow('11:00:00', '14:00:00')],
|
|
);
|
|
$variant = $this->variant(
|
|
new EloquentCollection([$this->eventDate('2026-08-20')]),
|
|
new EloquentCollection([
|
|
...$scheduleDefinitions,
|
|
...$admissionDefinitions,
|
|
]),
|
|
);
|
|
|
|
$validity = $this->resolver->resolveVariant($variant);
|
|
|
|
$this->assertCount(1, $validity->groups);
|
|
$this->assertCount(3, $validity->groups->sole()->validityTimes);
|
|
$this->assertSame(
|
|
'2026-08-20 12:00:00',
|
|
$validity->effectiveStartsAt(Carbon::parse('2026-08-20 13:00:00'))->format('Y-m-d H:i:s'),
|
|
);
|
|
$this->assertSame(
|
|
'2026-08-20 14:00:00',
|
|
$validity->effectiveExpiresAt(Carbon::parse('2026-08-20 13:00:00'))->format('Y-m-d H:i:s'),
|
|
);
|
|
$this->assertTrue($validity->isValid(Carbon::parse('2026-08-20 13:00:00')));
|
|
$this->assertFalse($validity->isValid(Carbon::parse('2026-08-20 14:00:00')));
|
|
}
|
|
|
|
public function test_variant_without_temporal_dimensions_is_unrestricted(): void
|
|
{
|
|
$validity = $this->resolver->resolveVariant(
|
|
$this->variant(new EloquentCollection, new EloquentCollection)
|
|
);
|
|
|
|
$this->assertTrue($validity->isResolvable);
|
|
$this->assertTrue($validity->isUnrestricted);
|
|
$this->assertTrue($validity->isValid());
|
|
$this->assertFalse($validity->isExpired());
|
|
}
|
|
|
|
public function test_non_option_attributes_do_not_affect_validity(): void
|
|
{
|
|
$attribute = new Attribute(['type' => FieldType::String]);
|
|
$attribute->setRelation('options', new EloquentCollection);
|
|
$itemAttribute = new ItemAttribute(['allow_multi_select' => false]);
|
|
$itemAttribute->setRelation('attribute', $attribute);
|
|
$definition = new VariantDefinition(['value' => 'Comedor principal']);
|
|
$definition->setAttribute('item_attribute_id', 123);
|
|
$definition->setRelation('itemAttribute', $itemAttribute);
|
|
|
|
$validity = $this->resolver->resolveVariant(
|
|
$this->variant(new EloquentCollection, new EloquentCollection([$definition]))
|
|
);
|
|
|
|
$this->assertTrue($validity->isResolvable);
|
|
$this->assertTrue($validity->isUnrestricted);
|
|
}
|
|
|
|
public function test_multiple_values_for_a_single_select_attribute_are_unresolvable(): void
|
|
{
|
|
[, $definitions] = $this->attributeDimension(
|
|
allowMultiSelect: false,
|
|
options: [
|
|
'Desayuno' => $this->timeWindow('07:00:00', '12:00:00'),
|
|
'Cena' => $this->timeWindow('20:00:00', '24:00:00'),
|
|
],
|
|
);
|
|
|
|
$validity = $this->resolver->resolveVariant(
|
|
$this->variant(new EloquentCollection, $definitions)
|
|
);
|
|
|
|
$this->assertFalse($validity->isResolvable);
|
|
$this->assertFalse($validity->isValid());
|
|
}
|
|
|
|
private function eventDate(string $date): EventDate
|
|
{
|
|
$validityTime = new ValidityTime([
|
|
'type' => ValidityTimeType::FixedWindow,
|
|
'fixed_starts_at' => "{$date} 00:00:00",
|
|
'fixed_expires_at' => "{$date} 23:59:59",
|
|
]);
|
|
$eventDate = new EventDate([
|
|
'date' => $date,
|
|
'time_start' => '00:00:00',
|
|
'time_end' => '23:59:59',
|
|
]);
|
|
$eventDate->setRelation('validityTime', $validityTime);
|
|
|
|
return $eventDate;
|
|
}
|
|
|
|
private function timeWindow(string $start, string $end): ValidityTime
|
|
{
|
|
return new ValidityTime([
|
|
'type' => ValidityTimeType::TimeWindow,
|
|
'start_time' => $start,
|
|
'end_time' => $end,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* @param array<string, ValidityTime|null> $options
|
|
* @return array{ItemAttribute, EloquentCollection<int, VariantDefinition>}
|
|
*/
|
|
private function attributeDimension(bool $allowMultiSelect, array $options): array
|
|
{
|
|
$attribute = new Attribute([
|
|
'type' => FieldType::Select,
|
|
]);
|
|
$attributeOptions = collect($options)->map(
|
|
function (?ValidityTime $validityTime, string $value): AttributeOption {
|
|
$option = new AttributeOption(['value' => $value, 'label' => $value]);
|
|
$option->setRelation('validityTime', $validityTime);
|
|
|
|
return $option;
|
|
}
|
|
);
|
|
$attribute->setRelation('options', new EloquentCollection($attributeOptions));
|
|
|
|
$itemAttribute = new ItemAttribute(['allow_multi_select' => $allowMultiSelect]);
|
|
$itemAttribute->setRelation('attribute', $attribute);
|
|
$definitions = $attributeOptions->map(function (AttributeOption $option) use ($itemAttribute): VariantDefinition {
|
|
$definition = new VariantDefinition(['value' => $option->value]);
|
|
$definition->setAttribute('item_attribute_id', spl_object_id($itemAttribute));
|
|
$definition->setRelation('itemAttribute', $itemAttribute);
|
|
|
|
return $definition;
|
|
});
|
|
|
|
return [$itemAttribute, new EloquentCollection($definitions)];
|
|
}
|
|
|
|
/**
|
|
* @param EloquentCollection<int, EventDate> $eventDates
|
|
* @param EloquentCollection<int, VariantDefinition> $definitions
|
|
*/
|
|
private function variant(EloquentCollection $eventDates, EloquentCollection $definitions): Variant
|
|
{
|
|
$variant = new Variant;
|
|
$variant->setRelation('eventDates', $eventDates);
|
|
$variant->setRelation('eventDate', null);
|
|
$variant->setRelation('definitions', $definitions);
|
|
|
|
return $variant;
|
|
}
|
|
}
|