odeon_uk 4.0.1 → 5.1.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 (73) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/release.yml +73 -0
  3. data/.github/workflows/test.yml +43 -0
  4. data/.gitignore +1 -1
  5. data/.ruby-version +1 -0
  6. data/CHANGELOG.md +36 -0
  7. data/README.md +100 -21
  8. data/Rakefile +14 -15
  9. data/lib/odeon_uk/cinema.rb +40 -22
  10. data/lib/odeon_uk/internal/api_response.rb +19 -39
  11. data/lib/odeon_uk/internal/guest_token.rb +23 -0
  12. data/lib/odeon_uk/internal/parser/api/showtime.rb +81 -0
  13. data/lib/odeon_uk/internal/title_sanitizer.rb +3 -8
  14. data/lib/odeon_uk/performance.rb +34 -41
  15. data/lib/odeon_uk/version.rb +3 -3
  16. data/lib/odeon_uk.rb +16 -2
  17. data/odeon_uk.gemspec +10 -6
  18. data/test/fixtures/api/showtimes-824.json +66 -0
  19. data/test/fixtures/api/sites.json +32 -0
  20. data/test/lib/odeon_uk/cinema_test.rb +66 -215
  21. data/test/lib/odeon_uk/internal/api_response_test.rb +48 -29
  22. data/test/lib/odeon_uk/internal/fixture_shape_test.rb +14 -0
  23. data/test/lib/odeon_uk/internal/guest_token_test.rb +84 -0
  24. data/test/lib/odeon_uk/internal/parser/api/showtime_test.rb +77 -0
  25. data/test/lib/odeon_uk/internal/title_sanitizer_test.rb +109 -89
  26. data/test/lib/odeon_uk/performance_test.rb +129 -146
  27. data/test/lib/odeon_uk/version_test.rb +3 -3
  28. data/test/live/api_shape_test.rb +19 -0
  29. data/test/live/integration_test.rb +21 -0
  30. data/test/live/live_helper.rb +11 -0
  31. data/test/package_test.rb +56 -0
  32. data/test/support/api_fixtures_helper.rb +5 -30
  33. data/test/support/api_shapes.rb +46 -0
  34. data/test/support/fake_api_response.rb +9 -6
  35. data/test/test_helper.rb +6 -3
  36. metadata +67 -89
  37. data/.travis.yml +0 -7
  38. data/lib/odeon_uk/internal/parser/api/film_lookup.rb +0 -34
  39. data/lib/odeon_uk/internal/parser/api/performance_day.rb +0 -92
  40. data/rake/fixture_creator.rb +0 -46
  41. data/test/fixtures/api/all_cinemas.plist +0 -0
  42. data/test/fixtures/api/app_init.plist +0 -0
  43. data/test/fixtures/api/film_times/71-14876.plist +0 -0
  44. data/test/fixtures/api/film_times/71-15408.plist +0 -0
  45. data/test/fixtures/api/film_times/71-15684.plist +0 -0
  46. data/test/fixtures/api/film_times/71-15866.plist +0 -0
  47. data/test/fixtures/api/film_times/71-15868.plist +0 -0
  48. data/test/fixtures/api/film_times/71-15961.plist +0 -0
  49. data/test/fixtures/api/film_times/71-15963.plist +0 -0
  50. data/test/fixtures/api/film_times/71-15965.plist +0 -0
  51. data/test/fixtures/api/film_times/71-15967.plist +0 -0
  52. data/test/fixtures/api/film_times/71-15969.plist +0 -0
  53. data/test/fixtures/api/film_times/71-15977.plist +0 -0
  54. data/test/fixtures/api/film_times/71-15980.plist +0 -0
  55. data/test/fixtures/api/film_times/71-16037.plist +0 -0
  56. data/test/fixtures/api/film_times/71-16136.plist +0 -0
  57. data/test/fixtures/api/film_times/71-16142.plist +0 -0
  58. data/test/fixtures/api/film_times/71-16143.plist +0 -0
  59. data/test/fixtures/api/film_times/71-16187.plist +0 -0
  60. data/test/fixtures/api/film_times/71-16369.plist +0 -0
  61. data/test/fixtures/api/film_times/71-16370.plist +0 -0
  62. data/test/fixtures/api/film_times/71-16374.plist +0 -0
  63. data/test/fixtures/api/film_times/71-16386.plist +0 -0
  64. data/test/fixtures/api/film_times/71-16387.plist +0 -0
  65. data/test/fixtures/api/film_times/71-16388.plist +0 -0
  66. data/test/fixtures/api/film_times/71-16438.plist +0 -0
  67. data/test/fixtures/api/film_times/71-16454.plist +0 -0
  68. data/test/fixtures/api/film_times/71-16457.plist +0 -0
  69. data/test/fixtures/api/film_times/71-16477.plist +0 -0
  70. data/test/fixtures/api/film_times/71-16478.plist +0 -0
  71. data/test/fixtures/api/film_times/71-16480.plist +0 -0
  72. data/test/fixtures/api/film_times/71-16482.plist +0 -0
  73. data/test/fixtures/api/film_times/71-16489.plist +0 -0
@@ -1,121 +1,141 @@
1
1
  require_relative '../../../test_helper'
2
2
 
3
- describe OdeonUk::Internal::TitleSanitizer do
4
- let(:described_class) { OdeonUk::Internal::TitleSanitizer }
3
+ class OdeonUk::Internal::TitleSanitizerTest < Minitest::Test
4
+ def test_removes_2d
5
+ assert_sanitized 'Iron Man 3', 'Iron Man 3 2D'
6
+ end
5
7
 
6
- describe '#sanitized' do
7
- subject { described_class.new(title).sanitized }
8
+ def test_removes_3d
9
+ assert_sanitized 'Iron Man 3', 'Iron Man 3 3D'
10
+ end
8
11
 
9
- describe 'with 2d in title' do
10
- let(:title) { 'Iron Man 3 2D' }
12
+ def test_removes_autism_prefix
13
+ assert_sanitized 'Planes', 'Autism Friendly Screening - Planes'
14
+ end
11
15
 
12
- it 'removes dimension' do
13
- subject.must_equal('Iron Man 3')
14
- end
15
- end
16
+ def test_bolshoi
17
+ assert_sanitized 'Bolshoi: Spartacus', 'Bolshoi - Spartacus (Live)'
18
+ end
16
19
 
17
- describe 'with 3d in title' do
18
- let(:title) { 'Iron Man 3 3D' }
20
+ def test_removes_cinemagic_prefix
21
+ assert_sanitized 'Echo Planet', 'Cinemagic 2013 - Echo Planet'
22
+ end
19
23
 
20
- it 'removes dimension' do
21
- subject.must_equal('Iron Man 3')
22
- end
23
- end
24
+ def test_globe_on_screen
25
+ assert_sanitized 'Globe: Twelfth Night', 'Globe On Screen: Twelfth Night'
26
+ end
24
27
 
25
- describe 'Austism screening' do
26
- let(:title) { 'Autism Friendly Screening - Planes' }
28
+ def test_met_opera
29
+ assert_sanitized 'Met Opera: Eugene Onegin 2013', 'Met Opera - Eugene Onegin 2013'
30
+ end
27
31
 
28
- it 'removes prefix' do
29
- subject.must_equal('Planes')
30
- end
31
- end
32
+ def test_met_opera_season
33
+ assert_sanitized 'Met Opera: La Fanciulla del West',
34
+ 'Met Opera 2026-27: La Fanciulla del West'
35
+ end
32
36
 
33
- describe 'Bolshoi screeening' do
34
- let(:title) { 'Bolshoi - Spartacus (Live)' }
37
+ def test_nt_live
38
+ assert_sanitized 'National Theatre: War Horse', 'NT Live - War Horse'
39
+ end
35
40
 
36
- it 'removes prefix' do
37
- subject.must_equal('Bolshoi: Spartacus')
38
- end
39
- end
41
+ def test_national_theatre_encore
42
+ assert_sanitized 'National Theatre: Frankenstein',
43
+ 'National Theatre Live - Frankenstein (Encore Screening)'
44
+ end
40
45
 
41
- describe 'Cinemagic screeening' do
42
- let(:title) { 'Cinemagic 2013 - Echo Planet' }
46
+ def test_roh
47
+ assert_sanitized 'Royal Opera House: Carmen', 'ROH - Carmen'
48
+ end
43
49
 
44
- it 'removes prefix' do
45
- subject.must_equal('Echo Planet')
46
- end
47
- end
50
+ def test_rbo_season
51
+ assert_sanitized 'Royal Ballet and Opera: Swan Lake',
52
+ 'RBO Cinema Season 2026-27: Swan Lake'
53
+ end
48
54
 
49
- describe 'Globe on Screen screeening' do
50
- let(:title) { 'Globe On Screen: Twelfth Night' }
55
+ def test_short_rbo_season
56
+ assert_sanitized "Royal Ballet and Opera: Alice's Adventures in Wonderland",
57
+ "RBO 2026-27: Alice's Adventures in Wonderland"
58
+ end
51
59
 
52
- it 'removes prefix' do
53
- subject.must_equal('Globe: Twelfth Night')
54
- end
55
- end
60
+ def test_rsc
61
+ assert_sanitized 'Royal Shakespeare Company: Richard II', 'Richard II - RSC Live 2013'
62
+ end
56
63
 
57
- describe 'Met Opera screeening' do
58
- let(:title) { 'Met Opera - Eugene Onegin 2013' }
64
+ def test_removes_ukjff_prefix
65
+ assert_sanitized 'From Cable Street To Brick Lane',
66
+ 'UKJFF - From Cable Street To Brick Lane'
67
+ end
59
68
 
60
- it 'removes prefix' do
61
- subject.must_equal('Met Opera: Eugene Onegin 2013')
62
- end
63
- end
69
+ def test_removes_singalong
70
+ assert_sanitized 'Frozen', 'Frozen Sing-a-long'
71
+ end
64
72
 
65
- describe 'NT Live screeening' do
66
- let(:title) { 'NT Live - War Horse' }
73
+ def test_html_ampersands
74
+ assert_sanitized 'Fast & Furious 7', 'Fast &amp; Furious 7'
75
+ end
67
76
 
68
- it 'removes prefix' do
69
- subject.must_equal('National Theatre: War Horse')
70
- end
71
- end
77
+ def test_removes_language_suffix
78
+ assert_sanitized 'Jailer 2', 'Jailer 2 (Telugu)'
79
+ end
80
+
81
+ def test_removes_encore_suffix
82
+ assert_sanitized 'Avengers Endgame', 'Avengers Endgame: Encore'
83
+ end
84
+
85
+ def test_keeps_sing_along_within_title
86
+ assert_sanitized 'Bebefinn Sing-Along Movie', 'Bebefinn Sing-Along Movie'
87
+ end
88
+
89
+ def test_removes_dubbed
90
+ assert_sanitized 'Arrietty', 'Arrietty (Dubbed)'
91
+ end
72
92
 
73
- describe 'National Theatre screeening' do
74
- let(:title) { 'National Theatre Live - Frankenstein (Encore Screening)' }
93
+ def test_removes_subbed
94
+ assert_sanitized 'Arrietty', 'Arrietty (Subbed)'
95
+ end
75
96
 
76
- it 'removes prefix' do
77
- subject.must_equal('National Theatre: Frankenstein')
78
- end
79
- end
97
+ def test_removes_english_dub
98
+ assert_sanitized 'Kung Fu Soccer', 'Kung Fu Soccer (English Dub)'
99
+ end
80
100
 
81
- describe 'ROH screeening' do
82
- let(:title) { 'Autism Friendly Screening - Planes' }
101
+ def test_removes_subtitle_description
102
+ assert_sanitized 'Kung Fu Soccer', 'Kung Fu Soccer (Mandarin with EN/CH subtitles)'
103
+ end
83
104
 
84
- it 'removes prefix' do
85
- subject.must_equal('Planes')
86
- end
87
- end
105
+ def test_removes_q_and_a
106
+ assert_sanitized 'NAZA', 'NAZA + Q&A'
107
+ end
88
108
 
89
- describe 'RSC screeening' do
90
- let(:title) { 'Richard II - RSC Live 2013' }
109
+ def test_removes_in_person_q_and_a
110
+ assert_sanitized "Charlie Shaw's Revenge", "Charlie Shaw's Revenge + In-Person Q&A"
111
+ end
91
112
 
92
- it 'removes prefix' do
93
- subject.must_equal('Royal Shakespeare Company: Richard II')
94
- end
95
- end
113
+ def test_removes_festival_prefix
114
+ assert_sanitized 'Scum Of The Earth', 'Cine-Excess 2026: Scum Of The Earth'
115
+ end
96
116
 
97
- describe 'UK Jewish FF screeening' do
98
- let(:title) { 'UKJFF - From Cable Street To Brick Lane' }
117
+ def test_removes_anniversary
118
+ assert_sanitized 'SCREAM', 'SCREAM (30th Anniversary)'
119
+ end
99
120
 
100
- it 'removes prefix' do
101
- subject.must_equal('From Cable Street To Brick Lane')
102
- end
103
- end
121
+ def test_world_tour_is_not_capitalised
122
+ assert_sanitized "ENHYPEN World Tour 'BLOOD SAGA'", "ENHYPEN WORLD TOUR 'BLOOD SAGA'"
123
+ end
104
124
 
105
- describe 'singalong screeening' do
106
- let(:title) { 'Frozen Sing-a-long' }
125
+ def test_keeps_titles_with_their_own_subtitles
126
+ [
127
+ 'Harry Potter and the Deathly Hallows: Part 1',
128
+ 'Spider-Man: Brand New Day',
129
+ 'Hadestown: The Musical',
130
+ 'Rammstein - Live in Mexico City',
131
+ 'Ozzy & Black Sabbath: Back to the Beginning',
132
+ "BTS 'ARIRANG' IN BUENOS AIRES: LIVE VIEWING"
133
+ ].each { |title| assert_sanitized title, title }
134
+ end
107
135
 
108
- it 'removes prefix' do
109
- subject.must_equal('Frozen')
110
- end
111
- end
112
-
113
- describe 'HTML ampersands' do
114
- let(:title) { 'Fast &amp; Furious 7' }
136
+ private
115
137
 
116
- it 'removes prefix' do
117
- subject.must_equal('Fast & Furious 7')
118
- end
119
- end
138
+ def assert_sanitized(expected, title)
139
+ assert_equal expected, OdeonUk::Internal::TitleSanitizer.new(title).sanitized
120
140
  end
121
141
  end
@@ -1,172 +1,155 @@
1
1
  require_relative '../../test_helper'
2
2
 
3
- describe OdeonUk::Performance do
4
- let(:described_class) { OdeonUk::Performance }
5
- let(:api_response) { FakeApiResponse.new }
6
-
7
- before { WebMock.disable_net_connect! }
8
- after { WebMock.allow_net_connect! }
9
-
10
- describe '.at(cinema_id)' do
11
- subject { described_class.at(cinema_id) }
12
-
13
- describe 'passed Fixnum' do
14
- let(:cinema_id) { 71 }
15
-
16
- it 'returns an array of performances' do
17
- OdeonUk::Internal::ApiResponse.stub :new, api_response do
18
- subject.must_be_instance_of(Array)
19
- subject.each do |element|
20
- element.must_be_instance_of(described_class)
21
- end
22
- end
23
- end
24
-
25
- it 'returns at least a sensible number' do
26
- OdeonUk::Internal::ApiResponse.stub :new, api_response do
27
- subject.count.must_be :>, 110
28
- end
29
- end
30
- end
3
+ class OdeonUk::PerformanceTest < Minitest::Test
4
+ DATE = Date.new(2026, 10, 24)
31
5
 
32
- describe 'passed String' do
33
- let(:cinema_id) { '71' }
34
-
35
- it 'returns an array of performances' do
36
- OdeonUk::Internal::ApiResponse.stub :new, api_response do
37
- subject.must_be_instance_of(Array)
38
- subject.each do |element|
39
- element.must_be_instance_of(described_class)
40
- end
41
- end
42
- end
43
-
44
- it 'returns at least a sensible number' do
45
- OdeonUk::Internal::ApiResponse.stub :new, api_response do
46
- subject.count.must_be :>, 110
47
- end
48
- end
49
- end
6
+ def setup
7
+ @api_response = FakeApiResponse.new
50
8
  end
51
9
 
52
- describe '.new' do
53
- subject { described_class.new(options) }
54
-
55
- describe 'simple' do
56
- let(:options) do
57
- {
58
- film_name: 'The Big Short',
59
- cinema_id: 71,
60
- cinema_name: 'Brighton',
61
- starting_at: Time.utc(2013, 9, 12, 11, 0)
62
- }
63
- end
64
-
65
- it 'sets cinema name and film name' do
66
- subject.film_name.must_equal 'The Big Short'
67
- subject.cinema_name.must_equal 'Brighton'
68
- end
69
-
70
- it 'booking url, dimension & varient are set to defaults' do
71
- subject.booking_url.must_equal nil
72
- subject.dimension.must_equal '2d'
73
- subject.variant.must_equal []
74
- end
75
- end
10
+ def test_at_returns_performances
11
+ performances = performances_at(824)
12
+
13
+ assert(performances.all? { |p| p.instance_of?(OdeonUk::Performance) })
14
+ assert_equal 6, performances.count
76
15
  end
77
16
 
78
- describe '#dimension' do
79
- let(:options) do
80
- {
81
- film_name: 'Ant Man',
82
- dimension: '3d',
83
- cinema_id: 71,
84
- cinema_name: 'Brighton',
85
- starting_at: Time.utc(2013, 9, 12, 11, 0)
86
- }
87
- end
17
+ def test_at_accepts_string_id
18
+ assert_equal 6, performances_at('824').count
19
+ end
88
20
 
89
- subject { described_class.new(options).dimension }
21
+ def test_at_sets_cinema_details
22
+ performances = performances_at(824)
90
23
 
91
- it 'returns 2d or 3d' do
92
- subject.must_be_instance_of(String)
93
- subject.must_equal '3d'
94
- end
24
+ assert_equal ['824'], performances.map(&:cinema_id).uniq
25
+ assert_equal ['Brighton'], performances.map(&:cinema_name).uniq
95
26
  end
96
27
 
97
- describe '#starting_at' do
98
- subject { described_class.new(options).starting_at }
28
+ def test_at_parses_first_performance
29
+ performance = performances_at(824).first
99
30
 
100
- describe 'with utc time' do
101
- let(:options) do
102
- {
103
- film_name: 'Steve Jobs',
104
- cinema_id: 71,
105
- cinema_name: 'Brighton',
106
- starting_at: Time.utc(2013, 9, 12, 11, 0)
107
- }
108
- end
31
+ assert_equal 'The Example Film', performance.film_name
32
+ assert_equal Time.utc(2026, 10, 24, 8, 0), performance.starting_at
33
+ assert_equal '2d', performance.dimension
34
+ assert_equal %w(audio_described), performance.variant
35
+ assert_equal 'https://www.odeon.co.uk/ticketing/seat-picker/?showtimeId=824-1001',
36
+ performance.booking_url
37
+ end
109
38
 
110
- it 'returns UTC time' do
111
- subject.must_be_instance_of Time
112
- subject.must_equal Time.utc(2013, 9, 12, 11, 0)
113
- end
114
- end
39
+ def test_at_parses_3d_imax
40
+ performance = performances_at(824)[1]
115
41
 
116
- describe 'with non-utc time' do
117
- let(:options) do
118
- {
119
- film_name: 'Steve Jobs',
120
- cinema_id: 71,
121
- cinema_name: 'Brighton',
122
- starting_at: Time.new(2013, 9, 12, 11, 0)
123
- }
124
- end
125
-
126
- it 'returns UTC time' do
127
- subject.must_be_instance_of Time
128
- subject.must_equal Time.utc(2013, 9, 12, 10, 0)
129
- subject.utc?.must_equal(true)
130
- end
131
- end
42
+ assert_equal '3d', performance.dimension
43
+ assert_equal %w(imax), performance.variant
132
44
  end
133
45
 
134
- describe '#showing_on' do
135
- let(:options) do
136
- {
137
- film_name: 'The Hunger Games',
138
- cinema_id: 71,
139
- cinema_name: 'Brighton',
140
- starting_at: Time.utc(2013, 9, 12, 11, 0)
141
- }
142
- end
46
+ def test_at_returns_language_and_subtitles
47
+ performances = performances_at(824)
48
+ tamil = performances[3]
49
+ korean = performances[2]
50
+
51
+ assert_equal 'Imaginary Picture', tamil.film_name
52
+ assert_equal 2025, tamil.film_year
53
+ assert_equal 'Tamil', tamil.language
54
+ assert tamil.subtitled?
55
+ assert_equal 'Korean', korean.language
56
+ assert_equal %w(arts subtitled), korean.variant
57
+ end
143
58
 
144
- subject { described_class.new(options).showing_on }
59
+ def test_at_treats_open_captions_as_subtitled
60
+ kids = performances_at(824)[4]
145
61
 
146
- it 'returns date of showing' do
147
- subject.must_be_instance_of(Date)
148
- subject.must_equal Date.new(2013, 9, 12)
149
- end
62
+ assert_nil kids.language
63
+ assert_equal %w(kids subtitled), kids.variant
64
+ assert kids.subtitled?
150
65
  end
151
66
 
152
- describe '#variant' do
153
- subject { described_class.new(options).variant }
67
+ def test_at_keeps_after_midnight_on_next_utc_day
68
+ assert_equal Time.utc(2026, 10, 24, 23, 15), performances_at(824).last.starting_at
69
+ end
154
70
 
155
- let(:options) do
156
- {
157
- film_name: 'Carol',
158
- cinema_id: 71,
159
- cinema_name: 'Brighton',
160
- starting_at: Time.utc(2013, 9, 12, 11, 0),
161
- variant: %w(Subtitles Senior)
162
- }
163
- end
71
+ def test_at_requests_each_date_in_range
72
+ to = DATE + 2
73
+
74
+ with_fake_api { OdeonUk::Performance.at(824, from: DATE, to: to) }
75
+
76
+ assert_equal (DATE..to).to_a, @api_response.requested_dates
77
+ end
164
78
 
165
- it 'is an alphabetically ordered array of lower-cased strings' do
166
- subject.must_be_instance_of Array
167
- subject.each { |element| element.must_be_instance_of String }
168
- subject.must_equal %w(senior subtitles)
79
+ def test_at_rejects_range_ending_before_start
80
+ assert_raises(ArgumentError) do
81
+ OdeonUk::Performance.at(824, from: DATE, to: DATE - 1)
169
82
  end
170
83
  end
171
84
 
85
+ def test_new_sets_names
86
+ performance = OdeonUk::Performance.new(options)
87
+
88
+ assert_equal 'The Big Short', performance.film_name
89
+ assert_equal 'Brighton', performance.cinema_name
90
+ end
91
+
92
+ def test_new_sets_defaults
93
+ performance = OdeonUk::Performance.new(options)
94
+
95
+ assert_nil performance.booking_url
96
+ assert_nil performance.festival
97
+ assert_equal '2d', performance.dimension
98
+ assert_nil performance.language
99
+ refute performance.subtitled?
100
+ assert_equal [], performance.variant
101
+ end
102
+
103
+ def test_language
104
+ assert_equal 'Tamil', OdeonUk::Performance.new(options(language: 'Tamil')).language
105
+ end
106
+
107
+ def test_festival
108
+ festival = Cinebase::Festival.new(name: 'Cine-Excess', year: 2026)
109
+ performance = OdeonUk::Performance.new(options(festival: festival))
110
+
111
+ assert_equal festival, performance.festival
112
+ end
113
+
114
+ def test_film_year
115
+ assert_equal 1996, OdeonUk::Performance.new(options(film_year: 1996)).film_year
116
+ end
117
+
118
+ def test_subtitled
119
+ assert OdeonUk::Performance.new(options(variant: %w(subtitled))).subtitled?
120
+ end
121
+
122
+ def test_starting_at_converts_local_time_to_utc
123
+ starting_at = OdeonUk::Performance.new(
124
+ options(starting_at: Time.new(2013, 9, 12, 11, 0))
125
+ ).starting_at
126
+
127
+ assert_equal Time.utc(2013, 9, 12, 10, 0), starting_at
128
+ assert starting_at.utc?
129
+ end
130
+
131
+ def test_variant_is_sorted_and_lower_case
132
+ performance = OdeonUk::Performance.new(options(variant: %w(Subtitled Senior)))
133
+
134
+ assert_equal %w(senior subtitled), performance.variant
135
+ end
136
+
137
+ private
138
+
139
+ def options(overrides = {})
140
+ {
141
+ film_name: 'The Big Short',
142
+ cinema_id: '824',
143
+ cinema_name: 'Brighton',
144
+ starting_at: Time.utc(2013, 9, 12, 11, 0)
145
+ }.merge(overrides)
146
+ end
147
+
148
+ def performances_at(cinema_id)
149
+ with_fake_api { OdeonUk::Performance.at(cinema_id, from: DATE, to: DATE) }
150
+ end
151
+
152
+ def with_fake_api(&block)
153
+ OdeonUk::Internal::ApiResponse.stub(:new, @api_response, &block)
154
+ end
172
155
  end
@@ -1,7 +1,7 @@
1
1
  require_relative '../../test_helper'
2
2
 
3
- describe OdeonUk do
4
- it 'must be defined' do
5
- OdeonUk::VERSION.wont_be_nil
3
+ class OdeonUk::VersionTest < Minitest::Test
4
+ def test_version_is_defined
5
+ refute_nil OdeonUk::VERSION
6
6
  end
7
7
  end
@@ -0,0 +1,19 @@
1
+ require_relative 'live_helper'
2
+
3
+ class LiveApiShapeTest < Minitest::Test
4
+ include ApiShapes
5
+
6
+ def test_sites_are_readable
7
+ assert_sites_shape('sites' => api.sites)
8
+ end
9
+
10
+ def test_showtimes_are_readable
11
+ assert_showtimes_shape(api.showtimes('824', Date.today + 1))
12
+ end
13
+
14
+ private
15
+
16
+ def api
17
+ OdeonUk::Internal::ApiResponse.new
18
+ end
19
+ end
@@ -0,0 +1,21 @@
1
+ require_relative 'live_helper'
2
+
3
+ class LiveCinemaTest < Minitest::Test
4
+ def test_all_returns_cinemas
5
+ cinemas = OdeonUk::Cinema.all
6
+
7
+ assert(cinemas.all? { |cinema| cinema.instance_of?(OdeonUk::Cinema) })
8
+ assert_operator cinemas.size, :>, 90
9
+ assert_equal 'Brighton', cinemas.detect { |cinema| cinema.id == '824' }.name
10
+ end
11
+ end
12
+
13
+ class LivePerformanceTest < Minitest::Test
14
+ def test_at_returns_performances
15
+ tomorrow = Date.today + 1
16
+ performances = OdeonUk::Performance.at(824, from: tomorrow, to: tomorrow)
17
+
18
+ assert(performances.all? { |p| p.instance_of?(OdeonUk::Performance) })
19
+ assert_operator performances.count, :>, 10
20
+ end
21
+ end
@@ -0,0 +1,11 @@
1
+ require 'minitest/autorun'
2
+ require 'minitest/reporters'
3
+
4
+ Minitest::Reporters.use! [
5
+ Minitest::Reporters::DefaultReporter.new(color: true)
6
+ ]
7
+
8
+ require File.expand_path('../../../lib/odeon_uk.rb', __FILE__)
9
+ require_relative '../support/api_shapes'
10
+
11
+ OdeonUk.token = ENV.fetch('ODEON_TOKEN') { OdeonUk.acquire_token! }
@@ -0,0 +1,56 @@
1
+ # Run separately after building: ODEON_PACKAGE=pkg/...gem bundle exec ruby test/package_test.rb
2
+ require "minitest/autorun"
3
+ require "rubygems/installer"
4
+ require "tmpdir"
5
+ require "fileutils"
6
+ require "open3"
7
+ require "base64"
8
+ require "json"
9
+
10
+ class PlatformPackageTest < Minitest::Test
11
+ def setup
12
+ @directory = Dir.mktmpdir("odeon-package-test")
13
+ @spec = Gem::Installer.at(
14
+ ENV.fetch("ODEON_PACKAGE"), install_dir: @directory, ignore_dependencies: true
15
+ ).install
16
+ @vendor = File.join(@spec.full_gem_path, "vendor", "curl-impersonate")
17
+ @binary = File.join(@vendor, "bin", "curl-impersonate")
18
+ end
19
+
20
+ def teardown
21
+ FileUtils.remove_entry(@directory)
22
+ end
23
+
24
+ def test_installs_runnable_executable_with_notices_and_provenance
25
+ assert File.executable?(@binary)
26
+ output, status = Open3.capture2(@binary, "--version")
27
+ assert status.success?
28
+ assert_includes output, "curl"
29
+ assert File.file?(File.join(@vendor, "licenses", "LICENSE_CURL"))
30
+ assert_includes File.read(File.join(@vendor, "SOURCE")), "2.2.3"
31
+ end
32
+
33
+ def test_acquires_token_with_gem_local_executable_and_no_curl_on_path
34
+ payload = Base64.urlsafe_encode64(JSON.generate("exp" => Time.now.to_i + 3600), padding: false)
35
+ token = "header.#{payload}.signature"
36
+ data = { "api" => { "authToken" => token } }
37
+ page = "<script>\nwindow.initialData = #{JSON.generate(data)};\n</script>\n200"
38
+ # Substitute only the network response, not the installed package's path resolution.
39
+ File.write(@binary, "#!#{RbConfig.ruby}\nSTDOUT.write(#{page.dump})\n")
40
+ output, error, status = Open3.capture3(
41
+ { "PATH" => @directory, "RUBYOPT" => nil, "BUNDLER_SETUP" => nil,
42
+ "GEM_PATH" => Gem.path.join(File::PATH_SEPARATOR) }, RbConfig.ruby,
43
+ "-I#{File.join(@spec.full_gem_path, 'lib')}", *cinebase_load_path, "-rodeon_uk",
44
+ "-e", "print OdeonUk.acquire_token!"
45
+ )
46
+ assert status.success?, error
47
+ assert_equal token, output
48
+ end
49
+
50
+ private
51
+
52
+ # The package is installed without dependencies, so load the bundle's cinebase.
53
+ def cinebase_load_path
54
+ Gem.loaded_specs.fetch("cinebase").full_require_paths.map { |path| "-I#{path}" }
55
+ end
56
+ end