Exemple #1
0
        /// <summary>
        /// Runs the find and hold seats ticket service
        /// </summary>
        /// <param name="numSeats">the number of seats to be held</param>
        /// <param name="email">the customer email address</param>
        /// <returns>The seathold object</returns>
        public static SeatHold FindAndHoldSeats(int numSeats, string email)
        {
            Console.WriteLine("Attempting to find " + numSeats.ToString() + " for " + email);
            SeatHold seatHold = null;

            try
            {
                seatHold = ticketService.findAndHoldSeats(numSeats, email);
            }
            catch (Exception e)
            {
                Console.WriteLine("SeatHold failed: " + e.Message.ToString());
                return(seatHold);
            }


            if (seatHold != null)
            {
                Console.WriteLine("Seat hold successful. Expires in " + SeatHold.ExpiresAfter.TotalSeconds.ToString() + " seconds\n");
                Console.WriteLine(seatHold.ToString());
                return(seatHold);
            }

            Console.WriteLine("Seat Hold was not successful\n");
            return(seatHold);
        }
Exemple #2
0
        /// <summary>
        /// Tests the reserveSeats function of the ticket service
        /// </summary>
        public static void TestReserveSeats()
        {
            if (ticketService == null || ticketedEvent == null)
            {
                Console.WriteLine("there is no ticket service. Run ts create or test case 1");
            }
            int reservationStartCount   = ticketedEvent.Reservations.Count;
            int availableSeatStartCount = ticketedEvent.NumberOfAvailableSeats;
            int seatHoldStartCount      = ticketedEvent.SeatHolds.Count;

            TimeSpan seatHoldExpiresAfter = SeatHold.ExpiresAfter;

            SeatHold.SetSeatHoldDuration(new TimeSpan(0, 0, 3));

            Console.WriteLine("Creating seat hold");
            SeatHold seatHold = ticketService.findAndHoldSeats(10, "*****@*****.**");

            Console.WriteLine("Creating reservation");

            string confirmationCode = ticketService.reserveSeats(seatHold.Id, seatHold.CustomerEmail);

            Assert.IsTrue(!string.IsNullOrEmpty(confirmationCode));
            Assert.AreEqual(ticketedEvent.SeatHolds.Count, seatHoldStartCount);
            Assert.AreEqual(ticketedEvent.Reservations.Count, reservationStartCount + 1);
            Assert.AreEqual(ticketedEvent.NumberOfAvailableSeats, availableSeatStartCount - 10);
            Assert.IsTrue(ticketedEvent.Reservations.Contains((confirmationCode, seatHold)));

            // wait for seat holds to expire. I'd want to use something other than thread sleep in production, but this works for now.
            Thread.Sleep((int)SeatHold.ExpiresAfter.TotalMilliseconds + 100);

            // reset seat hold expiration
            SeatHold.SetSeatHoldDuration(seatHoldExpiresAfter);
        }
 /// <summary>
 /// Releases the specified seat hold.
 /// </summary>
 /// <param name="seatHold">The seat hold object.</param>
 private void ReleaseSeatHold(SeatHold seatHold)
 {
     foreach (Seat seat in seatHold.Seats)
     {
         this.MakeSeatAvailable(seat);
     }
     seatHold.Expired = true;
 }
Exemple #4
0
        /// <summary>
        /// Tests the findAndHoldSeats function of the ticket service
        /// </summary>
        public static void TestFindAndHoldSeats()
        {
            if (ticketService == null || ticketedEvent == null)
            {
                Console.WriteLine("there is no ticket service. Run ts create or test case 1");
            }

            TimeSpan seatHoldExpiresAfter = SeatHold.ExpiresAfter;

            SeatHold.SetSeatHoldDuration(new TimeSpan(0, 0, 3));
            int seatHoldStartCount     = ticketedEvent.SeatHolds.Count;
            int numAvailableStartCount = ticketedEvent.NumberOfAvailableSeats;

            Console.WriteLine("Creating Seat Hold");
            SeatHold seatHold = ticketService.findAndHoldSeats(10, "*****@*****.**");

            System.Timers.Timer seatHoldExpiration = new System.Timers.Timer(SeatHold.ExpiresAfter.TotalMilliseconds + 1);
            seatHoldExpiration.Elapsed += (source, args) =>
            {
                Console.WriteLine("Seat hold expired!");
                Assert.AreEqual(ticketedEvent.NumberOfAvailableSeats, numAvailableStartCount);
                Assert.AreEqual(ticketedEvent.SeatHolds.Count, seatHoldStartCount);
                seatHoldExpiration.Stop();
            };
            seatHoldExpiration.Start();
            Assert.AreEqual(ticketedEvent.SeatHolds.Count, seatHoldStartCount + 1);
            Assert.AreEqual(seatHold.CustomerEmail, "*****@*****.**");
            Assert.AreEqual(seatHold.Seats.Count, 10);
            Assert.AreEqual(ticketedEvent.NumberOfAvailableSeats, numAvailableStartCount - 10);

            try
            {
                ticketService.findAndHoldSeats(0, "*****@*****.**");
                Assert.Fail("find and hold seats failed to throw exception from passing 0 for numseats");
            }
            catch (ArgumentException e)
            {
                Assert.IsTrue(e.Message.Contains("Cannot hold 0 or less seats."));
            }

            try
            {
                ticketService.findAndHoldSeats(1, "invalidemailaddress");
                Assert.Fail("find and hold seats failed to throw exception after invalid email address passed");
            }
            catch (ArgumentException e)
            {
                Assert.IsTrue(e.Message.Contains("Invalid email address. "));
            }

            Console.WriteLine("Waiting for seat holds to expire");

            // wait for seat holds to expire. I'd want to use something other than thread sleep in production, but this works for now.
            Thread.Sleep((int)SeatHold.ExpiresAfter.TotalMilliseconds + 100);

            // reset seat hold expiration
            SeatHold.SetSeatHoldDuration(seatHoldExpiresAfter);
        }
        public void ReserveSeats_WithDifferentEmail_ShouldThrowException()
        {
            // Arrange
            BasicVenue     venue         = new BasicVenue(3, 3);
            ITicketService ticketService = new BasicTicketService(venue, TestData.HourHoldDuration);

            // Act
            SeatHold seatHold = ticketService.FindAndHoldSeats(1, TestData.TestCustomerEmail);

            ticketService.ReserveSeats(seatHold.Id, TestData.SecondTestCustomerEmail);
        }
        public void ReserveSeats_ExpiredSeatHold_ShouldThrowException()
        {
            // Arrange
            BasicVenue     venue         = new BasicVenue(3, 3);
            ITicketService ticketService = new BasicTicketService(venue, TestData.NegativeHoldDuration);

            // Act
            SeatHold seatHold = ticketService.FindAndHoldSeats(1, TestData.TestCustomerEmail);

            ticketService.ReserveSeats(seatHold.Id, TestData.TestCustomerEmail);
        }
        public void FindAndHoldSeats_RequestingOneSeat_ShouldReturnOneSeat()
        {
            // Arrange
            BasicVenue     singleSeatVenue = new BasicVenue(1, 1);
            ITicketService ticketService   = new BasicTicketService(singleSeatVenue, TestData.HourHoldDuration);

            // Act
            SeatHold seatHold = ticketService.FindAndHoldSeats(1, TestData.TestCustomerEmail);

            // Assert
            Assert.AreEqual(1, seatHold.Seats.Count);
        }
        public void ReserveSeats_ValidSeatHold_ShouldReserveSeats()
        {
            // Arrange
            BasicVenue     venue         = new BasicVenue(3, 3);
            ITicketService ticketService = new BasicTicketService(venue, TestData.HourHoldDuration, 8);

            // Act
            SeatHold seatHold        = ticketService.FindAndHoldSeats(1, TestData.TestCustomerEmail);
            string   reservationCode = ticketService.ReserveSeats(seatHold.Id, TestData.TestCustomerEmail);

            // Assert
            Assert.AreEqual(8, reservationCode.Length);
        }
        public void FindAndHoldSeats_WhenReservationSizeLargerThanRowSize_ShouldSplitGroup()
        {
            // Arrange
            BasicVenue     venue         = new BasicVenue(3, 3);
            ITicketService ticketService = new BasicTicketService(venue, TestData.HourHoldDuration);

            // Act
            SeatHold seatHold = ticketService.FindAndHoldSeats(4, TestData.TestCustomerEmail);

            // Assert
            Assert.AreEqual(0, seatHold.Seats[0].RowNumber);
            Assert.AreEqual(0, seatHold.Seats[2].RowNumber);
            Assert.AreEqual(1, seatHold.Seats[3].RowNumber);
        }
        public void FindAndHoldSeats_RequestingThreeSeats_ShouldReturnThreeAdjacentSeats()
        {
            // Arrange
            BasicVenue     venue         = new BasicVenue(5, 5);
            ITicketService ticketService = new BasicTicketService(venue, TestData.HourHoldDuration);

            // Act
            SeatHold seatHold = ticketService.FindAndHoldSeats(5, TestData.TestCustomerEmail);

            // Assert
            Assert.AreEqual(0, seatHold.Seats[0].SeatNumber);
            Assert.AreEqual(1, seatHold.Seats[1].SeatNumber);
            Assert.AreEqual(2, seatHold.Seats[2].SeatNumber);
        }
        public void FindAndHoldSeats_FirstHoldReleased_ShouldGiveSecondHoldSameSeats()
        {
            // Arrange
            BasicVenue     venue         = new BasicVenue(3, 3);
            ITicketService ticketService = new BasicTicketService(venue, TestData.NegativeHoldDuration);

            // Act
            SeatHold firstSeatHold  = ticketService.FindAndHoldSeats(3, TestData.TestCustomerEmail);
            SeatHold secondSeatHold = ticketService.FindAndHoldSeats(3, TestData.TestCustomerEmail);

            // Assert
            Assert.AreEqual(0, secondSeatHold.Seats[0].RowNumber);
            Assert.AreEqual(0, secondSeatHold.Seats[1].RowNumber);
            Assert.AreEqual(0, secondSeatHold.Seats[2].RowNumber);
        }
        public void FindAndHoldSeats_ThreeSeatHolds_ShouldHaveIncreasingSeatIds()
        {
            // Arrange
            BasicVenue     venue         = new BasicVenue(3, 3);
            ITicketService ticketService = new BasicTicketService(venue, TestData.HourHoldDuration);

            // Act
            SeatHold firstSeatHold  = ticketService.FindAndHoldSeats(1, TestData.TestCustomerEmail);
            SeatHold secondSeatHold = ticketService.FindAndHoldSeats(1, TestData.TestCustomerEmail);
            SeatHold thirdSeatHold  = ticketService.FindAndHoldSeats(1, TestData.TestCustomerEmail);

            // Assert
            Assert.IsTrue(firstSeatHold.Id < secondSeatHold.Id);
            Assert.IsTrue(secondSeatHold.Id < thirdSeatHold.Id);
        }
        public void ReserveSeats_TwoReservations_ShouldHaveDifferentReservationCodes()
        {
            // Arrange
            BasicVenue     venue         = new BasicVenue(3, 3);
            ITicketService ticketService = new BasicTicketService(venue, TestData.HourHoldDuration);

            // Act
            SeatHold firstSeatHold  = ticketService.FindAndHoldSeats(1, TestData.TestCustomerEmail);
            SeatHold secondSeatHold = ticketService.FindAndHoldSeats(1, TestData.TestCustomerEmail);

            string firstReservationCode  = ticketService.ReserveSeats(firstSeatHold.Id, TestData.TestCustomerEmail);
            string secondReservationCode = ticketService.ReserveSeats(secondSeatHold.Id, TestData.TestCustomerEmail);

            // Assert
            Assert.AreNotEqual(firstReservationCode, secondReservationCode, "Reservation codes should never be the same.");
        }
        public void NumSeatsAvailable_AfterReservingSeatHold_ShouldReturnSameNumberOfAvailableSeats()
        {
            // Arrange
            BasicVenue     oneRowFiveSeatsVenue = new BasicVenue(1, 5);
            ITicketService ticketService        = new BasicTicketService(oneRowFiveSeatsVenue, TestData.HourHoldDuration);

            // Act
            SeatHold seatHold = ticketService.FindAndHoldSeats(3, TestData.TestCustomerEmail);
            int      numSeatsAvailableAfterHold = ticketService.NumSeatsAvailable();

            ticketService.ReserveSeats(seatHold.Id, TestData.TestCustomerEmail);
            int numSeatsAvailableAfterReservation = ticketService.NumSeatsAvailable();

            // Assert
            Assert.AreEqual(2, numSeatsAvailableAfterHold);
            Assert.AreEqual(2, numSeatsAvailableAfterReservation);
        }
        public void FindAndHoldSeats_WhenInsufficientAvailableSeatsInCloserRow_ShouldHoldSeatsInHigherRow()
        {
            // Arrange
            BasicVenue     venue         = new BasicVenue(3, 3);
            ITicketService ticketService = new BasicTicketService(venue, TestData.HourHoldDuration);

            // Act
            SeatHold firstSeatHold  = ticketService.FindAndHoldSeats(2, TestData.TestCustomerEmail);
            SeatHold secondSeatHold = ticketService.FindAndHoldSeats(2, TestData.TestCustomerEmail);

            // Assert
            Assert.AreEqual(0, firstSeatHold.Seats[0].RowNumber);
            Assert.AreEqual(0, firstSeatHold.Seats[1].RowNumber);

            Assert.AreEqual(1, secondSeatHold.Seats[0].RowNumber);
            Assert.AreEqual(1, secondSeatHold.Seats[1].RowNumber);
        }
        public void FindAndHoldSeats_WhenReservationSizeLargerThanAvailableSeatsInEachRow_ShouldSplitGroup()
        {
            // Arrange
            BasicVenue     venue         = new BasicVenue(2, 3);
            ITicketService ticketService = new BasicTicketService(venue, TestData.HourHoldDuration);

            // Act
            SeatHold firstSeatHold  = ticketService.FindAndHoldSeats(2, TestData.TestCustomerEmail);
            SeatHold secondSeatHold = ticketService.FindAndHoldSeats(2, TestData.TestCustomerEmail);
            SeatHold thirdSeatHold  = ticketService.FindAndHoldSeats(2, TestData.TestCustomerEmail);

            // Assert
            Assert.AreEqual(0, thirdSeatHold.Seats[0].RowNumber);
            Assert.AreEqual(2, thirdSeatHold.Seats[0].SeatNumber);
            Assert.AreEqual(1, thirdSeatHold.Seats[1].RowNumber);
            Assert.AreEqual(2, thirdSeatHold.Seats[1].SeatNumber);
        }
Exemple #17
0
        public async Task <SeatHold> FindAndHoldSeats(int numSeats, string customerEmail)
        {
            var unreservedSeats = (await FindUnreservedSeats()).Take(numSeats).ToList();

            if (unreservedSeats.Count() < numSeats)
            {
                throw new NoSeatsAvailableException();
            }

            var seatHold = new SeatHold()
            {
                HeldSeats     = string.Join(',', unreservedSeats),
                CustomerEmail = customerEmail
            };

            var options = new MemoryCacheEntryOptions
            {
                AbsoluteExpirationRelativeToNow = _settings.TicketHoldExpiration
            };

            options.RegisterPostEvictionCallback((o, v, r, s) =>
            {
                if (r == EvictionReason.Removed)
                {
                    return;
                }
                var cnx = factory();
                cnx.SeatHolds.Remove(v as SeatHold);
                cnx.SaveChanges();
            });

            await _context.SeatHolds.AddAsync(seatHold);

            await _context.SaveChangesAsync();

            _cache.Set <SeatHold>(seatHold.SeatHoldId.ToString(), seatHold, options);



            return(seatHold);
        }
        public void FindAndHoldSeats_FirstHoldExpiresWhileSecondStillValid_ShouldGiveLaterHoldsFirstHoldsSeats()
        {
            // Arrange
            BasicVenue venue = new BasicVenue(3, 3);
            // To mock DateTime.UtcNow, make it so if there are an even number of seats in the seat hold and there are an
            // even number of seat holds, it will expire immediately. Otherwise it will never expire.
            Func <bool, DateTime> dateTimeFunc  = (b => b ? DateTime.MaxValue : DateTime.MinValue);
            ITicketService        ticketService = new BasicTicketService(venue, TestData.HourHoldDuration, getNow: dateTimeFunc);

            // Act
            SeatHold firstSeatHold  = ticketService.FindAndHoldSeats(2, TestData.TestCustomerEmail);
            SeatHold secondSeatHold = ticketService.FindAndHoldSeats(1, TestData.TestCustomerEmail);
            SeatHold thirdSeatHold  = ticketService.FindAndHoldSeats(1, TestData.TestCustomerEmail);
            SeatHold fourthSeatHold = ticketService.FindAndHoldSeats(1, TestData.TestCustomerEmail);

            // Assert
            Assert.IsFalse(secondSeatHold.Expired);
            Assert.AreEqual(0, thirdSeatHold.Seats[0].SeatNumber);
            Assert.AreEqual(0, thirdSeatHold.Seats[0].RowNumber);
            Assert.AreEqual(1, fourthSeatHold.Seats[0].SeatNumber);
            Assert.AreEqual(0, fourthSeatHold.Seats[0].RowNumber);
        }
        public SeatHold findAndHoldSeats(int numSeats, string customerEmail)
        {
            string error = string.Empty;

            if (!RegexUtilities.IsValidEmail(customerEmail))
            {
                error += "Invalid email address. ";
            }

            if (numSeats < 1)
            {
                error += "Cannot hold 0 or less seats.";
            }

            if (!string.IsNullOrEmpty(error))
            {
                throw new ArgumentException(error);
            }

            SeatHold seatHold = DataController.Instance.FindAndHoldSeats(TicketedEventName, numSeats, customerEmail);

            return(seatHold);
        }
 /// <summary>
 /// Checks whether the seat hold has expired and outputs the number of seconds that the seat hold has been held.
 /// The argument for getNow is used for unit testing and should not affect production code.
 /// </summary>
 /// <param name="seatHold">The seat hold.</param>
 /// <param name="elapsedHoldTime">The time in seconds that the seat hold has been held.</param>
 /// <returns></returns>
 private bool SeatHoldHasExpired(SeatHold seatHold, out double elapsedHoldTime)
 {
     elapsedHoldTime = (this.getNow(this.seatHolds.Count % 2 == 0 && seatHold.Seats.Count % 2 == 0) - seatHold.HoldTime).TotalSeconds;
     return(elapsedHoldTime > this.maxHoldDuration);
 }
        /// <summary>
        /// Find and hold the best available seats for a customer.
        /// </summary>
        /// <param name="numSeats">The number of seats to find and hold.</param>
        /// <param name="customerEmail">Unique identifier for the customer.</param>
        /// <returns>A SeatHold object identifying the specific seats and related information.</returns>
        public SeatHold FindAndHoldSeats(int numSeats, string customerEmail)
        {
            if (numSeats <= 0)
            {
                throw new ArgumentOutOfRangeException($"At least 1 seat must be requested.");
            }

            this.ReleaseExpiredSeatHolds();
            int numSeatsAvailable = this.NumSeatsAvailable();

            if (numSeatsAvailable < numSeats)
            {
                throw new TicketServiceException($"There are {numSeatsAvailable} seats available and so {numSeats} seats cannot be reserved.");
            }

            SeatHold seatHold = new SeatHold
            {
                CustomerEmail = customerEmail,
                Seats         = new List <Seat>()
            };

            // Try to find the closest row to the front with enough seats to seat everyone together. If there is no row in which everyone
            // can sit together, split group amongst rows closest to the front. Populate backup rows in case no single row with enough seats for whole group.
            int             numSeatsToFill           = numSeats;
            bool            existsRowWithEnoughSeats = false;
            List <BasicRow> backupRows = new List <BasicRow>();

            for (int i = firstRowWithAvailableSeats.RowNumber; i < this.Venue.NumRows; i++)
            {
                BasicRow row = this.Venue.Rows[i];
                if (row.NumAvailableSeats >= numSeats)
                {
                    existsRowWithEnoughSeats = true;
                    List <Seat> heldSeats = this.HoldAvailableSeatsInRow(row, numSeats);
                    seatHold.Seats.AddRange(heldSeats);
                    break;
                }

                if (numSeatsToFill > 0 && row.NumAvailableSeats > 0)
                {
                    backupRows.Add(row);
                    numSeatsToFill -= row.NumAvailableSeats;
                }
            }

            if (!existsRowWithEnoughSeats)
            {
                numSeatsToFill = numSeats;
                foreach (BasicRow backupRow in backupRows)
                {
                    List <Seat> heldSeats = this.HoldAvailableSeatsInRow(backupRow, numSeatsToFill);
                    seatHold.Seats.AddRange(heldSeats);
                    numSeatsToFill -= heldSeats.Count;
                }
            }

            this.Venue.TotalAvailableSeats -= seatHold.Seats.Count;
            seatHold.HoldTime = DateTime.UtcNow;
            seatHold.Id       = Interlocked.Increment(ref incrementingSeatHoldId);
            this.seatHolds.Add(seatHold.Id, seatHold);
            return(seatHold);
        }