rthbound-suitcase 1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +4 -0
- data/CHANGELOG.md +17 -0
- data/Gemfile +3 -0
- data/README.md +83 -0
- data/Rakefile +13 -0
- data/examples/hash_adapter.rb +15 -0
- data/examples/hotel_image_db.rb +24 -0
- data/examples/redis_adapter.rb +29 -0
- data/lib/suitcase.rb +16 -0
- data/lib/suitcase/car_rental.rb +57 -0
- data/lib/suitcase/codes.rb +5 -0
- data/lib/suitcase/configuration.rb +29 -0
- data/lib/suitcase/core_ext/string.rb +13 -0
- data/lib/suitcase/hotel.rb +355 -0
- data/lib/suitcase/hotel/amenity.rb +46 -0
- data/lib/suitcase/hotel/bed_type.rb +21 -0
- data/lib/suitcase/hotel/cache.rb +52 -0
- data/lib/suitcase/hotel/ean_exception.rb +35 -0
- data/lib/suitcase/hotel/helpers.rb +198 -0
- data/lib/suitcase/hotel/image.rb +19 -0
- data/lib/suitcase/hotel/location.rb +67 -0
- data/lib/suitcase/hotel/nightly_rate.rb +14 -0
- data/lib/suitcase/hotel/payment_option.rb +41 -0
- data/lib/suitcase/hotel/reservation.rb +15 -0
- data/lib/suitcase/hotel/room.rb +138 -0
- data/lib/suitcase/hotel/session.rb +7 -0
- data/lib/suitcase/hotel/surcharge.rb +23 -0
- data/lib/suitcase/version.rb +3 -0
- data/rthbound-suitcase.gemspec +30 -0
- data/test/car_rentals/car_rental_test.rb +30 -0
- data/test/hotels/amenity_test.rb +23 -0
- data/test/hotels/caching_test.rb +42 -0
- data/test/hotels/ean_exception_test.rb +24 -0
- data/test/hotels/helpers_test.rb +52 -0
- data/test/hotels/hotel_location_test.rb +23 -0
- data/test/hotels/hotel_test.rb +112 -0
- data/test/hotels/image_test.rb +20 -0
- data/test/hotels/payment_option_test.rb +15 -0
- data/test/hotels/reservation_test.rb +15 -0
- data/test/hotels/room_test.rb +50 -0
- data/test/hotels/session_test.rb +14 -0
- data/test/keys.rb +43 -0
- data/test/minitest_helper.rb +29 -0
- data/test/support/fake_response.rb +13 -0
- metadata +220 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module Suitcase
|
|
2
|
+
class NightlyRate
|
|
3
|
+
attr_accessor :promo, :rate, :base_rate
|
|
4
|
+
|
|
5
|
+
# Internal: Create a NightlyRate from the API response.
|
|
6
|
+
#
|
|
7
|
+
# info - A Hash from the API response containing nightly rate information.
|
|
8
|
+
def initialize(info)
|
|
9
|
+
@promo = info["@promo"]
|
|
10
|
+
@rate = info["@rate"]
|
|
11
|
+
@base_rate = info["@baseRate"]
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module Suitcase
|
|
2
|
+
class Hotel
|
|
3
|
+
class PaymentOption
|
|
4
|
+
attr_accessor :code, :name
|
|
5
|
+
|
|
6
|
+
extend Helpers
|
|
7
|
+
|
|
8
|
+
# Internal: Create a PaymentOption.
|
|
9
|
+
#
|
|
10
|
+
# code - The String code from the API response (e.g. "VI").
|
|
11
|
+
# name - The String name of the PaymentOption.
|
|
12
|
+
def initialize(code, name)
|
|
13
|
+
@code, @name = code, name
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Public: Find PaymentOptions for a specific currency.
|
|
17
|
+
#
|
|
18
|
+
# info - A Hash of information with one key: :currency_code.
|
|
19
|
+
#
|
|
20
|
+
# Returns an Array of PaymentOption's.
|
|
21
|
+
def self.find(info)
|
|
22
|
+
params = { "currencyCode" => info[:currency_code] }
|
|
23
|
+
|
|
24
|
+
if Configuration.cache? and Configuration.cache.cached?(:paymentInfo, params)
|
|
25
|
+
types_raw = Configuration.cache.get_query(:paymentInfo, params)
|
|
26
|
+
else
|
|
27
|
+
types_raw = parse_response(url(:method => "paymentInfo", :params => params, :session => info[:session]))
|
|
28
|
+
Configuration.cache.save_query(:paymentInfo, params, types_raw) if Configuration.cache?
|
|
29
|
+
end
|
|
30
|
+
update_session(types_raw, info[:session])
|
|
31
|
+
|
|
32
|
+
types_raw["HotelPaymentResponse"].map do |raw|
|
|
33
|
+
types = raw[0] != "PaymentType" ? [] : raw[1]
|
|
34
|
+
types.map do |type|
|
|
35
|
+
PaymentOption.new(type["code"], type["name"])
|
|
36
|
+
end
|
|
37
|
+
end.flatten
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Suitcase
|
|
2
|
+
class Hotel
|
|
3
|
+
class Reservation
|
|
4
|
+
attr_accessor :itinerary_id, :confirmation_numbers, :raw, :surcharges
|
|
5
|
+
|
|
6
|
+
# Internal: Create a new Reservation from the API response.
|
|
7
|
+
#
|
|
8
|
+
# info - The Hash of information returned from the API.
|
|
9
|
+
def initialize(info)
|
|
10
|
+
@itinerary_id, @confirmation_numbers = info[:itinerary_id], [info[:confirmation_numbers]].flatten
|
|
11
|
+
@surcharges = info[:surcharges]
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
module Suitcase
|
|
2
|
+
class Hotel
|
|
3
|
+
class Room
|
|
4
|
+
attr_accessor :rate_key, :hotel_id, :supplier_type, :rate_code,
|
|
5
|
+
:room_type_code, :supplier_type, :tax_rate, :non_refundable,
|
|
6
|
+
:occupancy, :quoted_occupancy, :min_guest_age, :total,
|
|
7
|
+
:surcharge_total, :nightly_rate_total, :average_base_rate,
|
|
8
|
+
:average_rate, :max_nightly_rate, :currency_code, :value_adds,
|
|
9
|
+
:room_type_description, :price_breakdown, :total_price,
|
|
10
|
+
:average_nightly_rate, :promo, :arrival, :departure, :rooms,
|
|
11
|
+
:bed_types, :cancellation_policy, :non_refundable,
|
|
12
|
+
:guarantee_required, :deposit_required, :surcharges,
|
|
13
|
+
:rate_description, :raw, :rate_change, :guarantee_only
|
|
14
|
+
|
|
15
|
+
extend Helpers
|
|
16
|
+
include Helpers
|
|
17
|
+
|
|
18
|
+
# Internal: Create a new Room from within a Room search query.
|
|
19
|
+
#
|
|
20
|
+
# info - A Hash of parsed information from the API, with any of the keys
|
|
21
|
+
# from the attr_accessor's list.
|
|
22
|
+
def initialize(info)
|
|
23
|
+
info.each do |k, v|
|
|
24
|
+
instance_variable_set("@" + k.to_s, v)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Public: Reserve a room.
|
|
29
|
+
#
|
|
30
|
+
# info - A Hash of the information described on the Suitcase
|
|
31
|
+
# [wiki](http://github.com/thoughtfusion/suitcase/wiki/User-flow).
|
|
32
|
+
#
|
|
33
|
+
# Returns a Suitcase::Reservation.
|
|
34
|
+
def reserve!(info)
|
|
35
|
+
params = {}
|
|
36
|
+
params["hotelId"] = @hotel_id
|
|
37
|
+
params["arrivalDate"] = @arrival
|
|
38
|
+
params["departureDate"] = @departure
|
|
39
|
+
params["supplierType"] = @supplier_type
|
|
40
|
+
# Only submit the rateKey if it is a merchant hotel
|
|
41
|
+
params["rateKey"] = @rate_key if @supplier_type == "E"
|
|
42
|
+
params["rateTypeCode"] = @room_type_code
|
|
43
|
+
params["rateCode"] = @rate_code
|
|
44
|
+
params["roomTypeCode"] = @room_type_code
|
|
45
|
+
params["chargeableRate"] = chargeable_rate
|
|
46
|
+
params["email"] = info[:email]
|
|
47
|
+
params["firstName"] = info[:first_name]
|
|
48
|
+
params["lastName"] = info[:last_name]
|
|
49
|
+
params["homePhone"] = info[:home_phone]
|
|
50
|
+
params["workPhone"] = info[:work_phone] if info[:work_phone]
|
|
51
|
+
params["extension"] = info[:work_phone_extension] if info[:work_phone_extension]
|
|
52
|
+
params["faxPhone"] = info[:fax_phone] if info[:fax_phone]
|
|
53
|
+
params["companyName"] = info[:company_name] if info[:company_name]
|
|
54
|
+
if info[:additional_emails]
|
|
55
|
+
params["emailIntineraryList"] = info[:additional_emails].join(",")
|
|
56
|
+
end
|
|
57
|
+
params["creditCardType"] = info[:payment_option].code
|
|
58
|
+
params["creditCardNumber"] = info[:credit_card_number]
|
|
59
|
+
params["creditCardIdentifier"] = info[:credit_card_verification_code]
|
|
60
|
+
expiration_date = Date._parse(info[:credit_card_expiration_date])
|
|
61
|
+
params["creditCardExpirationMonth"] = if expiration_date[:mon] < 10
|
|
62
|
+
"0" + expiration_date[:mon].to_s
|
|
63
|
+
else
|
|
64
|
+
expiration_date[:mon].to_s
|
|
65
|
+
end
|
|
66
|
+
params["creditCardExpirationYear"] = expiration_date[:year].to_s
|
|
67
|
+
params["address1"] = info[:address1]
|
|
68
|
+
params["address2"] = info[:address2] if info[:address2]
|
|
69
|
+
params["address3"] = info[:address3] if info[:address3]
|
|
70
|
+
params["city"] = info[:city]
|
|
71
|
+
@rooms.each_with_index do |room, index|
|
|
72
|
+
index += 1
|
|
73
|
+
params["room#{index}"] = "#{room[:adults].to_s},#{room[:children_ages].join(",")}"
|
|
74
|
+
params["room#{index}FirstName"] = room[:first_name] || params["firstName"] # defaults to the billing
|
|
75
|
+
params["room#{index}LastName"] = room[:last_name] || params["lastName"] # person's name
|
|
76
|
+
params["room#{index}BedTypeId"] = room[:bed_type].id if @supplier_type == "E"
|
|
77
|
+
params["room#{index}SmokingPreference"] = room[:smoking_preference] || "E"
|
|
78
|
+
end
|
|
79
|
+
params["stateProvinceCode"] = info[:province]
|
|
80
|
+
params["countryCode"] = info[:country]
|
|
81
|
+
params["postalCode"] = info[:postal_code]
|
|
82
|
+
|
|
83
|
+
uri = Room.url(
|
|
84
|
+
method: "res",
|
|
85
|
+
params: params,
|
|
86
|
+
include_key: true,
|
|
87
|
+
include_cid: true,
|
|
88
|
+
secure: true
|
|
89
|
+
)
|
|
90
|
+
session = Patron::Session.new
|
|
91
|
+
session.timeout = 30000
|
|
92
|
+
session.base_url = "https://" + uri.host
|
|
93
|
+
res = session.post uri.request_uri, {}
|
|
94
|
+
parsed = JSON.parse res.body
|
|
95
|
+
|
|
96
|
+
reservation_res = parsed["HotelRoomReservationResponse"]
|
|
97
|
+
handle_errors(parsed)
|
|
98
|
+
rate_info = reservation_res["RateInfos"]["RateInfo"]
|
|
99
|
+
surcharges = if @supplier_type == "E" && rate_info["ChargeableRateInfo"]["Surcharges"]
|
|
100
|
+
[rate_info["ChargeableRateInfo"]["Surcharges"]["Surcharge"]].
|
|
101
|
+
flatten.map { |s| Surcharge.parse(s) }
|
|
102
|
+
else
|
|
103
|
+
[]
|
|
104
|
+
end
|
|
105
|
+
r = Reservation.new(
|
|
106
|
+
itinerary_id: reservation_res["itineraryId"],
|
|
107
|
+
confirmation_numbers: reservation_res["confirmationNumbers"],
|
|
108
|
+
surcharges: surcharges
|
|
109
|
+
)
|
|
110
|
+
r.raw = parsed
|
|
111
|
+
r
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Public: The chargeable rate for the Hotel room.
|
|
115
|
+
#
|
|
116
|
+
# Returns the chargeable rate for the Room.
|
|
117
|
+
def chargeable_rate
|
|
118
|
+
if @supplier_type == "E"
|
|
119
|
+
@total_price
|
|
120
|
+
else
|
|
121
|
+
@max_nightly_rate
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Public: The description of the displayed rate.
|
|
126
|
+
#
|
|
127
|
+
# Returns the rate description based on the `rate_change` attribute.
|
|
128
|
+
def room_rate_description
|
|
129
|
+
if @rate_change
|
|
130
|
+
"rate changes during the dates requested and the single nightly rate displayed is the highest nightly rate of the dates requested without taxes and fees."
|
|
131
|
+
else
|
|
132
|
+
"highest single night rate during the dates selected without taxes or fees"
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Suitcase
|
|
2
|
+
class Hotel
|
|
3
|
+
# Public: A Surcharge represents a single surcharge on a Room.
|
|
4
|
+
class Surcharge
|
|
5
|
+
attr_accessor :amount, :type
|
|
6
|
+
# Internal: Create a new Surcharge.
|
|
7
|
+
#
|
|
8
|
+
# info - A Hash of parsed info from Surcharge.parse.
|
|
9
|
+
def initialize(info)
|
|
10
|
+
@amount, @type = info[:amount], info[:type]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Internal: Parse a Surcharge from the room response.
|
|
14
|
+
#
|
|
15
|
+
# info - A Hash of the parsed JSON relevant to the surhcarge.
|
|
16
|
+
#
|
|
17
|
+
# Returns a Surcharge representing the info.
|
|
18
|
+
def self.parse(info)
|
|
19
|
+
new(amount: info["@amount"], type: info["@type"])
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
|
3
|
+
require "suitcase/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |s|
|
|
6
|
+
s.name = "rthbound-suitcase"
|
|
7
|
+
s.version = Suitcase::VERSION
|
|
8
|
+
s.authors = ["Walter Nelson", "Tad Hosford"]
|
|
9
|
+
s.email = ["tad@isotope11.com"]
|
|
10
|
+
s.homepage = "http://github.com/rthbound/suitcase"
|
|
11
|
+
s.summary = %q{Locates available hotels and rental cars through Expedia and Hotwire. This is a fork of Walter Nelson's gem - suitcase}
|
|
12
|
+
s.description = %q{Ruby library that utilizes the EAN (Expedia.com) API for locating available hotels and the Hotwire API for rental cars.}
|
|
13
|
+
|
|
14
|
+
s.files = `git ls-files`.split("\n")
|
|
15
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
16
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
|
17
|
+
s.require_paths = ["lib"]
|
|
18
|
+
|
|
19
|
+
s.add_development_dependency "minitest"
|
|
20
|
+
s.add_development_dependency "mocha"
|
|
21
|
+
s.add_development_dependency "turn"
|
|
22
|
+
|
|
23
|
+
s.add_development_dependency "chronic"
|
|
24
|
+
|
|
25
|
+
s.add_development_dependency "rake"
|
|
26
|
+
s.add_development_dependency "pry"
|
|
27
|
+
|
|
28
|
+
s.add_runtime_dependency "json"
|
|
29
|
+
s.add_runtime_dependency "patron"
|
|
30
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require "minitest_helper"
|
|
2
|
+
|
|
3
|
+
describe Suitcase::CarRental do
|
|
4
|
+
before :each do
|
|
5
|
+
info = {
|
|
6
|
+
destination: "Seattle",
|
|
7
|
+
start_date: "07/04/2012",
|
|
8
|
+
end_date: "07/11/2012",
|
|
9
|
+
pickup_time: "07:30",
|
|
10
|
+
dropoff_time: "11:30"
|
|
11
|
+
}
|
|
12
|
+
@rentals = Suitcase::CarRental.find(info)
|
|
13
|
+
@rental = @rentals.first
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
[:seating, :type_name, :type_code, :possible_features,
|
|
17
|
+
:possible_models].each do |accessor|
|
|
18
|
+
it "has an accessor #{accessor}" do
|
|
19
|
+
@rental.must_respond_to(accessor)
|
|
20
|
+
@rental.must_respond_to((accessor.to_s + "=").to_sym)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
describe ".find" do
|
|
25
|
+
it "returns an Array of CarRental's" do
|
|
26
|
+
@rentals.must_be_kind_of(Array)
|
|
27
|
+
@rental.must_be_kind_of(Suitcase::CarRental)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
require "minitest_helper"
|
|
2
|
+
|
|
3
|
+
describe Suitcase::Hotel::Amenity do
|
|
4
|
+
describe ".parse_mask" do
|
|
5
|
+
describe "when provided bitmask is not nil or 0" do
|
|
6
|
+
it "returns an Array of Symbols representing given Amenities" do
|
|
7
|
+
Suitcase::Hotel::Amenity.parse_mask(5).must_equal [:business_services, :hot_tub]
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
describe "when bitmask is 0" do
|
|
12
|
+
it "returns an empty Array" do
|
|
13
|
+
Suitcase::Hotel::Amenity.parse_mask(0).must_equal []
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
describe "when provided bitmask is nil" do
|
|
18
|
+
it "returns nil" do
|
|
19
|
+
Suitcase::Hotel::Amenity.parse_mask(nil).must_equal nil
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
require "minitest_helper"
|
|
2
|
+
|
|
3
|
+
describe Suitcase do
|
|
4
|
+
before :each do
|
|
5
|
+
Suitcase::Configuration.cache = {}
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
it "caches all non-secure queries" do
|
|
9
|
+
# Query 1
|
|
10
|
+
hotel = Suitcase::Hotel.find(id: 123904)
|
|
11
|
+
|
|
12
|
+
# Query 2
|
|
13
|
+
Suitcase::Hotel.find(location: "Boston, US")
|
|
14
|
+
|
|
15
|
+
# Query 3
|
|
16
|
+
room = hotel.rooms(arrival: Keys::RESERVATION_START_TIME, departure: Keys::RESERVATION_END_TIME).first
|
|
17
|
+
|
|
18
|
+
# Query 4
|
|
19
|
+
Suitcase::Hotel::PaymentOption.find currency_code: "USD"
|
|
20
|
+
|
|
21
|
+
# Query 5, don't cache though
|
|
22
|
+
room.rooms[0][:bed_type] = room.bed_types[0]
|
|
23
|
+
room.rooms[0][:smoking_preference] = "NS"
|
|
24
|
+
room.reserve!(Keys::VALID_RESERVATION_INFO)
|
|
25
|
+
|
|
26
|
+
Suitcase::Configuration.cache.keys.count.must_equal 4
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "retrieves from the cache if it's there" do
|
|
30
|
+
hotel = Suitcase::Hotel.find(id: 123904)
|
|
31
|
+
Suitcase::Hotel.find(location: "Boston, US")
|
|
32
|
+
hotel.rooms(arrival: Keys::RESERVATION_START_TIME, departure: Keys::RESERVATION_END_TIME)
|
|
33
|
+
Suitcase::Hotel::PaymentOption.find currency_code: "USD"
|
|
34
|
+
|
|
35
|
+
# Disable API access
|
|
36
|
+
Net::HTTP.expects(:get_response).never
|
|
37
|
+
hotel = Suitcase::Hotel.find(id: 123904)
|
|
38
|
+
Suitcase::Hotel.find(location: "Boston, US")
|
|
39
|
+
hotel.rooms(arrival: Keys::RESERVATION_START_TIME, departure: Keys::RESERVATION_END_TIME)
|
|
40
|
+
Suitcase::Hotel::PaymentOption.find currency_code: "USD"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require "minitest_helper"
|
|
2
|
+
|
|
3
|
+
describe Suitcase::Hotel::EANException do
|
|
4
|
+
before :each do
|
|
5
|
+
@exception = Suitcase::Hotel::EANException.new(nil)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
it "has an accessor recovery" do
|
|
9
|
+
@exception.must_respond_to(:recovery)
|
|
10
|
+
@exception.must_respond_to(:recovery=)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
describe "#recoverable" do
|
|
14
|
+
it "returns true if the recovery attribute is set" do
|
|
15
|
+
@exception.recovery = { locations: ["London", "New London"] }
|
|
16
|
+
@exception.recoverable?.must_equal(true)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "returns false if the recovery attribute is not set" do
|
|
20
|
+
@exception.recovery = nil
|
|
21
|
+
@exception.recoverable?.must_equal(false)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
require "minitest_helper"
|
|
2
|
+
|
|
3
|
+
describe Suitcase::Hotel::Helpers do
|
|
4
|
+
before :each do
|
|
5
|
+
class Dummy; extend Suitcase::Hotel::Helpers; end
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
describe "#url" do
|
|
9
|
+
it "returns a URI with the proper base URL" do
|
|
10
|
+
url = Dummy.url(method: "action", params: {})
|
|
11
|
+
url.must_be_kind_of(URI)
|
|
12
|
+
url.host.must_match(/api.ean.com/)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
describe "when using digital signature authorization" do
|
|
16
|
+
it "adds a 'sig' parameter" do
|
|
17
|
+
Suitcase::Configuration.expects(:use_signature_auth?).returns(true)
|
|
18
|
+
Dummy.expects(:generate_signature).returns("test")
|
|
19
|
+
|
|
20
|
+
url = Dummy.url(method: "action", params: {})
|
|
21
|
+
url.query.must_match(/sig=test/)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
describe "#parse_response" do
|
|
27
|
+
it "raises an exception when passed an invalid URI" do
|
|
28
|
+
proc do
|
|
29
|
+
Dummy.parse_response(URI.parse("http://google.com"))
|
|
30
|
+
end.must_raise JSON::ParserError
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it "raises an error if a 403 code is received" do
|
|
34
|
+
proc do
|
|
35
|
+
response = FakeResponse.new(code: 403, body: "<h1>An error occurred.</h1>")
|
|
36
|
+
Net::HTTP.stubs(:get_response).returns(response)
|
|
37
|
+
Dummy.parse_response(URI.parse("http://fake.response.will.be.used"))
|
|
38
|
+
end.must_raise Suitcase::Hotel::EANException
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
describe "#generate_signature" do
|
|
43
|
+
it "returns the encrypted API key, shared secret, and timestamp" do
|
|
44
|
+
Suitcase::Configuration.expects(:hotel_api_key).returns("abc")
|
|
45
|
+
Suitcase::Configuration.expects(:hotel_shared_secret).returns("123")
|
|
46
|
+
Time.expects(:now).returns("10")
|
|
47
|
+
|
|
48
|
+
signature = Dummy.generate_signature
|
|
49
|
+
signature.must_equal(Digest::MD5.hexdigest("abc12310"))
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|