| /* | |
| Copyright 2019 Google Inc. | |
| Licensed under the Apache License, Version 2.0 (the "License"); | |
| you may not use this file except in compliance with the License. | |
| You may obtain a copy of the License at | |
| https://www.apache.org/licenses/LICENSE-2.0 | |
| Unless required by applicable law or agreed to in writing, software | |
| distributed under the License is distributed on an "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| See the License for the specific language governing permissions and | |
| limitations under the License. | |
| */ | |
| // Package booking contains test logic for booking related endpoints. | |
| package booking | |
| import ( | |
| "context" | |
| "log" | |
| "github.com/maps-booking-v3/api" | |
| "github.com/maps-booking-v3/utils" | |
| fpb "github.com/maps-booking-v3/feeds" | |
| mpb "github.com/maps-booking-v3/v3" | |
| ) | |
| // GenerateBookings creates bookings from an availability feed. | |
| func GenerateBookings(ctx context.Context, logger *log.Logger, av []*fpb.Availability, stats *utils.TestSummary, conn *utils.HTTPConnection, config *utils.Config) api.Bookings { | |
| logger.Println("no previous bookings to use, acquiring new inventory") | |
| utils.LogFlow(logger, "Generate Fresh Inventory", "Start") | |
| defer utils.LogFlow(logger, "Generate Fresh Inventory", "End") | |
| var out api.Bookings | |
| totalSlots := len(av) | |
| var available []*fpb.Availability | |
| if config.BookingUseBal { | |
| if avSlot, err := api.AvailableSlotsFromBAL(ctx, logger, av, conn); err != nil { | |
| logger.Printf("BAL error: %s. skipping %d slots", err.Error(), totalSlots) | |
| stats.BookingBatchAvailabilityLookupErrors++ | |
| } else if len(avSlot) > 0 { | |
| available = append(available, avSlot...) | |
| stats.BookingBatchAvailabilityLookupSuccess++ | |
| } | |
| } else { | |
| for i, a := range av { | |
| if err := api.CheckAvailability(ctx, logger, a, conn); err != nil { | |
| logger.Printf("%s. skipping slot %d/%d", err.Error(), i, totalSlots) | |
| stats.BookingCheckAvailabilityErrors++ | |
| continue | |
| } | |
| available = append(available, a) | |
| stats.BookingCheckAvailabilitySuccess++ | |
| } | |
| } | |
| totalSlots = len(available) | |
| for i, a := range available { | |
| booking, err := api.CreateBooking(ctx, logger, a, conn) | |
| if err != nil { | |
| logger.Printf("%s. skipping slot %d/%d", err.Error(), i, totalSlots) | |
| stats.BookingCreateBookingErrors++ | |
| continue | |
| } | |
| out = append(out, booking) | |
| stats.BookingCreateBookingSuccess++ | |
| } | |
| return out | |
| } | |
| func logStats(stats *utils.TestSummary, logger *log.Logger, config *utils.Config) { | |
| logger.Println("\n************* Begin Stats *************\n") | |
| var totalErrors int | |
| if config.BookingHealthFlow || config.BookingAllFlows { | |
| if stats.BookingHealthCheckSuccess { | |
| logger.Println("HealthCheck Succeeded") | |
| } else { | |
| totalErrors++ | |
| logger.Println("HealthCheck Failed") | |
| } | |
| } | |
| if config.BookingCheckFlow || config.BookingAllFlows { | |
| if config.BookingUseBal { | |
| totalErrors += stats.BookingBatchAvailabilityLookupErrors | |
| logger.Printf("BatchAvailabilityLookup Errors: %d/%d", stats.BookingBatchAvailabilityLookupErrors, stats.BookingBatchAvailabilityLookupErrors+stats.BookingBatchAvailabilityLookupSuccess) | |
| } else { | |
| totalErrors += stats.BookingCheckAvailabilityErrors | |
| logger.Printf("CheckAvailability Errors: %d/%d", stats.BookingCheckAvailabilityErrors, stats.BookingCheckAvailabilityErrors+stats.BookingCheckAvailabilitySuccess) | |
| } | |
| } | |
| if config.BookingBookFlow || config.BookingAllFlows { | |
| totalErrors += stats.BookingCreateBookingErrors | |
| logger.Printf("CreateBooking Errors: %d/%d", stats.BookingCreateBookingErrors, stats.BookingCreateBookingErrors+stats.BookingCreateBookingSuccess) | |
| } | |
| if config.BookingListFlow || config.BookingAllFlows || config.BookingCancelAllBookings { | |
| if stats.BookingListBookingsSuccess { | |
| logger.Println("ListBookings Succeeded") | |
| } else { | |
| totalErrors++ | |
| logger.Println("ListBookings Failed") | |
| } | |
| } | |
| if config.BookingStatusFlow || config.BookingAllFlows { | |
| totalErrors += stats.BookingGetBookingStatusErrors | |
| logger.Printf("GetBookingStatus Errors: %d/%d", stats.BookingGetBookingStatusErrors, stats.BookingGetBookingStatusErrors+stats.BookingGetBookingStatusSuccess) | |
| } | |
| if config.BookingRescheduleFlow || config.BookingAllFlows { | |
| if stats.BookingReschedulingSuccess { | |
| logger.Println("Rescheduling Succeeded") | |
| } else { | |
| totalErrors++ | |
| logger.Println("Rescheduling Failed") | |
| } | |
| } | |
| logger.Println("\n\n\n") | |
| if totalErrors == 0 { | |
| logger.Println("All Tests Pass!") | |
| } else { | |
| logger.Printf("Found %d Errors", totalErrors) | |
| } | |
| logger.Println("\n************* End Stats *************\n") | |
| } | |
| // RunTests runs booking tests. | |
| func RunTests(ctx context.Context, logger *log.Logger, config *utils.Config, av []*fpb.Availability, avForRescheduling []*fpb.Availability, stats *utils.TestSummary) { | |
| conn := config.Conn | |
| var slots = av | |
| // HealthCheck Flow | |
| if config.BookingHealthFlow || config.BookingAllFlows { | |
| stats.BookingHealthCheckSuccess = true | |
| if err := api.HealthCheck(ctx, logger, conn); err != nil { | |
| stats.BookingHealthCheckSuccess = false | |
| logger.Println(err.Error()) | |
| } | |
| stats.BookingHealthCheckCompleted = true | |
| if !config.BookingAllFlows && !config.BookingCheckFlow && !config.BookingBookFlow && | |
| !config.BookingListFlow && !config.BookingStatusFlow && !config.BookingRescheduleFlow { | |
| logStats(stats, logger, config) | |
| return | |
| } | |
| } | |
| if config.BookingCheckFlow || config.BookingAllFlows { | |
| slots = nil | |
| if config.BookingUseBal { | |
| utils.LogFlow(logger, "Batch Availability Lookup", "Start") | |
| for _, a := range utils.SplitAvailabilityByMerchant(av) { | |
| if avSlots, err := api.AvailableSlotsFromBAL(ctx, logger, a, conn); err != nil { | |
| logger.Printf("BatchAvailabilityLookup returned error: %v", err) | |
| stats.BookingBatchAvailabilityLookupErrors++ | |
| } else { | |
| // Only test CreateBooking on available slots. | |
| slots = append(slots, avSlots...) | |
| stats.BookingBatchAvailabilityLookupSuccess++ | |
| } | |
| } | |
| utils.LogFlow(logger, "Batch Availability Lookup", "End") | |
| stats.BookingBatchAvailabilityLookupCompleted = true | |
| } else { | |
| // AvailabilityCheck Flow | |
| utils.LogFlow(logger, "Availability Check", "Start") | |
| totalSlots := len(av) | |
| for i, a := range av { | |
| if err := api.CheckAvailability(ctx, logger, a, conn); err != nil { | |
| logger.Printf("%s. skipping slot %d/%d", err.Error(), i, totalSlots) | |
| stats.BookingCheckAvailabilityErrors++ | |
| continue | |
| } | |
| // Only test CreateBooking on available slots. | |
| slots = append(slots, a) | |
| stats.BookingCheckAvailabilitySuccess++ | |
| } | |
| utils.LogFlow(logger, "Availability Check", "End") | |
| stats.BookingCheckAvailabilityCompleted = true | |
| } | |
| } | |
| // CreateBooking Flow. | |
| var b []*mpb.Booking | |
| if config.BookingBookFlow || config.BookingAllFlows { | |
| utils.LogFlow(logger, "Booking", "Start") | |
| totalSlots := len(slots) | |
| logger.Printf("total slots %d", totalSlots) | |
| for i, a := range slots { | |
| logger.Printf("creating booking") | |
| booking, err := api.CreateBooking(ctx, logger, a, conn) | |
| if err != nil { | |
| logger.Printf("%s. skipping slot %d/%d", err.Error(), i, totalSlots) | |
| stats.BookingCreateBookingErrors++ | |
| continue | |
| } | |
| b = append(b, booking) | |
| stats.BookingCreateBookingSuccess++ | |
| } | |
| utils.LogFlow(logger, "Booking", "End") | |
| stats.BookingCreateBookingCompleted = true | |
| } | |
| // ListBookings Flow | |
| if config.BookingListFlow || config.BookingAllFlows || config.BookingCancelAllBookings { | |
| if len(b) == 0 && !config.BookingCancelAllBookings { | |
| b = GenerateBookings(ctx, logger, slots, stats, conn, config) | |
| } | |
| utils.LogFlow(logger, "List Bookings", "Start") | |
| if len(b) > 0 || config.BookingCancelAllBookings { | |
| var err error | |
| b, err = api.ListBookings(ctx, logger, b, conn) | |
| stats.BookingListBookingsSuccess = true | |
| if err != nil { | |
| stats.BookingListBookingsSuccess = false | |
| logger.Println(err.Error()) | |
| } | |
| } else { | |
| logger.Println("Could not create bookings to test ListBookings flow with.") | |
| stats.BookingListBookingsSuccess = false | |
| } | |
| utils.LogFlow(logger, "List Bookings", "End") | |
| stats.BookingListBookingsCompleted = true | |
| } | |
| // GetBookingStatus Flow | |
| if config.BookingStatusFlow || config.BookingAllFlows { | |
| if len(b) == 0 { | |
| b = GenerateBookings(ctx, logger, slots, stats, conn, config) | |
| } | |
| utils.LogFlow(logger, "BookingStatus", "Start") | |
| totalBookings := len(b) | |
| if totalBookings > 0 { | |
| j := 0 | |
| for i, booking := range b { | |
| if err := api.GetBookingStatus(ctx, logger, booking, conn); err != nil { | |
| logger.Printf("%s. abandoning booking %d/%d", err.Error(), i, totalBookings) | |
| stats.BookingGetBookingStatusErrors++ | |
| continue | |
| } | |
| stats.BookingGetBookingStatusSuccess++ | |
| b[j] = booking | |
| j++ | |
| } | |
| b = b[:j] | |
| } else { | |
| logger.Println("Could not create bookings to test GetBookingStatus flow with.") | |
| } | |
| utils.LogFlow(logger, "BookingStatus", "End") | |
| stats.BookingGetBookingStatusCompleted = true | |
| } | |
| // CancelBooking Flow | |
| if len(b) > 0 { | |
| utils.LogFlow(logger, "Cancel Booking", "Start") | |
| for i, booking := range b { | |
| if err := api.CancelBooking(ctx, logger, booking.GetBookingId(), conn); err != nil { | |
| logger.Printf("%s. abandoning booking %d/%d", err.Error(), i, len(b)) | |
| stats.BookingCancelBookingsErrors++ | |
| continue | |
| } | |
| stats.BookingCancelBookingsSuccess++ | |
| } | |
| utils.LogFlow(logger, "Cancel Booking", "End") | |
| stats.BookingCancelBookingsCompleted = true | |
| } | |
| // Rescheduling is nuanced and can be isolated | |
| // from the rest of the tests. | |
| if config.BookingRescheduleFlow || config.BookingAllFlows { | |
| utils.LogFlow(logger, "Rescheduling", "Start") | |
| stats.BookingReschedulingSuccess = true | |
| if err := api.Rescheduling(ctx, logger, avForRescheduling, conn); err != nil { | |
| logger.Println(err.Error()) | |
| stats.BookingReschedulingSuccess = false | |
| } | |
| utils.LogFlow(logger, "Rescheduling", "End") | |
| stats.BookingReschedulingCompleted = true | |
| } | |
| logStats(stats, logger, config) | |
| } |