yt 0.25.13 → 0.32.2

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 (96) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +305 -1
  3. data/MIT-LICENSE +1 -1
  4. data/README.md +86 -5
  5. data/YOUTUBE_IT.md +3 -3
  6. data/lib/yt.rb +5 -2
  7. data/lib/yt/actions/list.rb +3 -3
  8. data/lib/yt/associations/has_authentication.rb +33 -1
  9. data/lib/yt/associations/has_reports.rb +13 -18
  10. data/lib/yt/collections/assets.rb +2 -2
  11. data/lib/yt/collections/authentications.rb +9 -2
  12. data/lib/yt/collections/base.rb +3 -3
  13. data/lib/yt/collections/bulk_report_jobs.rb +28 -0
  14. data/lib/yt/collections/bulk_reports.rb +24 -0
  15. data/lib/yt/collections/claims.rb +22 -1
  16. data/lib/yt/collections/comment_threads.rb +41 -0
  17. data/lib/yt/collections/content_owners.rb +1 -1
  18. data/lib/yt/collections/group_infos.rb +27 -0
  19. data/lib/yt/collections/group_items.rb +45 -0
  20. data/lib/yt/collections/reports.rb +75 -13
  21. data/lib/yt/collections/revocations.rb +30 -0
  22. data/lib/yt/collections/video_groups.rb +29 -0
  23. data/lib/yt/collections/videos.rb +34 -9
  24. data/lib/yt/constants/geography.rb +326 -0
  25. data/lib/yt/errors/forbidden.rb +1 -3
  26. data/lib/yt/errors/no_items.rb +1 -3
  27. data/lib/yt/errors/request_error.rb +10 -7
  28. data/lib/yt/errors/server_error.rb +1 -3
  29. data/lib/yt/errors/unauthorized.rb +3 -3
  30. data/lib/yt/models/account.rb +12 -0
  31. data/lib/yt/models/advertising_options_set.rb +4 -4
  32. data/lib/yt/models/bulk_report.rb +23 -0
  33. data/lib/yt/models/bulk_report_job.rb +23 -0
  34. data/lib/yt/models/channel.rb +21 -12
  35. data/lib/yt/models/claim.rb +13 -2
  36. data/lib/yt/models/comment.rb +37 -0
  37. data/lib/yt/models/comment_thread.rb +50 -0
  38. data/lib/yt/models/content_detail.rb +6 -0
  39. data/lib/yt/models/content_owner.rb +31 -1
  40. data/lib/yt/models/group_info.rb +16 -0
  41. data/lib/yt/models/group_item.rb +15 -0
  42. data/lib/yt/models/resource.rb +3 -10
  43. data/lib/yt/models/revocation.rb +12 -0
  44. data/lib/yt/models/right_owner.rb +0 -2
  45. data/lib/yt/models/snippet.rb +24 -3
  46. data/lib/yt/models/video.rb +42 -11
  47. data/lib/yt/models/video_group.rb +186 -0
  48. data/lib/yt/request.rb +5 -3
  49. data/lib/yt/version.rb +2 -2
  50. data/spec/collections/comment_threads_spec.rb +46 -0
  51. data/spec/collections/playlist_items_spec.rb +1 -1
  52. data/spec/collections/reports_spec.rb +2 -2
  53. data/spec/constants/geography_spec.rb +16 -0
  54. data/spec/models/annotation_spec.rb +1 -1
  55. data/spec/models/claim_spec.rb +15 -3
  56. data/spec/models/comment_spec.rb +40 -0
  57. data/spec/models/comment_thread_spec.rb +93 -0
  58. data/spec/models/content_detail_spec.rb +7 -0
  59. data/spec/models/reference_spec.rb +2 -2
  60. data/spec/models/request_spec.rb +21 -0
  61. data/spec/models/resource_spec.rb +0 -15
  62. data/spec/models/video_spec.rb +1 -1
  63. data/spec/requests/as_account/account_spec.rb +16 -4
  64. data/spec/requests/as_account/authentications_spec.rb +1 -13
  65. data/spec/requests/as_account/channel_spec.rb +15 -45
  66. data/spec/requests/as_account/playlist_item_spec.rb +3 -3
  67. data/spec/requests/as_account/playlist_spec.rb +5 -32
  68. data/spec/requests/as_account/video_spec.rb +2022 -21
  69. data/spec/requests/as_content_owner/account_spec.rb +4 -0
  70. data/spec/requests/as_content_owner/bulk_report_job_spec.rb +19 -0
  71. data/spec/requests/as_content_owner/channel_spec.rb +59 -270
  72. data/spec/requests/as_content_owner/content_owner_spec.rb +89 -1
  73. data/spec/requests/as_content_owner/playlist_spec.rb +0 -15
  74. data/spec/requests/as_content_owner/video_group_spec.rb +112 -0
  75. data/spec/requests/as_content_owner/video_spec.rb +72 -146
  76. data/spec/requests/as_server_app/channel_spec.rb +1 -21
  77. data/spec/requests/as_server_app/comment_spec.rb +22 -0
  78. data/spec/requests/as_server_app/comment_thread_spec.rb +27 -0
  79. data/spec/requests/as_server_app/comment_threads_spec.rb +41 -0
  80. data/spec/requests/as_server_app/playlist_item_spec.rb +2 -2
  81. data/spec/requests/as_server_app/playlist_spec.rb +1 -22
  82. data/spec/requests/as_server_app/video_spec.rb +21 -19
  83. data/spec/requests/as_server_app/videos_spec.rb +5 -5
  84. data/spec/requests/unauthenticated/video_spec.rb +1 -9
  85. data/spec/spec_helper.rb +1 -1
  86. data/yt.gemspec +2 -1
  87. metadata +51 -17
  88. data/lib/yt/collections/ids.rb +0 -27
  89. data/lib/yt/config.rb +0 -54
  90. data/lib/yt/models/configuration.rb +0 -70
  91. data/lib/yt/models/description.rb +0 -58
  92. data/lib/yt/models/url.rb +0 -91
  93. data/spec/models/configuration_spec.rb +0 -44
  94. data/spec/models/description_spec.rb +0 -94
  95. data/spec/models/url_spec.rb +0 -84
  96. data/spec/requests/as_account/resource_spec.rb +0 -18
@@ -197,7 +197,7 @@ module Yt
197
197
  # for a couple of seconds might solve the connection issues.
198
198
  def run_again?
199
199
  refresh_token_and_retry? ||
200
- server_error? && sleep_and_retry? ||
200
+ server_error? && sleep_and_retry?(3) ||
201
201
  exceeded_quota? && sleep_and_retry?(3)
202
202
  end
203
203
 
@@ -209,6 +209,8 @@ module Yt
209
209
  Errno::EHOSTUNREACH,
210
210
  Errno::ENETUNREACH,
211
211
  Errno::ECONNRESET,
212
+ Net::OpenTimeout,
213
+ SocketError,
212
214
  Net::HTTPServerError
213
215
  ] + extra_server_errors
214
216
  end
@@ -232,7 +234,7 @@ module Yt
232
234
  @retries_so_far += 1
233
235
  if (@retries_so_far < max_retries)
234
236
  @response = @http_request = @uri = nil
235
- sleep 3
237
+ sleep 3 + (10 * @retries_so_far)
236
238
  end
237
239
  end
238
240
 
@@ -267,7 +269,7 @@ module Yt
267
269
 
268
270
  # @return [Boolean] whether the request exceeds the YouTube quota
269
271
  def exceeded_quota?
270
- response_error == Errors::Forbidden && response.body =~ /quotaExceeded/
272
+ response_error == Errors::Forbidden && response.body =~ /Exceeded/i
271
273
  end
272
274
 
273
275
  # @return [Boolean] whether the request lacks proper authorization.
@@ -1,3 +1,3 @@
1
1
  module Yt
2
- VERSION = '0.25.13'
3
- end
2
+ VERSION = '0.32.2'
3
+ end
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+ require 'yt/collections/comment_threads'
3
+ require 'yt/models/video'
4
+ require 'yt/models/channel'
5
+
6
+ describe Yt::Collections::CommentThreads do
7
+ subject(:collection) { Yt::Collections::CommentThreads.new parent: parent}
8
+
9
+ describe '#size', :ruby2 do
10
+ describe 'sends only one request and return the total results' do
11
+ let(:total_results) { 1234 }
12
+ let(:parent) { Yt::Video.new id: 'any-id' }
13
+
14
+ before do
15
+ expect_any_instance_of(Yt::Request).to receive(:run).once do
16
+ double(body: {'pageInfo'=>{'totalResults'=>total_results}})
17
+ end
18
+ end
19
+ it { expect(collection.size).to be total_results }
20
+ end
21
+ end
22
+
23
+ describe '#count' do
24
+ let(:query) { {q: 'search string'} }
25
+ let(:parent) { Yt::Video.new id: 'any-id' }
26
+ let(:page) { {items: [], token: 'any-token'} }
27
+
28
+ context 'called once with .where(query) and once without' do
29
+ after do
30
+ collection.where(query).count
31
+ collection.count
32
+ end
33
+
34
+ it 'only applies the query on the first call' do
35
+ expect(collection).to receive(:fetch_page) do |options|
36
+ expect(options[:params]).to include query
37
+ page
38
+ end
39
+ expect(collection).to receive(:fetch_page) do |options|
40
+ expect(options[:params]).not_to include query
41
+ page
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -6,7 +6,7 @@ require 'yt/collections/playlist_items'
6
6
  describe Yt::Collections::PlaylistItems do
7
7
  subject(:collection) { Yt::Collections::PlaylistItems.new parent: playlist }
8
8
  let(:playlist) { Yt::Playlist.new id: 'LLxO1tY8h1AhOz0T4ENwmpow' }
9
- let(:attrs) { {id: 'MESycYJytkU', kind: :video} }
9
+ let(:attrs) { {id: '9bZkp7q19f0', kind: :video} }
10
10
  let(:msg) { {response_body: {error: {errors: [{reason: reason}]}}}.to_json }
11
11
  before { expect(collection).to behave }
12
12
 
@@ -9,7 +9,7 @@ describe Yt::Collections::Reports do
9
9
  let(:msg) { {response_body: {error: {errors: [error]}}}.to_json }
10
10
 
11
11
  describe '#within' do
12
- let(:result) { reports.within Range.new(5.days.ago, 4.days.ago), nil, nil, :day, nil }
12
+ let(:result) { reports.within Range.new(5.days.ago, 4.days.ago), nil, nil, :day, nil, nil }
13
13
  context 'given the request raises error 400 with "Invalid Query" message' do
14
14
  let(:reason) { 'badRequest' }
15
15
  let(:message) { 'Invalid query. Query did not conform to the expectations' }
@@ -27,4 +27,4 @@ describe Yt::Collections::Reports do
27
27
  end
28
28
  end
29
29
  end
30
- end
30
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+ require 'yt/constants/geography'
3
+
4
+ describe 'Yt::COUNTRIES' do
5
+ it 'returns all country codes and names' do
6
+ expect(Yt::COUNTRIES[:US]).to eq 'United States'
7
+ expect(Yt::COUNTRIES['IT']).to eq 'Italy'
8
+ end
9
+ end
10
+
11
+ describe 'Yt::US_STATES' do
12
+ it 'returns all U.S. state codes and names' do
13
+ expect(Yt::US_STATES[:CA]).to eq 'California'
14
+ expect(Yt::US_STATES['CO']).to eq 'Colorado'
15
+ end
16
+ end
@@ -86,7 +86,7 @@ describe Yt::Annotation do
86
86
  end
87
87
 
88
88
  context 'given an annotation with an embedded playlist link' do
89
- let(:xml) { '<TEXT>https://www.youtube.com/watch?v=MESycYJytkU&amp;list=LLxO1tY8h1AhOz0T4ENwmpow"</TEXT>' }
89
+ let(:xml) { '<TEXT>https://www.youtube.com/watch?v=9bZkp7q19f0&amp;list=LLxO1tY8h1AhOz0T4ENwmpow"</TEXT>' }
90
90
  it { expect(annotation).to have_link_to_playlist }
91
91
  end
92
92
 
@@ -20,8 +20,8 @@ describe Yt::Claim do
20
20
 
21
21
  describe '#video_id' do
22
22
  context 'given fetching a claim returns an videoId' do
23
- let(:data) { {"videoId"=>"MESycYJytkU"} }
24
- it { expect(claim.video_id).to eq 'MESycYJytkU' }
23
+ let(:data) { {"videoId"=>"9bZkp7q19f0"} }
24
+ it { expect(claim.video_id).to eq '9bZkp7q19f0' }
25
25
  end
26
26
  end
27
27
 
@@ -208,4 +208,16 @@ describe Yt::Claim do
208
208
  it { expect(claim.third_party?).to be false }
209
209
  end
210
210
  end
211
- end
211
+
212
+ describe '#source' do
213
+ context 'given fetching a claim returns a source' do
214
+ let(:data) { {"origin"=>{"source"=>"webUpload"}} }
215
+ it { expect(claim.source).to eq 'webUpload' }
216
+ end
217
+
218
+ context 'given fetching a claim does not return a source' do
219
+ let(:data) { {"origin"=>{}} }
220
+ it { expect(claim.source).to eq nil }
221
+ end
222
+ end
223
+ end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+ require 'yt/models/comment'
3
+
4
+ describe Yt::Comment do
5
+ subject(:comment) { Yt::Comment.new attrs }
6
+
7
+ describe '#snippet' do
8
+ context 'given fetching a comment returns a snippet' do
9
+ let(:attrs) { {snippet: {"videoId" => "12345"}} }
10
+ it { expect(comment.snippet).to be_a Yt::Snippet }
11
+ end
12
+ end
13
+
14
+ describe 'attributes' do
15
+ examples = {
16
+ video_id: {with: 'xyz123', without: nil},
17
+ parent_id: {with: 'abc123', without: nil},
18
+ text_display: {with: 'awesome', without: nil},
19
+ author_display_name: {with: 'John', without: nil},
20
+ like_count: {with: 10, without: nil},
21
+ updated_at: {input: '2016-03-22T12:56:56.3Z', with: Time.parse('2016-03-22T12:56:56.3Z'), without: nil},
22
+ }
23
+
24
+ examples.each do |attr, cases|
25
+ describe "##{attr}" do
26
+ context "given a snippet with a #{attr}" do
27
+ let(:attrs) {
28
+ {snippet: {"#{attr.to_s.camelize(:lower)}" => cases[:input] || cases[:with]}}}
29
+ it { expect(comment.send(attr)).to eq cases[:with] }
30
+ end
31
+
32
+ context "given a snippet without a #{attr}" do
33
+ let(:attrs) { {snippet: {}} }
34
+ it { expect(comment.send(attr)).to eq cases[:without] }
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+
@@ -0,0 +1,93 @@
1
+ require 'spec_helper'
2
+ require 'yt/models/comment_thread'
3
+
4
+ describe Yt::CommentThread do
5
+ subject(:comment_thread) { Yt::CommentThread.new attrs }
6
+
7
+ describe '#snippet' do
8
+ context 'given fetching a comment thread returns a snippet' do
9
+ let(:attrs) { {snippet: {"videoId" => "12345"}} }
10
+ it { expect(comment_thread.snippet).to be_a Yt::Snippet }
11
+ end
12
+ end
13
+
14
+ describe '#video_id' do
15
+ context 'given a snippet with a video id' do
16
+ let(:attrs) { {snippet: {"videoId"=>"12345"}} }
17
+ it { expect(comment_thread.video_id).to eq '12345' }
18
+ end
19
+
20
+ context 'given a snippet without a video id' do
21
+ let(:attrs) { {snippet: {}} }
22
+ it { expect(comment_thread.video_id).to be_nil }
23
+ end
24
+ end
25
+
26
+ describe '#top_level_comment' do
27
+ context 'given a snippet with a top level comment' do
28
+ let(:attrs) { {snippet: {"topLevelComment"=> {}}} }
29
+ it { expect(comment_thread.top_level_comment).to be_a Yt::Comment }
30
+ end
31
+ end
32
+
33
+ describe 'attributes from #top_level_comment delegations' do
34
+ context 'with values' do
35
+ let(:attrs) { {snippet: {"topLevelComment"=> {"id" => "xyz123", "snippet" => {
36
+ "textDisplay" => "funny video!",
37
+ "authorDisplayName" => "fullscreen",
38
+ "likeCount" => 99,
39
+ "updatedAt" => "2016-03-22T12:56:56.3Z"}}}} }
40
+
41
+ it { expect(comment_thread.top_level_comment.id).to eq 'xyz123' }
42
+ it { expect(comment_thread.text_display).to eq 'funny video!' }
43
+ it { expect(comment_thread.author_display_name).to eq 'fullscreen' }
44
+ it { expect(comment_thread.like_count).to eq 99 }
45
+ it { expect(comment_thread.updated_at).to eq Time.parse('2016-03-22T12:56:56.3Z') }
46
+ end
47
+
48
+ context 'without values' do
49
+ let(:attrs) { {snippet: {"topLevelComment"=> {"snippet" => {}}}} }
50
+
51
+ it { expect(comment_thread.text_display).to be_nil }
52
+ it { expect(comment_thread.author_display_name).to be_nil }
53
+ it { expect(comment_thread.like_count).to be_nil }
54
+ it { expect(comment_thread.updated_at).to be_nil }
55
+ end
56
+ end
57
+
58
+ describe '#total_reply_count' do
59
+ context 'given a snippet with a total reply count' do
60
+ let(:attrs) { {snippet: {"totalReplyCount"=>1}} }
61
+ it { expect(comment_thread.total_reply_count).to eq 1 }
62
+ end
63
+
64
+ context 'given a snippet without a total reply count' do
65
+ let(:attrs) { {snippet: {}} }
66
+ it { expect(comment_thread.total_reply_count).to be_nil }
67
+ end
68
+ end
69
+
70
+ describe '#can_reply?' do
71
+ context 'given a snippet with canReply set' do
72
+ let(:attrs) { {snippet: {"canReply"=>true}} }
73
+ it { expect(comment_thread.can_reply?).to be true }
74
+ end
75
+
76
+ context 'given a snippet without canReply set' do
77
+ let(:attrs) { {snippet: {}} }
78
+ it { expect(comment_thread.can_reply?).to be false }
79
+ end
80
+ end
81
+
82
+ describe '#is_public?' do
83
+ context 'given a snippet with isPublic set' do
84
+ let(:attrs) { {snippet: {"isPublic"=>true}} }
85
+ it { expect(comment_thread).to be_public }
86
+ end
87
+
88
+ context 'given a snippet without isPublic set' do
89
+ let(:attrs) { {snippet: {}} }
90
+ it { expect(comment_thread).to_not be_public }
91
+ end
92
+ end
93
+ end
@@ -42,4 +42,11 @@ describe Yt::ContentDetail do
42
42
  it { expect(content_detail.duration).to eq 51 }
43
43
  end
44
44
  end
45
+
46
+ describe '#length' do
47
+ context 'returns the duration in HH:MM:SS' do
48
+ let(:data) { {"duration"=>"PT1H18M52S"} }
49
+ it { expect(content_detail.length).to eq '01:18:52' }
50
+ end
51
+ end
45
52
  end
@@ -156,8 +156,8 @@ describe Yt::Reference do
156
156
 
157
157
  describe '#video_id' do
158
158
  context 'given fetching a reference returns an videoId' do
159
- let(:data) { {"videoId"=>"MESycYJytkU"} }
160
- it { expect(reference.video_id).to eq 'MESycYJytkU' }
159
+ let(:data) { {"videoId"=>"9bZkp7q19f0"} }
160
+ it { expect(reference.video_id).to eq '9bZkp7q19f0' }
161
161
  end
162
162
  end
163
163
 
@@ -178,6 +178,27 @@ describe Yt::Request do
178
178
  it { expect{request.run}.not_to fail }
179
179
  end
180
180
  end
181
+
182
+ # NOTE: This test is just a reflection of YouTube irrational behavior of
183
+ # being unavailable once in a while, and therefore causing Net::HTTP to
184
+ # fail, although retrying after some seconds works.
185
+ context 'a SocketError', ruby21: true do
186
+ let(:http_error) { SocketError.new }
187
+
188
+ context 'every time' do
189
+ before { expect(Net::HTTP).to receive(:start).at_least(:once).and_raise http_error }
190
+
191
+ it { expect{request.run}.to fail }
192
+ end
193
+
194
+ context 'but works the second time' do
195
+ before { expect(Net::HTTP).to receive(:start).at_least(:once).and_return retry_response }
196
+ before { allow(retry_response).to receive(:body) }
197
+ let(:retry_response) { Net::HTTPOK.new nil, nil, nil }
198
+
199
+ it { expect{request.run}.not_to fail }
200
+ end
201
+ end
181
202
  end
182
203
  end
183
204
  end
@@ -4,21 +4,6 @@ require 'yt/models/resource'
4
4
  describe Yt::Resource do
5
5
  subject(:resource) { Yt::Resource.new attrs }
6
6
 
7
- context 'given a resource initialized with a URL (containing an ID)' do
8
- let(:attrs) { {url: 'youtu.be/MESycYJytkU'} }
9
-
10
- it { expect(resource.id).to eq 'MESycYJytkU' }
11
- it { expect(resource.kind).to eq 'video' }
12
- it { expect(resource.username).to be_nil }
13
- end
14
-
15
- context 'given a resource initialized with a URL (containing a username)' do
16
- let(:attrs) { {url: 'youtube.com/fullscreen'} }
17
-
18
- it { expect(resource.kind).to eq 'channel' }
19
- it { expect(resource.username).to eq 'fullscreen' }
20
- end
21
-
22
7
  describe '#public?' do
23
8
  context 'given fetching a status returns privacyStatus "public"' do
24
9
  let(:attrs) { {status: {"privacyStatus"=>"public"}} }
@@ -649,7 +649,7 @@ describe Yt::Video do
649
649
  end
650
650
 
651
651
  describe '#update' do
652
- let(:attrs) { {id: 'MESycYJytkU', snippet: {'title'=>'old'}} }
652
+ let(:attrs) { {id: '9bZkp7q19f0', snippet: {'title'=>'old'}} }
653
653
  before { expect(video).to receive(:do_update).and_yield 'snippet'=>{'title'=>'new'} }
654
654
 
655
655
  it { expect(video.update title: 'new').to be true }
@@ -27,10 +27,7 @@ describe Yt::Account, :device_app do
27
27
  expect(uploads).not_to be_empty
28
28
  end
29
29
 
30
- specify 'includes private playlists (such as Watch Later or History)' do
31
- watch_later = related_playlists.select{|p| p.title == 'Watch Later'}
32
- expect(watch_later).not_to be_empty
33
-
30
+ specify 'includes private playlists (such as History)' do
34
31
  history = related_playlists.select{|p| p.title == 'History'}
35
32
  expect(history).not_to be_empty
36
33
  end
@@ -97,6 +94,21 @@ describe Yt::Account, :device_app do
97
94
  end
98
95
  end
99
96
 
97
+ describe '.video_groups' do
98
+ let(:video_group) { $account.video_groups.first }
99
+
100
+ specify 'returns the first video-group created by the account' do
101
+ expect(video_group).to be_a Yt::VideoGroup
102
+ expect(video_group.title).to be_a String
103
+ expect(video_group.item_count).to be_an Integer
104
+ expect(video_group.published_at).to be_a Time
105
+ end
106
+
107
+ specify 'allows to run reports against each video-group' do
108
+ expect(video_group.views).to be
109
+ end
110
+ end
111
+
100
112
  describe '.upload_video' do
101
113
  let(:video_params) { {title: 'Test Yt upload', privacy_status: 'private', category_id: 17} }
102
114
  let(:video) { $account.upload_video path_or_url, video_params }
@@ -41,7 +41,7 @@ describe Yt::Account, :device_app do
41
41
  end
42
42
 
43
43
  context 'that is invalid' do
44
- let(:authorization_code) { '--not-a-valid-authorization-code--' }
44
+ let(:authorization_code) { rand(36**20).to_s(36) }
45
45
  it { expect{account.authentication}.to raise_error Yt::Errors::Unauthorized }
46
46
  end
47
47
  end
@@ -104,18 +104,6 @@ describe Yt::Account, :device_app do
104
104
 
105
105
  it { expect{account.authentication}.to raise_error Yt::Errors::MissingAuth }
106
106
  end
107
-
108
- context 'and no device token' do
109
- it { expect{account.authentication}.to raise_error Yt::Errors::MissingAuth }
110
- end
111
-
112
- # NOTE: This test is commented out because of YouTube irrational behavior
113
- # of using to return "MissingAuth" when passing a wrong device code, and
114
- # now randomly returning `{"error"=>"internal_failure"}` instead.
115
- # context 'and an invalid device code' do
116
- # before { attrs[:device_code] = '--not-a-valid-device-code--' }
117
- # it { expect{account.authentication}.to raise_error Yt::Errors::MissingAuth }
118
- # end
119
107
  end
120
108
 
121
109
  context 'given no token or code' do