improvements
Some checks failed
Build and Push Docker Image / build_and_push (push) Failing after 3m6s
Some checks failed
Build and Push Docker Image / build_and_push (push) Failing after 3m6s
This commit is contained in:
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func (h Handler) BookDriverJourney(passengerid string, driverid string, journeyid string, returnWaitingDuration time.Duration, priceAmount float64, priceCurrency string, data map[string]any) (*types.Booking, error) {
|
||||
func (h Handler) BookDriverJourney(passengerid string, driverid string, journeyid string, returnWaitingDuration time.Duration, priceAmount float64, priceCurrency string, driverCompensationAmount float64, driverCompensationCurrency string, data map[string]any) (*types.Booking, error) {
|
||||
journey, err := h.Storage.GetDriverJourney(journeyid)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("could not find driver journey")
|
||||
@@ -26,14 +26,16 @@ func (h Handler) BookDriverJourney(passengerid string, driverid string, journeyi
|
||||
log.Debug().Float64("Price", priceAmount).Any("journey", journey.Price).Msg("store booking")
|
||||
|
||||
booking := types.Booking{
|
||||
Id: uuid.NewString(),
|
||||
GroupId: uuid.NewString(),
|
||||
Status: "WAITING_CONFIRMATION",
|
||||
PassengerId: passengerid,
|
||||
DriverId: driverid,
|
||||
Journey: journey,
|
||||
ReturnWaitingDuration: returnWaitingDuration,
|
||||
Data: data,
|
||||
Id: uuid.NewString(),
|
||||
GroupId: uuid.NewString(),
|
||||
Status: "WAITING_CONFIRMATION",
|
||||
PassengerId: passengerid,
|
||||
DriverId: driverid,
|
||||
Journey: journey,
|
||||
ReturnWaitingDuration: returnWaitingDuration,
|
||||
Data: data,
|
||||
DriverCompensationAmount: driverCompensationAmount,
|
||||
DriverCompensationCurrency: driverCompensationCurrency,
|
||||
}
|
||||
|
||||
if err := h.Storage.CreateBooking(booking); err != nil {
|
||||
|
||||
335
handler/journeys_test.go
Normal file
335
handler/journeys_test.go
Normal file
@@ -0,0 +1,335 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.coopgo.io/coopgo-platform/routing-service"
|
||||
"git.coopgo.io/coopgo-platform/solidarity-transport/storage"
|
||||
"git.coopgo.io/coopgo-platform/solidarity-transport/types"
|
||||
"github.com/google/uuid"
|
||||
"github.com/paulmach/orb"
|
||||
"github.com/paulmach/orb/geojson"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type MockRoutingService struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (m *MockRoutingService) Route(points []orb.Point) (*routing.Route, error) {
|
||||
args := m.Called(points)
|
||||
return args.Get(0).(*routing.Route), args.Error(1)
|
||||
}
|
||||
|
||||
func createTestConfig() *viper.Viper {
|
||||
cfg := viper.New()
|
||||
cfg.Set("storage.db.type", "mock")
|
||||
cfg.Set("parameters.limits.distance.min", 1000)
|
||||
cfg.Set("parameters.limits.distance.max", 50000)
|
||||
return cfg
|
||||
}
|
||||
|
||||
func createTestGeoJSONFeature(lat, lng float64, label string) *geojson.Feature {
|
||||
feature := geojson.NewFeature(orb.Point{lng, lat})
|
||||
feature.Properties = make(map[string]interface{})
|
||||
feature.Properties["label"] = label
|
||||
return feature
|
||||
}
|
||||
|
||||
func createTestRoute() *routing.Route {
|
||||
return createTestRouteWithDistance(6000) // Default route with 6km passenger distance
|
||||
}
|
||||
|
||||
func createTestRouteNoReturn(passengerDistance float64) *routing.Route {
|
||||
// No-return: 3 legs (driver→pickup, pickup→dropoff, dropoff→driver)
|
||||
return &routing.Route{
|
||||
Summary: routing.Summary{
|
||||
Distance: 4000 + passengerDistance,
|
||||
Duration: 15 * time.Minute,
|
||||
Polyline: "test_polyline",
|
||||
},
|
||||
Legs: []routing.RouteLeg{
|
||||
{Summary: routing.Summary{Distance: 2000, Duration: 5 * time.Minute}}, // driver to pickup
|
||||
{Summary: routing.Summary{Distance: passengerDistance, Duration: 8 * time.Minute}}, // pickup to dropoff
|
||||
{Summary: routing.Summary{Distance: 2000, Duration: 5 * time.Minute}}, // dropoff to driver destination
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func createTestRouteReturn(passengerDistance float64) *routing.Route {
|
||||
// Return: 4+ legs (driver→pickup, pickup→dropoff, dropoff→pickup, pickup→driver)
|
||||
// For return journeys: passenger distance = legs[1] + legs[2]
|
||||
singleLegDistance := passengerDistance / 2
|
||||
|
||||
return &routing.Route{
|
||||
Summary: routing.Summary{
|
||||
Distance: 4000 + passengerDistance,
|
||||
Duration: 18 * time.Minute,
|
||||
Polyline: "test_polyline",
|
||||
},
|
||||
Legs: []routing.RouteLeg{
|
||||
{Summary: routing.Summary{Distance: 2000, Duration: 5 * time.Minute}}, // driver to pickup
|
||||
{Summary: routing.Summary{Distance: singleLegDistance, Duration: 8 * time.Minute}}, // pickup to dropoff
|
||||
{Summary: routing.Summary{Distance: singleLegDistance, Duration: 2 * time.Minute}}, // dropoff to pickup (return)
|
||||
{Summary: routing.Summary{Distance: 2000, Duration: 3 * time.Minute}}, // pickup to driver destination
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func createTestRouteWithDistance(passengerDistance float64) *routing.Route {
|
||||
// Default to return journey for backward compatibility
|
||||
return createTestRouteReturn(passengerDistance)
|
||||
}
|
||||
|
||||
func createTestAvailability(driverID string, day int, startTime, endTime string) *types.DriverRegularAvailability {
|
||||
address := createTestGeoJSONFeature(45.7640, 4.8357, "Driver Address")
|
||||
return &types.DriverRegularAvailability{
|
||||
ID: uuid.New().String(),
|
||||
DriverId: driverID,
|
||||
Day: day,
|
||||
StartTime: startTime,
|
||||
EndTime: endTime,
|
||||
Address: address,
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandler_GetDriverJourneys(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
departureDate time.Time
|
||||
noreturn bool
|
||||
availabilities []*types.DriverRegularAvailability
|
||||
routingResponse *routing.Route
|
||||
expectedJourneys int
|
||||
expectedDriverIDs []string
|
||||
description string
|
||||
}{
|
||||
{
|
||||
name: "journey within distance limits",
|
||||
departureDate: time.Date(2024, 1, 15, 8, 0, 0, 0, time.UTC),
|
||||
noreturn: false,
|
||||
availabilities: []*types.DriverRegularAvailability{
|
||||
createTestAvailability("driver-1", 1, "07:00", "09:00"),
|
||||
},
|
||||
routingResponse: createTestRouteWithDistance(6000), // 6km - within limits (1km to 50km)
|
||||
expectedJourneys: 1,
|
||||
expectedDriverIDs: []string{"driver-1"},
|
||||
description: "6km journey should be within distance limits",
|
||||
},
|
||||
{
|
||||
name: "journey too short - below minimum distance",
|
||||
departureDate: time.Date(2024, 1, 15, 8, 0, 0, 0, time.UTC),
|
||||
noreturn: false,
|
||||
availabilities: []*types.DriverRegularAvailability{
|
||||
createTestAvailability("driver-1", 1, "07:00", "09:00"),
|
||||
},
|
||||
routingResponse: createTestRouteWithDistance(500), // 500m - below 1km minimum
|
||||
expectedJourneys: 0,
|
||||
expectedDriverIDs: []string{},
|
||||
description: "500m journey should be rejected (below minimum distance)",
|
||||
},
|
||||
{
|
||||
name: "journey too long - above maximum distance",
|
||||
departureDate: time.Date(2024, 1, 15, 8, 0, 0, 0, time.UTC),
|
||||
noreturn: false,
|
||||
availabilities: []*types.DriverRegularAvailability{
|
||||
createTestAvailability("driver-1", 1, "07:00", "09:00"),
|
||||
},
|
||||
routingResponse: createTestRouteWithDistance(60000), // 60km - above 50km maximum
|
||||
expectedJourneys: 0,
|
||||
expectedDriverIDs: []string{},
|
||||
description: "60km journey should be rejected (above maximum distance)",
|
||||
},
|
||||
{
|
||||
name: "journey at minimum distance boundary",
|
||||
departureDate: time.Date(2024, 1, 15, 8, 0, 0, 0, time.UTC),
|
||||
noreturn: false,
|
||||
availabilities: []*types.DriverRegularAvailability{
|
||||
createTestAvailability("driver-1", 1, "07:00", "09:00"),
|
||||
},
|
||||
routingResponse: createTestRouteWithDistance(1000), // Exactly 1km minimum
|
||||
expectedJourneys: 1,
|
||||
expectedDriverIDs: []string{"driver-1"},
|
||||
description: "1km journey should be accepted (at minimum boundary)",
|
||||
},
|
||||
{
|
||||
name: "journey at maximum distance boundary",
|
||||
departureDate: time.Date(2024, 1, 15, 8, 0, 0, 0, time.UTC),
|
||||
noreturn: false,
|
||||
availabilities: []*types.DriverRegularAvailability{
|
||||
createTestAvailability("driver-1", 1, "07:00", "09:00"),
|
||||
},
|
||||
routingResponse: createTestRouteWithDistance(50000), // Exactly 50km maximum
|
||||
expectedJourneys: 1,
|
||||
expectedDriverIDs: []string{"driver-1"},
|
||||
description: "50km journey should be accepted (at maximum boundary)",
|
||||
},
|
||||
{
|
||||
name: "wrong day no availabilities",
|
||||
departureDate: time.Date(2024, 1, 16, 9, 0, 0, 0, time.UTC),
|
||||
noreturn: false,
|
||||
availabilities: []*types.DriverRegularAvailability{
|
||||
createTestAvailability("driver-1", 1, "08:00", "10:00"),
|
||||
},
|
||||
expectedJourneys: 0,
|
||||
expectedDriverIDs: []string{},
|
||||
description: "Tuesday departure should not match Monday availability",
|
||||
},
|
||||
}
|
||||
|
||||
departure := createTestGeoJSONFeature(45.7578, 4.8320, "Departure")
|
||||
arrival := createTestGeoJSONFeature(45.7485, 4.8467, "Arrival")
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cfg := createTestConfig()
|
||||
mockStorage := storage.NewMockStorage(cfg)
|
||||
mockRouting := &MockRoutingService{}
|
||||
|
||||
for _, availability := range tt.availabilities {
|
||||
err := mockStorage.CreateDriverRegularAvailability(*availability)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
routeResponse := tt.routingResponse
|
||||
if routeResponse == nil {
|
||||
routeResponse = createTestRoute() // Default route
|
||||
}
|
||||
mockRouting.On("Route", mock.AnythingOfType("[]orb.Point")).Return(routeResponse, nil)
|
||||
|
||||
handler := &Handler{
|
||||
Config: cfg,
|
||||
Storage: mockStorage,
|
||||
Routing: mockRouting,
|
||||
}
|
||||
|
||||
journeys, err := handler.GetDriverJourneys(departure, arrival, tt.departureDate, tt.noreturn)
|
||||
|
||||
assert.NoError(t, err, tt.description)
|
||||
assert.Len(t, journeys, tt.expectedJourneys, tt.description)
|
||||
|
||||
if tt.expectedJourneys > 0 {
|
||||
actualDriverIDs := make([]string, len(journeys))
|
||||
for i, journey := range journeys {
|
||||
assert.NotEmpty(t, journey.Id)
|
||||
actualDriverIDs[i] = journey.DriverId
|
||||
}
|
||||
assert.ElementsMatch(t, tt.expectedDriverIDs, actualDriverIDs, tt.description)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandler_GetDriverJourney(t *testing.T) {
|
||||
cfg := createTestConfig()
|
||||
mockStorage := storage.NewMockStorage(cfg)
|
||||
|
||||
testJourney := &types.DriverJourney{
|
||||
Id: uuid.New().String(),
|
||||
DriverId: "driver-1",
|
||||
}
|
||||
err := mockStorage.PushDriverJourneys([]*types.DriverJourney{testJourney})
|
||||
require.NoError(t, err)
|
||||
|
||||
handler := &Handler{
|
||||
Config: cfg,
|
||||
Storage: mockStorage,
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
driverID string
|
||||
journeyID string
|
||||
expectedError bool
|
||||
errorMessage string
|
||||
}{
|
||||
{
|
||||
name: "successful retrieval",
|
||||
driverID: "driver-1",
|
||||
journeyID: testJourney.Id,
|
||||
expectedError: false,
|
||||
},
|
||||
{
|
||||
name: "driver mismatch",
|
||||
driverID: "driver-2",
|
||||
journeyID: testJourney.Id,
|
||||
expectedError: true,
|
||||
errorMessage: "not allowed, driver id mismatch",
|
||||
},
|
||||
{
|
||||
name: "journey not found",
|
||||
driverID: "driver-1",
|
||||
journeyID: "non-existent",
|
||||
expectedError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
journey, err := handler.GetDriverJourney(tt.driverID, tt.journeyID)
|
||||
|
||||
if tt.expectedError {
|
||||
assert.Error(t, err)
|
||||
if tt.errorMessage != "" {
|
||||
assert.Contains(t, err.Error(), tt.errorMessage)
|
||||
}
|
||||
assert.Nil(t, journey)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, journey)
|
||||
assert.Equal(t, tt.journeyID, journey.Id)
|
||||
assert.Equal(t, tt.driverID, journey.DriverId)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandler_ToggleDriverJourneyNoreturn(t *testing.T) {
|
||||
cfg := createTestConfig()
|
||||
mockStorage := storage.NewMockStorage(cfg)
|
||||
mockRouting := &MockRoutingService{}
|
||||
|
||||
departure := createTestGeoJSONFeature(45.7578, 4.8320, "Departure")
|
||||
arrival := createTestGeoJSONFeature(45.7485, 4.8467, "Arrival")
|
||||
driverAddress := createTestGeoJSONFeature(45.7640, 4.8357, "Driver Address")
|
||||
|
||||
testJourney := &types.DriverJourney{
|
||||
Id: uuid.New().String(),
|
||||
DriverId: "driver-1",
|
||||
PassengerPickup: departure,
|
||||
PassengerDrop: arrival,
|
||||
DriverDeparture: driverAddress,
|
||||
Noreturn: false,
|
||||
}
|
||||
|
||||
err := mockStorage.PushDriverJourneys([]*types.DriverJourney{testJourney})
|
||||
require.NoError(t, err)
|
||||
|
||||
mockRouting.On("Route", mock.AnythingOfType("[]orb.Point")).Return(createTestRoute(), nil)
|
||||
|
||||
handler := &Handler{
|
||||
Config: cfg,
|
||||
Storage: mockStorage,
|
||||
Routing: mockRouting,
|
||||
}
|
||||
|
||||
err = handler.ToggleDriverJourneyNoreturn(testJourney.Id)
|
||||
assert.NoError(t, err)
|
||||
|
||||
updatedJourney, err := mockStorage.GetDriverJourney(testJourney.Id)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, updatedJourney.Noreturn)
|
||||
|
||||
err = handler.ToggleDriverJourneyNoreturn(testJourney.Id)
|
||||
assert.NoError(t, err)
|
||||
|
||||
updatedJourney, err = mockStorage.GetDriverJourney(testJourney.Id)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, updatedJourney.Noreturn)
|
||||
|
||||
mockRouting.AssertExpectations(t)
|
||||
}
|
||||
Reference in New Issue
Block a user