improvements
Build and Push Docker Image / build_and_push (push) Failing after 3m6s Details

This commit is contained in:
Arnaud Delcasse 2025-10-08 09:01:37 +02:00
parent fc5b33e88c
commit 723c12a657
14 changed files with 733 additions and 64 deletions

1
.gitignore vendored
View File

@ -1 +1,2 @@
config.yaml config.yaml
solidarity-transport

8
go.mod
View File

@ -5,16 +5,19 @@ go 1.23.3
//replace git.coopgo.io/coopgo-platform/routing-service => ../../coopgo-platform/routing-service/ //replace git.coopgo.io/coopgo-platform/routing-service => ../../coopgo-platform/routing-service/
require ( require (
git.coopgo.io/coopgo-platform/routing-service v0.0.0-20250304234521-faabcc54f536
github.com/google/uuid v1.6.0 github.com/google/uuid v1.6.0
github.com/paulmach/orb v0.11.1
github.com/rs/zerolog v1.33.0 github.com/rs/zerolog v1.33.0
github.com/spf13/viper v1.19.0 github.com/spf13/viper v1.19.0
github.com/stretchr/testify v1.9.0
go.mongodb.org/mongo-driver/v2 v2.1.0 go.mongodb.org/mongo-driver/v2 v2.1.0
google.golang.org/grpc v1.70.0 google.golang.org/grpc v1.70.0
google.golang.org/protobuf v1.36.6 google.golang.org/protobuf v1.36.6
) )
require ( require (
git.coopgo.io/coopgo-platform/routing-service v0.0.0-20250304234521-faabcc54f536 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/golang/snappy v0.0.4 // indirect github.com/golang/snappy v0.0.4 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect
@ -23,14 +26,15 @@ require (
github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/paulmach/orb v0.11.1 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/subosito/gotenv v1.6.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect
github.com/twpayne/go-polyline v1.1.1 // indirect github.com/twpayne/go-polyline v1.1.1 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect

6
go.sum
View File

@ -1,5 +1,3 @@
git.coopgo.io/coopgo-platform/routing-service v0.0.0-20240919052617-d03cd410081c h1:I0pJtlpW7Eloiro+VXVRzdQYJW9AMmjNIczBlROCX9Y=
git.coopgo.io/coopgo-platform/routing-service v0.0.0-20240919052617-d03cd410081c/go.mod h1:Nh7o15LlV0OuO9zxvJIs9FlelpeAaLYkXtFdgIkFrgg=
git.coopgo.io/coopgo-platform/routing-service v0.0.0-20250304234521-faabcc54f536 h1:SllXX1VJXulfhNi+Pd0R9chksm8zO6gkWcTQ/uSMsdc= git.coopgo.io/coopgo-platform/routing-service v0.0.0-20250304234521-faabcc54f536 h1:SllXX1VJXulfhNi+Pd0R9chksm8zO6gkWcTQ/uSMsdc=
git.coopgo.io/coopgo-platform/routing-service v0.0.0-20250304234521-faabcc54f536/go.mod h1:Nh7o15LlV0OuO9zxvJIs9FlelpeAaLYkXtFdgIkFrgg= git.coopgo.io/coopgo-platform/routing-service v0.0.0-20250304234521-faabcc54f536/go.mod h1:Nh7o15LlV0OuO9zxvJIs9FlelpeAaLYkXtFdgIkFrgg=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
@ -86,6 +84,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
@ -97,6 +96,7 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/twpayne/go-polyline v1.1.1 h1:/tSF1BR7rN4HWj4XKqvRUNrCiYVMCvywxTFVofvDV0w= github.com/twpayne/go-polyline v1.1.1 h1:/tSF1BR7rN4HWj4XKqvRUNrCiYVMCvywxTFVofvDV0w=
github.com/twpayne/go-polyline v1.1.1/go.mod h1:ybd9IWWivW/rlXPXuuckeKUyF3yrIim+iqA7kSl4NFY= github.com/twpayne/go-polyline v1.1.1/go.mod h1:ybd9IWWivW/rlXPXuuckeKUyF3yrIim+iqA7kSl4NFY=
@ -197,8 +197,6 @@ google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@ -9,7 +9,7 @@ import (
"github.com/rs/zerolog/log" "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) journey, err := h.Storage.GetDriverJourney(journeyid)
if err != nil { if err != nil {
log.Error().Err(err).Msg("could not find driver journey") log.Error().Err(err).Msg("could not find driver journey")
@ -34,6 +34,8 @@ func (h Handler) BookDriverJourney(passengerid string, driverid string, journeyi
Journey: journey, Journey: journey,
ReturnWaitingDuration: returnWaitingDuration, ReturnWaitingDuration: returnWaitingDuration,
Data: data, Data: data,
DriverCompensationAmount: driverCompensationAmount,
DriverCompensationCurrency: driverCompensationCurrency,
} }
if err := h.Storage.CreateBooking(booking); err != nil { if err := h.Storage.CreateBooking(booking); err != nil {

335
handler/journeys_test.go Normal file
View 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)
}

View File

@ -404,6 +404,8 @@ type SolidarityTransportBooking struct {
Status string `protobuf:"bytes,5,opt,name=status,proto3" json:"status,omitempty"` Status string `protobuf:"bytes,5,opt,name=status,proto3" json:"status,omitempty"`
ReturnWaitingDuration int64 `protobuf:"varint,6,opt,name=return_waiting_duration,json=returnWaitingDuration,proto3" json:"return_waiting_duration,omitempty"` ReturnWaitingDuration int64 `protobuf:"varint,6,opt,name=return_waiting_duration,json=returnWaitingDuration,proto3" json:"return_waiting_duration,omitempty"`
Data *structpb.Struct `protobuf:"bytes,7,opt,name=data,proto3" json:"data,omitempty"` Data *structpb.Struct `protobuf:"bytes,7,opt,name=data,proto3" json:"data,omitempty"`
DriverCompensationAmount float64 `protobuf:"fixed64,8,opt,name=driver_compensation_amount,json=driverCompensationAmount,proto3" json:"driver_compensation_amount,omitempty"`
DriverCompensationCurrency string `protobuf:"bytes,9,opt,name=driver_compensation_currency,json=driverCompensationCurrency,proto3" json:"driver_compensation_currency,omitempty"`
Journey *SolidarityTransportDriverJourney `protobuf:"bytes,10,opt,name=journey,proto3" json:"journey,omitempty"` Journey *SolidarityTransportDriverJourney `protobuf:"bytes,10,opt,name=journey,proto3" json:"journey,omitempty"`
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@ -488,6 +490,20 @@ func (x *SolidarityTransportBooking) GetData() *structpb.Struct {
return nil return nil
} }
func (x *SolidarityTransportBooking) GetDriverCompensationAmount() float64 {
if x != nil {
return x.DriverCompensationAmount
}
return 0
}
func (x *SolidarityTransportBooking) GetDriverCompensationCurrency() string {
if x != nil {
return x.DriverCompensationCurrency
}
return ""
}
func (x *SolidarityTransportBooking) GetJourney() *SolidarityTransportDriverJourney { func (x *SolidarityTransportBooking) GetJourney() *SolidarityTransportDriverJourney {
if x != nil { if x != nil {
return x.Journey return x.Journey
@ -537,7 +553,7 @@ const file_solidarity_transport_types_proto_rawDesc = "" +
"\x06_price\"N\n" + "\x06_price\"N\n" +
"\x18SolidarityTransportPrice\x12\x16\n" + "\x18SolidarityTransportPrice\x12\x16\n" +
"\x06amount\x18\x01 \x01(\x01R\x06amount\x12\x1a\n" + "\x06amount\x18\x01 \x01(\x01R\x06amount\x12\x1a\n" +
"\bcurrency\x18\x02 \x01(\tR\bcurrency\"\xc1\x02\n" + "\bcurrency\x18\x02 \x01(\tR\bcurrency\"\xc1\x03\n" +
"\x1aSolidarityTransportBooking\x12\x0e\n" + "\x1aSolidarityTransportBooking\x12\x0e\n" +
"\x02id\x18\x01 \x01(\tR\x02id\x12\x19\n" + "\x02id\x18\x01 \x01(\tR\x02id\x12\x19\n" +
"\bgroup_id\x18\x02 \x01(\tR\agroupId\x12\x1b\n" + "\bgroup_id\x18\x02 \x01(\tR\agroupId\x12\x1b\n" +
@ -545,7 +561,9 @@ const file_solidarity_transport_types_proto_rawDesc = "" +
"\fpassenger_id\x18\x04 \x01(\tR\vpassengerId\x12\x16\n" + "\fpassenger_id\x18\x04 \x01(\tR\vpassengerId\x12\x16\n" +
"\x06status\x18\x05 \x01(\tR\x06status\x126\n" + "\x06status\x18\x05 \x01(\tR\x06status\x126\n" +
"\x17return_waiting_duration\x18\x06 \x01(\x03R\x15returnWaitingDuration\x12+\n" + "\x17return_waiting_duration\x18\x06 \x01(\x03R\x15returnWaitingDuration\x12+\n" +
"\x04data\x18\a \x01(\v2\x17.google.protobuf.StructR\x04data\x12;\n" + "\x04data\x18\a \x01(\v2\x17.google.protobuf.StructR\x04data\x12<\n" +
"\x1adriver_compensation_amount\x18\b \x01(\x01R\x18driverCompensationAmount\x12@\n" +
"\x1cdriver_compensation_currency\x18\t \x01(\tR\x1adriverCompensationCurrency\x12;\n" +
"\ajourney\x18\n" + "\ajourney\x18\n" +
" \x01(\v2!.SolidarityTransportDriverJourneyR\ajourneyBKZIgit.coopgo.io/coopgo-platform/solidarity-transport/servers/grpc/proto/genb\x06proto3" " \x01(\v2!.SolidarityTransportDriverJourneyR\ajourneyBKZIgit.coopgo.io/coopgo-platform/solidarity-transport/servers/grpc/proto/genb\x06proto3"

View File

@ -616,6 +616,8 @@ type BookDriverJourneyRequest struct {
PriceAmount float64 `protobuf:"fixed64,5,opt,name=price_amount,json=priceAmount,proto3" json:"price_amount,omitempty"` PriceAmount float64 `protobuf:"fixed64,5,opt,name=price_amount,json=priceAmount,proto3" json:"price_amount,omitempty"`
PriceCurrency string `protobuf:"bytes,6,opt,name=price_currency,json=priceCurrency,proto3" json:"price_currency,omitempty"` PriceCurrency string `protobuf:"bytes,6,opt,name=price_currency,json=priceCurrency,proto3" json:"price_currency,omitempty"`
Data *structpb.Struct `protobuf:"bytes,7,opt,name=data,proto3" json:"data,omitempty"` Data *structpb.Struct `protobuf:"bytes,7,opt,name=data,proto3" json:"data,omitempty"`
DriverCompensationAmount float64 `protobuf:"fixed64,8,opt,name=driver_compensation_amount,json=driverCompensationAmount,proto3" json:"driver_compensation_amount,omitempty"`
DriverCompensationCurrency string `protobuf:"bytes,9,opt,name=driver_compensation_currency,json=driverCompensationCurrency,proto3" json:"driver_compensation_currency,omitempty"`
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
} }
@ -699,6 +701,20 @@ func (x *BookDriverJourneyRequest) GetData() *structpb.Struct {
return nil return nil
} }
func (x *BookDriverJourneyRequest) GetDriverCompensationAmount() float64 {
if x != nil {
return x.DriverCompensationAmount
}
return 0
}
func (x *BookDriverJourneyRequest) GetDriverCompensationCurrency() string {
if x != nil {
return x.DriverCompensationCurrency
}
return ""
}
type BookDriverJourneyResponse struct { type BookDriverJourneyResponse struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
Booking *SolidarityTransportBooking `protobuf:"bytes,1,opt,name=booking,proto3" json:"booking,omitempty"` Booking *SolidarityTransportBooking `protobuf:"bytes,1,opt,name=booking,proto3" json:"booking,omitempty"`
@ -1246,7 +1262,7 @@ const file_solidarity_transport_proto_rawDesc = "" +
"\n" + "\n" +
"journey_id\x18\x02 \x01(\tR\tjourneyId\"d\n" + "journey_id\x18\x02 \x01(\tR\tjourneyId\"d\n" +
"\x18GetDriverJourneyResponse\x12H\n" + "\x18GetDriverJourneyResponse\x12H\n" +
"\x0edriver_journey\x18\x01 \x01(\v2!.SolidarityTransportDriverJourneyR\rdriverJourney\"\xb5\x02\n" + "\x0edriver_journey\x18\x01 \x01(\v2!.SolidarityTransportDriverJourneyR\rdriverJourney\"\xb5\x03\n" +
"\x18BookDriverJourneyRequest\x12!\n" + "\x18BookDriverJourneyRequest\x12!\n" +
"\fpassenger_id\x18\x01 \x01(\tR\vpassengerId\x12\x1b\n" + "\fpassenger_id\x18\x01 \x01(\tR\vpassengerId\x12\x1b\n" +
"\tdriver_id\x18\x02 \x01(\tR\bdriverId\x12*\n" + "\tdriver_id\x18\x02 \x01(\tR\bdriverId\x12*\n" +
@ -1254,7 +1270,9 @@ const file_solidarity_transport_proto_rawDesc = "" +
"\x17return_waiting_duration\x18\x04 \x01(\x03R\x15returnWaitingDuration\x12!\n" + "\x17return_waiting_duration\x18\x04 \x01(\x03R\x15returnWaitingDuration\x12!\n" +
"\fprice_amount\x18\x05 \x01(\x01R\vpriceAmount\x12%\n" + "\fprice_amount\x18\x05 \x01(\x01R\vpriceAmount\x12%\n" +
"\x0eprice_currency\x18\x06 \x01(\tR\rpriceCurrency\x12+\n" + "\x0eprice_currency\x18\x06 \x01(\tR\rpriceCurrency\x12+\n" +
"\x04data\x18\a \x01(\v2\x17.google.protobuf.StructR\x04data\"R\n" + "\x04data\x18\a \x01(\v2\x17.google.protobuf.StructR\x04data\x12<\n" +
"\x1adriver_compensation_amount\x18\b \x01(\x01R\x18driverCompensationAmount\x12@\n" +
"\x1cdriver_compensation_currency\x18\t \x01(\tR\x1adriverCompensationCurrency\"R\n" +
"\x19BookDriverJourneyResponse\x125\n" + "\x19BookDriverJourneyResponse\x125\n" +
"\abooking\x18\x01 \x01(\v2\x1b.SolidarityTransportBookingR\abooking\"\xef\x01\n" + "\abooking\x18\x01 \x01(\v2\x1b.SolidarityTransportBookingR\abooking\"\xef\x01\n" +
"%GetSolidarityTransportBookingsRequest\x129\n" + "%GetSolidarityTransportBookingsRequest\x129\n" +

View File

@ -51,6 +51,8 @@ message SolidarityTransportBooking {
string status = 5; string status = 5;
int64 return_waiting_duration = 6; int64 return_waiting_duration = 6;
google.protobuf.Struct data = 7; google.protobuf.Struct data = 7;
double driver_compensation_amount = 8;
string driver_compensation_currency = 9;
SolidarityTransportDriverJourney journey = 10; SolidarityTransportDriverJourney journey = 10;
} }

View File

@ -88,6 +88,8 @@ message BookDriverJourneyRequest {
double price_amount = 5; double price_amount = 5;
string price_currency = 6; string price_currency = 6;
google.protobuf.Struct data = 7; google.protobuf.Struct data = 7;
double driver_compensation_amount = 8;
string driver_compensation_currency = 9;
} }
message BookDriverJourneyResponse { message BookDriverJourneyResponse {

View File

@ -17,10 +17,12 @@ func (s SolidarityTransportServerImpl) BookDriverJourney(ctx context.Context, re
journeyId := req.DriverJourneyId journeyId := req.DriverJourneyId
priceAmount := req.PriceAmount priceAmount := req.PriceAmount
priceCurrency := req.PriceCurrency priceCurrency := req.PriceCurrency
driverCompensationAmount := req.DriverCompensationAmount
driverCompensationCurrency := req.DriverCompensationCurrency
returnWaitingDuration := time.Duration(req.ReturnWaitingDuration) returnWaitingDuration := time.Duration(req.ReturnWaitingDuration)
data := req.Data.AsMap() data := req.Data.AsMap()
booking, err := s.Handler.BookDriverJourney(passengerId, driverId, journeyId, returnWaitingDuration, priceAmount, priceCurrency, data) booking, err := s.Handler.BookDriverJourney(passengerId, driverId, journeyId, returnWaitingDuration, priceAmount, priceCurrency, driverCompensationAmount, driverCompensationCurrency, data)
if err != nil { if err != nil {
log.Error().Err(err).Msg("issue in BookDriverJourney handler") log.Error().Err(err).Msg("issue in BookDriverJourney handler")
return nil, status.Errorf(codes.Internal, "could not create booking : %v", err) return nil, status.Errorf(codes.Internal, "could not create booking : %v", err)

View File

@ -28,6 +28,8 @@ func BookingTypeToProto(booking *types.Booking) (*gen.SolidarityTransportBooking
ReturnWaitingDuration: int64(booking.ReturnWaitingDuration), ReturnWaitingDuration: int64(booking.ReturnWaitingDuration),
Journey: journey, Journey: journey,
Data: data, Data: data,
DriverCompensationAmount: booking.DriverCompensationAmount,
DriverCompensationCurrency: booking.DriverCompensationCurrency,
}, nil }, nil
} }
@ -45,5 +47,7 @@ func BookingProtoToType(booking *gen.SolidarityTransportBooking) (*types.Booking
ReturnWaitingDuration: time.Duration(booking.ReturnWaitingDuration), ReturnWaitingDuration: time.Duration(booking.ReturnWaitingDuration),
Journey: journey, Journey: journey,
Data: booking.Data.AsMap(), Data: booking.Data.AsMap(),
DriverCompensationAmount: booking.DriverCompensationAmount,
DriverCompensationCurrency: booking.DriverCompensationCurrency,
}, nil }, nil
} }

279
storage/mock.go Normal file
View File

@ -0,0 +1,279 @@
package storage
import (
"fmt"
"strings"
"sync"
"git.coopgo.io/coopgo-platform/solidarity-transport/types"
"github.com/google/uuid"
"github.com/spf13/viper"
)
// MockStorage implements the Storage interface for testing purposes
type MockStorage struct {
config *viper.Viper
mu sync.RWMutex
availabilities map[string]*types.DriverRegularAvailability
journeys map[string]*types.DriverJourney
bookings map[string]*types.Booking
availabilitiesByDriver map[string][]*types.DriverRegularAvailability
}
// NewMockStorage creates a new mock storage instance
func NewMockStorage(cfg *viper.Viper) Storage {
return &MockStorage{
config: cfg,
availabilities: make(map[string]*types.DriverRegularAvailability),
journeys: make(map[string]*types.DriverJourney),
bookings: make(map[string]*types.Booking),
availabilitiesByDriver: make(map[string][]*types.DriverRegularAvailability),
}
}
// Driver Regular Availability methods
func (m *MockStorage) CreateDriverRegularAvailability(availability types.DriverRegularAvailability) error {
m.mu.Lock()
defer m.mu.Unlock()
if availability.ID == "" {
availability.ID = uuid.New().String()
}
m.availabilities[availability.ID] = &availability
// Update driver index
m.availabilitiesByDriver[availability.DriverId] = append(
m.availabilitiesByDriver[availability.DriverId],
&availability,
)
return nil
}
func (m *MockStorage) CreateDriverRegularAvailabilities(availabilities []*types.DriverRegularAvailability) error {
for _, availability := range availabilities {
if err := m.CreateDriverRegularAvailability(*availability); err != nil {
return err
}
}
return nil
}
func (m *MockStorage) DeleteDriverRegularAvailability(driverid string, availabilityid string) error {
m.mu.Lock()
defer m.mu.Unlock()
availability, exists := m.availabilities[availabilityid]
if !exists {
return fmt.Errorf("availability %s not found", availabilityid)
}
if availability.DriverId != driverid {
return fmt.Errorf("availability %s does not belong to driver %s", availabilityid, driverid)
}
delete(m.availabilities, availabilityid)
// Update driver index
driverAvailabilities := m.availabilitiesByDriver[driverid]
for i, a := range driverAvailabilities {
if a.ID == availabilityid {
m.availabilitiesByDriver[driverid] = append(driverAvailabilities[:i], driverAvailabilities[i+1:]...)
break
}
}
return nil
}
func (m *MockStorage) GetDriverRegularAvailability(driverid string, availabilityid string) (*types.DriverRegularAvailability, error) {
m.mu.RLock()
defer m.mu.RUnlock()
availability, exists := m.availabilities[availabilityid]
if !exists {
return nil, fmt.Errorf("availability %s not found", availabilityid)
}
if availability.DriverId != driverid {
return nil, fmt.Errorf("availability %s does not belong to driver %s", availabilityid, driverid)
}
return availability, nil
}
func (m *MockStorage) GetDriverRegularAvailabilities(driverid string) ([]*types.DriverRegularAvailability, error) {
m.mu.RLock()
defer m.mu.RUnlock()
return m.availabilitiesByDriver[driverid], nil
}
func (m *MockStorage) GetRegularAvailabilities(day int, timeInDay string) ([]*types.DriverRegularAvailability, error) {
m.mu.RLock()
defer m.mu.RUnlock()
var result []*types.DriverRegularAvailability
for _, availability := range m.availabilities {
// Match day
if availability.Day != day {
continue
}
// For testing, just return all availabilities for the day
// Let the handler do the time filtering logic
result = append(result, availability)
}
return result, nil
}
// timeMatches is a simple time matching function for testing
func (m *MockStorage) timeMatches(availabilityTime, requestedTime string) bool {
// Simple string matching for testing - real implementation would parse times
return strings.HasPrefix(availabilityTime, requestedTime[:2]) // Match hour
}
// Driver Journey methods
func (m *MockStorage) PushDriverJourneys(journeys []*types.DriverJourney) error {
m.mu.Lock()
defer m.mu.Unlock()
for _, journey := range journeys {
if journey.Id == "" {
journey.Id = uuid.New().String()
}
m.journeys[journey.Id] = journey
}
return nil
}
func (m *MockStorage) GetDriverJourney(id string) (*types.DriverJourney, error) {
m.mu.RLock()
defer m.mu.RUnlock()
journey, exists := m.journeys[id]
if !exists {
return nil, fmt.Errorf("journey %s not found", id)
}
return journey, nil
}
func (m *MockStorage) UpdateDriverJourney(journey types.DriverJourney) error {
m.mu.Lock()
defer m.mu.Unlock()
if _, exists := m.journeys[journey.Id]; !exists {
return fmt.Errorf("journey %s not found", journey.Id)
}
m.journeys[journey.Id] = &journey
return nil
}
// Booking methods
func (m *MockStorage) CreateBooking(booking types.Booking) error {
m.mu.Lock()
defer m.mu.Unlock()
if booking.Id == "" {
booking.Id = uuid.New().String()
}
m.bookings[booking.Id] = &booking
return nil
}
func (m *MockStorage) GetAllBookings() ([]*types.Booking, error) {
m.mu.RLock()
defer m.mu.RUnlock()
var result []*types.Booking
for _, booking := range m.bookings {
result = append(result, booking)
}
return result, nil
}
func (m *MockStorage) GetBooking(id string) (*types.Booking, error) {
m.mu.RLock()
defer m.mu.RUnlock()
booking, exists := m.bookings[id]
if !exists {
return nil, fmt.Errorf("booking %s not found", id)
}
return booking, nil
}
func (m *MockStorage) UpdateBooking(booking types.Booking) error {
m.mu.Lock()
defer m.mu.Unlock()
if _, exists := m.bookings[booking.Id]; !exists {
return fmt.Errorf("booking %s not found", booking.Id)
}
m.bookings[booking.Id] = &booking
return nil
}
func (m *MockStorage) UpdateBookingStatus(bookingid string, newStatus string, reason string) error {
m.mu.Lock()
defer m.mu.Unlock()
booking, exists := m.bookings[bookingid]
if !exists {
return fmt.Errorf("booking %s not found", bookingid)
}
booking.Status = newStatus
// In a real implementation, we might store the reason in a separate field
return nil
}
// Helper methods for testing
// Reset clears all data - useful for test setup
func (m *MockStorage) Reset() {
m.mu.Lock()
defer m.mu.Unlock()
m.availabilities = make(map[string]*types.DriverRegularAvailability)
m.journeys = make(map[string]*types.DriverJourney)
m.bookings = make(map[string]*types.Booking)
m.availabilitiesByDriver = make(map[string][]*types.DriverRegularAvailability)
}
// GetAllJourneys returns all stored journeys - useful for testing
func (m *MockStorage) GetAllJourneys() []*types.DriverJourney {
m.mu.RLock()
defer m.mu.RUnlock()
var result []*types.DriverJourney
for _, journey := range m.journeys {
result = append(result, journey)
}
return result
}
// GetAllAvailabilities returns all stored availabilities - useful for testing
func (m *MockStorage) GetAllAvailabilities() []*types.DriverRegularAvailability {
m.mu.RLock()
defer m.mu.RUnlock()
var result []*types.DriverRegularAvailability
for _, availability := range m.availabilities {
result = append(result, availability)
}
return result
}

View File

@ -35,6 +35,8 @@ func NewStorage(cfg *viper.Viper) (Storage, error) {
return NewMongoDBStorage(cfg) return NewMongoDBStorage(cfg)
case "memory": case "memory":
return NewMemoryStorage(cfg) return NewMemoryStorage(cfg)
case "mock":
return NewMockStorage(cfg), nil
default: default:
return nil, fmt.Errorf("storage type %v is not supported", storage_type) return nil, fmt.Errorf("storage type %v is not supported", storage_type)

View File

@ -10,6 +10,8 @@ type Booking struct {
Status string `json:"status" bson:"status"` Status string `json:"status" bson:"status"`
ReturnWaitingDuration time.Duration `json:"return_waiting_duration" bson:"return_waiting_duration"` ReturnWaitingDuration time.Duration `json:"return_waiting_duration" bson:"return_waiting_duration"`
Data map[string]any `json:"data" bson:"data"` Data map[string]any `json:"data" bson:"data"`
DriverCompensationAmount float64 `json:"driver_compensation_amount" bson:"driver_compensation_amount"`
DriverCompensationCurrency string `json:"driver_compensation_currency" bson:"driver_compensation_currency"`
Journey *DriverJourney `json:"journey,omitempty" bson:"journey,omitempty"` Journey *DriverJourney `json:"journey,omitempty" bson:"journey,omitempty"`
} }