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/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..d48a0e4eb --- /dev/null +++ b/lib/date_range.rb @@ -0,0 +1,28 @@ +module Hotel + class DateRange + attr_accessor :start_date, :end_date + + def initialize(start_date, end_date) + # if start date is on or after end Date, raise an exception + if start_date >= end_date + raise ArgumentError, " Invalid Date Range." + + end + + @start_date = start_date + @end_date = end_date + end + # check if date selected overlaps with the date_range + def overlap?(new_start_date, new_end_date) + return(start_date <= new_end_date && end_date >= new_start_date) + end + # check if new_date is between start_date and end_date + def include?(new_date) + return start_date <= new_date && end_date > new_date + end + + def nights + return end_date - start_date + end + end +end diff --git a/lib/hotel_controller.rb b/lib/hotel_controller.rb new file mode 100644 index 000000000..bea58a578 --- /dev/null +++ b/lib/hotel_controller.rb @@ -0,0 +1,73 @@ +module Hotel + class HotelController + # Wave 1 + attr_reader :reservations, :rooms + + # I can access the list of all of the rooms in the hotel + def initialize + @reservations = [] + @rooms = [] #create a list of rooms + + room_number = 1 + 20.times do # 20 rooms from requirements + #20 times... do... + @rooms << room_number + room_number = room_number + 1 + end + end +# access the list of reservations for a specified room and a given date range + def list_reservations_for_room(room, start_date, end_date) + list = [] # returns an array of reservations that falls within the date range + @reservations.each do |reservation| + if room == reservation.room_number + if reservation.date_range.overlap?(start_date, end_date) + list << reservation + end + end + end + return list + end + + def reserve_room(start_date, end_date) + # start_date and end_date should be instances of class Date + # looks through room numbers and get reservations for the date in question until we find one with no reservations + available = find_available_rooms(start_date, end_date) + + # check for available rooms + if available.length > 0 + newly_created_reservation = Reservation.new(start_date, end_date, available[0]) + reservations << newly_created_reservation + return newly_created_reservation + else + raise ArgumentError, "No available rooms." + end + end + # access the list of reservations for a specific date, + # so that it can track reservations by date + def show_reservations_for_date(date) + # go through reservations and add the ones whose range include the given date + list = [] + @reservations.each do |reservation| + if reservation.date_range.include?(date) + list << reservation + end + end + return list + end + + # Wave 2 + def find_available_rooms(start_date, end_date) + # start_date and end_date should be instances of class Date + available_rooms = [] + # iterate through the rooms to find available room + @rooms.each do |room| + reservations = list_reservations_for_room(room, start_date, end_date) + + if(reservations.length() == 0) # rooms that are empty + available_rooms << room + end + end + return available_rooms + end + end +end \ No newline at end of file diff --git a/lib/reservation.rb b/lib/reservation.rb new file mode 100644 index 000000000..7e86acca7 --- /dev/null +++ b/lib/reservation.rb @@ -0,0 +1,19 @@ +module Hotel + class Reservation + attr_reader :date_range, :start_date, :end_date, :room_number + + # initizlize + def initialize(start_date, end_date, room_number) + @date_range = DateRange.new(start_date, end_date) + @room_number = room_number + @start_date = start_date + @end_date = end_date + + end + + # get the total cost for a given reservation + def cost + return @date_range.nights * 200 + end + end +end diff --git a/test/date_range_test.rb b/test/date_range_test.rb new file mode 100644 index 000000000..760486f03 --- /dev/null +++ b/test/date_range_test.rb @@ -0,0 +1,254 @@ +require_relative "test_helper" + +describe Hotel::DateRange do + describe "consructor" do + it "Can be initialized with two dates" do + start_date = Date.new(2017, 01, 01) + 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-length ranges" do + #arrange + start_date = Date.new(2017, 01, 01) + end_date = start_date - 3 + #act #assert + expect {Hotel::DateRange.new(start_date, end_date)}.must_raise ArgumentError + + end + + it "is an error to create a 0-length range" do + #arrange + start_date = Date.new(2017, 01, 01) + end_date = start_date + #Act #assert + expect {Hotel::DateRange.new(start_date, end_date)}.must_raise ArgumentError + end + + it "is an invalid date range if start date is greater than end date" do + #arrange + start_date = Date.new(2017, 01, 04) + end_date = Date.new(2017, 01, 01) + #Act #assert + expect {Hotel::DateRange.new(start_date, end_date)}.must_raise ArgumentError + end + + it "is invalid if start date is same as end date" do + #arrange + start_date = Date.new(2017, 01, 01) + end_date = Date.new(2017, 01, 01) + #Act #assert + expect {Hotel::DateRange.new(start_date, end_date)}.must_raise ArgumentError + end + end + +############################################# + + describe "overlap?" do + before do + start_date = Date.new(2017, 01, 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 + + expect(@range.overlap?(start_date, end_date)).must_equal true + end + + it "return true if date selected overlaps with the date_range" do + #arrange + start_date = Date.new(2017, 01, 01) + end_date = Date.new(2017, 01, 05) + test_start_date = Date.new(2017, 01, 01) + test_end_date = Date.new(2017, 01, 05) + #act + @range = Hotel::DateRange.new(start_date, end_date) + #assert + expect(@range.overlap?(test_start_date, test_end_date)).must_equal true + end + + + it "returns true for a contained range" do + # arrange + start_date = Date.new(2017, 01, 01) + end_date = Date.new(2017, 01, 05) + test_start_date = Date.new(2017, 01, 03) + test_end_date = Date.new(2017, 01, 04) + # act + @range = Hotel::DateRange.new(start_date, end_date) + # assert + expect(@range.overlap?(test_start_date, test_end_date)).must_equal true + end + + it "returns true for a range that overlaps in front" do + # arrange + start_date = Date.new(2017, 01, 01) + end_date = Date.new(2017, 01, 04) + test_start_date = Date.new(2017, 01, 01) + test_end_date = Date.new(2017, 01, 8) + # act + @range = Hotel::DateRange.new(start_date, end_date) + # assert + expect(@range.overlap?(test_start_date, test_end_date)).must_equal true + end + it "returns true for a range that overlaps when the new_start_date is in the middle" do + # arrange + start_date = Date.new(2017, 01, 01) + end_date = Date.new(2017, 01, 04) + test_start_date = Date.new(2017, 01, 03) + test_end_date = Date.new(2017, 01, 8) + # act + @range = Hotel::DateRange.new(start_date, end_date) + # assert + expect(@range.overlap?(test_start_date, test_end_date)).must_equal true + end + + it "returns true for a range that overlaps when the new_end_date is before end date" do + start_date = Date.new(2017, 01, 01) + end_date = Date.new(2017, 01, 03) + test_start_date = Date.new(2016, 12, 30) + test_end_date = Date.new(2017, 01, 03) + # act + @range = Hotel::DateRange.new(start_date, end_date) + # assert + expect(@range.overlap?(test_start_date, test_end_date)).must_equal true + end + + it "returns true for a range that overlaps in the back" do + start_date = Date.new(2017, 01, 01) + end_date = Date.new(2017, 01, 05) + test_start_date = Date.new(2017, 01, 03) + test_end_date = Date.new(2017, 01, 05) + # act + @range = Hotel::DateRange.new(start_date, end_date) + # assert + expect(@range.overlap?(test_start_date, test_end_date)).must_equal true + end + + it "returns true for a containing range" do + start_date = Date.new(2017, 01, 03) + end_date = Date.new(2017, 01, 04) + test_start_date = Date.new(2017, 01, 01) + test_end_date = Date.new(2017, 01, 05) + # act + @range = Hotel::DateRange.new(start_date, end_date) + # assert + expect(@range.overlap?(test_start_date, test_end_date)).must_equal true + end + + it "returns true for a range starting on the end_date date" do + start_date = Date.new(2017, 01, 01) + end_date = Date.new(2017, 01, 05) + test_start_date = Date.new(2017, 01, 05) + test_end_date = Date.new(2017, 01, 8) + # act + @range = Hotel::DateRange.new(start_date, end_date) + # assert + expect(@range.overlap?(test_start_date, test_end_date)).must_equal true + end + + + it "returns true for a range ending on the start_date date" do + start_date = Date.new(2017, 01, 01) + end_date = Date.new(2017, 01, 05) + test_start_date = Date.new(2016, 12, 28) + test_end_date = Date.new(2017, 01, 01) + # act + @range = Hotel::DateRange.new(start_date, end_date) + # assert + expect(@range.overlap?(test_start_date, test_end_date)).must_equal true + end + + it "returns false for a range completely before" do + # arrange + start_date = Date.new(2017, 01, 01) + end_date = Date.new(2017, 01, 03) + test_start_date = Date.new(2016, 12, 25) + test_end_date = Date.new(2016, 12, 30) + # act + @range = Hotel::DateRange.new(start_date, end_date) + # assert + expect(@range.overlap?(test_start_date, test_end_date)).must_equal false + end + + it "returns false for a date completely after" do + # arrange + start_date = Date.new(2017, 01, 01) + end_date = Date.new(2017, 01, 03) + test_start_date = Date.new(2018, 07, 2) + test_end_date = Date.new(2018, 07, 06) + # act + @range = Hotel::DateRange.new(start_date, end_date) + # assert + expect(@range.overlap?(test_start_date, test_end_date)).must_equal false + end + end + + +################################################### + + describe "include?" do + it "returns false if the date is clearly out" do + # arrange + start_date = Date.new(2017, 01, 01) + end_date = Date.new(2017, 01, 04) + test_date = Date.new(2017, 01, 07) + # act + range = Hotel::DateRange.new(start_date, end_date) + does_include = range.include?(test_date) + + # assert + expect(does_include).must_equal false + end + + it "returns true for dates in the range" do + # arrange + start_date = Date.new(2017, 01, 01) + end_date = Date.new(2017, 01, 04) + test_date = Date.new(2017, 01, 02) + # act + range = Hotel::DateRange.new(start_date, end_date) + does_include = range.include?(test_date) + + # assert + expect(does_include).must_equal true + end + + it "returns false for the end_date date" do + # arrange + start_date = Date.new(2017, 01, 01) + end_date = Date.new(2017, 01, 04) + test_date = Date.new(2017, 01, 04) + # act + range = Hotel::DateRange.new(start_date, end_date) + does_include = range.include?(test_date) + + # assert + expect(does_include).must_equal false + end + end + + + describe "nights" do + it "returns the correct number of nights" do + # arrange + start_date = Date.new(2017, 01, 01) + end_date = start_date + 3 + # act + range = Hotel::DateRange.new(start_date, end_date) + nights = range.nights + + # assert + expect(nights).must_equal 3 + 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..2ab57d5de --- /dev/null +++ b/test/hotel_controller_test.rb @@ -0,0 +1,113 @@ +require_relative "test_helper" + +describe Hotel::HotelController do + before do + @hotel_controller = Hotel::HotelController.new + @date = Date.parse("2020-08-04") + end + describe "hotelcontroller" do + describe "rooms" do + it "returns a list" do + rooms = @hotel_controller.rooms + expect(rooms).must_be_kind_of Array + end + end + end + describe "list_reservations_for_room" do + it "returns a list of reservations that fall with in the given date range for a specific room." do + + # arrange : add a new Reservation for room #2 from March 1, March 5 + start_date = Date.new(2017, 03, 01) + end_date = Date.new(2017, 03, 05) + room = 2 + + @hotel_controller.reservations << Hotel::Reservation.new(start_date, end_date, room) + + # arrange : add a new Reservation for room #2 from March 10, March 15 + start_date = Date.new(2017, 03, 10) + end_date = Date.new(2017, 03, 15) + room = 2 + + @hotel_controller.reservations << Hotel::Reservation.new(start_date, end_date, room) + + # arrange : add a new Reservation for room #2 from March 20, March 23 + start_date = Date.new(2017, 03, 20) + end_date = Date.new(2017, 03, 23) + room = 2 + + @hotel_controller.reservations << Hotel::Reservation.new(start_date, end_date, room) + # act + all_found_reservations = @hotel_controller.list_reservations_for_room(2, Date.new(2017, 03, 01), Date.new(2017, 03, 31)) + # assert + expect(all_found_reservations.length).must_equal 3 + end + end + + describe "reserve_room" do + it "takes two Date objects and returns a Reservation" do + # arrange + start_date = @date + end_date = start_date + 3 + # act + reservation = @hotel_controller.reserve_room(start_date, end_date) + # assert + expect(reservation).must_be_kind_of Hotel::Reservation + end + + it "return false if there are no availalbe rooms" do + # arrange + start_date = Date.new(2017, 01, 01) + end_date = Date.new(2017, 02, 01) + + 20.times do # create a list of 20 resereved rooms + @hotel_controller.reserve_room(start_date, end_date) # act + end + + # assert + expect{(@hotel_controller.reserve_room(start_date, end_date))}.must_raise ArgumentError + end + end + + + describe "reservations" do + + it "takes a Date and returns a list of Reservations" do + reservation_list = @hotel_controller.show_reservations_for_date(@date) + + expect(reservation_list).must_be_kind_of Array + reservation_list.each do |res| + res.must_be_kind_of Reservation + end + end + + end + + describe "show_reservations_for_date" do + it "show reservations whose range include the given date" do + #arrange + @hotel_controller.reserve_room(Date.new(2017, 02, 20), Date.new(2017, 03, 03)) + @hotel_controller.reserve_room(Date.new(2017, 03, 01), Date.new(2017, 03, 05)) + + #act + reserved = @hotel_controller.show_reservations_for_date(Date.new(2017, 03, 01)) + + #assert + expect(reserved.length).must_equal 2 + end + + end + + + describe "wave 2" do + describe "find_available_rooms" do + it "takes two dates and returns a list" do + start_date = @date + end_date = start_date + 3 + + room_list = @hotel_controller.find_available_rooms(start_date, end_date) + + expect(room_list).must_be_kind_of Array + end + end + end +end diff --git a/test/reservation_test.rb b/test/reservation_test.rb new file mode 100644 index 000000000..7635a4f3a --- /dev/null +++ b/test/reservation_test.rb @@ -0,0 +1,56 @@ +require_relative "test_helper" + +describe "Hotel::Reservation" do + describe "reservation" do + it "can be initialized with two dates and room number" do + #arrange + start_date = Date.new(2017, 01, 01) + end_date = start_date + 3 + room_number = 1 + #act + reservation = Hotel::Reservation.new(start_date, end_date, room_number) + date_range = Hotel::DateRange.new(start_date, end_date) + + # assert + expect(reservation.start_date).must_equal start_date + expect(reservation.end_date).must_equal end_date + expect(reservation.room_number).must_equal room_number + + end + end +end + +########################## + describe "cost" do + it "returns a number" do + # arrange + start_date = Date.new(2017, 01, 01) + end_date = start_date + 3 + # act + reservation = Hotel::Reservation.new(start_date, end_date, 5) + #arrange + expect(reservation.cost).must_be_kind_of Numeric + end + + it "returns the correct amount for nights spent" do + #arrange + start_date = Date.new(2017, 01, 01) + end_date = start_date + 3 + #act + reservation = Hotel::Reservation.new(start_date, end_date, 4) + #assert + expect(reservation.cost).must_equal 600 + end + + + it "returns total cost for a given date range" do + #arrange + + new_reservation = Hotel::Reservation.new(Date.new(2017,01,01),Date.new(2017,01,03),2) + #act + total_cost = new_reservation.cost + #assert + expect(total_cost).must_equal 400 + end + end + diff --git a/test/test_helper.rb b/test/test_helper.rb index c3a7695cf..52f8012b5 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,8 +1,17 @@ # Add simplecov +require "simplecov" + +SimpleCov.start do + add_filter 'test/' +end require "minitest" require "minitest/autorun" require "minitest/reporters" + Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new # require_relative your lib files here! +require_relative '../lib/date_range' +require_relative '../lib/hotel_controller' +require_relative '../lib/reservation'