diff --git a/design-scaffolding-notes.md b/design-scaffolding-notes.md new file mode 100644 index 000000000..8efb3d487 --- /dev/null +++ b/design-scaffolding-notes.md @@ -0,0 +1,49 @@ +# Hotel Design Scaffolding + +## How to Read This Scaffolding to Create a Second Draft + +This scaffolding is one solution that answers some of the initial questions about how project files can be laid out. + +Use [this view of our branch on GitHub.com](https://github.com/AdaGold/hotel/tree/design-scaffolding) to explore the files that exist on this repo. + - What classes exist? + - Why? What are they named, what do they represent, and what state and behavior do they have? + - What tests exist? + - What parts of this design inspires you, and you want to steal? + - What parts of this design are you unsure about, and need to consider again later? + - What parts of this design do you think you can do without? + +Spend **no more than 1 hour** answering those questions and adjusting your project's first draft design. After one hour, get started; don't forget that a useful skill for the programmer is the ability to get started, and adjust in small ways often. + +### What it includes + +- Three class stubs, `HotelController`, `Reservation` and `DateRange` +- Stubs for public methods of each class from waves 1 and 2, as described in the user stories +- "Interface" tests for each class method that invoke it with the right parameters and verify the return type +- Full test stubs for the `DateRange` class + +### What it does not include + +- Opinions about how classes should interact or data should be stored +- Opinions about whether there should be a `Room` class, or whether it should know about `Reservation`s +- Private helper methods to keep code organized + +Students should feel free to modify any code as they see fit, including changing method signatures, adding new classes and methods, reordering things, not looking at the `DateRange` tests because they want to give it a shot on their own, etc. + +## How to use this code + +Design scaffolding code lives on the `design-scaffolding` branch. + +You can use this code either as inspiration, or as a starting point. If using it as an inspiration, it follows our standard project layout, with product code under `lib/` and tests under `test/`. + +If you choose to use the code on this branch as a starting point, you can either: + +1. Copy and paste each file from this project into your existing `hotel` folder +2. Or start your project anew with the following steps: + + ``` + $ git clone + $ cd hotel + $ git merge origin/design-scaffolding + ``` + + - Note: You can try to merge in the design scaffolding after you've started, but you'll probably end up with merge conflicts. See an instructor if you're not able to resolve them yourself. diff --git a/lib/date_range.rb b/lib/date_range.rb new file mode 100644 index 000000000..955ae2b4c --- /dev/null +++ b/lib/date_range.rb @@ -0,0 +1,44 @@ +module Hotel + class DateRange + attr_accessor :start_date, :end_date + + def initialize(start_date, end_date) + unless start_date.instance_of?(Date) || end_date.instance_of?(Date) + raise ArgumentError.new("start_date and end_date have to be a date type") + end + + if end_date < start_date + raise ArgumentError.new("The end_date cannot be less than start_end") + end + + if start_date == end_date + raise ArgumentError.new("The start_date cannot be the same as end_date") + end + @start_date = start_date + @end_date = end_date + + end + + # define exactly what it means for two instances of its own class to be equal. + def ==(other) + self.start_date == other.start_date && self.end_date == other.end_date + end + + def overlap?(other) #other is another instance of date_range + start_date < other.end_date && other.start_date < end_date + end + + def include?(date) # date is a date + start_date <= date && end_date > date + end + + # def include?(date_range) # other is an instance of date_range + # start_date <= date_range.start_date && end_date > date_range.end_date + # end + + def count_nights + total_nights = (end_date - start_date).to_i + return total_nights + end + end +end diff --git a/lib/hotel_block.rb b/lib/hotel_block.rb new file mode 100644 index 000000000..7ec4178bf --- /dev/null +++ b/lib/hotel_block.rb @@ -0,0 +1,14 @@ +module Hotel + class HotelBlock + attr_reader :date_range , :rooms , :discounted_rate + def initialize(date_range, rooms, discounted_rate) + @date_range = date_range + raise ArgumentError.new("A block can contain a maximum of 5 rooms") if rooms.length > 5 + raise ArgumentError.new("The hotel block cannot be made without having a room") if rooms.empty? + @rooms = rooms + @discounted_rate = 0.1 + end + end +end + + diff --git a/lib/hotel_controller.rb b/lib/hotel_controller.rb new file mode 100644 index 000000000..ffd5d07f8 --- /dev/null +++ b/lib/hotel_controller.rb @@ -0,0 +1,228 @@ +module Hotel + class HotelController + attr_reader :rooms + attr_accessor :reservations, :hotel_blocks + def initialize + @rooms = Array.new(20){|i| Hotel::Room.new(i+1)} + @reservations = [] + @hotel_blocks = [] #contain a number of rooms and a specific set of days + end + + ################# Wave 1 + + # Access the list of all of the rooms in the hotel + def self.rooms + room_list = HotelController.rooms + return room_list + end + + # Access the list of reservations for a specified room and a given date range + def reservations_list_room_and_date(given_room, given_date_range) + reservations_list_of_given_room_date_range = reservations.select do |res| + res.date_range.overlap?(given_date_range) && res.room == given_room + end + return reservations_list_of_given_room_date_range + end + + # Add reservation to the list of the reservations + def add_reservation(reservation) + @reservations << reservation + end + + # Access the list of reservations for a specific date, so that I can track reservations by date + def reservations_list(date) + reservation_list = reservations.select do |res| + res.date_range.include?(date) + end + return reservation_list + end + + # Can get the total cost for a given reservation + def total_cost(reservation) + total_cost = reservation.cost + return total_cost + end + + ##################### Wave 2 + # Get reservations_list for a given date range + def reservations_list_by_date_range(start_date, end_date) + given_date_range = Hotel::DateRange.new(start_date, end_date) + # Get the reservations_list for the given date range + reservations_list_by_date_range = reservations.select do |res| + res.date_range.overlap?(given_date_range) + end + end + + def reserved_rooms_list(start_date, end_date) + given_date_range = Hotel::DateRange.new(start_date, end_date) + # Get reservations_list for a given date range + reservations_list_by_date_range = reservations_list_by_date_range(start_date, end_date) + + # Get all the room that have been reserved for the given date range + all_reserved_rooms = reservations_list_by_date_range.map do |res| + res.room + end + return all_reserved_rooms + end + + # I can view a list of rooms that are not reserved for a given date range, + # so that I can see all available rooms for that day + def available_rooms(start_date, end_date) + given_date_range = Hotel::DateRange.new(start_date, end_date) + + # get all the room that have been reserved for the given date range + all_reserved_rooms = reserved_rooms_list(start_date, end_date) + # get all rooms that are in the hotel blocks + all_rooms_hotel_block = rooms_list_for_hotel_block(start_date, end_date) + + # get the aviable_rooms_list + available_rooms_list = rooms.reject do |room| + all_reserved_rooms.any? {|res_room| res_room.id == room.id} || all_rooms_hotel_block.any? {|block_room| block_room.id == room.id} + end + return available_rooms_list + end + + # I can make a reservation of a room for a given date range, + # and that room will not be part of any other reservation overlapping that date range + # done already in wave 1 + + def reserve_room(start_date, end_date) + given_date_range = Hotel::DateRange.new(start_date, end_date) + avialable_rooms_list = available_rooms(start_date, end_date) + if avialable_rooms_list.empty? + raise NoRoomAvailableError.new("there is no available room.") + end + id = reservations.length + 1 + reservation = Hotel::Reservation.new(id,given_date_range, avialable_rooms_list.first) + add_reservation(reservation) + return reservation + end + + ############## Wave 3 + + # Add hotel_block method + def add_hotel_block(hotel_block) + @hotel_blocks << hotel_block + end + # Get the hotel_block_list for a given date (not exact match date range) + def hotel_block_list(start_date, end_date) + given_date_range = Hotel::DateRange.new(start_date, end_date) + # get the list of the hotel_block for the given date + hotel_block_list = hotel_blocks.select do |hotel_block| + hotel_block.date_range.overlap?(given_date_range) + end + return hotel_block_list + end + + # Get the rooms_list_for_hotel_block + def rooms_list_for_hotel_block(start_date, end_date) + hotel_block_list = hotel_block_list(start_date, end_date) + # get all arrays of rooms from the hotel_block_list for the given date + rooms_hotel_block_arrays = hotel_block_list.map do |hotel_block| + hotel_block.rooms + end + # get all the rooms from the list of + all_rooms_hotel_block = rooms_hotel_block_arrays.flatten + return all_rooms_hotel_block + end + + # Create a hotel_block + def create_hotel_block(date_range, rooms_array, discount_rate) + # all rooms of rooms_array must be available (not in the reservations list) during the given date range + all_reserved_rooms = reserved_rooms_list(date_range.start_date, date_range.end_date) + + # Cannot create another hotel block that any existing block includes that specific room for that specific date + all_rooms_hotel_block = rooms_list_for_hotel_block(date_range.start_date, date_range.end_date) + # an exception raised if I try to create a Hotel Block + # and at least one of the rooms is unavailable for the given date range + + rooms_array.each do |room| + if all_reserved_rooms.any? {|r|r == room} || all_rooms_hotel_block.any? {|r| r == room} + raise ArgumentError.new("There is reservation conflict") + end + end + hotel_block = Hotel::HotelBlock.new(date_range, rooms_array, discount_rate) + add_hotel_block(hotel_block) + return hotel_block + end + + # check whether a given block has any rooms available + def available_rooms_of_block(hotel_block) + if hotel_block.rooms.empty? + raise ArgumentError.new("The hotel block cannot be made without having a room") + else + return hotel_block.rooms + end + end + + # Get the list of the hotel_block for a specific full date range (exact match the full date range) + def hotel_blocks_for_specific_date_range(start_date, end_date) + specific_date_range = Hotel::DateRange.new(start_date, end_date) + hotel_blocks_for_specific_date_range = hotel_blocks.select do |hotel_block| + hotel_block.date_range == specific_date_range + end + return hotel_blocks_for_specific_date_range + end + + # Get available_rooms_of_hotel_blocks for a specific full date range (exact match the full date range) + def available_rooms_of_hotel_blocks(start_date, end_date) + hotel_blocks_for_specific_date_range = hotel_blocks_for_specific_date_range(start_date, end_date) + if hotel_blocks_for_specific_date_range.empty? + raise ArgumentError.new ("no hotel_block for the given date range.") + end + available_rooms_of_hotel_blocks = hotel_blocks_for_specific_date_range.map do |hotel_block| + hotel_block.rooms + end + all_available_rooms_of_hotel_blocks = available_rooms_of_hotel_blocks.flatten + end + + # Get specific hotel_block with a given room, start_date, and end_date + def specific_hotel_block(room, start_date, end_date) + specific_date_range = Hotel::DateRange.new(start_date, end_date) + # Check to see which hotel_block the room lives in (one room can be assigned to only one hotel_block for a specific date range ) + specific_hotel_block_list = hotel_blocks.select do |hotel_block| + hotel_block.date_range == specific_date_range && hotel_block.rooms.any? {|r| r.id == room.id} + end + if specific_hotel_block_list.empty? + raise ArgumentError.new("there is no hotel_block for the given room or date range.") + end + specific_hotel_block = specific_hotel_block_list[0] + return specific_hotel_block + end + + # remove the room from the hotel_block for a specific date_range + def remove_room_from_hotel_block(room, start_date, end_date) + specific_hotel_block = specific_hotel_block(room, start_date, end_date) + specific_hotel_block.rooms.delete(room) + return specific_hotel_block + end + + # remove/delete the whole hotel block + def delete_hotel_block(hotel_block) + hotel_blocks.delete(hotel_block) + end + + # I can reserve a specific room from a hotel block + # I can only reserve that room from a hotel block for the full duration of the block + # I can see a reservation made from a hotel block from the list of reservations for that date (see wave 1 requirements) + def reserve_room_from_hotel_block(room,start_date, end_date) + given_date_range = Hotel::DateRange.new(start_date, end_date) + all_available_rooms_of_hotel_blocks = available_rooms_of_hotel_blocks(start_date, end_date) + if all_available_rooms_of_hotel_blocks.empty? + raise NoRoomAvailableError.new("there is no available room.") + end + if all_available_rooms_of_hotel_blocks.any?(room) + id = reservations.length + 1 + reservation = Hotel::Reservation.new(id,given_date_range,room) + # Add the reservation to the reservation list + add_reservation(reservation) + # remove the room from the all_available_rooms_of_hotel_blocks + hotel_block_after_reservation = remove_room_from_hotel_block(room, start_date, end_date) + if hotel_block_after_reservation.rooms.empty? + delete_hotel_block(hotel_block_after_reservation) + end + end + return reservation + end + end +end \ No newline at end of file diff --git a/lib/no_room_available_error.rb b/lib/no_room_available_error.rb new file mode 100644 index 000000000..cf5c9a7a5 --- /dev/null +++ b/lib/no_room_available_error.rb @@ -0,0 +1,4 @@ +module Hotel + class NoRoomAvailableError < StandardError + end +end \ No newline at end of file diff --git a/lib/reservation.rb b/lib/reservation.rb new file mode 100644 index 000000000..95b3584dd --- /dev/null +++ b/lib/reservation.rb @@ -0,0 +1,18 @@ +require 'date' +module Hotel + class Reservation + attr_reader :id, :room, :date_range + def initialize(id,date_range,room) + unless id.instance_of?(Integer) && id >0 + raise ArgumentError.new("Id must be a positive integer") + end + @id = id + @date_range = date_range + @room = room + end + + def cost + total_cost = date_range.count_nights * 200 + end + end +end diff --git a/lib/room.rb b/lib/room.rb new file mode 100644 index 000000000..c386bf56f --- /dev/null +++ b/lib/room.rb @@ -0,0 +1,19 @@ +module Hotel + class Room + attr_reader :id, :cost + def initialize(id, cost = 200) + unless id.instance_of?(Integer) && id >0 + raise ArgumentError.new("Id must be a positive integer") + end + unless cost.instance_of?(Integer) && cost >= 0 + raise ArgumentError.new("cost must be a positive value") + end + @id = id + @cost = cost + end + + def ==(other) + self.id == other.id + end + end +end \ No newline at end of file diff --git a/refactors.txt b/refactors.txt new file mode 100644 index 000000000..93e63ebc0 --- /dev/null +++ b/refactors.txt @@ -0,0 +1,77 @@ +1. created Room class +2. created Reservation class +3. created DateRange class +4. created HotelController class where rooms and Reservations are the instance attributes of the HotelController + added the following methods in HotelController class: + + # Access the list of all of the rooms in the hotel + def self.rooms + + # Access the list of reservations for a specified room and a given date range + def reservations_list_room_and_date(given_room, given_date_range) + + + # Add reservation to the list of the reservations + def add_reservation(reservation) + + # Access the list of reservations for a specific date, so that I can track reservations by date + def reservations_list(date) + + # Can get the total cost for a given reservation + def total_cost(reservation) + + + ##################### Wave 2 + # Get reservations_list for a given date range + def reservations_list_by_date_range(start_date, end_date) + + def reserved_rooms_list(start_date, end_date) + + # I can view a list of rooms that are not reserved for a given date range, + # so that I can see all available rooms for that day + def available_rooms(start_date, end_date) + + # I can make a reservation of a room for a given date range, + # and that room will not be part of any other reservation overlapping that date range + # done already in wave 1 + + def reserve_room(start_date, end_date) + + ############## Wave 3 + + # Add hotel_block method + def add_hotel_block(hotel_block) + + # Get the hotel_block_list for a given date (not exact match date range) + def hotel_block_list(start_date, end_date) + + + # Get the rooms_list_for_hotel_block + def rooms_list_for_hotel_block(start_date, end_date) + + # Create a hotel_block + def create_hotel_block(date_range, rooms_array, discount_rate) + + # check whether a given block has any rooms available + def available_rooms_of_block(hotel_block) + + # Get the list of the hotel_block for a specific full date range (exact match the full date range) + def hotel_blocks_for_specific_date_range(start_date, end_date) + + # Get available_rooms_of_hotel_blocks for a specific full date range (exact match the full date range) + def available_rooms_of_hotel_blocks(start_date, end_date) + + # Get specific hotel_block with a given room, start_date, and end_date + def specific_hotel_block(room, start_date, end_date) + + # remove the room from the hotel_block for a specific date_range + def remove_room_from_hotel_block(room, start_date, end_date) + + # remove/delete the whole hotel block + def delete_hotel_block(hotel_block) + + # I can reserve a specific room from a hotel block + # I can only reserve that room from a hotel block for the full duration of the block + # I can see a reservation made from a hotel block from the list of reservations for that date (see wave 1 requirements) + def reserve_room_from_hotel_block(room,start_date, end_date) + \ No newline at end of file diff --git a/test/date_range_test.rb b/test/date_range_test.rb new file mode 100644 index 000000000..719d9f105 --- /dev/null +++ b/test/date_range_test.rb @@ -0,0 +1,133 @@ +require_relative "test_helper" + +describe Hotel::DateRange do + describe "consructor" do + it "Can be initialized with two dates" do + start_date = Date.today + end_date = start_date + 3 + + range = Hotel::DateRange.new(start_date, end_date) + + expect(range.start_date).must_equal start_date + expect(range.end_date).must_equal end_date + end + + it "is an an error for negative-lenght ranges" do + start_date = Date.new(2017, 01, 01) + end_date = start_date -2 + expect{Hotel::DateRange.new(start_date, end_date)}.must_raise ArgumentError + end + + it "is an error to create a 0-length range" do + start_date = Date.today + end_date = Date.today + expect{Hotel::DateRange.new(start_date, end_date)}.must_raise ArgumentError + end + end + + describe "overlap?" do + before do + start_date = Date.new(2017, 02, 01) + end_date = start_date + 3 + + @range = Hotel::DateRange.new(start_date, end_date) + end + + it "returns true for the same range" do + start_date = @range.start_date + end_date = @range.end_date + test_range = Hotel::DateRange.new(start_date, end_date) + + expect(@range.overlap?(test_range)).must_equal true + end + + it "returns true for a contained range" do + start_date = Date.new(2017, 01, 20) + end_date = start_date + 30 + test_range = Hotel::DateRange.new(start_date, end_date) + expect(@range.overlap?(test_range)).must_equal true + end + + it "returns true for a range that overlaps in front" do + start_date = Date.new(2017, 01, 20) + end_date = Date.new(2017, 02, 02) + test_range = Hotel::DateRange.new(start_date, end_date) + expect(@range.overlap?(test_range)).must_equal true + end + + it "returns true for a range that overlaps in the back" do + start_date = Date.new(2017, 02, 02) + end_date = start_date + 10 + test_range = Hotel::DateRange.new(start_date, end_date) + expect(@range.overlap?(test_range)).must_equal true + end + + it "returns true for a containing range" do + start_date = Date.new(2017, 02, 02) + end_date = start_date + 1 + test_range = Hotel::DateRange.new(start_date, end_date) + expect(@range.overlap?(test_range)).must_equal true + end + + it "returns false for a range starting on the end_date date" do + start_date = Date.new(2017, 02, 04) + end_date = start_date + 10 + test_range = Hotel::DateRange.new(start_date, end_date) + expect(@range.overlap?(test_range)).must_equal false + end + + it "returns false for a range ending on the start_date date" do + start_date = Date.new(2017, 01, 28) + end_date = Date.new(2017, 02, 01) + test_range = Hotel::DateRange.new(start_date, end_date) + expect(@range.overlap?(test_range)).must_equal false + end + + it "returns false for a range completely before" do + start_date = Date.new(2017, 01, 20) + end_date = start_date + 2 + test_range = Hotel::DateRange.new(start_date, end_date) + expect(@range.overlap?(test_range)).must_equal false + + end + + it "returns false for a date completely after" do + start_date = Date.new(2017, 02, 15) + end_date = start_date + 5 + test_range = Hotel::DateRange.new(start_date, end_date) + expect(@range.overlap?(test_range)).must_equal false + end + end + + describe "include?" do + before do + start_date = Date.new(2017, 02, 01) + end_date = start_date + 3 + @range = Hotel::DateRange.new(start_date, end_date) + end + it "reutrns false if the date is clearly out" do + test_date = Date.new(2017, 03, 01) + expect(@range.include?(test_date)).must_equal false + end + + it "returns true for dates in the range" do + test_date = Date.new(2017, 02, 02) + expect(@range.include?(test_date)).must_equal true + end + + it "returns false for the end_date date" do + test_date = Date.new(2017, 02, 04) + expect(@range.include?(test_date)).must_equal false + end + end + + describe "count_nights" do + it "returns the correct number of nights" do + start_date = Date.new(2017, 02, 01) + end_date = start_date + 3 + range = Hotel::DateRange.new(start_date, end_date) + expect(range.count_nights).must_equal 3 + expect(range.count_nights).must_be_kind_of Integer + end + end +end diff --git a/test/hotel_block_test.rb b/test/hotel_block_test.rb new file mode 100644 index 000000000..e09e8adcc --- /dev/null +++ b/test/hotel_block_test.rb @@ -0,0 +1,35 @@ +require_relative "test_helper" +require 'date' + + +describe Hotel::HotelBlock do + before do + @room1 = Hotel::Room.new(1, 200) + @room2 = Hotel::Room.new(2, 200) + @room3 = Hotel::Room.new(3, 200) + @room4 = Hotel::Room.new(4, 200) + @room5 = Hotel::Room.new(5, 200) + @room6 = Hotel::Room.new(6, 200) + @room_array1 = [@room1, @room2] + @room_array2 = [@room1,@room2,@room3,@room4,@room5,@room6] + @room_array3 = [] + @start_date = Date.today + @end_date = @start_date + 3 + @date_range = Hotel::DateRange.new(@start_date,@end_date) + @discount_rate = 0.1 + end + describe "initailize HotelBlcok" do + it "create an instance " do + hotel_block = Hotel::HotelBlock.new(@date_range,@room_array1,@discount_rate) + expect(hotel_block).must_be_kind_of Hotel::HotelBlock + end + + it "raise ArgumentError when the length of rooms_array is greater than 5" do + expect{Hotel::HotelBlock.new(@date_range,@room_array2,@discount_rate)}.must_raise ArgumentError + end + + it "raise ArgumentError when rooms is an empty array" do + expect{Hotel::HotelBlock.new(@date_range,@room_array3,@discount_rate)}.must_raise ArgumentError + end + end +end \ No newline at end of file diff --git a/test/hotel_controller_test.rb b/test/hotel_controller_test.rb new file mode 100644 index 000000000..b0192f336 --- /dev/null +++ b/test/hotel_controller_test.rb @@ -0,0 +1,547 @@ +require_relative "test_helper" + +describe Hotel::HotelController do + before do + @hotel_controller = Hotel::HotelController.new + @date = Date.parse("2020-08-04") + end + describe "wave 1" do + describe "initializer" do + it "Creates an instance of hotel controler" do + @hotel_controller.must_be_kind_of Hotel::HotelController + end + + it "create the rooms" do + rooms = @hotel_controller.rooms + expect(rooms).must_be_kind_of Array + end + + it "create 20 rooms" do + rooms = @hotel_controller.rooms + expect(rooms.length).must_equal 20 + end + end + + describe "reservations_list_room_and_date" do + it "when there is no reservation, retrun a empty reservation list for a specified room and a given date range" do + room = Hotel::Room.new(1, 200) + start_date = Date.today + end_date = start_date + 3 + date_range = Hotel::DateRange.new(start_date,end_date) + reservations_list = @hotel_controller.reservations_list_room_and_date(room,date_range) + + expect(reservations_list).must_equal [] + end + + it "return a reservation list for a specified room and a given date range" do + # reservation with date_range1 + room = Hotel::Room.new(1, 200) + start_date1 = Date.new(2017, 01, 03) + end_date1 = start_date1 + 3 + date_range1 = Hotel::DateRange.new(start_date1,end_date1) + reservation1 = Hotel::Reservation.new(1, date_range1,room) + @hotel_controller.add_reservation(reservation1) + + # reservation with data_range2 + + start_date2 = Date.new(2017, 01, 10) + end_date2 = start_date2 + 4 + date_range2 = Hotel::DateRange.new(start_date2,end_date2) + reservation2 = Hotel::Reservation.new(1, date_range2,room) + @hotel_controller.add_reservation(reservation2) + + # test_date_range + test_start_date = Date.new(2017, 01, 01) + test_end_date = test_start_date + 30 + test_date_range = Hotel::DateRange.new(test_start_date,test_end_date) + + # we expect to return a list of reservations of the same rooms with different date_ranges + reservations_list_of_given_room_date_range = @hotel_controller.reservations_list_room_and_date(room, test_date_range) + # print reservations_list_of_given_room_date_range + + expect(reservations_list_of_given_room_date_range).must_be_kind_of Array + reservations_list_of_given_room_date_range.each do |res| + res.must_be_kind_of Hotel::Reservation + end + + expect(reservations_list_of_given_room_date_range.length).must_equal 2 + end + end + + describe "add_reservation" do + it "add a new reservation to the reservations list" do + room = @hotel_controller.rooms[0] + start_date1 = Date.new(2017, 01, 03) + end_date1 = start_date1 + 3 + date_range1 = Hotel::DateRange.new(start_date1,end_date1) + reservation1 = Hotel::Reservation.new(1, date_range1,room) + + expect(@hotel_controller.reservations).must_equal [] + @hotel_controller.add_reservation(reservation1) + expect(@hotel_controller.reservations.length).must_equal 1 + end + end + + # Access the list of reservations for a specific date (NOT a DATE RANGE), so that I can track reservations by date + describe "reservations_list" do + it "return a list of reservations for a specific date" do + room = Hotel::Room.new(1, 200) + start_date1 = Date.new(2017, 01, 03) + end_date1 = start_date1 + 3 + date_range1 = Hotel::DateRange.new(start_date1,end_date1) + reservation1 = Hotel::Reservation.new(1, date_range1,room) + @hotel_controller.add_reservation(reservation1) + + # reservation with data_range2 + + start_date2 = Date.new(2017, 01, 10) + end_date2 = start_date2 + 4 + date_range2 = Hotel::DateRange.new(start_date2,end_date2) + reservation2 = Hotel::Reservation.new(1, date_range2,room) + @hotel_controller.add_reservation(reservation2) + + test_date1 = Date.new(2017, 01, 01) + test_date2 = Date.new(2017, 01, 03) + reservation_list1 = @hotel_controller.reservations_list(test_date1) + reservation_list2 = @hotel_controller.reservations_list(test_date2) + + expect(reservation_list1).must_be_kind_of Array + reservation_list1.each do |res| + res.must_be_kind_of Hotel::Reservation + end + expect(reservation_list1.length).must_equal 0 + expect(reservation_list2).must_be_kind_of Array + reservation_list2.each do |res| + res.must_be_kind_of Hotel::Reservation + end + expect(reservation_list2.length).must_equal 1 + end + end + + describe "total_cost" do + it "return a total cost of a given reservation" do + start_date = Date.today + end_date = start_date + 4 + reservation = @hotel_controller.reserve_room(start_date, end_date) + expect(reservation.cost).must_equal 800 + end + end + + # Get reserved_rooms_list for a given date range + describe "reservations_list_by_date_range" do + it "return a reservations_list_by_date_range for a given date range" do + room = Hotel::Room.new(1, 200) + start_date1 = Date.new(2017, 01, 03) + end_date1 = start_date1 + 3 + date_range1 = Hotel::DateRange.new(start_date1,end_date1) + reservation1 = Hotel::Reservation.new(1, date_range1,room) + @hotel_controller.add_reservation(reservation1) + + # reservation with data_range2 + + start_date2 = Date.new(2017, 01, 28) + end_date2 = start_date2 + 11 + date_range2 = Hotel::DateRange.new(start_date2,end_date2) + reservation2 = Hotel::Reservation.new(1, date_range2,room) + @hotel_controller.add_reservation(reservation2) + + test_start_date = Date.new(2017, 01, 01 ) + test_end_date = test_start_date + 30 + reservations_list_by_date_range = @hotel_controller.reservations_list_by_date_range(test_start_date, test_end_date) + + expect(reservations_list_by_date_range).must_be_kind_of Array + reservations_list_by_date_range.each do |res| + res.must_be_kind_of Hotel::Reservation + end + + expect(reservations_list_by_date_range.length).must_equal 2 + end + end + end + + describe "wave 2" do + before do + @start_date = Date.new(2020, 8, 10) + @end_date = @start_date + 4 + @date_range = Hotel::DateRange.new(@start_date,@end_date) + end + + describe "available_rooms" do + before do + @room1 = @hotel_controller.rooms[0] + @room2 = @hotel_controller.rooms[1] + @room3 = @hotel_controller.rooms[2] + @room4 = @hotel_controller.rooms[3] + @room5 = @hotel_controller.rooms[4] + @rooms_array = [@room2, @room3] + @discount_rate = 0.1 + end + it "takes two dates and returns a list of available_rooms" do + reservation = Hotel::Reservation.new(1, @date_range,@room1) + @hotel_controller.add_reservation(reservation) + + test_start_date = Date.new(2020, 8, 01) + test_end_date = test_start_date + 29 + room_list = @hotel_controller.available_rooms(test_start_date, test_end_date) + + expect(room_list).must_be_kind_of Array + expect(room_list.length).must_equal 19 + expect(@hotel_controller.hotel_blocks).must_equal [] + expect(@hotel_controller.hotel_blocks.length).must_equal 0 + end + + it "available_rooms should not already been reserved in reservations or in the hotel block for those specific date range" do + start_date = Date.new(2020, 8, 20) + end_date = start_date + 5 + block_date_range = Hotel::DateRange.new(start_date, end_date) + reservation = Hotel::Reservation.new(1, @date_range,@room1) + @hotel_controller.add_reservation(reservation) + new_hotel_block = @hotel_controller.create_hotel_block(block_date_range, @rooms_array, @discount_rate) + + test_start_date = Date.new(2020, 8, 01) + test_end_date = test_start_date + 29 + room_list = @hotel_controller.available_rooms(test_start_date, test_end_date) + + expect(room_list).must_be_kind_of Array + expect(room_list.length).must_equal 17 + + end + + describe "reserve_room" do + it "takes two Date objects and returns a Reservation" do + reservation = @hotel_controller.reserve_room(@start_date, @end_date) + + expect(reservation).must_be_kind_of Hotel::Reservation + expect(@hotel_controller.reservations.length).must_equal 1 + end + + it "The ability to reserve room when there are reservations with the same start_date and end_date" do + start_date = Date.today + end_date = start_date + 4 + reservation1 = @hotel_controller.reserve_room(start_date, end_date) + reservation2 = @hotel_controller.reserve_room(start_date, end_date) + + expect(@hotel_controller.reservations.length).must_equal 2 + expect(reservation2.room.id).must_equal 2 + end + + it "raise ArgumentError when there is no room available" do + start_date = Date.today + end_date = start_date + 4 + + num_room = 0 + while num_room < 20 do + @hotel_controller.reserve_room(start_date, end_date) + num_room +=1 + end + + expect{(@hotel_controller.reserve_room(start_date, end_date))}.must_raise Hotel::NoRoomAvailableError + end + end + end + + describe "Wave 3" do + before do + @start_date = Date.new(2020, 8, 10) + @end_date = @start_date + 4 + @date_range = Hotel::DateRange.new(@start_date,@end_date) + @room1 = @hotel_controller.rooms[0] + @room2 = @hotel_controller.rooms[1] + @room3 = @hotel_controller.rooms[2] + @room4 = @hotel_controller.rooms[3] + @room5 = @hotel_controller.rooms[4] + @rooms_array = [@room1, @room2] + @rooms_array1 = [@room1, @room2, @room3, @room4] + @rooms_array2 = [@room3, @room4] + @rooms_array_empty = [] + @discount_rate = 0.1 + end + + describe "create_hotel_block" do + it "create a HotelBlock when all the rooms of rooms_array are avaible " do + new_hotel_block = @hotel_controller.create_hotel_block(@date_range, @rooms_array, @discount_rate) + expect(new_hotel_block).must_be_kind_of Hotel::HotelBlock + expect(@hotel_controller.hotel_blocks).must_be_kind_of Array + expect(@hotel_controller.hotel_blocks.length).must_equal 1 + end + + it "raise an ArgumentEorror if at least one of the rooms is unavailable for the given date range - by checking the reservations list " do + # Make a reservation so it will take the first room, so the first room is no longer available + reservation1 = @hotel_controller.reserve_room(@start_date, @end_date) + + # Now, when we try to create a block that has room_id = 1, it will raise ArgumentError + # @room_array contains @room1 which was already reserved for the same date range + expect{(@hotel_controller.create_hotel_block(@date_range, @rooms_array, @discount_rate))}.must_raise ArgumentError + end + it "raise an ArgumentEorror if at least one of the rooms is unavailable for the given date range - by checking the existing hotel_blocks" do + + new_hotel_block1 = @hotel_controller.create_hotel_block(@date_range, @rooms_array, @discount_rate) + # @rooms_array = [@room1, @room2] + # @rooms_array1 = [@room1, @room2, @room3, @room4] + # Cannot create a hotel block that any existing block includes that specific room for that specific date + expect{(@hotel_controller.create_hotel_block(@date_range, @rooms_array1, @discount_rate))}.must_raise ArgumentError + end + end + + describe "add_hotel_block" do + it "add a hotel_block to the hotel_blocks list" do + # before creating the hotel_block, hotel_blocks list is empty + expect(@hotel_controller.hotel_blocks).must_equal [] + + new_hotel_block = @hotel_controller.create_hotel_block(@date_range, @rooms_array, @discount_rate) + expect(@hotel_controller.hotel_blocks.length).must_equal 1 + end + end + + # Get the hotel_block_list for a given date (not exact match date range) + describe "hotel_block_list" do + it "return an empty array of hotel_block_list if there is no hotel_block created" do + test_start_date = Date.new(2020, 8, 01) + test_end_date = test_start_date + 29 + hotel_block_list = @hotel_controller.hotel_block_list(test_start_date, test_end_date) + + expect(hotel_block_list).must_equal [] + end + it "return a hotel_block_list for a given date range" do + start_date2 = Date.new(2020, 8, 16) + end_date2 = start_date2 + 5 + date_range2 = Hotel::DateRange.new(start_date2, end_date2) + new_hotel_block1 = @hotel_controller.create_hotel_block(@date_range, @rooms_array, @discount_rate) + new_hotel_block2 = @hotel_controller.create_hotel_block(date_range2, @rooms_array1, @discount_rate) + + test_start_date = Date.new(2020, 8, 01) + test_end_date = test_start_date + 29 + hotel_block_list = @hotel_controller.hotel_block_list(test_start_date, test_end_date) + + expect(hotel_block_list).must_be_kind_of Array + expect(hotel_block_list.length).must_equal 2 + end + end + + # Get the hotel_block_list for a given date (not exact match date range) + describe "rooms_list_for_hotel_block" do + it "return a room_list_of_hotel_block for a given data range" do + start_date2 = Date.new(2020, 8, 16) + end_date2 = start_date2 + 5 + date_range2 = Hotel::DateRange.new(start_date2, end_date2) + new_hotel_block1 = @hotel_controller.create_hotel_block(@date_range, @rooms_array, @discount_rate) + new_hotel_block2 = @hotel_controller.create_hotel_block(date_range2, @rooms_array1, @discount_rate) + + # serching the room_list_for_hotel_block for the whole month + # we have two hotel_blocks that over lap for the whole month in Aguest + test_start_date = Date.new(2020, 8, 01) + test_end_date = test_start_date + 29 + rooms_list_for_hotel_block = @hotel_controller.rooms_list_for_hotel_block(test_start_date, test_end_date) + + expect(@hotel_controller.hotel_blocks).must_be_kind_of Array + expect(@hotel_controller.hotel_blocks.length).must_equal 2 + expect(rooms_list_for_hotel_block).must_be_kind_of Array + expect(rooms_list_for_hotel_block.length).must_equal 6 + end + end + + describe "available_rooms_of_block" do + it "check whether a given hotel block has any rooms available" do + new_hotel_block = @hotel_controller.create_hotel_block(@date_range, @rooms_array, @discount_rate) + available_rooms_of_block = @hotel_controller.available_rooms_of_block(new_hotel_block) + expect(@hotel_controller.rooms).must_be_kind_of Array + expect(new_hotel_block.rooms.length).must_equal 2 + expect(available_rooms_of_block).must_equal @rooms_array + end + + it "check whether a given block has any rooms available - if @rooms_array is empty print There is no room available" do + + expect{(@hotel_controller.create_hotel_block(@date_range, @rooms_array_empty, @discount_rate))}.must_raise ArgumentError + end + end + + # Get the list of the hotel_block for a specific full date range (exact match the full date range) + describe "hotel_blocks_for_specific_date_range" do + it "return a list of hotel_blocks_for_specific_date_range for the exact match the full date range" do + start_date = Date.new(2020, 8, 16) + end_date = start_date + 5 + date_range = Hotel::DateRange.new(start_date, end_date) + new_hotel_block1 = @hotel_controller.create_hotel_block(date_range, @rooms_array, @discount_rate) + new_hotel_block2 = @hotel_controller.create_hotel_block(date_range, @rooms_array2, @discount_rate) + + test_start_date = Date.new(2020, 8, 16) + test_end_date = test_start_date + 5 + hotel_blocks_for_specific_date_range = @hotel_controller.hotel_blocks_for_specific_date_range(test_start_date, test_end_date) + + expect(hotel_blocks_for_specific_date_range).must_be_kind_of Array + expect(hotel_blocks_for_specific_date_range.length).must_equal 2 + end + + it "return a empty array of list of hotel_blocks_for_specific_date_range for given date_range that doesn't exactly match the full date range of the hotel blocks" do + start_date = Date.new(2020, 8, 16) + end_date = start_date + 5 + date_range = Hotel::DateRange.new(start_date, end_date) + new_hotel_block1 = @hotel_controller.create_hotel_block(date_range, @rooms_array, @discount_rate) + new_hotel_block2 = @hotel_controller.create_hotel_block(date_range, @rooms_array2, @discount_rate) + + test_start_date = Date.new(2020, 8, 16) + test_end_date = test_start_date + 4 + hotel_blocks_for_specific_date_range = @hotel_controller.hotel_blocks_for_specific_date_range(test_start_date, test_end_date) + + expect(hotel_blocks_for_specific_date_range).must_be_kind_of Array + expect(hotel_blocks_for_specific_date_range.length).must_equal 0 + end + end + + # Get available_rooms_of_hotel_blocks for a specific full date range (exact match the full date range) + describe "available_rooms_of_hotel_blocks" do + it "return a available_room_of_hotel_blocks for the exact match of given date range" do + start_date = Date.new(2020, 8, 16) + end_date = start_date + 5 + date_range = Hotel::DateRange.new(start_date, end_date) + new_hotel_block1 = @hotel_controller.create_hotel_block(date_range, @rooms_array, @discount_rate) + new_hotel_block2 = @hotel_controller.create_hotel_block(date_range, @rooms_array2, @discount_rate) + + test_start_date = Date.new(2020, 8, 16) + test_end_date = test_start_date + 5 + hotel_blocks_for_specific_date_range = @hotel_controller.available_rooms_of_hotel_blocks(test_start_date, test_end_date) + + expect(hotel_blocks_for_specific_date_range).must_be_kind_of Array + expect(hotel_blocks_for_specific_date_range.length).must_equal 4 + end + + it "return a empty array of list of hotel_blocks_for_specific_date_range for given date_range that doesn't exactly match the full date range of the hotel blocks" do + start_date = Date.new(2020, 8, 16) + end_date = start_date + 5 + date_range = Hotel::DateRange.new(start_date, end_date) + new_hotel_block1 = @hotel_controller.create_hotel_block(date_range, @rooms_array, @discount_rate) + new_hotel_block2 = @hotel_controller.create_hotel_block(date_range, @rooms_array2, @discount_rate) + + test_start_date = Date.new(2020, 8, 16) + test_end_date = test_start_date + 4 + + expect{(@hotel_controller.available_rooms_of_hotel_blocks(test_start_date, test_end_date))}.must_raise ArgumentError + end + end + + # Get specific hotel_block with a given room, start_date, and end_date + describe "specific_hotel_block" do + it "return specific_hotel_block when we know at least one room of the rooms_array and date_range" do + start_date = Date.new(2020, 8, 16) + end_date = start_date + 5 + date_range = Hotel::DateRange.new(start_date, end_date) + + # @rooms_array = [@room1, @room2] + # @rooms_array2 = [@room3, @room4] + new_hotel_block1 = @hotel_controller.create_hotel_block(date_range, @rooms_array, @discount_rate) + new_hotel_block2 = @hotel_controller.create_hotel_block(date_range, @rooms_array2, @discount_rate) + + specific_hotel_block = @hotel_controller.specific_hotel_block(@room1, start_date, end_date) + expect(specific_hotel_block).must_be_kind_of Hotel::HotelBlock + expect(specific_hotel_block.rooms).must_equal @rooms_array + end + + it "raise ArgumentError when there is no hotel_block found for that given room or date_range" do + start_date = Date.new(2020, 8, 16) + end_date = start_date + 5 + date_range = Hotel::DateRange.new(start_date, end_date) + + # @rooms_array = [@room1, @room2] + # @rooms_array2 = [@room3, @room4] + new_hotel_block1 = @hotel_controller.create_hotel_block(date_range, @rooms_array, @discount_rate) + new_hotel_block2 = @hotel_controller.create_hotel_block(date_range, @rooms_array2, @discount_rate) + + test_start_date = Date.new(2020, 8, 16) + test_end_date = test_start_date + 6 + + expect{(@hotel_controller.specific_hotel_block(@room1, test_start_date, test_end_date))}.must_raise ArgumentError + end + end + + describe "remove_room_from_hotel_block" do + it "the specific room is removed from the list of rooms of the specific hotel_block" do + # @rooms_array = [@room1, @room2] + start_date = Date.new(2020, 8, 16) + end_date = start_date + 5 + date_range = Hotel::DateRange.new(start_date, end_date) + new_hotel_block1 = @hotel_controller.create_hotel_block(date_range, @rooms_array, @discount_rate) + + # @rooms_array1 = [@room1, @room2, @room3, @room4] + start_date2 = Date.new(2020, 8, 10) + end_date2 = start_date2 + 2 + date_range2 = Hotel::DateRange.new(start_date2, end_date2) + new_hotel_block2 = @hotel_controller.create_hotel_block(date_range2, @rooms_array1, @discount_rate) + + # Before removing @room1 + expect(new_hotel_block1).must_be_kind_of Hotel::HotelBlock + expect(new_hotel_block1.rooms).must_equal @rooms_array + expect(new_hotel_block1.rooms.length).must_equal 2 + expect(p new_hotel_block1.rooms).must_equal [@room1, @room2] + + # After removing @room1 + @hotel_controller.remove_room_from_hotel_block(@room1, start_date, end_date) + expect(new_hotel_block1).must_be_kind_of Hotel::HotelBlock + expect(new_hotel_block1.rooms).must_equal @rooms_array + expect(new_hotel_block1.rooms.length).must_equal 1 + expect(p new_hotel_block1.rooms).must_equal [@room2] + + # the removing doesn't impact new_hotel_block2 + expect(new_hotel_block2).must_be_kind_of Hotel::HotelBlock + expect(new_hotel_block2.rooms).must_equal @rooms_array1 + expect(new_hotel_block2.rooms.length).must_equal 4 + expect(p new_hotel_block2.rooms).must_equal [@room1, @room2, @room3, @room4] + end + end + + describe "delete_hotel_block" do + it "delete a hotel_block from hotel_blocks list for given hotel_block" do + # @rooms_array = [@room1, @room2] + start_date = Date.new(2020, 8, 16) + end_date = start_date + 5 + date_range = Hotel::DateRange.new(start_date, end_date) + new_hotel_block1 = @hotel_controller.create_hotel_block(date_range, @rooms_array, @discount_rate) + + # @rooms_array1 = [@room1, @room2, @room3, @room4] + start_date2 = Date.new(2020, 8, 10) + end_date2 = start_date2 + 2 + date_range2 = Hotel::DateRange.new(start_date2, end_date2) + new_hotel_block2 = @hotel_controller.create_hotel_block(date_range2, @rooms_array1, @discount_rate) + + # Before deleting a hotel_block + expect(@hotel_controller.hotel_blocks).must_be_kind_of Array + expect(@hotel_controller.hotel_blocks.length).must_equal 2 + + # After deleting a hotel_block + @hotel_controller.delete_hotel_block(new_hotel_block2) + expect(@hotel_controller.hotel_blocks).must_be_kind_of Array + expect(@hotel_controller.hotel_blocks.length).must_equal 1 + end + end + + describe "reserve_from_hotel_block" do + it "Can reserve a room form a hotel block for a full duration of the block" do + # @rooms_array = [@room1, @room2] + start_date = Date.new(2020, 8, 16) + end_date = start_date + 5 + date_range = Hotel::DateRange.new(start_date, end_date) + new_hotel_block1 = @hotel_controller.create_hotel_block(date_range, @rooms_array, @discount_rate) + + expect(@hotel_controller.reservations).must_equal [] + expect(@hotel_controller.reservations.length).must_equal 0 + expect(@hotel_controller.hotel_blocks[0]).must_be_kind_of Hotel::HotelBlock + expect(@hotel_controller.hotel_blocks.length).must_equal 1 + # reserve @room1 from new_hotel_block1 + @hotel_controller.reserve_room_from_hotel_block(@room1,start_date, end_date) + + # After reserve @room1 from new_hotel_block1 + expect(@hotel_controller.reservations.length).must_equal 1 + expect(@hotel_controller.reservations[0]).must_be_kind_of Hotel::Reservation + expect(@hotel_controller.hotel_blocks[0]).must_be_kind_of Hotel::HotelBlock + expect(@hotel_controller.hotel_blocks.length).must_equal 1 + expect(new_hotel_block1.rooms.length).must_equal 1 # @room 1 was removed from @rooms_array = [@room1, @room2] + + # reserve @room2 from new_hotel_block1 + @hotel_controller.reserve_room_from_hotel_block(@room2,start_date, end_date) + expect(@hotel_controller.reservations.length).must_equal 2 + # since @room1 and @room2 were removed the new_hotel_block1 will be deleted beacuse it's rooms_array is an empty array + expect(@hotel_controller.hotel_blocks.length).must_equal 0 + end + end + end + end +end diff --git a/test/reservation_test.rb b/test/reservation_test.rb new file mode 100644 index 000000000..1d335ca22 --- /dev/null +++ b/test/reservation_test.rb @@ -0,0 +1,34 @@ +require_relative "test_helper" +require 'date' + + +describe Hotel::Reservation do + before do + @room = Hotel::Room.new(1, 200) + @start_date = Date.today + @end_date = @start_date + 3 + @date_range = Hotel::DateRange.new(@start_date,@end_date) + @reservation = Hotel::Reservation.new(1, @date_range,@room) + end + describe "#initialize" do + it 'Create an instance of reservation' do + expect(@reservation).must_be_kind_of Hotel::Reservation + end + + it 'Keep track of reservation_id' do + expect(@reservation.id).must_equal 1 + end + + it "raise ArgumentError when id is not a positive integer" do + expect{(Hotel::Reservation.new("kdjfkasdjfa",@date_range,@room))}.must_raise ArgumentError + end + end + + describe "cost" do + it "returns a number" do + expect(@reservation.room.cost).must_equal 200 + expect(@reservation.cost).must_be_kind_of Numeric + expect(@reservation.cost).must_equal 600 + end + end +end diff --git a/test/room_test.rb b/test/room_test.rb new file mode 100644 index 000000000..c4d210f87 --- /dev/null +++ b/test/room_test.rb @@ -0,0 +1,32 @@ +require_relative "test_helper" +require 'date' + +describe "Room" do + describe "#initialize" do + it 'Create an instance of Room' do + room = Hotel::Room.new(1, 200) + room.must_be_kind_of Hotel::Room + end + + it 'Keeps track of room_id' do + id = 1 + room = Hotel::Room.new(id, 200) + room.must_respond_to :id + room.id.must_equal id + end + + it 'Keeps track of cost' do + id = 1 + cost = 200 + room = Hotel::Room.new(id, cost) + room.must_respond_to :cost + room.cost.must_equal cost + end + + it 'raise ArgumentError when id is a negative number' do + expect{Hotel::Room.new(-2, 200)}.must_raise ArgumentError + end + end +end + + diff --git a/test/test_helper.rb b/test/test_helper.rb index c3a7695cf..fdd35223e 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,8 +1,22 @@ # Add simplecov -require "minitest" -require "minitest/autorun" -require "minitest/reporters" +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/pride' +require 'minitest/skip_dsl' + Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new # require_relative your lib files here! +require_relative '../lib/date_range.rb' +require_relative '../lib/hotel_controller.rb' +require_relative '../lib/reservation.rb' +require_relative '../lib/room.rb' +require_relative '../lib/no_room_available_error.rb' +require_relative '../lib/hotel_block.rb'