Skip to content

Commit

Permalink
Added feature preventing user from overbooking own reservation (#16)
Browse files Browse the repository at this point in the history
Co-authored-by: Patryk Fuhrman <patryk,fuhrman@gmail.com>
Co-authored-by: Jan Matusz <me@marahin.pl>
  • Loading branch information
3 people authored Mar 27, 2024
1 parent 0bafe4c commit 110015d
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 8 deletions.
12 changes: 10 additions & 2 deletions internal/core/booking/booking.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,20 +107,28 @@ func (a *Adapter) Book(member *discord.Member, guild *discord.Guild, spotName st
}

if endAt.Sub(startAt) > 3*time.Hour {
return []*reservation.Reservation{}, fmt.Errorf("reservation cannot take more than 3 hours")
return []*reservation.Reservation{}, errors.New("reservation cannot take more than 3 hours")
}

conflictingReservations, err := a.reservationRepo.SelectOverlappingReservations(context.Background(), spotName, startAt, endAt, guild.ID)
if err != nil {
return []*reservation.Reservation{}, fmt.Errorf("could not select overlapping reservations: %w", err)
}

authorsConflictingReservations, _ := collections.PoorMansFind(conflictingReservations, func(r *reservation.Reservation) bool {
return r.AuthorDiscordID == member.ID
})

if authorsConflictingReservations != nil && overbook {
return []*reservation.Reservation{}, errors.New("you cannot overbook yourself")
}

if len(conflictingReservations) > 0 {
switch canDo := overbook && isPotentiallyAbandonedReservation(conflictingReservations) || overbook && hasPermissions; canDo {
case true:
break
case false:
return conflictingReservations, fmt.Errorf("There are conflicting reservation which prevented booking this reservation. If you would like to overbook them, ensure you have a @Postman role, then repeat the command and set 'overbook' parameter to 'true'.")
return conflictingReservations, errors.New("There are conflicting reservation which prevented booking this reservation. If you would like to overbook them, ensure you have a @Postman role, then repeat the command and set 'overbook' parameter to 'true'.")
}
}

Expand Down
43 changes: 43 additions & 0 deletions internal/core/booking/booking_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -418,3 +418,46 @@ func TestBookOnMultizoneCase(t *testing.T) {
assert.Nil(err)
assert.NotNil(res)
}

func TestBookFailOnOverbookAuthorsReservation(t *testing.T) {
// given
assert := assert.New(t)
guild := &discord.Guild{
ID: "test-id",
Name: "test-guild-name",
}
member := &discord.Member{
ID: "test-member",
Nick: "test-nick",
}
startAt := time.Now()
endAt := startAt.Add(1 * time.Hour)
spotInput := &spot.Spot{
Name: "test-spot",
ID: 1,
CreatedAt: time.Now(),
}
conflictingReservations := []*reservation.Reservation{
{
Author: member.Username,
CreatedAt: time.Now(),
StartAt: time.Date(1, 1, 1, 16, 0, 0, 0, time.UTC),
EndAt: time.Date(1, 1, 1, 17, 0, 0, 0, time.UTC),
SpotID: 1,
GuildID: guild.ID,
AuthorDiscordID: member.ID,
},
}
spotService := new(mocks.MockSpotRepo)
spotService.On("SelectAllSpots", mocks.ContextMock).Return([]*spot.Spot{spotInput}, nil)
reservationService := new(mocks.MockReservationRepo)
reservationService.On("SelectOverlappingReservations", mocks.ContextMock, spotInput.Name, startAt, endAt, guild.ID).Return(conflictingReservations, nil)
adapter := NewAdapter(spotService, reservationService)

// when
res, err := adapter.Book(member, guild, spotInput.Name, startAt, endAt, true, true)

// assert
assert.NotNil(err)
assert.Empty(res)
}
15 changes: 9 additions & 6 deletions internal/infrastructure/bot/handlers.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package bot

import (
"errors"
"fmt"
"strings"
"time"
Expand All @@ -14,8 +15,10 @@ import (
"spot-assistant/internal/core/dto/summary"
)

// System events
// Events that are sent by discord itself
/*
*
System events that are initialized by Discord.
*/

func (b *Bot) GuildCreate(s *discordgo.Session, g *discordgo.GuildCreate) {
b.log.Debug("GuildCreate")
Expand All @@ -29,7 +32,7 @@ func (b *Bot) Ready(s *discordgo.Session, r *discordgo.Ready) {
defer b.eventHandler.OnReady(b)
}

// When a slash command is invoked, this is the entry point.
// InteractionCreate this is the entry point when a slash command is invoked.
func (b *Bot) InteractionCreate(s *discordgo.Session, i *discordgo.InteractionCreate) {
b.log.Debug("InteractionCreate")
tStart := time.Now()
Expand Down Expand Up @@ -68,7 +71,7 @@ func (b *Bot) Book(i *discordgo.InteractionCreate) error {
case 3:
break
default:
return fmt.Errorf("Book command requires 3 arguments")
return errors.New("book command requires 3 arguments")
}

startAt, err := time.Parse(stringsHelper.DC_TIME_FORMAT, i.ApplicationCommandData().Options[1].StringValue())
Expand Down Expand Up @@ -173,7 +176,7 @@ func (b *Bot) BookAutocomplete(i *discordgo.InteractionCreate) error {
return o.Focused
})
if index == -1 {
return fmt.Errorf("none of the options were selected for autocompletion")
return errors.New("none of the options were selected for autocompletion")
}

response, err := b.eventHandler.OnBookAutocomplete(book.BookAutocompleteRequest{
Expand All @@ -192,7 +195,7 @@ func (b *Bot) BookAutocomplete(i *discordgo.InteractionCreate) error {

func (b *Bot) Unbook(i *discordgo.InteractionCreate) error {
if len(i.ApplicationCommandData().Options) < 1 {
return fmt.Errorf("you must select a reservation to unbook")
return errors.New("you must select a reservation to unbook")
}

reservationId, err := stringsHelper.StrToInt64(i.ApplicationCommandData().Options[0].StringValue())
Expand Down

0 comments on commit 110015d

Please sign in to comment.