diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 000000000..1ad899dbc Binary files /dev/null and b/.DS_Store differ diff --git a/.gitignore b/.gitignore index 5e1422c9c..932ce5a55 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ *.gem *.rbc /.config -/coverage/ +coverage /InstalledFiles /pkg/ /spec/reports/ diff --git a/Guardfile b/Guardfile index 6760f9177..471693a4d 100644 --- a/Guardfile +++ b/Guardfile @@ -1,5 +1,4 @@ -guard :minitest, bundler: false, rubygems: false do - # with Minitest::Spec +guard :minitest, bundler: false, autorun: false, rubygems: false do # with Minitest::Spec watch(%r{^spec/(.*)_spec\.rb$}) watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" } watch(%r{^spec/spec_helper\.rb$}) { 'spec' } diff --git a/design-activity.md b/design-activity.md new file mode 100644 index 000000000..ad85ce5dc --- /dev/null +++ b/design-activity.md @@ -0,0 +1,42 @@ +**What classes does each implementation include? Are the lists the same?** +They each include ```CartEntry```, ```ShoppingCart```, and ```Order```. The lists are the same. + +**Write down a sentence to describe each class.** +```CartEntry``` is in charge of each item placed into the cart (quantity+price). +```ShoppingCart``` is in charge of all the items placed into the cart (as an array). +```Order``` is in charge of finalizing all pricing details for all the items in the cart. + +**How do the classes relate to each other? It might be helpful to draw a diagram on a whiteboard or piece of paper.** +Each cart item is instantiated via ```CartEntry```. When we instantiate ```Order```, we also instantiate a new instance of ```ShoppingCart``` within the constructor. I imagine in the driver code, we would pass in ```CartEntry``` items to ```Order.cart```when instantiating ```Order```. SO ```ShoppingCart``` has many ```CartEntry``` items -- and ```Order``` has one ```ShoppingCart``` (along with its many ```CartEntry``` items) + +**What data does each class store? How (if at all) does this differ between the two implementations?** +In both, ```CartEntry``` stores ```@unit_price``` and ```@quantity``` of each item. In both, ```ShoppingCart``` stores ```@entries``` (as an array). In both, ```Order``` stores ```@cart``` (an instance of ```ShoppingCart```). There are no differences in state between the two. + +**What methods does each class have? How (if at all) does this differ between the two implementations?** +```CartEntry``` +Implementation A: nothing beyond the constructor. +Implementation B: ```price```, which returns the price for each item entry. +```ShoppingCart``` +Implementation A: nothing beyond the constructor. +Implementation B: ```price```, which returns the subtotal for the entire cart. +```Order``` +Both have ```total_price```, which calculates the total price for the cart, including the sales tax. + +**Consider the Order#total_price method. In each implementation:** +* **Is logic to compute the price delegated to "lower level" classes like ShoppingCart and CartEntry, or is it retained in Order?** +In Implementation A, the logic is relegated only to ```Order```. In Implementation B, the pricing is built in the "lower level" classes first before ```Order``` completes the final calculation. +* **Does total_price directly manipulate the instance variables of other classes?** +In Implementation A, ```total_price``` directly manipulates ```CartEntry```'s instance variables (like ```@price```). +In Implementation B, ```total_price``` only interacts with ```ShoppingCart```'s methods. + +**If we decide items are cheaper if bought in bulk, how would this change the code? Which implementation is easier to modify?** +We would need to change the code in ```CartEntry``` to add a discount for bulk items (likely an optional parameter) and a way to determine if ```quantity``` meets the threshold for the bulk item discount (likely through a conditional). Implementation B would be easier to modify, since you'd really only need to make an edit in one place (```CartEntry```), whereas more work would be needed in Implementation A since it doesn't know anything about a discount or bulk quantity criteria yet those influence the pricing for each item (which Implementation A's ```Order``` directly references). + +**Which implementation better adheres to the single responsibility principle?** +Implementation B. ```Order``` in Implementation A does too much work and knows too much about ```CartEntry``` in particular. + +***Bonus question once you've read Metz ch. 3: Which implementation is more loosely coupled?** +Implementation B (see above). + +Activity +- I will add a method to my Calendar class to calculate the number of nights reserved. Right now, this is something I calculate in Reservation in #total_stay_cost. But because it is date-related, I think I should move it to Calendar. And then I can interact with it in Reservation. I can also interact with it in the RoomBlock class since I calculate costs there. diff --git a/lib/booking_system.rb b/lib/booking_system.rb new file mode 100644 index 000000000..1ff912afb --- /dev/null +++ b/lib/booking_system.rb @@ -0,0 +1,104 @@ +require "date" + +require_relative 'calendar' +require_relative 'reservation' +require_relative 'room_block' + +module Hotel + class BookingSystem + attr_reader :rooms, :reservations, :room_blocks + + def initialize() + @rooms = list_all_rooms() #<-- array of all room numbeers + @reservations = [] + @room_blocks = [] + end + + def list_all_rooms() + return (1..20).to_a + end + + def construct_cal_checker(check_in:, check_out:) + return Calendar.new(check_in: check_in, check_out: check_out) + end + + def generate_res_id() + if @reservations.empty? + return 1 + else + return @reservations.max_by { |reservation| reservation.id}.id + 1 + end + end + + def generate_block_id() + if @room_blocks.empty? + return 1 + else + return @room_blocks.max_by { |block| block.id}.id + 1 + end + end + + def list_res_for_date(check_date) + matching_res = @reservations.select { |reservation| reservation.has_date?(check_date) } + + return matching_res.empty? ? nil : matching_res + end + + def list_avail_rooms_for_range(check_in:, check_out:) + date_range = construct_cal_checker(check_in: check_in, check_out: check_out) + + booked_rooms = @reservations.select { |reservation| reservation.overlap?(date_range) }.map { |reservation| reservation.room_num } + + avail_rooms = @rooms - booked_rooms + + held_rooms = @room_blocks.select { |block| block.overlap?(date_range) }.map {|block| block.rooms} + + if !held_rooms.empty? + held_rooms = held_rooms[0] # list within a list + end + + avail_rooms -= held_rooms + + return avail_rooms.empty? ? nil : avail_rooms + end + + def create_reservation(check_in:, check_out:) + id = generate_res_id() #<-- create new reservation id + avail_rooms = list_avail_rooms_for_range(check_in: check_in, check_out: check_out) #<-- grab first available room + + if avail_rooms.nil? + raise StandardError, "No rooms are available for the given date range: #{check_in} - #{check_out}." + else + avail_room = avail_rooms[0] + end + + new_reservation = Hotel::Reservation.new( + id: id, + room_num: avail_room, + check_in: check_in, + check_out: check_out) + + @reservations << new_reservation + + return new_reservation + end + + def create_room_block(check_in:, check_out:, block_size:) + avail_rooms = list_avail_rooms_for_range(check_in: check_in, check_out: check_out) + + if avail_rooms.nil? || avail_rooms.length < block_size + raise StandardError, "Not enough rooms are available for the given date range: #{check_in} - #{check_out}." + else + avail_room = avail_rooms[0] + end + + hold_rooms = avail_rooms[0..block_size-1] + id = generate_block_id() + + new_room_block = Hotel::RoomBlock.new(id: id, check_in: check_in, check_out: check_out, rooms: hold_rooms) + + @room_blocks << new_room_block + return new_room_block + end + end +end diff --git a/lib/calendar.rb b/lib/calendar.rb new file mode 100644 index 000000000..a363d2cdb --- /dev/null +++ b/lib/calendar.rb @@ -0,0 +1,42 @@ +require 'date' + +module Hotel + class Calendar + attr_reader :check_in, :check_out + + def initialize(check_in:, check_out:) + + unless /\d{4}-\d{1,2}-\d{1,2}/.match(check_in) && /\d{4}-\d{1,2}-\d{1,2}/.match(check_out) + raise StandardError, "Improper date format: date must be entered as YYYY-MM-DD." + end + + @check_in = Date.parse(check_in) + @check_out = Date.parse(check_out) + + unless @check_in < @check_out + raise StandardError, "Invalid date range: end date must occur after start date." + end + end + + def has_date?(other_date) + other_date = Date.parse(other_date) + if (other_date >= @check_in) && (other_date <= @check_out - 1) + return true + else + return false + end + end + + def overlap?(other_dates) # param: Calendar obj + if !(other_dates.check_in <= @check_out-1) || !(other_dates.check_out-1 >= @check_in) + return false + else + return true + end + end + + def nights_reserved() + return @check_out - @check_in + end + end +end diff --git a/lib/reservation.rb b/lib/reservation.rb new file mode 100644 index 000000000..51fc0ac25 --- /dev/null +++ b/lib/reservation.rb @@ -0,0 +1,20 @@ +require_relative 'calendar' + +module Hotel + class Reservation < Calendar + attr_reader :id, :room_num, :daily_rate + + def initialize(check_in:, check_out:, id:, room_num:, daily_rate: 200) + super(check_in: check_in, check_out: check_out) + + @id = id.to_i + @room_num = room_num.to_i + @daily_rate = daily_rate.to_f + + end + + def total_stay_cost() + return @daily_rate * nights_reserved + end + end +end diff --git a/lib/room_block.rb b/lib/room_block.rb new file mode 100644 index 000000000..a682ebabe --- /dev/null +++ b/lib/room_block.rb @@ -0,0 +1,44 @@ +require_relative 'reservation' + +module Hotel + class RoomBlock < Reservation + attr_reader :block_size, :discount, :rooms, :status + + def initialize(id:, daily_rate: 200, check_in:, check_out:, discount:0, rooms:[], status: :available) + super(id: id, daily_rate: daily_rate, check_in: check_in, check_out: check_out, room_num: room_num) + + unless !rooms.empty? + raise StandardError, "Room blocks cannot be empty!" + end + + @discount = discount/100.to_f + @rooms = rooms # array of ints + @status = status + @block_size = rooms.length + + unless @block_size <= 5 && @block_size > 1 + raise StandardError, "Room blocks must hold at least two rooms and at most five. You entered #{block_size} rooms." + end + end + + def show_status() + rooms_hash = {} + @rooms.each do |room| + rooms_hash[room.to_s] = @status + end + return rooms_hash + end + + def discounted_rate() + return @daily_rate * (1-@discount) + end + + def total_stay_cost_room() + return discounted_rate() * nights_reserved + end + + def total_stay_cost_block() + return total_stay_cost_room() * @block_size + end + end +end diff --git a/refactors.txt b/refactors.txt new file mode 100644 index 000000000..c0379eae0 --- /dev/null +++ b/refactors.txt @@ -0,0 +1,21 @@ +Calendar +- add separate eror handling method or class +- spec: add more edge cases: multi month stay? +- in #overlap? maybe we don't need the object (so remove BookingSystem#construct_cal_checker and change date_range checks back to check_in, check_out + +Reservation +- add hundredths place rounding for money +- make @daily_rate a constant? + +RoomBlock +- maybe RoomBlock shouldn't inherit from Reservation b/c I don't use the instance var @room_num in RoomBlock -- but got errors when I didn't include it in super() in initialize() +- like Calendar, this class also has a lot of error handling going on --> split into methods or a class? +- add more functionality (didn't finish wave 3), like maybe check whether a given room number exists within the block? + +BookingSystem +- spec: #initialize: maybe here see if new reservations and room blocks can be added to @reservations and @room_blocks??? Not sure if this is the right place to test that. +- spec: check in #initialize that list_all_rooms() worked +- spec: more testing for #create_reservation?: reject same start-day overlaps, check to see if res are added to @reservations (here?) + +Overall +- Finish Wave 3 diff --git a/spec/booking_system_spec.rb b/spec/booking_system_spec.rb new file mode 100644 index 000000000..e7ea6caf0 --- /dev/null +++ b/spec/booking_system_spec.rb @@ -0,0 +1,314 @@ +require_relative 'spec_helper' + +describe "BookingSystem class" do + let(:booking_system) {Hotel::BookingSystem.new()} + + describe "#initialize" do + it "can create a new instance of BookingSystem" do + expect(booking_system).must_be_kind_of Hotel::BookingSystem + end + + it "creates accurate attribute types" do + expect(booking_system.rooms).must_be_kind_of Array + expect(booking_system.reservations).must_be_kind_of Array + end + end + + describe "#list_all_rooms" do + let(:rooms_list) {booking_system.list_all_rooms()} + + it "can load a list of rooms" do + expect(rooms_list).must_be_kind_of Array + end + + it "accurately loads each instance of room as an integer" do + rooms_list.each do |room| + expect(room).must_be_kind_of Integer + end + end + + it "accurately describes first room" do + first_room = rooms_list[0] + expect(first_room).must_equal 1 + end + + it "accurately describes last room" do + last_room= rooms_list[-1] + expect(last_room).must_equal 20 + end + end + + describe "#construct_cal_checker" do + # see additional constructor tests in Calendar + it "creates a new instance of Calendar" do + booking_system.construct_cal_checker(check_in: "1990-10-01", check_out: "1990-10-15").must_be_kind_of Hotel::Calendar + end + end + + describe "#generate_res_id" do + let(:second_res) {Hotel::Reservation.new(id: 2, room_num: 19, check_in: "1996-09-09", check_out: "1996-08-13")} + let(:third_res) {Hotel::Reservation.new(id: 3, room_num: 20, check_in: "1996-10-01-", check_out: "1996-10-10")} + + it "can create ID for first reservation ID instance" do + new_id = booking_system.generate_res_id() + new_res = Hotel::Reservation.new(id: new_id, room_num: 16, check_in: "1996-08-01", check_out: "1996-08-03") + + booking_system.reservations << new_res + + expect(booking_system.reservations[0].id).must_equal 1 + end + + it "can generate accurate IDs when new reservations are added to reservations list" do + first_res = Hotel::Reservation.new(id: 10, room_num: 19, check_in: "1996-09-09", check_out: "1996-09-13") + booking_system.reservations << first_res + + new_id = booking_system.generate_res_id() + second_res = Hotel::Reservation.new(id: new_id, room_num: 20, check_in: "1996-10-01", check_out: "1996-10-10") + booking_system.reservations << second_res + + expect(booking_system.reservations[1].id).must_equal 11 + end + end + + describe "#generate_block_id" do + let(:block_1) {Hotel::RoomBlock.new(id: 2, rooms: [1,2,3], check_in: "1996-09-09", check_out: "1996-08-13")} + let(:block_2) {Hotel::RoomBlock.new(id: 3, rooms: [1,2,3], check_in: "1996-10-01-", check_out: "1996-10-10")} + + it "can create ID for first room block ID instance" do + new_id = booking_system.generate_block_id() + new_block = Hotel::RoomBlock.new(id: new_id, rooms: [1,2,3], check_in: "1996-08-01", check_out: "1996-08-03") + + booking_system.room_blocks << new_block + + expect(booking_system.room_blocks[0].id).must_equal 1 + end + + it "can generate accurate IDs when new blocks are added to block list" do + first_block = Hotel::RoomBlock.new(id: 10, rooms: [1,2,3], check_in: "1996-09-09", check_out: "1996-09-13") + booking_system.room_blocks << first_block + + new_id = booking_system.generate_block_id() + second_block= Hotel::RoomBlock.new(id: new_id, rooms: [1,2,3], check_in: "1996-10-01", check_out: "1996-10-10") + booking_system.room_blocks << second_block + + expect(booking_system.room_blocks[1].id).must_equal 11 + end + end + + describe "#list_res_for_date" do + # see Calendar#has_date? for additional tests + let(:res_1) {Hotel::Reservation.new( + id: "1", + room_num: "20", + check_in: "2010-8-1", + check_out: "2010-8-10" + )} + + let(:res_2) {Hotel::Reservation.new( + id: "2", + room_num: "19", + check_in: "2010-8-15", + check_out: "2010-8-18" + )} + + let(:res_3) {Hotel::Reservation.new( + id: "4", + room_num: "15", + check_in: "2010-8-4", + check_out: "2010-8-20" + )} + + let(:matching_res) {booking_system.list_res_for_date("2010-8-5")} + + it "should return an array of Reservation objects" do + booking_system.reservations.push(res_1, res_2, res_3) + + expect(matching_res).must_be_kind_of Array + expect(matching_res[0]).must_be_kind_of Hotel::Reservation + end + + it "accurately loads Reservation objects for specified date" do + booking_system.reservations.push(res_1, res_2, res_3) + + expect(matching_res.length).must_equal 2 + expect(matching_res[0].id).must_equal 1 + expect(matching_res[1].id).must_equal 4 + end + + it "returns nil if no Reservations are found for specified date" do + booking_system.reservations.push(res_1, res_2, res_3) + + matching_res = booking_system.list_res_for_date("2010-8-30") + expect(matching_res).must_be_nil + end + end + + describe "#list_avail_rooms_for_range" do + # see Calendar#overlap? for additonal tests + let(:res_1) {Hotel::Reservation.new( + id: "1", + room_num: "18", + check_in: "2010-6-15", + check_out: "2010-6-20" + )} + + let(:res_2) {Hotel::Reservation.new( + id: "2", + room_num: "19", + check_in: "2010-7-15", + check_out: "2010-7-20" + )} + + let(:res_3) {Hotel::Reservation.new( + id: "3", + room_num: "20", + check_in: "2010-8-15", + check_out: "2010-8-20" + )} + + it "returns an array of room numbers if rooms are available" do + booking_system.reservations.push(res_1, res_2, res_3) + + avail_rooms = booking_system.list_avail_rooms_for_range(check_in: "2010-10-15", check_out: "2010-10-26") + + expect(avail_rooms).must_be_kind_of Array + expect(avail_rooms[0]).must_be_kind_of Integer + end + + it "accurately returns a list of available rooms by number if rooms are available" do + booking_system.reservations.push(res_1, res_2, res_3) + + all_rooms = booking_system.rooms + + booked_rooms = [19, 20] + + avail_rooms = booking_system.list_avail_rooms_for_range(check_in: "2010-7-1", check_out: "2010-8-30") + + expect(avail_rooms.length).must_equal 18 + expect(avail_rooms).must_equal (all_rooms - booked_rooms) + end + + it "returns nil if no rooms are available" do + 20.times do |i| + res = Hotel::Reservation.new(id:1, room_num: i+1, check_in: "1999-1-1", check_out: "1999-12-31") + booking_system.reservations << res + end + + all_rooms = booking_system.rooms + + avail_rooms = booking_system.list_avail_rooms_for_range(check_in: "1999-6-1", check_out: "1999-7-1") + + expect(avail_rooms).must_be_nil + end + + it "returns all rooms available if there are no reservations" do + #edge case + avail_rooms = booking_system.list_avail_rooms_for_range(check_in: "2010-8-1", check_out: "2010-8-20") + + all_rooms = (1..20).to_a + + expect(avail_rooms).must_equal all_rooms + end + + it "can take rooms in room blocks into account" do + block = Hotel::RoomBlock.new(id: 1, check_in: "1970-03-04", check_out: "1970-03-15", rooms: [1,2,3]) + booking_system.room_blocks << block + + all_rooms = (1..20).to_a + booked_rooms = block.rooms + + avail_rooms = booking_system.list_avail_rooms_for_range(check_in: "1970-03-01", check_out: "1970-03-07") + + expect(avail_rooms).must_equal all_rooms - booked_rooms + end + end + + describe "#create_reservation" do + it "throws an error when no rooms are available for a given date range" do + 20.times do |i| + res = Hotel::Reservation.new(id:1, room_num: i+1, check_in: "1999-1-1", check_out: "1999-12-31") + booking_system.reservations << res + end + + expect { + booking_system.create_reservation( + check_in: "1999-7-1", + check_out: "1999-7-4")}.must_raise StandardError + end + + it "creates a new reservation successfully with correct data types" do + res_1 = booking_system.create_reservation(check_in: "1992-10-15", check_out: "1992-10-25") + + expect(res_1).must_be_kind_of Hotel::Reservation + expect(res_1.id).must_be_kind_of Integer + expect(res_1.room_num).must_be_kind_of Integer + expect(res_1.check_in).must_be_kind_of Date + expect(res_1.check_out).must_be_kind_of Date + end + + it "accurately adds information to first reservation made" do + res_1 = booking_system.create_reservation(check_in: "1992-10-15", check_out: "1992-10-25") + + expect(res_1.id).must_equal 1 + expect(res_1.room_num).must_equal 1 + expect(res_1.check_in.strftime('%Y %b %d')).must_equal "1992 Oct 15" + expect(res_1.check_out.strftime('%Y %b %d')).must_equal "1992 Oct 25" + end + + it "ignores reservations for the same room room but on other dates" do + res_1 = booking_system.create_reservation(check_in: "1992-10-15", check_out: "1992-10-25") + res_2 = booking_system.create_reservation(check_in: "1992-11-15", check_out: "1992-11-25") + + expect(res_2.id).must_equal 2 + expect(res_2.room_num).must_equal 1 + expect(res_2.check_in.strftime('%Y %b %d')).must_equal "1992 Nov 15" + expect(res_2.check_out.strftime('%Y %b %d')).must_equal "1992 Nov 25" + end + + it "selects the next available room if reservation dates conflict with other reservations" do + res_1 = booking_system.create_reservation(check_in: "1992-10-15", check_out: "1992-10-25") + res_2 = booking_system.create_reservation(check_in: "1992-11-15", check_out: "1992-11-25") + res_3 = booking_system.create_reservation(check_in: "1992-10-15", check_out: "1992-10-25") + + expect(res_3.id).must_equal 3 + expect(res_3.room_num).must_equal 2 + expect(res_3.check_in.strftime('%Y %b %d')).must_equal "1992 Oct 15" + expect(res_3.check_out.strftime('%Y %b %d')).must_equal "1992 Oct 25" + end + end + + describe "#create_room_block" do + let(:room_block) {booking_system.create_room_block( + check_in: "1990-01-01", + check_out: "1990-01-15", + block_size: 2 + )} + + it "throws an error when no rooms are available for a given date range" do + 20.times do |i| + res = Hotel::Reservation.new(id:1, room_num: i+1, check_in: "1999-1-1", check_out: "1999-12-31") + booking_system.reservations << res + end + + expect { + booking_system.create_room_block( + block_size: 4, + check_in: "1999-7-1", + check_out: "1999-7-4")}.must_raise StandardError + end + + it "creates a block using room numbers" do + expect(room_block).must_be_kind_of Hotel::RoomBlock + expect(room_block.rooms).must_be_kind_of Array + expect(room_block.rooms.length).must_equal 2 + expect(room_block.rooms[0]).must_be_kind_of Integer + end + + it "accurately adds information to first room block made" do + expect(room_block.id).must_equal 1 + expect(room_block.rooms.length).must_equal 2 + expect(room_block.check_in.strftime('%Y %b %d')).must_equal "1990 Jan 01" + expect(room_block.check_out.strftime('%Y %b %d')).must_equal "1990 Jan 15" + end + end +end diff --git a/spec/calendar_spec.rb b/spec/calendar_spec.rb new file mode 100644 index 000000000..339a57985 --- /dev/null +++ b/spec/calendar_spec.rb @@ -0,0 +1,173 @@ +require_relative 'spec_helper' + +describe "Calendar" do + let(:cal) {Hotel::Calendar.new(check_in: "1986-07-20", check_out: "1986-07-29")} + + describe "#initialize" do + it "can create a new instance of Calendar" do + expect(cal).must_be_kind_of Hotel::Calendar + end + + it "correctly loads date attributes" do + expect(cal.check_in).must_be_kind_of Date + expect(cal.check_in.strftime('%Y %b %d')).must_equal "1986 Jul 20" + + expect(cal.check_out).must_be_kind_of Date + expect(cal.check_out.strftime('%Y %b %d')).must_equal "1986 Jul 29" + end + + it "throws a StandardError if fed improper date format for start or end date" do + expect { + Hotel::Calendar.new( + check_in: "2009,7,2", + check_out: "2009-7-1" + )}.must_raise StandardError + + expect { + Hotel::Calendar.new({ + check_in: "2009-7-2", + check_out: "2009,7,1" + })}.must_raise StandardError + end + + it "throws a StandardError if end date occurs before or on same day as start date" do + expect { + Hotel::Calendar.new({ + check_in: "2009-7-2", + check_out: "2009-7-1" + })}.must_raise StandardError + + expect { + Hotel::Calendar.new({ + check_in: "2009-7-1", + check_out: "2009-7-1" + })}.must_raise StandardError + end + end + + describe "#has_date?" do + it "returns true if other date is between check in and check out dates: in the middle" do + other_date = "1986-07-25" + + has_date = cal.has_date?(other_date) + + expect(has_date).must_equal true + end + + it "returns true if other date is between check in and check out dates: on check in date" do + other_date = "1986-07-20" + + has_date = cal.has_date?(other_date) + + expect(has_date).must_equal true + end + + it "returns true if other date is between check in and check out dates: on day before check out" do + other_date = "1986-07-28" + + has_date = cal.has_date?(other_date) + + expect(has_date).must_equal true + end + + it "returns false if other date is outside of check in and check out dates: before check in date" do + other_date = "1986-07-15" + + has_date = cal.has_date?(other_date) + + expect(has_date).must_equal false + end + + it "returns false if other date is outside of check in and check out dates: after check out date" do + other_date = "1986-07-31" + + has_date = cal.has_date?(other_date) + + expect(has_date).must_equal false + end + + it "returns false if other date is outside of check in and check out dates: on check out date" do + # edge case + other_date = "1986-07-29" + + has_date = cal.has_date?(other_date) + + expect(has_date).must_equal false + end + end + + describe "#overlap?" do + it "returns false for no overlap: other dates begin and end before reserved dates" do + other_dates = Hotel::Calendar.new(check_in: "1986-07-01", check_out: "1986-07-15") + + dates_overlap = cal.overlap?(other_dates) + + expect(dates_overlap).must_equal false + end + + it "returns false for no overlap: other dates begin and end after reserved dates" do + other_dates = Hotel::Calendar.new(check_in: "1986-07-30", check_out: "1986-07-31") + + dates_overlap = cal.overlap?(other_dates) + + expect(dates_overlap).must_equal false + end + + it "returns false for no overlap: other dates begin during reserved dates' check out date" do + # edge case + other_dates = Hotel::Calendar.new(check_in: "1986-07-29", check_out: "1986-07-31") + + dates_overlap = cal.overlap?(other_dates) + + expect(dates_overlap).must_equal false + end + + it "returns false for no overlap: other dates end during reserved dates' check in date" do + # edge case + other_dates = Hotel::Calendar.new(check_in: "1986-07-15", check_out: "1986-07-20") + + dates_overlap = cal.overlap?(other_dates) + + expect(dates_overlap).must_equal false + end + + it "returns true if dates overlap: other dates begin and end beyond reserved dates range" do + other_dates = Hotel::Calendar.new(check_in: "1986-07-01", check_out: "1986-07-31") + + dates_overlap = cal.overlap?(other_dates) + + expect(dates_overlap).must_equal true + end + + it "returns true if dates overlap: other dates are fully contained within reserved dates" do + other_dates = Hotel::Calendar.new(check_in: "1986-07-25", check_out: "1986-07-28") + + dates_overlap = cal.overlap?(other_dates) + + expect(dates_overlap).must_equal true + end + + it "returns true if dates overlap: other dates begin before but end during reserved dates" do + other_dates = Hotel::Calendar.new(check_in: "1986-07-10", check_out: "1986-07-28") + + dates_overlap = cal.overlap?(other_dates) + + expect(dates_overlap).must_equal true + end + + it "returns true if dates overlap: other dates begin during but end after reserved dates" do + other_dates = Hotel::Calendar.new(check_in: "1986-07-25", check_out: "1986-07-31") + + dates_overlap = cal.overlap?(other_dates) + + expect(dates_overlap).must_equal true + end + end + + describe "#nights_reserved" do + it "returns the number of nights reserved" do + num_nights = cal.check_out - cal.check_in + expect(num_nights).must_equal 9 + end + end + end diff --git a/spec/reservation_spec.rb b/spec/reservation_spec.rb new file mode 100644 index 000000000..dcacb5d60 --- /dev/null +++ b/spec/reservation_spec.rb @@ -0,0 +1,64 @@ +require_relative 'spec_helper' + +describe "Reservation" do + + let(:reservation) {Hotel::Reservation.new( + id: "2", + room_num: "3", + check_in: "2004-7-1", + check_out: "2004-7-4")} + + describe "#initialize" do + it "can create a new instance of reservation" do + expect(reservation).must_be_kind_of Hotel::Reservation + end + + it "correctly loads the initialized datatypes" do + expect(reservation.id).must_be_kind_of Integer + expect(reservation.room_num).must_be_kind_of Integer + expect(reservation.daily_rate).must_be_kind_of Float + end + + it "can create a reservation with a one-night stay" do + # edge case + one_night_stay = Hotel::Reservation.new( + id: "66", + room_num: "19", + check_in:"2009-7-29", + check_out: "2009-7-30") + expect(one_night_stay).must_be_kind_of Hotel::Reservation + expect(one_night_stay.id).must_equal 66 + end + + it "sets daily rate to default value if no value given" do + typical_room = Hotel::Reservation.new( + id: "66", + room_num: "19", + check_in:"2009-7-29", + check_out: "2009-7-30") + + expect(typical_room.daily_rate).must_equal 200 + end + + it "can override default value for daily rate" do + rare_room = Hotel::Reservation.new( + id: "66", + room_num: "19", + check_in:"2009-7-29", + check_out: "2009-7-30", + daily_rate: 500) + + expect(rare_room.daily_rate).must_equal 500 + end + end + + describe "#total_stay_cost" do + it "correctly calculates total cost for a reservation" do + rate = reservation.daily_rate + dates = 3 + correct_cost = rate * dates + + expect(reservation.total_stay_cost).must_equal 600 + end + end +end diff --git a/spec/room_block_spec.rb b/spec/room_block_spec.rb new file mode 100644 index 000000000..d7f706636 --- /dev/null +++ b/spec/room_block_spec.rb @@ -0,0 +1,124 @@ +require_relative 'spec_helper' + +describe "RoomBlock" do + let(:block) {Hotel::RoomBlock.new( + id: "2", + rooms: [1,2,3,4,5], + check_in: "2004-7-1", + check_out: "2004-7-4")} + + describe "#initialize" do + it "can create a new instance of RoomBlock" do + expect(block).must_be_kind_of Hotel::RoomBlock + expect(block.id).must_equal 2 + end + + it "throws an error for an empty list of rooms" do + expect { + Hotel::RoomBlock.new( + id: "2", + rooms: [], + check_in: "2004-7-1", + check_out: "2004-7-4")}.must_raise StandardError + end + + it "throws an error for a single room" do + expect { + Hotel::RoomBlock.new( + id: "2", + rooms: [1], + check_in: "2004-7-1", + check_out: "2004-7-4")}.must_raise StandardError + end + + it "throws an error for too many rooms" do + expect { + Hotel::RoomBlock.new( + id: "2", + rooms: [1,2,3,4,5,6], + check_in: "2004-7-1", + check_out: "2004-7-4")}.must_raise StandardError + end + + it "can create a room block with a one-night stay" do + # edge case + one_night_stay = Hotel::RoomBlock.new( + id: "66", + rooms: [1,2,3], + check_in:"2009-7-29", + check_out: "2009-7-30") + expect(one_night_stay).must_be_kind_of Hotel::RoomBlock + expect(one_night_stay.id).must_equal 66 + end + + it "sets daily rate to default value if no value given" do + expect(block.daily_rate).must_equal 200 + end + + it "sets discount to default value if no value given" do + expect(block.discount).must_equal 0 + end + + it "can override daily rate's default value" do + expensive_stay = Hotel::RoomBlock.new( + id: "66", + rooms: [1,2,3], + check_in:"2009-5-15", + check_out: "2009-7-30", + daily_rate: 500) + + expect(expensive_stay.daily_rate).must_equal 500 + end + end + + describe "#show_status" do + it "returns a hash" do + expect(block.show_status).must_be_kind_of Hash + end + + it "correctly returns first and last values, thanks to Ruby's ordered hashes" do + expect(block.show_status.keys[0]).must_equal "1" + expect(block.show_status.values[0]).must_equal :available + + expect(block.show_status.keys[-1]).must_equal "5" + expect(block.show_status.values[-1]).must_equal :available + end + end + + describe "#discounted_rate" do + it "accurately calculates discounted rate" do + block_room = Hotel::RoomBlock.new( + id: "66", + rooms: [1,2,3], + check_in:"2009-7-29", + check_out: "2009-7-30", + discount: 20) + + expect(block_room.discounted_rate).must_equal 160 + end + end + + describe "#total_stay_cost_room" do + it "accurately calculates the total cost for one room within the block" do + block_room = Hotel::RoomBlock.new( + id: "66", + rooms: [1,2,3], + check_in:"2009-7-29", + check_out: "2009-7-30") + + expect(block_room.total_stay_cost_room).must_equal 200.0 + end + end + + describe "#total_stay_cost_block" do + it "accurately calculates the total cost for one entire block" do + entire_block = Hotel::RoomBlock.new( + id: "66", + rooms: [1,2,3], + check_in:"2009-7-29", + check_out: "2009-7-30") + + expect(entire_block.total_stay_cost_block).must_equal 600 + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 4d1e3fdc8..9bec37e96 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,8 +1,13 @@ +require 'simplecov' +SimpleCov.start + require 'minitest' require 'minitest/autorun' require 'minitest/reporters' -# Add simplecov Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new -# Require_relative your lib files here! +require_relative '../lib/calendar' +require_relative '../lib/reservation' +require_relative '../lib/room_block' +require_relative '../lib/booking_system'