This commit is contained in:
@@ -206,6 +206,33 @@ test('created_by_channel is taken from the Device-Type header', function (string
|
||||
'kbz_miniapp' => ['kbz_miniapp', BookingChannel::MiniApp],
|
||||
]);
|
||||
|
||||
test('customer-supplied notes are stored and returned', function () {
|
||||
[$route, $timeSlot] = bookableRouteAndSlot([[VehicleOption::BackSeat, '15000.00']]);
|
||||
|
||||
$payload = bookingPayload($route, $timeSlot, [
|
||||
['vehicle_option' => 'back_seat', 'passenger_count' => 1],
|
||||
]);
|
||||
$payload['notes'] = 'Please call before arriving.';
|
||||
|
||||
$this->withHeader('Authorization', "Bearer {$this->token}")
|
||||
->postJson('/api/v1/bookings', $payload)
|
||||
->assertCreated()
|
||||
->assertJsonPath('data.notes', 'Please call before arriving.');
|
||||
|
||||
expect(Booking::first()->notes)->toBe('Please call before arriving.');
|
||||
});
|
||||
|
||||
test('notes is optional and defaults to null', function () {
|
||||
[$route, $timeSlot] = bookableRouteAndSlot([[VehicleOption::BackSeat, '15000.00']]);
|
||||
|
||||
$this->withHeader('Authorization', "Bearer {$this->token}")
|
||||
->postJson('/api/v1/bookings', bookingPayload($route, $timeSlot, [
|
||||
['vehicle_option' => 'back_seat', 'passenger_count' => 1],
|
||||
]))
|
||||
->assertCreated()
|
||||
->assertJsonPath('data.notes', null);
|
||||
});
|
||||
|
||||
test('a Device-Type header cannot spoof the agent or admin channel', function (string $deviceType) {
|
||||
[$route, $timeSlot] = bookableRouteAndSlot([[VehicleOption::BackSeat, '15000.00']]);
|
||||
|
||||
@@ -221,3 +248,122 @@ test('a Device-Type header cannot spoof the agent or admin channel', function (s
|
||||
'admin' => ['admin'],
|
||||
'unrecognized value' => ['smart-fridge'],
|
||||
]);
|
||||
|
||||
/**
|
||||
* Same company as $outbound, from/to swapped — the true reverse route.
|
||||
*
|
||||
* @param array<int, array{0: VehicleOption, 1: string}> $pricedOptions
|
||||
*/
|
||||
function reverseRouteAndSlot(EvRoute $outbound, array $pricedOptions): array
|
||||
{
|
||||
$route = EvRoute::factory()->create([
|
||||
'ev_company_id' => $outbound->ev_company_id,
|
||||
'from_destination_id' => $outbound->to_destination_id,
|
||||
'to_destination_id' => $outbound->from_destination_id,
|
||||
'is_active' => true,
|
||||
]);
|
||||
$timeSlot = DepartureTimeSlot::factory()->create();
|
||||
$route->timeSlots()->attach($timeSlot->id, ['is_active' => true]);
|
||||
|
||||
foreach ($pricedOptions as [$vehicleOption, $price]) {
|
||||
RoutePricing::factory()->create([
|
||||
'ev_route_id' => $route->id,
|
||||
'vehicle_option' => $vehicleOption,
|
||||
'price' => $price,
|
||||
]);
|
||||
}
|
||||
|
||||
return [$route, $timeSlot];
|
||||
}
|
||||
|
||||
test('round trip: creates two linked bookings, each priced against its own route', function () {
|
||||
config(['booking.back_seat_enabled' => true]);
|
||||
|
||||
[$outboundRoute, $outboundSlot] = bookableRouteAndSlot([[VehicleOption::BackSeat, '9000.00']]);
|
||||
[$returnRoute, $returnSlot] = reverseRouteAndSlot($outboundRoute, [[VehicleOption::BackSeat, '11000.00']]);
|
||||
|
||||
$payload = bookingPayload($outboundRoute, $outboundSlot, [
|
||||
['vehicle_option' => 'back_seat', 'passenger_count' => 1],
|
||||
]);
|
||||
$payload['is_round_trip'] = true;
|
||||
$payload['return_ev_route_id'] = $returnRoute->id;
|
||||
$payload['return_departure_time_slot_id'] = $returnSlot->id;
|
||||
$payload['return_travel_date'] = now()->addDays(3)->toDateString();
|
||||
$payload['return_selections'] = [['vehicle_option' => 'back_seat', 'passenger_count' => 1]];
|
||||
|
||||
$this->withHeader('Authorization', "Bearer {$this->token}")
|
||||
->postJson('/api/v1/bookings', $payload)
|
||||
->assertCreated()
|
||||
->assertJsonPath('data.is_round_trip', true)
|
||||
->assertJsonPath('data.is_return_leg', false)
|
||||
->assertJsonPath('data.price', '9000.00')
|
||||
->assertJsonPath('data.linked_booking.is_return_leg', true)
|
||||
->assertJsonPath('data.linked_booking.route.id', $returnRoute->id)
|
||||
->assertJsonPath('data.linked_booking.vehicle_options.0.vehicle_option', 'back_seat')
|
||||
->assertJsonPath('data.linked_booking.vehicle_options.0.unit_price', '11000.00');
|
||||
|
||||
expect(Booking::count())->toBe(2);
|
||||
|
||||
$return = Booking::where('is_return_leg', true)->firstOrFail();
|
||||
expect($return->price)->toEqual('11000.00')
|
||||
->and($return->ev_route_id)->toBe($returnRoute->id);
|
||||
});
|
||||
|
||||
test('round trip: a return route that is not the reverse of the outbound route surfaces as 422', function () {
|
||||
config(['booking.back_seat_enabled' => true]);
|
||||
|
||||
[$outboundRoute, $outboundSlot] = bookableRouteAndSlot([[VehicleOption::BackSeat, '9000.00']]);
|
||||
[$unrelatedRoute, $unrelatedSlot] = bookableRouteAndSlot([[VehicleOption::BackSeat, '9000.00']]);
|
||||
|
||||
$payload = bookingPayload($outboundRoute, $outboundSlot, [
|
||||
['vehicle_option' => 'back_seat', 'passenger_count' => 1],
|
||||
]);
|
||||
$payload['is_round_trip'] = true;
|
||||
$payload['return_ev_route_id'] = $unrelatedRoute->id;
|
||||
$payload['return_departure_time_slot_id'] = $unrelatedSlot->id;
|
||||
$payload['return_travel_date'] = now()->addDays(3)->toDateString();
|
||||
$payload['return_selections'] = [['vehicle_option' => 'back_seat', 'passenger_count' => 1]];
|
||||
|
||||
$this->withHeader('Authorization', "Bearer {$this->token}")
|
||||
->postJson('/api/v1/bookings', $payload)
|
||||
->assertStatus(422);
|
||||
|
||||
expect(Booking::count())->toBe(0);
|
||||
});
|
||||
|
||||
test('round trip: return fields are required when is_round_trip is true', function () {
|
||||
[$route, $timeSlot] = bookableRouteAndSlot([[VehicleOption::BackSeat, '15000.00']]);
|
||||
|
||||
$payload = bookingPayload($route, $timeSlot, [
|
||||
['vehicle_option' => 'back_seat', 'passenger_count' => 1],
|
||||
]);
|
||||
$payload['is_round_trip'] = true;
|
||||
|
||||
$this->withHeader('Authorization', "Bearer {$this->token}")
|
||||
->postJson('/api/v1/bookings', $payload)
|
||||
->assertStatus(422)
|
||||
->assertJsonValidationErrors([
|
||||
'return_ev_route_id', 'return_departure_time_slot_id', 'return_travel_date', 'return_selections',
|
||||
]);
|
||||
});
|
||||
|
||||
test('round trip: return_travel_date before travel_date is rejected', function () {
|
||||
config(['booking.back_seat_enabled' => true]);
|
||||
|
||||
[$outboundRoute, $outboundSlot] = bookableRouteAndSlot([[VehicleOption::BackSeat, '9000.00']]);
|
||||
[$returnRoute, $returnSlot] = reverseRouteAndSlot($outboundRoute, [[VehicleOption::BackSeat, '9000.00']]);
|
||||
|
||||
$payload = bookingPayload($outboundRoute, $outboundSlot, [
|
||||
['vehicle_option' => 'back_seat', 'passenger_count' => 1],
|
||||
]);
|
||||
$payload['is_round_trip'] = true;
|
||||
$payload['return_ev_route_id'] = $returnRoute->id;
|
||||
$payload['return_departure_time_slot_id'] = $returnSlot->id;
|
||||
$payload['return_travel_date'] = now()->toDateString(); // before travel_date (addDay())
|
||||
$payload['return_selections'] = [['vehicle_option' => 'back_seat', 'passenger_count' => 1]];
|
||||
|
||||
$this->withHeader('Authorization', "Bearer {$this->token}")
|
||||
->postJson('/api/v1/bookings', $payload)
|
||||
->assertStatus(422)
|
||||
->assertJsonValidationErrors(['return_travel_date']);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user