cineworld_uk 4.1.0-aarch64-linux-gnu → 4.2.0-aarch64-linux-gnu
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -0
- data/README.md +23 -1
- data/Rakefile +3 -24
- data/cineworld_uk.gemspec +2 -2
- data/lib/cineworld_uk/internal/api_response.rb +7 -30
- data/lib/cineworld_uk/internal/guest_token.rb +9 -52
- data/lib/cineworld_uk/internal/parser/api/film.rb +6 -0
- data/lib/cineworld_uk/internal/parser/api/performance.rb +5 -0
- data/lib/cineworld_uk/internal/title_sanitizer.rb +2 -10
- data/lib/cineworld_uk/performance.rb +4 -1
- data/lib/cineworld_uk/version.rb +2 -2
- data/lib/cineworld_uk.rb +2 -1
- data/test/fixtures/api/showtimes-014.json +67 -0
- data/test/fixtures/api/sites.json +52 -0
- data/test/lib/cineworld_uk/cinema_test.rb +63 -223
- data/test/lib/cineworld_uk/internal/api_response_test.rb +27 -182
- data/test/lib/cineworld_uk/internal/fixture_shape_test.rb +14 -0
- data/test/lib/cineworld_uk/internal/guest_token_test.rb +87 -0
- data/test/lib/cineworld_uk/internal/parser/api/cinema_address_test.rb +44 -85
- data/test/lib/cineworld_uk/internal/parser/api/film_test.rb +53 -131
- data/test/lib/cineworld_uk/internal/parser/api/performance_test.rb +27 -45
- data/test/lib/cineworld_uk/internal/title_sanitizer_test.rb +75 -126
- data/test/lib/cineworld_uk/performance_test.rb +153 -180
- data/test/lib/cineworld_uk/version_test.rb +3 -3
- data/test/live/api_shape_test.rb +22 -0
- data/test/live/integration_test.rb +13 -43
- data/test/live/live_helper.rb +11 -0
- data/test/package_test.rb +14 -7
- data/test/support/api_shapes.rb +48 -0
- data/test/support/vista_fixtures.rb +58 -0
- data/test/test_helper.rb +3 -0
- metadata +33 -28
- data/tasks/package.rake +0 -84
- data/test/fixtures/api/cinema-list.json +0 -1
- data/test/fixtures/api/dates-14.json +0 -1
- data/test/fixtures/api/performances-tomorrow-14.json +0 -1
- data/test/fixtures/api/vista-showtimes-brighton.json +0 -1
- data/test/support/fixture_reader.rb +0 -32
|
@@ -1,249 +1,89 @@
|
|
|
1
1
|
require_relative '../../test_helper'
|
|
2
|
-
require_relative '../../support/fixture_reader'
|
|
3
|
-
|
|
4
|
-
describe CineworldUk::Cinema do
|
|
5
|
-
include Support::FixtureReader
|
|
6
2
|
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
class CineworldUk::CinemaTest < Minitest::Test
|
|
4
|
+
include Support::VistaFixtures
|
|
9
5
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
describe '.all' do
|
|
14
|
-
subject { described_class.all }
|
|
15
|
-
|
|
16
|
-
before { api_response.expect(:cinema_list, cinema_list_json) }
|
|
17
|
-
|
|
18
|
-
it 'returns an Array of CineworldUK::Cinemas' do
|
|
19
|
-
CineworldUk::Internal::ApiResponse.stub :new, api_response do
|
|
20
|
-
_(subject).must_be_instance_of(Array)
|
|
21
|
-
subject.each do |value|
|
|
22
|
-
_(value).must_be_instance_of(CineworldUk::Cinema)
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
it 'returns the correctly sized array' do
|
|
28
|
-
CineworldUk::Internal::ApiResponse.stub :new, api_response do
|
|
29
|
-
_(subject.size).must_equal(87)
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
describe '#adr' do
|
|
35
|
-
subject { described_class.new(id).adr }
|
|
36
|
-
|
|
37
|
-
before do
|
|
38
|
-
api_response.expect(:cinema_list, cinema_list_json)
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
describe 'Brighton (14)' do
|
|
42
|
-
let(:id) { 14 }
|
|
43
|
-
|
|
44
|
-
it 'returns the address hash' do
|
|
45
|
-
CineworldUk::Internal::ApiResponse.stub :new, api_response do
|
|
46
|
-
_(subject).must_equal(
|
|
47
|
-
street_address: 'Brighton Marina Village',
|
|
48
|
-
extended_address: nil,
|
|
49
|
-
locality: 'Brighton',
|
|
50
|
-
region: nil,
|
|
51
|
-
postal_code: 'BN2 5UF',
|
|
52
|
-
country: 'United Kingdom'
|
|
53
|
-
)
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
describe '#address' do
|
|
60
|
-
subject { described_class.new(id).address }
|
|
61
|
-
|
|
62
|
-
before do
|
|
63
|
-
api_response.expect(:cinema_list, cinema_list_json)
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
describe 'Brighton (14)' do
|
|
67
|
-
let(:id) { 14 }
|
|
68
|
-
|
|
69
|
-
it 'returns the address hash' do
|
|
70
|
-
CineworldUk::Internal::ApiResponse.stub :new, api_response do
|
|
71
|
-
_(subject).must_equal(
|
|
72
|
-
street_address: 'Brighton Marina Village',
|
|
73
|
-
extended_address: nil,
|
|
74
|
-
locality: 'Brighton',
|
|
75
|
-
region: nil,
|
|
76
|
-
postal_code: 'BN2 5UF',
|
|
77
|
-
country: 'United Kingdom'
|
|
78
|
-
)
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
describe '#extended_address' do
|
|
85
|
-
subject { described_class.new(id).extended_address }
|
|
86
|
-
|
|
87
|
-
before do
|
|
88
|
-
api_response.expect(:cinema_list, cinema_list_json)
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
describe 'Brighton (14)' do
|
|
92
|
-
let(:id) { 14 }
|
|
93
|
-
|
|
94
|
-
it 'returns the address hash' do
|
|
95
|
-
CineworldUk::Internal::ApiResponse.stub :new, api_response do
|
|
96
|
-
_(subject).must_equal('')
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
end
|
|
6
|
+
def setup
|
|
7
|
+
setup_vista
|
|
8
|
+
stub_sites
|
|
100
9
|
end
|
|
101
10
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
before { api_response.expect(:cinema_list, cinema_list_json) }
|
|
106
|
-
|
|
107
|
-
describe 'simple name (Brighton)' do
|
|
108
|
-
let(:id) { 14 }
|
|
109
|
-
|
|
110
|
-
it 'returns the brand in the name' do
|
|
111
|
-
CineworldUk::Internal::ApiResponse.stub :new, api_response do
|
|
112
|
-
_(subject).must_equal 'Cineworld Brighton'
|
|
113
|
-
end
|
|
114
|
-
end
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
describe 'simple name (Brighton) called with String' do
|
|
118
|
-
let(:id) { '14' }
|
|
119
|
-
|
|
120
|
-
it 'returns the brand in the name' do
|
|
121
|
-
CineworldUk::Internal::ApiResponse.stub :new, api_response do
|
|
122
|
-
_(subject).must_equal 'Cineworld Brighton'
|
|
123
|
-
end
|
|
124
|
-
end
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
describe 'complex name (Glasgow Silverburn)' do
|
|
128
|
-
let(:id) { 88 }
|
|
129
|
-
|
|
130
|
-
it 'returns the brand in the name' do
|
|
131
|
-
CineworldUk::Internal::ApiResponse.stub :new, api_response do
|
|
132
|
-
_(subject).must_equal 'Cineworld Glasgow: Silverburn'
|
|
133
|
-
end
|
|
134
|
-
end
|
|
135
|
-
end
|
|
11
|
+
def teardown
|
|
12
|
+
teardown_vista
|
|
13
|
+
super
|
|
136
14
|
end
|
|
137
15
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
before do
|
|
142
|
-
api_response.expect(:cinema_list, cinema_list_json)
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
describe 'Brighton (14)' do
|
|
146
|
-
let(:id) { 14 }
|
|
147
|
-
|
|
148
|
-
it 'returns the town/city' do
|
|
149
|
-
CineworldUk::Internal::ApiResponse.stub :new, api_response do
|
|
150
|
-
_(subject).must_equal 'Brighton'
|
|
151
|
-
end
|
|
152
|
-
end
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
describe 'London - Leicester Square (103)' do
|
|
156
|
-
let(:id) { 103 }
|
|
16
|
+
def test_all_returns_cinemas
|
|
17
|
+
cinemas = CineworldUk::Cinema.all
|
|
157
18
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
end
|
|
162
|
-
end
|
|
163
|
-
end
|
|
164
|
-
end
|
|
19
|
+
assert(cinemas.all? { |cinema| cinema.instance_of?(CineworldUk::Cinema) })
|
|
20
|
+
assert_equal [14, 88, 103, 999], cinemas.map(&:id)
|
|
21
|
+
end
|
|
165
22
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
before do
|
|
170
|
-
api_response.expect(:cinema_list, cinema_list_json)
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
describe 'Brighton (14)' do
|
|
174
|
-
let(:id) { 14 }
|
|
23
|
+
def test_all_fetches_sites_once
|
|
24
|
+
CineworldUk::Cinema.all
|
|
25
|
+
CineworldUk::Cinema.new(14).name
|
|
175
26
|
|
|
176
|
-
|
|
177
|
-
CineworldUk::Internal::ApiResponse.stub :new, api_response do
|
|
178
|
-
_(subject).must_equal 'BN2 5UF'
|
|
179
|
-
end
|
|
180
|
-
end
|
|
181
|
-
end
|
|
27
|
+
assert_requested :get, "#{BASE_URL}sites", times: 1
|
|
182
28
|
end
|
|
183
29
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
30
|
+
def test_adr
|
|
31
|
+
expected = {
|
|
32
|
+
street_address: '1 Example Marina',
|
|
33
|
+
extended_address: nil,
|
|
34
|
+
locality: 'Brighton',
|
|
35
|
+
region: 'East Sussex',
|
|
36
|
+
postal_code: 'BN2 0ZZ',
|
|
37
|
+
country: 'United Kingdom'
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
assert_equal expected, CineworldUk::Cinema.new(14).adr
|
|
41
|
+
assert_equal expected, CineworldUk::Cinema.new(14).address
|
|
42
|
+
end
|
|
190
43
|
|
|
191
|
-
|
|
192
|
-
|
|
44
|
+
def test_extended_address_is_empty
|
|
45
|
+
assert_equal '', CineworldUk::Cinema.new(14).extended_address
|
|
46
|
+
end
|
|
193
47
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
end
|
|
198
|
-
end
|
|
199
|
-
end
|
|
48
|
+
def test_full_name
|
|
49
|
+
assert_equal 'Cineworld Brighton', CineworldUk::Cinema.new(14).full_name
|
|
50
|
+
end
|
|
200
51
|
|
|
201
|
-
|
|
202
|
-
|
|
52
|
+
def test_full_name_accepts_string_id
|
|
53
|
+
assert_equal 'Cineworld Brighton', CineworldUk::Cinema.new('14').full_name
|
|
54
|
+
end
|
|
203
55
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
end
|
|
208
|
-
end
|
|
209
|
-
end
|
|
210
|
-
end
|
|
211
|
-
|
|
212
|
-
describe '#street_address' do
|
|
213
|
-
subject { described_class.new(id).street_address }
|
|
56
|
+
def test_full_name_with_multiple_words
|
|
57
|
+
assert_equal 'Cineworld Glasgow Silverburn', CineworldUk::Cinema.new(88).full_name
|
|
58
|
+
end
|
|
214
59
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
60
|
+
def test_name_replaces_dash_separator
|
|
61
|
+
assert_equal 'Example: Riverside', CineworldUk::Cinema.new(999).name
|
|
62
|
+
end
|
|
218
63
|
|
|
219
|
-
|
|
220
|
-
|
|
64
|
+
def test_locality
|
|
65
|
+
assert_equal 'Brighton', CineworldUk::Cinema.new(14).locality
|
|
66
|
+
assert_equal 'London', CineworldUk::Cinema.new(103).locality
|
|
67
|
+
end
|
|
221
68
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
_(subject).must_equal 'Brighton Marina Village'
|
|
225
|
-
end
|
|
226
|
-
end
|
|
227
|
-
end
|
|
69
|
+
def test_postal_code
|
|
70
|
+
assert_equal 'BN2 0ZZ', CineworldUk::Cinema.new(14).postal_code
|
|
228
71
|
end
|
|
229
72
|
|
|
230
|
-
|
|
231
|
-
|
|
73
|
+
def test_region
|
|
74
|
+
assert_equal 'East Sussex', CineworldUk::Cinema.new(14).region
|
|
75
|
+
end
|
|
232
76
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
77
|
+
def test_region_defaults_to_london_for_london_cinemas
|
|
78
|
+
assert_equal 'London', CineworldUk::Cinema.new(103).region
|
|
79
|
+
end
|
|
236
80
|
|
|
237
|
-
|
|
238
|
-
|
|
81
|
+
def test_street_address
|
|
82
|
+
assert_equal '1 Example Marina', CineworldUk::Cinema.new(14).street_address
|
|
83
|
+
end
|
|
239
84
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
"http://www.cineworld.co.uk/cinemas/#{id}/information"
|
|
244
|
-
)
|
|
245
|
-
end
|
|
246
|
-
end
|
|
247
|
-
end
|
|
85
|
+
def test_url
|
|
86
|
+
assert_equal 'http://www.cineworld.co.uk/cinemas/14/information',
|
|
87
|
+
CineworldUk::Cinema.new(14).url
|
|
248
88
|
end
|
|
249
89
|
end
|
|
@@ -1,207 +1,52 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "../../../test_helper"
|
|
4
|
-
require "tmpdir"
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
before do
|
|
9
|
-
CineworldUk.token = "test-guest-token"
|
|
10
|
-
[CineworldUk::Cinema, CineworldUk::Performance].each do |klass|
|
|
11
|
-
klass.instance_variables.each do |name|
|
|
12
|
-
klass.remove_instance_variable(name)
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
fixture = "../../../fixtures/api/vista-showtimes-brighton.json"
|
|
16
|
-
@capture = JSON.parse(File.read(File.expand_path(fixture, __dir__)))
|
|
17
|
-
@base = "https://digital-api.cineworld.co.uk/ocapi/v1/"
|
|
18
|
-
sites = @capture.fetch("relatedData").fetch("sites")
|
|
19
|
-
stub_request(:get, @base + "sites").with(
|
|
20
|
-
headers: {
|
|
21
|
-
"Authorization" => "Bearer test-guest-token",
|
|
22
|
-
"Connect-Region-Code" => "GB"
|
|
23
|
-
}
|
|
24
|
-
).to_return(body: JSON.generate("sites" => sites))
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
after do
|
|
28
|
-
CineworldUk.token = nil
|
|
29
|
-
[CineworldUk::Cinema, CineworldUk::Performance].each do |klass|
|
|
30
|
-
klass.instance_variables.each do |name|
|
|
31
|
-
klass.remove_instance_variable(name)
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def stub_day(day, body)
|
|
37
|
-
stub_request(:get, @base + "showtimes/by-business-date/#{day}").with(
|
|
38
|
-
query: { "siteIds" => "014" },
|
|
39
|
-
headers: { "Authorization" => "Bearer test-guest-token",
|
|
40
|
-
"Connect-Region-Code" => "GB" }
|
|
41
|
-
).to_return(body: JSON.generate(body))
|
|
42
|
-
end
|
|
5
|
+
class CineworldUk::Internal::ApiResponseTest < Minitest::Test
|
|
6
|
+
include Support::VistaFixtures
|
|
43
7
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
cinema = CineworldUk::Cinema.all.first
|
|
47
|
-
_(cinema.id).must_equal 14
|
|
48
|
-
_(cinema.name).must_equal "Brighton"
|
|
49
|
-
_(cinema.postal_code).must_equal "BN2 5UF"
|
|
50
|
-
_(cinema.street_address).must_equal "Brighton Marina Village"
|
|
51
|
-
assert_requested :get, @base + "sites", times: 1
|
|
52
|
-
performances = CineworldUk::Performance.at(
|
|
53
|
-
14, from: Date.new(2026, 10, 14), to: Date.new(2026, 10, 14)
|
|
54
|
-
)
|
|
55
|
-
_(performances.size).must_equal 17
|
|
56
|
-
first = performances.first
|
|
57
|
-
_(first.film_name).must_equal "Other Mommy"
|
|
58
|
-
_(first.starting_at).must_equal Time.utc(2026, 10, 14, 14, 40)
|
|
59
|
-
_(first.booking_url).must_equal(
|
|
60
|
-
"https://web.cineworld.co.uk/order/showtimes/014-165507/seats"
|
|
61
|
-
)
|
|
62
|
-
_(first.variant).must_include "audio_described"
|
|
63
|
-
_(first.dimension).must_equal "2d"
|
|
64
|
-
assert_requested :get, @base + "sites", times: 1
|
|
65
|
-
assert_requested :get, @base + "showtimes/by-business-date/2026-10-14",
|
|
66
|
-
query: { "siteIds" => "014" }, times: 1
|
|
8
|
+
def setup
|
|
9
|
+
setup_vista
|
|
67
10
|
end
|
|
68
11
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
Marshal.load(Marshal.dump(@capture["showtimes"].first))]
|
|
73
|
-
@capture["showtimes"][1]["id"] = "014-second"
|
|
74
|
-
@capture["showtimes"][1]["requires3dGlasses"] = true
|
|
75
|
-
@capture["showtimes"][1]["schedule"]["startsAt"] =
|
|
76
|
-
"2026-10-25T16:40:00+00:00"
|
|
77
|
-
stub_day("2026-10-24",
|
|
78
|
-
"showtimes" => [],
|
|
79
|
-
"relatedData" => { "films" => [], "attributes" => [],
|
|
80
|
-
"sites" => [] })
|
|
81
|
-
stub_day("2026-10-25", @capture)
|
|
82
|
-
results = CineworldUk::Performance.at("014", from: Date.new(2026, 10, 24),
|
|
83
|
-
to: Date.new(2026, 10, 25))
|
|
84
|
-
_(results.map(&:dimension)).must_equal %w[2d 3d]
|
|
85
|
-
_(results.last.starting_at).must_equal Time.utc(2026, 10, 25, 16, 40)
|
|
86
|
-
_(results.first.cinema_name).must_equal "Brighton"
|
|
87
|
-
assert_not_requested :get, @base + "sites"
|
|
12
|
+
def teardown
|
|
13
|
+
teardown_vista
|
|
14
|
+
super
|
|
88
15
|
end
|
|
89
16
|
|
|
90
|
-
|
|
17
|
+
def test_rejects_missing_token
|
|
91
18
|
CineworldUk.token = nil
|
|
92
|
-
assert_raises(CineworldUk::AuthenticationError) { CineworldUk::Cinema.all }
|
|
93
|
-
CineworldUk.token = "expired-secret"
|
|
94
|
-
stub_request(:get, @base + "sites").to_return(status: 401,
|
|
95
|
-
body: "expired-secret")
|
|
96
|
-
error = assert_raises(CineworldUk::AuthenticationError) do
|
|
97
|
-
CineworldUk::Cinema.all
|
|
98
|
-
end
|
|
99
|
-
refute_includes error.message, "expired-secret"
|
|
100
|
-
CineworldUk.token = "replacement"
|
|
101
|
-
stub_request(:get, @base + "sites").with(
|
|
102
|
-
headers: { "Authorization" => "Bearer replacement" }
|
|
103
|
-
).to_return(body: '{"sites":[]}')
|
|
104
|
-
_(CineworldUk::Cinema.all).must_equal []
|
|
105
|
-
end
|
|
106
19
|
|
|
107
|
-
|
|
108
|
-
day = Date.new(2026, 10, 14)
|
|
109
|
-
(day..day + 13).each do |date|
|
|
110
|
-
stub_day(date.iso8601,
|
|
111
|
-
"showtimes" => [],
|
|
112
|
-
"relatedData" => { "films" => [], "attributes" => [],
|
|
113
|
-
"sites" => [] })
|
|
114
|
-
end
|
|
115
|
-
_(CineworldUk::Performance.at(14, from: day)).must_equal []
|
|
116
|
-
assert_requested :get, %r{showtimes/by-business-date/}, times: 14
|
|
117
|
-
assert_raises(ArgumentError) do
|
|
118
|
-
CineworldUk::Performance.at(14, from: day, to: day - 1)
|
|
119
|
-
end
|
|
120
|
-
assert_not_requested :get, @base + "sites"
|
|
20
|
+
assert_raises(CineworldUk::AuthenticationError) { CineworldUk::Cinema.all }
|
|
121
21
|
end
|
|
122
22
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
status: status, headers: { "Location" => "https://example.com/" }
|
|
127
|
-
)
|
|
128
|
-
error = assert_raises(RuntimeError) { CineworldUk::Cinema.all }
|
|
129
|
-
assert_includes error.message, status.to_s
|
|
130
|
-
end
|
|
131
|
-
end
|
|
132
|
-
end
|
|
23
|
+
def test_rejects_token_without_exposing_it
|
|
24
|
+
CineworldUk.token = "expired-secret"
|
|
25
|
+
stub_request(:get, "#{BASE_URL}sites").to_return(status: 401, body: "expired-secret")
|
|
133
26
|
|
|
134
|
-
|
|
135
|
-
before do
|
|
136
|
-
@directory = Dir.mktmpdir("cineworld token ")
|
|
137
|
-
@curl = File.join(@directory, "curl-impersonate")
|
|
138
|
-
CineworldUk.token = "existing-token"
|
|
139
|
-
end
|
|
27
|
+
error = assert_raises(CineworldUk::AuthenticationError) { CineworldUk::Cinema.all }
|
|
140
28
|
|
|
141
|
-
|
|
142
|
-
FileUtils.remove_entry(@directory)
|
|
143
|
-
CineworldUk.token = nil
|
|
29
|
+
refute_includes error.message, "expired-secret"
|
|
144
30
|
end
|
|
145
31
|
|
|
146
|
-
def
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
32
|
+
def test_uses_replacement_token
|
|
33
|
+
CineworldUk.token = "replacement"
|
|
34
|
+
stub_request(:get, "#{BASE_URL}sites")
|
|
35
|
+
.with(headers: { "Authorization" => "Bearer replacement" })
|
|
36
|
+
.to_return(body: '{"sites":[]}')
|
|
151
37
|
|
|
152
|
-
|
|
153
|
-
payload = Base64.urlsafe_encode64(JSON.generate("exp" => expires), padding: false)
|
|
154
|
-
token = "header.#{payload}.signature"
|
|
155
|
-
data = { "props" => { "pageProps" => { "environment" => { "gasToken" => token } } } }
|
|
156
|
-
["<script type='application/json' id='__NEXT_DATA__'>#{JSON.generate(data)}</script>", token]
|
|
38
|
+
assert_equal [], CineworldUk::Cinema.all
|
|
157
39
|
end
|
|
158
40
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
_(CineworldUk.token).must_equal token
|
|
165
|
-
end
|
|
166
|
-
end
|
|
41
|
+
def test_does_not_follow_redirects_or_hide_forbidden_responses
|
|
42
|
+
[302, 403].each do |status|
|
|
43
|
+
reset_caches
|
|
44
|
+
stub_request(:get, "#{BASE_URL}sites")
|
|
45
|
+
.to_return(status: status, headers: { "Location" => "https://example.com/" })
|
|
167
46
|
|
|
168
|
-
|
|
169
|
-
now = Time.at(1_800_000_000)
|
|
170
|
-
Time.stub(:now, now) do
|
|
171
|
-
[-1, 0, 1].each do |offset|
|
|
172
|
-
html, token = token_page(now.to_i + offset)
|
|
173
|
-
serve_token_page(html)
|
|
174
|
-
if offset.positive?
|
|
175
|
-
_(CineworldUk.acquire_token!(curl: @curl)).must_equal token
|
|
176
|
-
else
|
|
177
|
-
assert_raises(CineworldUk::AuthenticationError) { CineworldUk.acquire_token!(curl: @curl) }
|
|
178
|
-
_(CineworldUk.token).must_equal "existing-token"
|
|
179
|
-
end
|
|
180
|
-
end
|
|
181
|
-
end
|
|
182
|
-
end
|
|
47
|
+
error = assert_raises(RuntimeError) { CineworldUk::Cinema.all }
|
|
183
48
|
|
|
184
|
-
|
|
185
|
-
html, token = token_page(Time.now.to_i + 3600)
|
|
186
|
-
invalid_payload = Base64.urlsafe_encode64("broken-secret", padding: false)
|
|
187
|
-
[[html, 403], [html, 302], ["Cloudflare block", 200],
|
|
188
|
-
["<script id='__NEXT_DATA__'>{</script>", 200],
|
|
189
|
-
["<script id='__NEXT_DATA__'>{}</script>", 200],
|
|
190
|
-
[html.sub(token, "broken-secret"), 200],
|
|
191
|
-
[html.sub(token, "header.#{invalid_payload}.signature"), 200]].each do |body, status|
|
|
192
|
-
serve_token_page(body, status: status)
|
|
193
|
-
error = assert_raises(CineworldUk::AuthenticationError) { CineworldUk.acquire_token!(curl: @curl) }
|
|
194
|
-
refute_includes error.full_message, token
|
|
195
|
-
refute_includes error.full_message, "broken-secret"
|
|
196
|
-
_(CineworldUk.token).must_equal "existing-token"
|
|
49
|
+
assert_includes error.message, status.to_s
|
|
197
50
|
end
|
|
198
51
|
end
|
|
199
|
-
|
|
200
|
-
it "reports a missing executable or curl failure as an authentication error" do
|
|
201
|
-
error = assert_raises(CineworldUk::AuthenticationError) { CineworldUk.acquire_token!(curl: @curl) }
|
|
202
|
-
assert_includes error.message, "Install curl-impersonate"
|
|
203
|
-
serve_token_page("", status: 0, exit_code: 28)
|
|
204
|
-
error = assert_raises(CineworldUk::AuthenticationError) { CineworldUk.acquire_token!(curl: @curl) }
|
|
205
|
-
assert_includes error.message, "curl exit 28"
|
|
206
|
-
end
|
|
207
52
|
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
require_relative '../../../test_helper'
|
|
2
|
+
|
|
3
|
+
class FixtureShapeTest < Minitest::Test
|
|
4
|
+
include Support::VistaFixtures
|
|
5
|
+
include ApiShapes
|
|
6
|
+
|
|
7
|
+
def test_sites_fixture_matches_shape
|
|
8
|
+
assert_sites_shape(JSON.parse(sites_json))
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def test_showtimes_fixture_matches_shape
|
|
12
|
+
assert_showtimes_shape(JSON.parse(showtimes_json))
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../../../test_helper"
|
|
4
|
+
require "base64"
|
|
5
|
+
require "tmpdir"
|
|
6
|
+
|
|
7
|
+
class CineworldUk::Internal::GuestTokenTest < Minitest::Test
|
|
8
|
+
def setup
|
|
9
|
+
@directory = Dir.mktmpdir("cineworld token ")
|
|
10
|
+
@curl = File.join(@directory, "curl-impersonate")
|
|
11
|
+
CineworldUk.token = "existing-token"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def teardown
|
|
15
|
+
FileUtils.remove_entry(@directory)
|
|
16
|
+
CineworldUk.token = nil
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def test_sets_and_returns_token_from_200_or_404_page
|
|
20
|
+
html, token = token_page(Time.now.to_i + 3600)
|
|
21
|
+
|
|
22
|
+
[200, 404].each do |status|
|
|
23
|
+
serve_token_page(html, status: status)
|
|
24
|
+
|
|
25
|
+
assert_equal token, CineworldUk.acquire_token!(curl: @curl)
|
|
26
|
+
assert_equal token, CineworldUk.token
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def test_rejects_expired_tokens_including_boundary_and_keeps_current_token
|
|
31
|
+
now = Time.at(1_800_000_000)
|
|
32
|
+
Time.stub(:now, now) do
|
|
33
|
+
[-1, 0, 1].each do |offset|
|
|
34
|
+
html, token = token_page(now.to_i + offset)
|
|
35
|
+
serve_token_page(html)
|
|
36
|
+
if offset.positive?
|
|
37
|
+
assert_equal token, CineworldUk.acquire_token!(curl: @curl)
|
|
38
|
+
else
|
|
39
|
+
assert_raises(CineworldUk::AuthenticationError) { CineworldUk.acquire_token!(curl: @curl) }
|
|
40
|
+
assert_equal "existing-token", CineworldUk.token
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def test_reports_bad_pages_without_exposing_the_response
|
|
47
|
+
html, token = token_page(Time.now.to_i + 3600)
|
|
48
|
+
invalid_payload = Base64.urlsafe_encode64("broken-secret", padding: false)
|
|
49
|
+
[[html, 403], [html, 302], ["Cloudflare block", 200],
|
|
50
|
+
["<script id='__NEXT_DATA__'>{</script>", 200],
|
|
51
|
+
["<script id='__NEXT_DATA__'>{}</script>", 200],
|
|
52
|
+
[html.sub(token, "broken-secret"), 200],
|
|
53
|
+
[html.sub(token, "header.#{invalid_payload}.signature"), 200]].each do |body, status|
|
|
54
|
+
serve_token_page(body, status: status)
|
|
55
|
+
|
|
56
|
+
error = assert_raises(CineworldUk::AuthenticationError) { CineworldUk.acquire_token!(curl: @curl) }
|
|
57
|
+
|
|
58
|
+
refute_includes error.full_message, token
|
|
59
|
+
refute_includes error.full_message, "broken-secret"
|
|
60
|
+
assert_equal "existing-token", CineworldUk.token
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def test_reports_missing_executable_or_curl_failure
|
|
65
|
+
error = assert_raises(CineworldUk::AuthenticationError) { CineworldUk.acquire_token!(curl: @curl) }
|
|
66
|
+
assert_includes error.message, "Install curl-impersonate"
|
|
67
|
+
|
|
68
|
+
serve_token_page("", status: 0, exit_code: 28)
|
|
69
|
+
error = assert_raises(CineworldUk::AuthenticationError) { CineworldUk.acquire_token!(curl: @curl) }
|
|
70
|
+
assert_includes error.message, "curl exit 28"
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
private
|
|
74
|
+
|
|
75
|
+
def serve_token_page(html, status: 200, exit_code: 0)
|
|
76
|
+
output = "#{html}\n#{status}"
|
|
77
|
+
File.write(@curl, "#!#{RbConfig.ruby}\nSTDOUT.write(#{output.dump})\nexit #{exit_code}\n")
|
|
78
|
+
File.chmod(0o700, @curl)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def token_page(expires)
|
|
82
|
+
payload = Base64.urlsafe_encode64(JSON.generate("exp" => expires), padding: false)
|
|
83
|
+
token = "header.#{payload}.signature"
|
|
84
|
+
data = { "props" => { "pageProps" => { "environment" => { "gasToken" => token } } } }
|
|
85
|
+
["<script type='application/json' id='__NEXT_DATA__'>#{JSON.generate(data)}</script>", token]
|
|
86
|
+
end
|
|
87
|
+
end
|