diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 000000000..50acc53f4 Binary files /dev/null and b/.DS_Store differ diff --git a/.gitignore b/.gitignore index 5e1422c9c..c0ac3dc53 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,4 @@ build-iPhoneSimulator/ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this: .rvmrc +coverage diff --git a/lib/.DS_Store b/lib/.DS_Store new file mode 100644 index 000000000..16b79835c Binary files /dev/null and b/lib/.DS_Store differ diff --git a/lib/block.rb b/lib/block.rb new file mode 100644 index 000000000..b47796113 --- /dev/null +++ b/lib/block.rb @@ -0,0 +1,17 @@ +require_relative 'room' +require_relative 'date_range' +require_relative 'reservation' + + +module Hotel + class Block < Reservation + attr_reader :block + + def initialize(date_range, room_id, cost = 200.00, block = 0) + + super(date_range, room_id, cost) + @block = block + + end + end +end \ No newline at end of file diff --git a/lib/date_range.rb b/lib/date_range.rb new file mode 100644 index 000000000..4728f306e --- /dev/null +++ b/lib/date_range.rb @@ -0,0 +1,52 @@ +require 'date' + +module Hotel + class DateRange + + attr_accessor :start_date, :end_date + + # your library code should assume that it's receiving Date objects to start + def initialize(start_date, end_date) + raise ArgumentError.new("Please pass in Date class instances.") if !(start_date.is_a? Date) || !(end_date.is_a? Date) + raise ArgumentError.new("Live in the present! Dates should not be prior to today.") if start_date < Date.today + raise ArgumentError.new("End date should not be earlier than or the same day as start date.") if end_date <= start_date + + @start_date = start_date + @end_date = end_date + end + + def count_nights + nights = (@end_date - @start_date).to_i + return nights + end + + def include_date(date) + return true if date >= @start_date && date < @end_date + return false + end + + def overlapping(other_range) + if other_range.start_date < @end_date && @start_date < other_range.end_date + return true + else + return false + end + end + + def exactly_matching(other_range) + if @start_date == other_range.start_date && @end_date == other_range.end_date + return true + else + return false + end + end + + def including(other_range) + if other_range.start_date >= @start_date && other_range.end_date <= @end_date + return true + else + return false + end + end + end +end diff --git a/lib/no_availability_error.rb b/lib/no_availability_error.rb new file mode 100644 index 000000000..7bda955ee --- /dev/null +++ b/lib/no_availability_error.rb @@ -0,0 +1,3 @@ + +class NoAvailabilityError < StandardError +end \ No newline at end of file diff --git a/lib/reservation.rb b/lib/reservation.rb new file mode 100644 index 000000000..3fbeecd37 --- /dev/null +++ b/lib/reservation.rb @@ -0,0 +1,28 @@ +require_relative 'date_range' + +module Hotel + class Reservation + attr_reader :id, :start_date, :end_date, :date_range, :room_id, :block + attr_accessor :cost + + @@next_id = 1 + + def initialize(date_range, room_id, cost = 200.00) + @date_range = date_range + @start_date = date_range.start_date + @end_date = date_range.end_date + + @room_id = room_id + @cost = cost + + @id = @@next_id + @@next_id += 1 + @block = -1 + end + + def get_total_price + total_price = date_range.count_nights * cost + return total_price + end + end +end diff --git a/lib/room.rb b/lib/room.rb new file mode 100644 index 000000000..9f40a05bf --- /dev/null +++ b/lib/room.rb @@ -0,0 +1,44 @@ +require_relative 'reservation' + +module Hotel + class Room + attr_reader :room_id,:bookings + attr_accessor :cost + + def initialize(room_id, cost = 200.00, bookings = nil) + raise ArgumentError if !room_id.is_a? Integer + raise ArgumentError if room_id < 1 + @room_id = room_id + @cost = cost + @bookings = bookings || [] + end + + def add_booking_to_room(new_reservation) + @bookings << new_reservation + end + + def get_price(reservation) + price = reservation.date_range.count_nights * cost + return price + end + + def is_available(given_range) + return true if bookings.empty? + + bookings.each do |item| + if item.block >= 0 #item.class == Hotel::Block + return false if item.date_range.overlapping(given_range) && item.date_range.exactly_matching(given_range)== false #it is not an exact match #method + elsif item.block < 0 #item.class == Hotel::Reservation + return false if item.date_range.overlapping(given_range) + end + end + + return true + end + + def change_rate(new_rate) + raise ArgumentError if !new_rate.is_a?(Numeric) || new_rate.to_f < 0 + @cost = new_rate.to_f + end + end +end diff --git a/lib/system_coordinator.rb b/lib/system_coordinator.rb new file mode 100644 index 000000000..5302047ed --- /dev/null +++ b/lib/system_coordinator.rb @@ -0,0 +1,174 @@ +require_relative 'room' +require_relative 'date_range' +require_relative 'reservation' +require_relative 'block' +require_relative 'no_availability_error' + +module Hotel + class SystemCoordinator + attr_reader :rooms + + @@next_block = 1 + + def initialize(room_quantity = 20) + @rooms = [] # Array.new(quantity){|i| Hotel::Room.new(i+1)} + build_rooms(room_quantity) + end + + def build_rooms(room_quantity) + room_quantity.times do + @rooms << Hotel::Room.new(@rooms.length + 1) + end + end + + def list_rooms + return rooms + end + + + def find_reservations_by_date(date) + reservations_by_date = [] + + rooms.each do |room| + room.bookings.each do |reservation| + reservations_by_date << reservation if reservation.date_range.include_date(date) + end + end + + return reservations_by_date + end + + + def find_reservations_room_date(room_id, date_range) + selected_room = find_room(room_id) + reservations_room_date = selected_room.bookings.reject{|reservation|false == reservation.date_range.overlapping(date_range)} + return reservations_room_date + end + + + def find_reservations_range(given_range) + reservations_range = [] + + rooms.each do |room| + room.bookings.each do |reservation| + reservations_range << reservation if reservation.date_range.overlapping(given_range) + end + end + + return reservations_range + end + + + def find_available_rooms(given_range) + available_rooms = [] + + rooms.each do |room| + if room.bookings.empty? + available_rooms << room + else + status = true + room.bookings.each do |item| + if item.block >= 0 #item.class == Hotel::Block + status = false if item.date_range.overlapping(given_range) && false == item.date_range.exactly_matching(given_range) #it is not an exact match #method + elsif item.block < 0 #item.class == Hotel::Reservation + status = false if item.date_range.overlapping(given_range) + end + end + available_rooms << room if true == status + end + end + + raise NoAvailabilityError.new "No Availability. Please try another date range." if available_rooms == [] + return available_rooms + end + + + def make_reservation(start_date, end_date) + range_created = Hotel::DateRange.new(start_date,end_date) + available_rooms = find_available_rooms(range_created) + + chosen_room = available_rooms.shift + new_reservation = Hotel::Reservation.new(range_created, chosen_room.room_id) + + chosen_room.add_booking_to_room(new_reservation) + + return new_reservation + end + + + def find_block_rooms(given_range) + available_rooms = rooms.reject do |room| + room.bookings.any? do |reservation| #returns true if there are overlapping reservations + reservation.date_range.overlapping(given_range) == true + end + end + return available_rooms + end + + + def make_specific_block(date_range, roomid_array, cost) + room_array = roomid_array.map{|id| find_room(id)} + raise ArgumentError.new "Maximum 5 rooms for a hotel block." if room_array.length > 5 || room_array.length == 0 + rooms_in_block = [] + + room_array.each do |room| + if room.is_available(date_range) == false + raise NoAvailabilityError.new "Room#{room.room_id} is not available for this given date range." + end + end + + room_array.each do |room| + room_block = Hotel::Block.new(date_range,room.room_id, cost, @@next_block) + room.add_booking_to_room(room_block) + rooms_in_block << room_block + end + + @@next_block += 1 + + return rooms_in_block + end + + + def make_block(date_range, room_quantity, cost) + raise ArgumentError.new "Maximum 5 rooms for a hotel block." if room_quantity > 5 + available_rooms = find_block_rooms(date_range) + raise NoAvailabilityError.new "No availability. Please try another date range." if available_rooms.length < room_quantity + + rooms_in_block = [] + + room_quantity.times do |i| + chosen_room = available_rooms[i] + room_id = chosen_room.room_id + room_block = Hotel::Block.new(date_range,room_id, cost, @@next_block) + chosen_room.add_booking_to_room(room_block) + rooms_in_block << room_block + end + + @@next_block += 1 + return rooms_in_block + end + + + def find_room(given_room_id) + room_found = rooms.find{|room|room.room_id == given_room_id} + return room_found + end + + + def check_block_availability(block_id) + rooms_in_block = [] + blocks = [] + rooms.each do |room| + room.bookings.each do |item| + if item.block > 0 && item.block == block_id + blocks << item + rooms_in_block << room + end + end + end + + date_range = blocks[0].date_range + rooms_in_block.any?{|room|room.is_available(date_range)} #returns true or false + end + end +end diff --git a/main.rb b/main.rb new file mode 100644 index 000000000..ef2c48e3d --- /dev/null +++ b/main.rb @@ -0,0 +1,150 @@ +require_relative 'lib/system_coordinator' +require_relative 'lib/room' +require_relative 'lib/date_range' +require_relative 'lib/reservation' +require_relative 'lib/no_availability_error' + +def display_intro + puts "\nGood day! You are now logged in the hotel system." +end + +def display_options + puts "\nEnter 1, 2, 3, 4, 5, 6 ,7 , or 0 for the following options." + puts "1. list rooms" + puts "2. find reservations by date" + puts "3. find reservations by room and date range" + puts "4. find available rooms for date range" + puts "5. find available rooms for hotel block" + puts "6. make hotel block" + puts "7. make reservation" + puts "0. exit" +end + +def create_date + puts "Please enter a date in DD/MM/YYYY format" + stringdate = gets.chomp + new_date = Date.parse(stringdate) + return new_date +end + +def create_start_date + puts "Please enter start date in DD/MM/YYYY format" + stringdate = gets.chomp + new_date = Date.parse(stringdate) + return new_date +end + +def create_end_date + puts "Please enter end date in DD/MM/YYYY format" + stringdate = gets.chomp + new_date = Date.parse(stringdate) + return new_date +end + +def ask_room + puts "Please enter the Room ID" + room_id = gets.chomp.to_i + return room_id +end + +def ask_block + puts "Please enter the Block ID" + block_id = gets.chomp.to_i + return block_id +end + +def ask_rate + puts "Please enter the rate" + rate = gets.chomp + return rate.to_f +end + +def ask_quantity + puts "Please enter the quantity of rooms for the hotel block" + quantity = gets.chomp.to_i + return quantity +end + +def list_reservations(reservations) + reservations.each do |rs| + puts "Reservation #{rs.id}: Room #{rs.room_id} from #{rs.start_date} to #{rs.end_date}" + end +end + +def list_rooms(rooms_array) + rooms_array.each do |room| + print "Room #{room.room_id}, Rate: $#{room.cost} | " + end +end + +def list_blocks(blocks_array) + blocks_array.each do |rs| + puts "Block #{rs.block}: Room #{rs.room_id} from #{rs.start_date} to #{rs.end_date}" + end +end + + +def main + coordinator = Hotel::SystemCoordinator.new + + display_intro + control_loop = true + + while control_loop + display_options + choice = gets.chomp + case choice + when "1", "1.", "list rooms" + all_rooms = coordinator.list_rooms + list_rooms(all_rooms) + when "2", "2.", "find reservations by date" + date_given = create_date + bookings = coordinator.find_reservations_by_date(date_given) + list_reservations(bookings) + when "3", "3.", "find reservations by room and date range" + start_date = create_start_date + end_date = create_end_date + range_given = Hotel::DateRange.new(start_date, end_date) + room_id = ask_room + bookings = coordinator.find_reservations_room_date(room_id.to_i,range_given) + list_reservations(bookings) + when "4", "4.", "find available rooms for date range" + start_date = create_start_date + end_date = create_end_date + range_given = Hotel::DateRange.new(start_date, end_date) + rooms_found = coordinator.find_available_rooms(range_given) + puts "There are #{rooms_found.length} rooms available." + list_rooms(rooms_found) + when "5", "5.", "find available rooms for hotel block" + block_given = ask_block + availability = coordinator.check_block_availability(block_given) + # puts availability + puts availability == true ? "Available rooms in this block" : "No vacant rooms in this block" + when "6", "6.", "make specific hotel block" + start_date = create_start_date + end_date = create_end_date + range_given = Hotel::DateRange.new(start_date, end_date) + quantity = ask_quantity + while quantity > 5 || quantity <= 0 + quantity = ask_quantity + end + room_array = [] + quantity.times do + room_id = ask_room.to_i + room_array << room_id + end + cost = ask_rate + block = coordinator.make_specific_block(range_given, room_array, cost) + list_blocks(block) + when "7", "7.", "make reservation" + start_date = create_start_date + end_date = create_end_date + reservation = coordinator.make_reservation(start_date,end_date) + puts "Reservation is made. It is Room #{reservation.room_id} from #{reservation.start_date} to #{reservation.end_date}" + when "0", "0.", "exit", "quit", "0. exit" + control_loop = false + end + end +end + +main \ No newline at end of file diff --git a/refactors.txt b/refactors.txt new file mode 100644 index 000000000..0c548ed09 --- /dev/null +++ b/refactors.txt @@ -0,0 +1,15 @@ +### Design Reconsiderations ### + +(1). The benefits of having everything in 1 container with shallow layers. +SystemCoordinator class knowing all @Reservations versus each Room knowing its own @[Reservations] +The down side of each Room having its own array of reservations is that methods will easily require at least one loop to iterate through each room and probably a second loop to loop through each reservation. + + +(2). Consider DateRange as a superclass. Reservation and block both inherit from DateRange. +It makes much sense after creating many methods for the DateRange class and fulfilling the user stories. +Ultimately, comparing date range was a crucial concept in our algorithm throughout all Reservations and Blocks. + +(3). View Block as an entire hotel block instead of splitting one hotel block hold into block per room. +Also, consider more elegant designs on how to handle blocks/reservations and mark room that is reserved from block. + + diff --git a/test/.DS_Store b/test/.DS_Store new file mode 100644 index 000000000..bf1216347 Binary files /dev/null and b/test/.DS_Store differ diff --git a/test/block_test.rb b/test/block_test.rb new file mode 100644 index 000000000..421c59bf5 --- /dev/null +++ b/test/block_test.rb @@ -0,0 +1,56 @@ +require_relative 'test_helper' + +describe Hotel::Block do + before do + @date_range = Hotel::DateRange.new(Date.today + 5, Date.today + 10) + @roomid01 = 1 + @roomid02 = 2 + end + + describe "#initialize" do + it "creates an instance of a Block class" do + block01 = Hotel::Block.new(@date_range, @roomid01) + expect(block01).must_be_instance_of Hotel::Block + end + + it "stores the correct room_id in the reservation" do + block01 = Hotel::Block.new(@date_range, @roomid01) + expect(block01.room_id).must_be_instance_of Integer + expect(block01.room_id).must_equal @roomid01 + end + + it "stores a date_range that is an instance of DateRange" do + block01 = Hotel::Block.new(@date_range, @roomid01) + expect(block01.date_range).must_be_instance_of Hotel::DateRange + end + + it "stores start_date and end_date that are both an instance of Date" do + block01 = Hotel::Block.new(@date_range, @roomid01) + expect(block01.date_range.start_date).must_be_instance_of Date + expect(block01.date_range.end_date).must_be_instance_of Date + end + + it "shares the same block id for multiple rooms within the block" do + block01 = Hotel::Block.new(@date_range, @roomid01, 100, 10) + block02 = Hotel::Block.new(@date_range, @roomid02, 100, 10) + expect(block01.block).must_equal 10 + expect(block02.block).must_equal 10 + end + + it "stores an attribute @cost that reflects the correct cost" do + block01 = Hotel::Block.new(@date_range, @roomid01, 200,10) + expect(block01.cost).must_equal 200.00 + block02 = Hotel::Block.new(@date_range, @roomid02, 100, 11) + expect(block02.cost).must_equal 100.00 + end + end + + describe "#get_total_price" do + it "returns the total price for the reservation" do + block01 = Hotel::Block.new(@date_range, @roomid01) + expect(block01.get_total_price).must_equal 1000.00 + block02 = Hotel::Block.new(@date_range, @roomid02, 100) + expect(block02.get_total_price).must_equal 500.00 + end + end +end \ No newline at end of file diff --git a/test/daterange_test.rb b/test/daterange_test.rb new file mode 100644 index 000000000..9102b808f --- /dev/null +++ b/test/daterange_test.rb @@ -0,0 +1,136 @@ +require_relative 'test_helper' + +describe "DateRange Class" do + before do + start_date = Date.today + 5 + end_date = Date.today + 10 + @range01 = Hotel::DateRange.new(start_date, end_date) + end + + describe "Initialize" do + it "is an instance of DateRange" do + expect(@range01).must_be_kind_of Hotel::DateRange + end + + it "stores a start date that is a Date class" do + expect(@range01.start_date).must_be_kind_of Date + end + + it "stores an end date that is a Date class" do + expect(@range01.end_date).must_be_kind_of Date + end + + it "raises an ArgumentError if end date is before start date" do + start_date = Date.today + 5 + end_date = Date.today + 1 + expect{Hotel::DateRange.new(start_date, end_date)}.must_raise ArgumentError + end + + it "raises ArgumentError if start_date or end_date is not a Date class" do + start_date = Date.today + 10 + end_date = Date.today + 15 + expect{Hotel::DateRange.new("2025/03/25", end_date)}.must_raise ArgumentError + expect{Hotel::DateRange.new(start_date, 20250327)}.must_raise ArgumentError + end + + it "raises ArgumentError if start_date is in the past" do + start_date = Date.today - 5 + end_date = Date.today + 1 + expect{Hotel::DateRange.new(start_date, end_date)}.must_raise ArgumentError + end + + it "raises ArgumentError for a 0-length date range" do + start_date = Date.today + end_date = start_date + expect{Hotel::DateRange.new(start_date, end_date)}.must_raise ArgumentError + end + + end + + describe "#count_nights" do + it "counts the total nights of stay within a date range" do + expect(@range01.count_nights).must_equal 5 + expect(@range01.count_nights).must_be_instance_of Integer + end + end + + describe "#including" do + it "returns true if the other date range is included in the date range" do + range21 = Hotel::DateRange.new(Date.today + 7, Date.today + 8) + expect(@range01.including(range21)).must_equal true + range23 = Hotel::DateRange.new(Date.today + 5, Date.today + 6) + expect(@range01.including(range23)).must_equal true + end + + it "returns false if the other date range is not included in the date range" do + range22 = Hotel::DateRange.new(Date.today + 1, Date.today + 2) + expect(@range01.including(range22)).must_equal false + range24 = Hotel::DateRange.new(Date.today + 8, Date.today + 20) + expect(@range01.including(range24)).must_equal false + range26 = Hotel::DateRange.new(Date.today + 10, Date.today + 11) #edge case for checkout date + expect(@range01.including(range26)).must_equal false + range28 = Hotel::DateRange.new(Date.today + 4, Date.today + 5) #edge case for checkout date + expect(@range01.including(range28)).must_equal false + end + end + + describe "#include_date" do + it "returns true if the specific date is included in the date range" do + date01 = Date.today + 8 + date03 = Date.today + 5 + expect(@range01.include_date(date01)).must_equal true + expect(@range01.include_date(date03)).must_equal true + end + + it "returns false if the specific date is not included in the date range" do + date02 = Date.today + 1 + date04 = Date.today + 20 + date06 = Date.today + 10 + expect(@range01.include_date(date02)).must_equal false + expect(@range01.include_date(date04)).must_equal false + expect(@range01.include_date(date06)).must_equal false + end + end + + describe "#overlapping" do + it "returns true if the two date ranges overlap" do + range03 = Hotel::DateRange.new(Date.today + 6, Date.today + 8) + expect(@range01.overlapping(range03)).must_equal true + range04 = Hotel::DateRange.new(Date.today + 5, Date.today + 6) + expect(@range01.overlapping(range04)).must_equal true + range05 = Hotel::DateRange.new(Date.today+8, Date.today+20) + expect(@range01.overlapping(range05)).must_equal true + end + + it "returns false if the two date ranges do not overlap" do + range06 = Hotel::DateRange.new(Date.today + 1, Date.today + 3) + expect(@range01.overlapping(range06)).must_equal false + range09 = Hotel::DateRange.new(Date.today + 100, Date.today + 103) + expect(@range01.overlapping(range09)).must_equal false + end + + it "returns false if it is only the start date overlapping end date" do + range07 = Hotel::DateRange.new(Date.today + 10, Date.today + 12) #edge case for checkout date + expect(@range01.overlapping(range07)).must_equal false + range08 = Hotel::DateRange.new(Date.today + 1, Date.today + 5) #edge case for checkout date + expect(@range01.overlapping(range08)).must_equal false + end + end + + describe "#exactly_matching" do + it "returns true if the date range exactly matches" do + range21 = Hotel::DateRange.new(Date.today + 5, Date.today + 10) + expect(@range01.exactly_matching(range21)).must_equal true + end + + it "returns false if the date range is not an exact match" do + range03 = Hotel::DateRange.new(Date.today + 6, Date.today + 8) + expect(@range01.exactly_matching(range03)).must_equal false + range04 = Hotel::DateRange.new(Date.today + 5, Date.today + 6) + expect(@range01.exactly_matching(range04)).must_equal false + range05 = Hotel::DateRange.new(Date.today+8, Date.today+20) + expect(@range01.exactly_matching(range05)).must_equal false + end + + end +end diff --git a/test/reservation_test.rb b/test/reservation_test.rb new file mode 100644 index 000000000..285c6e333 --- /dev/null +++ b/test/reservation_test.rb @@ -0,0 +1,40 @@ +require_relative 'test_helper' + +describe Hotel::Reservation do + before do + date_range = Hotel::DateRange.new(Date.today + 5, Date.today + 10) + @room_id = 10 + @reservation01 = Hotel::Reservation.new(date_range, @room_id) + end + + describe "#initialize" do + it "is an instance of Reservation" do + expect(@reservation01).must_be_instance_of Hotel::Reservation + end + + it "automatic generates an ID for the reservation" do + expect(@reservation01.id).must_be_instance_of Integer + end + + it "stores the correct room_id in the reservation" do + expect(@reservation01.room_id).must_be_instance_of Integer + expect(@reservation01.room_id).must_equal @room_id + end + + it "stores a date_range that is an instance of DateRange" do + expect(@reservation01.date_range).must_be_instance_of Hotel::DateRange + end + + it "stores start_date and end_date that are both an instance of Date" do + expect(@reservation01.date_range.start_date).must_be_instance_of Date + expect(@reservation01.date_range.end_date).must_be_instance_of Date + end + end + + describe "#get_total_price" do + it "returns the total price for the reservation" do + expect(@reservation01.get_total_price).must_be_instance_of Float + expect(@reservation01.get_total_price).must_equal 1000.00 + end + end +end \ No newline at end of file diff --git a/test/room_test.rb b/test/room_test.rb new file mode 100644 index 000000000..7c50fb4dc --- /dev/null +++ b/test/room_test.rb @@ -0,0 +1,147 @@ +require_relative 'test_helper' + +describe Hotel::Room do + before do + @room_id = 10 + @room01 = Hotel::Room.new(@room_id) + end + + describe "#initialize" do + it "is an instance of Room" do + expect(@room01).must_be_instance_of Hotel::Room + end + + it "stores a room_id as id" do + expect(@room01.room_id).must_equal @room_id + end + + it "raises ArgumentError if the room_id is not a positive integer" do + expect{Hotel::Room.new(-10)}.must_raise ArgumentError + expect{Hotel::Room.new("suite")}.must_raise ArgumentError + end + + it "stores a cost for the room instance" do + expect(@room01.cost).must_be_instance_of Float + expect(@room01.cost).must_equal 200.00 + end + end + + describe "@bookings" do + before do + date_range = Hotel::DateRange.new(Date.today + 5, Date.today + 10) + @reservation01 = Hotel::Reservation.new(date_range, @room_id) + @room01.add_booking_to_room(@reservation01) + end + + it "stores an instance variable @bookings that is an array" do + expect(@room01.bookings).must_be_instance_of Array + end + + it "stores each element as a Reservation instance " do + @room01.bookings.each do |reservation| + expect(reservation).must_be_instance_of Hotel::Reservation + end + end + end + + describe "#add_booking_to_room" do + before do + date_range = Hotel::DateRange.new(Date.today + 5, Date.today + 10) + @reservation01 = Hotel::Reservation.new(date_range, @room_id) + end + + it "returns an array of all the current bookings" do + expect(@room01.add_booking_to_room(@reservation01)).must_be_instance_of Array + end + + it "includes the new booking into @bookings" do + expect(@room01.add_booking_to_room(@reservation01)).must_include @reservation01 + end + + it "increases @bookings size by 1" do + before_size = @room01.bookings.length + after_size = (@room01.add_booking_to_room(@reservation01)).length + expect(after_size).must_equal before_size + 1 + end + end + + describe "#get_price" do + before do + date_range = Hotel::DateRange.new(Date.today + 5, Date.today + 10) + @reservation01 = Hotel::Reservation.new(date_range, @room_id) + @room01.add_booking_to_room(@reservation01) + end + + it "returns a Float for the reservation" do + expect(@room01.get_price(@reservation01)).must_be_instance_of Float + end + + it "returns the total price for the reservation" do + expect(@room01.get_price(@reservation01)).must_equal 1000.00 + end + end + + describe "#is_available" do + before do + @coordinator01 = Hotel::SystemCoordinator.new + @start_date = Date.today + 5 + @end_date = Date.today + 10 + @date_range = Hotel::DateRange.new(@start_date, @end_date) + end + + it "returns true if room is available with no reservations" do + expect(@room01.is_available(@date_range)).must_equal true + expect(@room01.bookings.length).must_equal 0 + end + + it "returns true if room is available with exact block match" do + block10 = Hotel::Block.new(@date_range, @room_id) + @room01.add_booking_to_room(block10) + expect(@room01.is_available(@date_range)).must_equal true + expect(@room01.bookings.length).must_equal 1 + end + + it "returns true if room is available with no overlapping reservations" do + range20 = Hotel::DateRange.new(Date.today + 10, Date.today + 12) + reservation01 = Hotel::Reservation.new(range20, @room_id) + @room01.add_booking_to_room(reservation01) + expect(@room01.is_available(@date_range)).must_equal true + expect(@room01.bookings.length).must_equal 1 + end + + it "returns false when room has overlapping block" do + range20 = Hotel::DateRange.new(Date.today + 5, Date.today + 11) + block20 = Hotel::Block.new(range20, @room_id) + @room01.add_booking_to_room(block20) + expect(@room01.is_available(@date_range)).must_equal false + expect(@room01.bookings.length).must_equal 1 + end + + it "returns false when room has overlapping reservation" do + range30 = Hotel::DateRange.new(Date.today + 5, Date.today + 6) + reservation01 = Hotel::Reservation.new(range30, @room_id) + @room01.add_booking_to_room(reservation01) + expect(@room01.is_available(@date_range)).must_equal false + expect(@room01.bookings.length).must_equal 1 + end + end + + describe "#change_rate" do + it "returns a float of the new rate" do + expect(@room01.change_rate(100)).must_be_instance_of Float + expect(@room01.change_rate(100)).must_equal 100.00 + end + + it "changes the room the correct new rate" do + expect(@room01.cost).must_equal 200.00 + new_rate = 100 + @room01.change_rate(new_rate) + expect(@room01.cost).must_equal new_rate + end + + it "raises an exception if the new rate is < 0" do + expect{@room01.change_rate(-99)}.must_raise ArgumentError + expect{@room01.change_rate("free")}.must_raise ArgumentError + end + end +end diff --git a/test/system_coordinator_test.rb b/test/system_coordinator_test.rb new file mode 100644 index 000000000..65cf55f01 --- /dev/null +++ b/test/system_coordinator_test.rb @@ -0,0 +1,413 @@ +require_relative 'test_helper' + +describe Hotel::SystemCoordinator do + before do + @coordinator01 = Hotel::SystemCoordinator.new + end + + describe "@rooms" do + it "is an instance variable @rooms that is an array" do + expect(@coordinator01.rooms).must_be_instance_of Array + end + + it "stores each element in the @rooms array as an instance of Room" do + @coordinator01.rooms.each do |room| + expect(room).must_be_instance_of Hotel::Room + end + end + + it "stores the correct room ids from 1 ~20" do + @coordinator01.rooms.each_with_index do |room,i| + expect(room.room_id).must_equal (i+1) + end + end + + it "stores the correct cost $200 in each room instance" do + @coordinator01.rooms.each do |room| + expect(room.cost).must_be_instance_of Float + expect(room.cost).must_equal 200.00 + end + end + + it "stores an array of its reservations" do + @coordinator01.rooms.each do |room| + expect(room.bookings).must_be_instance_of Array + end + end + end + + describe "#build_rooms" do + it "increases the number of rooms" do + before = @coordinator01.rooms.length + @coordinator01.build_rooms(5) + expect(@coordinator01.rooms.length).must_equal before + 5 + end + end + + describe "#list_rooms" do + it "returns an Array with size of 20" do + expect(@coordinator01.list_rooms).must_be_instance_of Array + expect(@coordinator01.list_rooms.length).must_equal 20 + end + + it "stores Room instance in each array element" do + @coordinator01.list_rooms.each do |room| + expect(room).must_be_instance_of Hotel::Room + end + end + end + + describe "#find_available_rooms" do + before do + @start_date = Date.today + 5 + @end_date = Date.today + 10 + @date_range = Hotel::DateRange.new(@start_date,@end_date) + end + + it "returns an array" do + available_rooms = @coordinator01.find_available_rooms(@date_range) + expect(available_rooms).must_be_instance_of Array + end + + it "stores Room instances or Block instances in the array" do + available_rooms = @coordinator01.find_available_rooms(@date_range) + available_rooms.each do |room| + expect(room).must_be_instance_of Hotel::Room || Hotel::Block + end + end + + it "returns the correct number of available rooms when there are no reservations" do + available_rooms = @coordinator01.find_available_rooms(@date_range) + expect(available_rooms.length).must_equal 20 + end + + it "returns the correct number of available rooms when there are reservations" do + @coordinator01.make_reservation(@start_date, @end_date) + expect(@coordinator01.find_available_rooms(@date_range).length).must_equal 19 + @coordinator01.make_reservation(Date.today + 10, Date.today + 12) + @coordinator01.make_reservation(Date.today + 1, Date.today + 2) + expect(@coordinator01.find_available_rooms(@date_range).length).must_equal 19 + @coordinator01.make_reservation(@start_date, @end_date) + expect(@coordinator01.find_available_rooms(@date_range).length).must_equal 18 + end + + it "should not include rooms that have reservations in the given date_range" do + @coordinator01.make_reservation(@start_date, @end_date) + available_rooms = @coordinator01.find_available_rooms(@date_range) + available_rooms.each do |room| + room.bookings.each do |booking| + expect(booking.date_range.overlapping(@date_range)).must_equal false + end + end + end + + it "returns the correct number of available rooms when there are blocks" do + @coordinator01.make_block(@date_range, 3, 150) + expect(@coordinator01.find_available_rooms(@date_range).length).must_equal 20 + range02 = Hotel::DateRange.new(Date.today + 5, Date.today + 7) + expect(@coordinator01.find_available_rooms(range02).length).must_equal 17 + range03 = Hotel::DateRange.new(Date.today + 5, Date.today + 15) + expect(@coordinator01.find_available_rooms(range03).length).must_equal 17 + range04 = Hotel::DateRange.new(Date.today + 3, Date.today + 12) + expect(@coordinator01.find_available_rooms(range04).length).must_equal 17 + @coordinator01.make_block(@date_range, 5, 100) + expect(@coordinator01.find_available_rooms(@date_range).length).must_equal 20 + expect(@coordinator01.find_available_rooms(range02).length).must_equal 12 + end + + it "raises NoAvailabilityError when there are no rooms available" do + 20.times do + @coordinator01.make_reservation(@start_date, @end_date) + end + expect{@coordinator01.find_available_rooms(@date_range)}.must_raise NoAvailabilityError + end + end + + describe "#find_block_rooms" do + before do + @start_date = Date.today + 5 + @end_date = Date.today + 10 + @date_range = Hotel::DateRange.new(@start_date,@end_date) + end + + it "returns an array" do + available_rooms = @coordinator01.find_block_rooms(@date_range) + expect(available_rooms).must_be_instance_of Array + end + + it "stores Room instances in the array" do + available_rooms = @coordinator01.find_block_rooms(@date_range) + available_rooms.each do |room| + expect(room).must_be_instance_of Hotel::Room + end + end + + it "returns the correct number of available rooms when there are no reservations" do + available_rooms = @coordinator01.find_block_rooms(@date_range) + expect(available_rooms.length).must_equal 20 + end + + it "returns the correct number of available rooms when there are reservations" do + @coordinator01.make_reservation(@start_date, @end_date) + expect(@coordinator01.find_block_rooms(@date_range).length).must_equal 19 + @coordinator01.make_reservation(Date.today + 10, Date.today + 12) + @coordinator01.make_reservation(Date.today + 1, Date.today + 2) + expect(@coordinator01.find_block_rooms(@date_range).length).must_equal 19 + @coordinator01.make_reservation(@start_date, @end_date) + expect(@coordinator01.find_block_rooms(@date_range).length).must_equal 18 + end + + it "returns the correct number of available rooms when there are blocks" do + @coordinator01.make_reservation(@start_date, @end_date) + expect(@coordinator01.find_block_rooms(@date_range).length).must_equal 19 + @coordinator01.make_block(@date_range, 5, 150) + expect(@coordinator01.find_block_rooms(@date_range).length).must_equal 14 + range02 = Hotel::DateRange.new(@start_date + 2, @end_date + 2) + @coordinator01.make_block(range02, 3, 180) + expect(@coordinator01.find_block_rooms(@date_range).length).must_equal 11 + end + + it "should not include rooms that have reservations in the given date_range" do + @coordinator01.make_reservation(@start_date, @end_date) + available_rooms = @coordinator01.find_block_rooms(@date_range) + available_rooms.each do |room| + room.bookings.each do |booking| + expect(booking.date_range.overlapping(@date_range)).must_equal false + end + end + end + + it "returns an empty array when there are no rooms available" do + 20.times do + @coordinator01.make_reservation(@start_date, @end_date) + end + available_rooms = @coordinator01.find_block_rooms(@date_range) + expect(available_rooms).must_equal [] + end + end + + describe "#make_reservation" do + before do + @start_date = Date.today + 5 + @end_date = Date.today + 10 + @date_range = Hotel::DateRange.new(@start_date,@end_date) + end + + it "returns a Reservation" do + new_reservation = @coordinator01.make_reservation(@start_date, @end_date) + expect(new_reservation).must_be_instance_of Hotel::Reservation + end + + it "stores the room_id in the Reservation returned" do + new_reservation = @coordinator01.make_reservation(@start_date, @end_date) + expect(new_reservation.room_id).must_equal 1 + end + end + + + describe "#find_room" do + it "returns an instance of Room" do + expect(@coordinator01.find_room(10)).must_be_instance_of Hotel::Room + end + + it "returns nil??? if an invalid room_id is passed in" do + expect(@coordinator01.find_room(999)).must_be_nil + end + end + + + describe "#find_reservations_by_date" do + before do + start_date = Date.today + 5 + end_date = Date.today + 10 + @coordinator01.make_reservation(start_date, end_date) + end + + it "returns an array" do + date10 = Date.today + 7 + expect(@coordinator01.find_reservations_by_date(date10)).must_be_instance_of Array + end + + it "returns empty array when the date is not included" do + date11 = Date.today + 1 + expect(@coordinator01.find_reservations_by_date(date11)).must_equal [] + end + + it "stores Reservation instances in the returned array" do + date10 = Date.today + 7 + found_reservations = @coordinator01.find_reservations_by_date(date10) + found_reservations.each do |reservation| + expect(reservation).must_be_instance_of Hotel::Reservation + end + end + end + + + describe "#find_reservations_room_date" do + before do + @start_date = Date.today + 5 + @end_date = Date.today + 10 + @date_range = Hotel::DateRange.new(@start_date,@end_date) + new_reservation = @coordinator01.make_reservation(@start_date, @end_date) + @room_id = new_reservation.room_id + end + + it "returns an array of reservations" do + reservation_list = @coordinator01.find_reservations_room_date(@room_id,@date_range) + expect(reservation_list).must_be_instance_of Array + end + + it "stores Reservation instances in each element of the array" do + reservation_list = @coordinator01.find_reservations_room_date(@room_id,@date_range) + reservation_list.each do |reservation| + expect(reservation).must_be_instance_of Hotel::Reservation + end + end + end + + + describe "#find_reservations_range" do + before do + @start_date = Date.today + 5 + @end_date = Date.today + 10 + @date_range = Hotel::DateRange.new(@start_date,@end_date) + @coordinator01.make_reservation(@start_date, @end_date) + end + + it "returns an array of reservations" do + reservations_range = @coordinator01.find_reservations_range(@date_range) + expect(reservations_range).must_be_instance_of Array + expect(reservations_range.length).must_equal 1 + end + + it "returns the correct quantity of reservations" do + @coordinator01.make_reservation(Date.today + 5, Date.today + 6) + @coordinator01.make_reservation(Date.today + 8, Date.today + 18) + reservations_range = @coordinator01.find_reservations_range(@date_range) + expect(reservations_range).must_be_instance_of Array + expect(reservations_range.length).must_equal 3 + end + end + + + describe "#make_block" do + before do + @date_range = Hotel::DateRange.new(Date.today + 50, Date.today + 60) + end + + it "returns an array" do + block10 = @coordinator01.make_block(@date_range, 5, 100) + expect(block10).must_be_instance_of Array + end + + it "returns the correct number of room blocks" do + block10 = @coordinator01.make_block(@date_range, 5, 100) + expect(block10.length).must_equal 5 + block20 = @coordinator01.make_block(@date_range, 3, 100) + expect(block20.length).must_equal 3 + end + + it "returns an array of Block instances that match the date range" do + block10 = @coordinator01.make_block(@date_range, 5, 100) + block10.each do |block| + expect(block).must_be_instance_of Hotel::Block + expect(block.date_range).must_equal @date_range + end + end + + it "has the same block_id for the same block" do + block10 = @coordinator01.make_block(@date_range, 5, 100) + block_id = block10[0].block + block10.each do |block| + expect(block.block).must_equal block_id + end + end + + it "reflects the correct cost that was passed in" do + block10 = @coordinator01.make_block(@date_range, 5, 100) + block10.each do |block| + expect(block.cost).must_equal 100.00 + expect(block.get_total_price).must_equal 1000.00 + end + end + + it "increases a booking in the room's tracking" do + block10 = @coordinator01.make_block(@date_range, 2, 100) + room_id01 = block10[0].room_id + room_id02 = block10[1].room_id + room01 = @coordinator01.find_room(room_id01) + room02 = @coordinator01.find_room(room_id02) + expect(room01.bookings.length).must_equal 1 + expect(room02.bookings.length).must_equal 1 + end + end + + + describe "#make_specific_block" do + before do + @start_date = Date.today + 50 + @end_date = Date.today + 60 + @date_range = Hotel::DateRange.new(@start_date, @end_date) + end + + it "returns an array" do + block10 = @coordinator01.make_specific_block(@date_range, [11,12], 100) + expect(block10).must_be_instance_of Array + end + + it "returns the correct number of room blocks" do + block10 = @coordinator01.make_specific_block(@date_range, [20], 180) + expect(block10.length).must_equal 1 + block20 = @coordinator01.make_specific_block(@date_range, [11,12,13,14,15], 100) + expect(block20.length).must_equal 5 + end + + it "returns an array of Block instances that match the date range and same block_id" do + block10 = @coordinator01.make_specific_block(@date_range, [11,12,13], 100) + block_id = block10[0].block + block10.each do |block| + expect(block).must_be_instance_of Hotel::Block + expect(block.date_range).must_equal @date_range + expect(block.block).must_equal block_id + end + end + + it "reflects the correct cost that was passed in" do + block10 = @coordinator01.make_specific_block(@date_range, [11,12,13,14,15], 100) + block10.each do |block| + expect(block.cost).must_equal 100.00 + expect(block.get_total_price).must_equal 1000.00 + end + end + + it "raises an Exception when there is a room unavailable" do + reservation10 = @coordinator01.make_reservation(@start_date, @end_date) + id = reservation10.room_id + expect{@coordinator01.make_specific_block(@date_range, [id,12,13], 100)}.must_raise NoAvailabilityError + end + end + + + describe "#check_block_availability" do + before do + @start_date = Date.today + 50 + @end_date = Date.today + 60 + @date_range = Hotel::DateRange.new(@start_date, @end_date) + end + + it "returns true if the given block has any available rooms" do + block10 = @coordinator01.make_specific_block(@date_range, [1,2], 100) + block_id=block10[0].block + @coordinator01.make_reservation(@start_date,@end_date) + expect(@coordinator01.check_block_availability(block_id)).must_equal true + end + + it "returns false if there are no available rooms" do + block10 = @coordinator01.make_specific_block(@date_range, [1,2], 100) + block_id=block10[0].block + @coordinator01.make_reservation(@start_date,@end_date) + @coordinator01.make_reservation(@start_date,@end_date) + expect(@coordinator01.check_block_availability(block_id)).must_equal false + end + end +end \ No newline at end of file diff --git a/test/test_helper.rb b/test/test_helper.rb index c3a7695cf..0201aca79 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,8 +1,20 @@ # Add simplecov +require 'simplecov' +SimpleCov.start do + add_filter 'test/' # Tests should not be checked for coverage. +end + require "minitest" require "minitest/autorun" require "minitest/reporters" +# require "minitest/skip_dsl" Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new # require_relative your lib files here! +require_relative '../lib/date_range' +require_relative '../lib/reservation' +require_relative '../lib/system_coordinator' +require_relative '../lib/room' +require_relative '../lib/no_availability_error' +require_relative '../lib/block'