quicktravel_client 3.7.0 → 3.8.0

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.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +2 -0
  3. data/CHANGELOG.md +12 -0
  4. data/lib/quick_travel/adapter.rb +13 -25
  5. data/lib/quick_travel/booking.rb +0 -4
  6. data/lib/quick_travel/cache.rb +16 -4
  7. data/lib/quick_travel/checkout.rb +2 -1
  8. data/lib/quick_travel/client.rb +9 -1
  9. data/lib/quick_travel/init_from_hash.rb +4 -0
  10. data/lib/quick_travel/product_configuration.rb +1 -1
  11. data/lib/quick_travel/products/base.rb +4 -4
  12. data/lib/quick_travel/reservation.rb +1 -1
  13. data/lib/quick_travel/resource.rb +1 -1
  14. data/lib/quick_travel/route.rb +1 -1
  15. data/lib/quick_travel/route_stop.rb +8 -1
  16. data/lib/quick_travel/version.rb +1 -1
  17. data/quicktravel_client.gemspec +1 -1
  18. data/spec/adapter_spec.rb +34 -3
  19. data/spec/clients_spec.rb +42 -0
  20. data/spec/passenger_type_spec.rb +1 -1
  21. data/spec/product_configuration_spec.rb +11 -0
  22. data/spec/reservation_spec.rb +20 -0
  23. data/spec/spec_helper.rb +1 -3
  24. data/spec/support/cassettes/accommodation_reserve.yml +12 -4
  25. data/spec/support/cassettes/basic_product_scheduled_trips.yml +3 -1
  26. data/spec/support/cassettes/basic_product_scheduled_trips_multi_sector.yml +3 -1
  27. data/spec/support/cassettes/basic_product_scheduled_trips_unbookable.yml +3 -1
  28. data/spec/support/cassettes/booking_activate.yml +6 -2
  29. data/spec/support/cassettes/booking_cancel.yml +6 -2
  30. data/spec/support/cassettes/booking_create.yml +3 -1
  31. data/spec/support/cassettes/booking_create_accommodation.yml +3 -1
  32. data/spec/support/cassettes/booking_documents.yml +3 -1
  33. data/spec/support/cassettes/booking_non_existant.yml +3 -1
  34. data/spec/support/cassettes/booking_price_changes.yml +3 -1
  35. data/spec/support/cassettes/booking_show.yml +3 -1
  36. data/spec/support/cassettes/booking_update.yml +6 -2
  37. data/spec/support/cassettes/booking_with_documents.yml +6 -2
  38. data/spec/support/cassettes/booking_with_nested_attributes.yml +9 -3
  39. data/spec/support/cassettes/booking_with_price_changes.yml +3 -1
  40. data/spec/support/cassettes/checkout_client_token.yml +3 -1
  41. data/spec/support/cassettes/client_templates.yml +120 -0
  42. data/spec/support/cassettes/countries.yml +3 -1
  43. data/spec/support/cassettes/country_all.yml +3 -1
  44. data/spec/support/cassettes/create_reservation_fail.yml +3 -1
  45. data/spec/support/cassettes/create_reservation_with_booking.yml +3 -1
  46. data/spec/support/cassettes/locations.yml +3 -1
  47. data/spec/support/cassettes/opal_modern_pay_failed_booking.yml +3 -1
  48. data/spec/support/cassettes/opal_modern_pay_failed_create.yml +3 -1
  49. data/spec/support/cassettes/opal_modern_pay_failed_update.yml +3 -1
  50. data/spec/support/cassettes/opal_modern_pay_successful_booking.yml +3 -1
  51. data/spec/support/cassettes/opal_modern_pay_successful_response.yml +3 -1
  52. data/spec/support/cassettes/opal_modern_pay_successful_update_response.yml +3 -1
  53. data/spec/support/cassettes/opal_pay.yml +3 -1
  54. data/spec/support/cassettes/opal_pay_booking.yml +3 -1
  55. data/spec/support/cassettes/passenger_all.yml +3 -1
  56. data/spec/support/cassettes/payment_info.yml +3 -1
  57. data/spec/support/cassettes/price_quote.yml +6 -2
  58. data/spec/support/cassettes/product_date_range_bookability.yml +3 -1
  59. data/spec/support/cassettes/product_show.yml +3 -1
  60. data/spec/support/cassettes/product_show_as_agent.yml +3 -1
  61. data/spec/support/cassettes/product_type_all.yml +3 -1
  62. data/spec/support/cassettes/product_type_resource_categories.yml +3 -1
  63. data/spec/support/cassettes/product_type_resource_categories_tickets.yml +3 -1
  64. data/spec/support/cassettes/product_type_routes.yml +3 -1
  65. data/spec/support/cassettes/property.yml +3 -1
  66. data/spec/support/cassettes/property_types.yml +3 -1
  67. data/spec/support/cassettes/region_show.yml +3 -1
  68. data/spec/support/cassettes/reservation_resource.yml +3 -1
  69. data/spec/support/cassettes/reservation_with_extra_picks.yml +9 -3
  70. data/spec/support/cassettes/resource_category_all.yml +3 -1
  71. data/spec/support/cassettes/resource_category_all_for_product_type_8.yml +3 -1
  72. data/spec/support/cassettes/resource_fare_bases.yml +3 -1
  73. data/spec/support/cassettes/resource_show.yml +3 -1
  74. data/spec/support/cassettes/resource_with_price.yml +3 -1
  75. data/spec/support/cassettes/settings_basic.yml +3 -1
  76. data/spec/support/cassettes/tenant_switcher.yml +6 -2
  77. data/spec/support/cassettes/wrong_url.yml +3 -1
  78. data/spec/support/coverage_loader.rb +1 -1
  79. metadata +10 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b60bf00c1c2d71210f2d1f462986abba8859b7acd216b13d68c8d4ad2c475e9d
4
- data.tar.gz: 79cb1bad6bdd5a2178577824c1196ff9e5728beda74e5474bac7f9e53e63c053
3
+ metadata.gz: f0ed55eb639dbe01ac2b6781edfccbc34b720ea04c6b8239830bf60b86406f5f
4
+ data.tar.gz: 776482e19be6a3df01a7e0f2670fda08d5a1a73d3e4fbc0429867126b2385c7b
5
5
  SHA512:
6
- metadata.gz: 205130ad17115f1f0491351af5b76242a8b67a1f384912708fa4ebad3211d428e83284c41aa8c024d82b1004371415848d47a7fd44bd86fcd375952c5eb68737
7
- data.tar.gz: b0cca6ec2c7b5dd695112ba71d112f7593c8bc174070c380aade2d3ddd5aed23b9a44eec3796c207b65ef3d87c6f6bd2f02ced5c3248e58a505907ddfcb2c1f4
6
+ metadata.gz: '098cd8f377681060a0656ac2759730d816d55b3a7e37484d0577d5644ada764566a32911909ead40a61a5f455d0a52690e4a9a29e5b551af44e4a954141a5cec'
7
+ data.tar.gz: 6efb0debce10413a072952301ba5c8fc3a2826ecd3e67a0d94550b0ff1671105e392f351e8485e29488cb6d56ad1a1be222cdcd6dc302ef887e3d9b57e8304d9
@@ -4,6 +4,8 @@ rvm:
4
4
  - 2.3
5
5
  - 2.4
6
6
  - 2.5
7
+ before_install:
8
+ - gem install bundler --version "1.17.3"
7
9
  script: bundle && bundle exec rake spec
8
10
  gemfile:
9
11
  - gemfiles/rails4.gemfile
@@ -3,6 +3,18 @@ All notable changes to this project will be documented in this file.
3
3
  This project adheres to [Semantic Versioning](http://semver.org/).
4
4
  This changelog adheres to [Keep a CHANGELOG](http://keepachangelog.com/).
5
5
 
6
+ ## [3.8.0]
7
+ ### Added
8
+ - [DC-1794] Update httparty to allow caching the whole response, also add namespace to cache key
9
+ - [DC-1418] Port NRMA portal availability cache back to EcomEngine
10
+ - [TT-4850] Added client templates
11
+ - [DC-1692] Fix error that can be thrown when checking if a product configuration can be priced
12
+ - [TT-6246] Remove Booking.delete_reservations method
13
+
14
+ ### Changed
15
+ - [DC-2997] Remove API key from body, and move it to header
16
+ - [DC-1767] include long/lat changes to Stop
17
+
6
18
  ## [3.7.0]
7
19
  ### Added
8
20
  - [DC-1437] Add relationship accesssor methods
@@ -65,7 +65,7 @@ module QuickTravel
65
65
  def self.all(opts = {})
66
66
  if lookup
67
67
  cache_name = ["#{name}.all-attrs", opts.to_param].reject(&:blank?).join('?')
68
- find_all!("#{api_base}.json", opts.merge(cache: cache_name))
68
+ find_all!("#{api_base}.json", opts.merge(cache_key: cache_name, cache_options: { disable_namespacing: true }))
69
69
  else
70
70
  find_all!("#{api_base}.json", opts)
71
71
  end
@@ -80,10 +80,6 @@ module QuickTravel
80
80
  put_and_validate("#{api_base}/#{id}.json", options)
81
81
  end
82
82
 
83
- def to_hash
84
- instance_values
85
- end
86
-
87
83
  def to_s
88
84
  if defined? @to_s
89
85
  @to_s
@@ -102,20 +98,14 @@ module QuickTravel
102
98
  end
103
99
 
104
100
  def self.find_all!(request_path, opts = {})
105
- response = if opts.key? :cache
106
- QuickTravel::Cache.cache(opts[:cache], opts[:cache_options]) {
107
- get_and_validate(request_path, opts.except(:cache, :cache_options))
108
- }
109
- else
110
- get_and_validate(request_path, opts, return_response_object: true)
111
- end
112
- full_response = response.respond_to? :parsed_response
113
- parsed_response = full_response ? response.parsed_response : response
101
+ response = QuickTravel::Cache.cache(opts[:cache_key], opts[:cache_options]) {
102
+ get_and_validate(request_path, opts.except(:cache_key, :cache_options), return_response_object: true)
103
+ }
114
104
 
115
- deserializer = Deserializer.new(parsed_response)
105
+ deserializer = Deserializer.new(response.parsed_response)
116
106
  objects = Array.wrap(deserializer.extract_under_root(self))
117
107
 
118
- if full_response && response.headers['pagination'].present?
108
+ if response.headers['pagination'].present?
119
109
  pagination_headers = ::JSON.parse(response.headers['pagination'])
120
110
  WillPaginate::Collection.create(pagination_headers['current_page'], pagination_headers['per_page'], pagination_headers['total_entries']) do |pager|
121
111
  pager.replace(objects)
@@ -202,7 +192,11 @@ module QuickTravel
202
192
  end
203
193
 
204
194
  def self.call_and_validate(http_method, path, query = {}, opts = {})
205
- Api.call_and_validate(http_method, path, query, opts)
195
+ response = QuickTravel::Cache.cache(opts[:cache_key], opts[:cache_options]) {
196
+ response_object = Api.call_and_validate(http_method, path, query, opts.except(:cache_key, :cache_options))
197
+ response_object = response_object.parsed_response if !opts[:cache_key] and !opts[:return_response_object]
198
+ response_object
199
+ }
206
200
  end
207
201
 
208
202
  def self.base_uri(uri = nil)
@@ -215,12 +209,11 @@ module QuickTravel
215
209
 
216
210
  def self.call_and_validate(http_method, path, query = {}, opts = {})
217
211
  http_params = opts.clone
218
- return_response_object = http_params.delete(:return_response_object)
219
-
220
212
  # Set default token
221
213
  http_params[:query] ||= FilterQuery.new(query).call
222
214
  http_params[:headers] ||= {}
223
215
  http_params[:headers]['Content-length'] = '0' if http_params[:body].blank?
216
+ http_params[:headers]['x-api-key'] = QuickTravel.config.access_key
224
217
  expect = http_params.delete(:expect)
225
218
 
226
219
  # Use :body instead of :query for put/post.
@@ -230,7 +223,6 @@ module QuickTravel
230
223
  if [:put, :post].include?(http_method.to_sym)
231
224
  http_params[:body].merge!(http_params.delete(:query))
232
225
  end
233
- http_params[:body][:access_key] = QuickTravel.config.access_key
234
226
  http_params[:follow_redirects] = false
235
227
 
236
228
  begin
@@ -256,11 +248,7 @@ module QuickTravel
256
248
 
257
249
  validate!(response)
258
250
 
259
- if return_response_object
260
- response
261
- else
262
- response.parsed_response
263
- end
251
+ response
264
252
  end
265
253
 
266
254
  # Do standard validations on response
@@ -157,10 +157,6 @@ module QuickTravel
157
157
  reserve(:packages, options)
158
158
  end
159
159
 
160
- def delete_reservations
161
- delete_and_validate("#{api_base}/#{@id}/reservations")
162
- end
163
-
164
160
  # Delete a reservation
165
161
  #
166
162
  # Returns current booking object after deleting the reservation
@@ -6,7 +6,10 @@ module QuickTravel
6
6
  end
7
7
  end
8
8
 
9
- def self.cache(key, cache_options = {})
9
+ def self.cache(key, cache_options = nil)
10
+ return yield unless key.present?
11
+ cache_options ||= {}
12
+ key = "#{@@namespace}_#{key}" unless cache_options[:disable_namespacing]
10
13
  cached_value = cache_store.read(key)
11
14
  return cached_value unless cached_value.nil?
12
15
  return nil unless block_given?
@@ -15,7 +18,8 @@ module QuickTravel
15
18
  yield.tap { |value| cache_store.write(key, value, cache_options) }
16
19
  end
17
20
 
18
- def self.delete(key)
21
+ def self.delete(key, namespace = true)
22
+ key = "#{@@namespace}_#{key}" if namespace
19
23
  cache_store.delete(key)
20
24
  end
21
25
 
@@ -27,8 +31,16 @@ module QuickTravel
27
31
  @@cache_store
28
32
  end
29
33
 
30
- def self.cache_store=(store)
31
- @@cache_store = store
34
+ def self.cache_store=(session)
35
+ @@cache_store = session
36
+ end
37
+
38
+ def self.namespace
39
+ @@namespace
40
+ end
41
+
42
+ def self.namespace=(namespace)
43
+ @@namespace = namespace
32
44
  end
33
45
  end
34
46
  end
@@ -46,7 +46,8 @@ module QuickTravel
46
46
  # TODO Move to an external builder?
47
47
  def self.attributes_for
48
48
  attrs = yield
49
- attrs[:completed] = attrs['progress'] == 'completed' || attrs['successful']
49
+ attrs[:completed] = attrs['progress'] == 'completed'
50
+ attrs[:completed] = attrs['successful'] if attrs['successful'].present?
50
51
  attrs[:successful] = attrs[:completed]
51
52
  attrs
52
53
  rescue AdapterError => e
@@ -1,7 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'quick_travel/adapter'
1
4
  require 'quick_travel/init_from_hash'
2
5
 
3
6
  module QuickTravel
4
- class Client
7
+ class Client < Adapter
5
8
  include QuickTravel::InitFromHash
9
+ self.api_base = '/api/clients'
10
+
11
+ def templates
12
+ get_and_validate("/api/clients/#{id}/templates")
13
+ end
6
14
  end
7
15
  end
@@ -28,6 +28,10 @@ module QuickTravel
28
28
  end
29
29
  end
30
30
  end
31
+
32
+ def to_hash
33
+ instance_values
34
+ end
31
35
  end
32
36
 
33
37
  class Parser
@@ -31,7 +31,7 @@ module QuickTravel
31
31
  end
32
32
 
33
33
  def priced?
34
- @product.pricing_details.present?
34
+ @product.try(:pricing_details).present?
35
35
  end
36
36
 
37
37
  def price
@@ -7,13 +7,13 @@ module QuickTravel
7
7
  bookable || exception_type == 'inventory'
8
8
  end
9
9
 
10
- def self.find(search_params = {})
11
- find_for_type(@reservation_for_type, search_params)
10
+ def self.find(search_params = {}, opts = {})
11
+ find_for_type(@reservation_for_type, search_params, opts)
12
12
  end
13
13
 
14
- def self.find_for_type(type, search_params = {})
14
+ def self.find_for_type(type, search_params = {}, opts = {})
15
15
  url = "/reservation_for/#{type}/find_services_for.json"
16
- product_maps = post_and_validate(url, search_params)
16
+ product_maps = post_and_validate(url, search_params, opts)
17
17
  product_maps.map { |product_map| new(product_map) }
18
18
  end
19
19
  end
@@ -42,7 +42,7 @@ module QuickTravel
42
42
  passenger_type_count = {}
43
43
  if passenger_splits.present?
44
44
  passenger_splits.each do |p|
45
- passenger = booking.find_passenger_by_id(p['consumer_id'])
45
+ passenger = booking.find_passenger_by_id(p.consumer_id)
46
46
 
47
47
  if passenger.present?
48
48
  passenger_type_count[passenger.passenger_type_id] ||= 0
@@ -13,7 +13,7 @@ module QuickTravel
13
13
 
14
14
  def self.all_with_price(opts)
15
15
  cache_key = GenerateCacheKey.new(name, opts).call
16
- find_all!("/api/resources/index_with_price.json", opts.merge(cache: cache_key))
16
+ find_all!("/api/resources/index_with_price.json", opts.merge(cache_key: cache_key))
17
17
  end
18
18
 
19
19
  def product_type
@@ -17,7 +17,7 @@ module QuickTravel
17
17
  # All routes for a given product type
18
18
  def self.all(product_type_id)
19
19
  find_all!("/product_types/#{product_type_id}/routes.json",
20
- cache: "QuickTravel::Route.all-#{product_type_id}-attrs")
20
+ cache_key: "QuickTravel::Route.all-#{product_type_id}-attrs")
21
21
  end
22
22
 
23
23
  def self.find(routes_list, route_id)
@@ -5,7 +5,14 @@ module QuickTravel
5
5
  include QuickTravel::InitFromHash
6
6
 
7
7
  def stop
8
- Stop.new({ id: stop_id, name: name, code: code, address: address })
8
+ Stop.new({
9
+ id: stop_id,
10
+ name: name,
11
+ code: code,
12
+ address: address,
13
+ longitude: longitude,
14
+ latitude: latitude
15
+ })
9
16
  end
10
17
  end
11
18
 
@@ -1,3 +1,3 @@
1
1
  module QuickTravel
2
- VERSION = '3.7.0'
2
+ VERSION = '3.8.0'
3
3
  end
@@ -16,7 +16,7 @@ Gem::Specification.new do |spec|
16
16
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
17
17
  spec.require_paths = ['lib']
18
18
 
19
- spec.add_dependency 'httparty', '0.15.7' # Bug in >16 relating to nested parameters
19
+ spec.add_dependency 'httparty', '~> 0.17'
20
20
  spec.add_dependency 'json'
21
21
  spec.add_dependency 'activesupport', '>= 2.0.0'
22
22
  spec.add_dependency 'facets'
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
  require 'quick_travel/adapter'
3
3
 
4
4
  describe QuickTravel::Adapter do
5
- let(:response) { double code: 200, parsed_response: parsed_response }
5
+ let(:response) { double code: 200, parsed_response: parsed_response, headers: {} }
6
6
  let(:parsed_response) { { test: true } }
7
7
 
8
8
  before do
@@ -26,8 +26,7 @@ describe QuickTravel::Adapter do
26
26
  let(:expected_body) {
27
27
  {
28
28
  test: true,
29
- sub_hash: { id: 42 },
30
- access_key: an_instance_of(String)
29
+ sub_hash: { id: 42 }
31
30
  }
32
31
  }
33
32
 
@@ -52,4 +51,36 @@ describe QuickTravel::Adapter do
52
51
  )
53
52
  end
54
53
  end
54
+
55
+ context 'when cache options present' do
56
+ subject(:all) do
57
+ QuickTravel::Adapter.call_and_validate(:get, 'some_path', {}, { cache_key: 'test_key', cache_options: { expires_in: 3.minutes } })
58
+ end
59
+ let(:api) { double }
60
+
61
+ before do
62
+ QuickTravel::Cache.cache_store.clear
63
+ stub_const('QuickTravel::Api', api)
64
+ allow(api).to receive(:call_and_validate) { [{id: 1}, {id: 2}] }
65
+ all
66
+ end
67
+
68
+ specify { expect(api).to have_received(:call_and_validate).once }
69
+
70
+ context 'when called again' do
71
+ before do
72
+ QuickTravel::Adapter.call_and_validate(:get, 'some_path', {}, { cache_key: 'test_key', cache_options: { expires_in: 3.minutes } })
73
+ end
74
+
75
+ specify { expect(api).to have_received(:call_and_validate).once } # not called again
76
+ end
77
+
78
+ context 'when called with different key' do
79
+ before do
80
+ QuickTravel::Adapter.call_and_validate(:get, 'some_path', {}, { cache_key: 'test_key1', cache_options: { expires_in: 3.minutes } })
81
+ end
82
+
83
+ specify { expect(api).to have_received(:call_and_validate).twice }
84
+ end
85
+ end
55
86
  end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'quick_travel/client'
5
+
6
+ describe QuickTravel::Client do
7
+ context '#find' do
8
+ let(:templates) do
9
+ VCR.use_cassette('client_templates') do
10
+ QuickTravel::Client.find(2).templates
11
+ end
12
+ end
13
+ it 'should find the templates correctly' do
14
+ expect(templates['vehicle_templates']).to eq([
15
+ {
16
+ 'id' => 1,
17
+ 'length' => 5.0,
18
+ 'height' => 1.5,
19
+ 'width' => 1.8,
20
+ 'weight' => 1000.0,
21
+ 'details' => 'Holden Commodore',
22
+ 'registration' => 'ABC123',
23
+ 'cargo' => 'None',
24
+ 'vehicle_type_id' => 1,
25
+ 'party_id' => 9
26
+ }
27
+ ])
28
+ expect(templates['passenger_templates']).to eq([
29
+ {
30
+ 'id' => 1,
31
+ 'age' => 30,
32
+ 'title' => 'Mr',
33
+ 'first_name' => 'Homer',
34
+ 'last_name' => 'Simpson',
35
+ 'gender' => 'Male',
36
+ 'passenger_type_id' => 1,
37
+ 'party_id' => 9
38
+ }
39
+ ])
40
+ end
41
+ end
42
+ end
@@ -39,7 +39,7 @@ describe QuickTravel::PassengerType do
39
39
 
40
40
  before do
41
41
  stub_const('QuickTravel::Api', api)
42
- allow(api).to receive(:call_and_validate) { [{id: 1}, {id: 2}] }
42
+ allow(api).to receive(:call_and_validate) { double(parsed_response: [{id: 1}, {id: 2}], headers: {}) }
43
43
  end
44
44
 
45
45
  context 'when called the first time' do
@@ -127,6 +127,17 @@ describe QuickTravel::ProductConfiguration do
127
127
  let(:pricing_details) { nil }
128
128
  it { is_expected.not_to be_priced }
129
129
  end
130
+
131
+ context 'when pricing details are omitted' do
132
+ let(:product) do
133
+ double(
134
+ extras: extras,
135
+ available?: available
136
+ )
137
+ end
138
+
139
+ it { is_expected.not_to be_priced }
140
+ end
130
141
  end
131
142
 
132
143
  context '#price' do
@@ -48,3 +48,23 @@ describe QuickTravel::Reservation do
48
48
  expect(@reservation.gross_including_packaged_item).to be_an_instance_of Money
49
49
  end
50
50
  end
51
+
52
+ describe QuickTravel::Reservation do
53
+ before(:each) do
54
+ VCR.use_cassette('reservation_with_extra_picks') do
55
+ @booking = QuickTravel::Booking.find(1)
56
+ @reservation = @booking.reservations.first
57
+ end
58
+ end
59
+
60
+ it 'should convert hash to objects' do
61
+ expect(@reservation.passenger_splits.first).to be_an_instance_of QuickTravel::PassengerSplit
62
+ end
63
+
64
+ let(:test_type) { double(name: 'test') }
65
+
66
+ it 'should not raise error in passenger count' do
67
+ allow(QuickTravel::PassengerType).to receive(:find).and_return(test_type)
68
+ expect{ @reservation.passengers_count(@booking) }.not_to raise_error
69
+ end
70
+ end