directlink 0.0.8.8 → 0.0.11.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: '097dd2d9c1fd6437aeeda4edb35f26ba8cbac99c'
4
- data.tar.gz: 191d639d9b83f310d2497f735257d34868694da9
3
+ metadata.gz: 30657b1850442ca6f1267344dc356cdd3e0e55e8
4
+ data.tar.gz: b8dc86a2fb80cadb1583207934840fc28fdb6177
5
5
  SHA512:
6
- metadata.gz: 7be490bc74cb4dd909cfc866735170c26bdf498047aa61ad8c6f22f7d3131f8b6ae532f228a07174dabf763248d99c60b02a16b7f4b464b58308c2d2359cc833
7
- data.tar.gz: b92394db6e24b26553b93d87af54ceecc9ee4b079de6c2d2823ce65a995022e870006045b4b4b4f24aedba53822499a4ac9b3a6402c90e61bcad703b86caf0a0
6
+ metadata.gz: d11e9b0f07127be8b711495f1496e527bd979c015a0376512e916c5f92fda19fcfd617d67584c768c1c223ebd41cb57c28bbf258b39659cdd12d5db4653f4120
7
+ data.tar.gz: 8d6beddd649ec2f1316009abe70c9224de3a34d8234443a040d492066b792b79d9f3fe3771b9906949982f1f5d12ff00a4dbebd9468bda2db63941e74e34f663
data/bin/directlink CHANGED
@@ -68,18 +68,13 @@ begin
68
68
  (t.is_a?(Array) ? t : [t]).each{ |s| puts "=> #{s.url}\n #{s.type} #{s.width}x#{s.height}" }
69
69
  end
70
70
  end
71
- rescue SocketError,
72
- Net::OpenTimeout,
73
- Errno::ECONNRESET,
74
- NetHTTPUtils::Error,
75
- FastImage::UnknownImageType,
76
- FastImage::ImageFetchFailure,
77
- # DirectLink::ErrorMissingEnvVar,
78
- # DirectLink::ErrorAssert,
79
- DirectLink::ErrorNotFound,
80
- DirectLink::ErrorBadLink => e
71
+ rescue *DirectLink::NORMAL_EXCEPTIONS => e
81
72
  puts e.backtrace if debug
82
73
  cause = e.cause if e.cause if e.respond_to? :cause
83
74
  c = e.class.to_s
84
75
  abort "#{c}#{": #{e}" if c != e.to_s}#{": #{cause}" if cause && c != cause.to_s}"
76
+ rescue
77
+ raise unless $!.cause
78
+ raise $!.cause unless $!.cause.cause
79
+ raise $!.cause.cause
85
80
  end
data/directlink.gemspec CHANGED
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |spec|
2
2
  spec.name = "directlink"
3
- spec.version = "0.0.8.8"
4
- spec.summary = "converts any kind of image hyperlink to direct link, type of image and its resolution"
3
+ spec.version = "0.0.11.0"
4
+ spec.summary = "obtains from any kind of hyperlink a link to an image, its format and resolution"
5
5
 
6
6
  spec.author = "Victor Maslov aka Nakilon"
7
7
  spec.email = "nakilon@gmail.com"
@@ -9,21 +9,23 @@ Gem::Specification.new do |spec|
9
9
  spec.homepage = "https://github.com/nakilon/directlink"
10
10
  spec.metadata = {"source_code_uri" => "https://github.com/nakilon/directlink"}
11
11
 
12
- spec.add_dependency "fastimage", "~>2.1.3"
13
- spec.add_dependency "nokogiri"
14
- spec.add_dependency "nethttputils", "~>0.4.0.0"
15
- spec.add_dependency "reddit_bot", "~>1.7.0"
12
+ spec.required_ruby_version = ">=2.3" # because <<~ heredocs in tests
13
+
14
+ spec.add_dependency "fastimage", "~>2.2.0"
15
+ spec.add_dependency "nokogiri", "<1.11" # 1.11 requires ruby 2.5
16
+ spec.add_dependency "nethttputils", "~>0.4.1.0"
17
+ spec.add_dependency "reddit_bot", "~>1.9.0"
16
18
  spec.add_dependency "kramdown"
17
19
  spec.add_dependency "addressable"
18
- spec.add_development_dependency "minitest"
19
- spec.add_development_dependency "byebug"
20
+ spec.add_development_dependency "minitest-around"
21
+ spec.add_development_dependency "webmock"
20
22
 
21
23
  spec.require_path = "lib"
22
24
  spec.bindir = "bin"
23
25
  spec.executable = "directlink"
24
- spec.test_file = "test.rb"
25
- spec.files = `git ls-files -z`.split(?\0) - spec.test_files
26
+ spec.test_file = "unit.test.rb"
27
+ spec.files = %w{ LICENSE directlink.gemspec lib/directlink.rb bin/directlink }
26
28
 
27
- spec.requirements << "you may need to create apps and provide their API tokens:"
29
+ spec.requirements << "you may want to create apps and provide API tokens:"
28
30
  spec.requirements << "IMGUR_CLIENT_ID, FLICKR_API_KEY, REDDIT_SECRETS"
29
31
  end
data/lib/directlink.rb CHANGED
@@ -2,17 +2,16 @@ module DirectLink
2
2
 
3
3
  class << self
4
4
  attr_accessor :silent
5
- end
6
- self.silent = false
7
- class << self
8
5
  attr_accessor :logger
6
+ attr_accessor :timeout
9
7
  end
8
+ self.silent = false
10
9
  self.logger = Object.new
11
10
  self.logger.define_singleton_method :error do |str|
12
11
  puts str unless Module.nesting.first.silent
13
12
  end
14
13
 
15
- class ErrorAssert < RuntimeError
14
+ class ErrorAssert < RuntimeError # gem user should not face this error
16
15
  def initialize msg
17
16
  super "#{msg} -- consider reporting this issue to GitHub"
18
17
  end
@@ -35,6 +34,22 @@ module DirectLink
35
34
  end
36
35
  end
37
36
 
37
+ require "nethttputils"
38
+ require "fastimage"
39
+ NORMAL_EXCEPTIONS = [
40
+ SocketError,
41
+ Net::OpenTimeout,
42
+ Errno::ECONNRESET,
43
+ Errno::ECONNREFUSED,
44
+ Errno::ETIMEDOUT, # from FastImage
45
+ NetHTTPUtils::Error,
46
+ NetHTTPUtils::EOFError_from_rbuf_fill,
47
+ FastImage::UnknownImageType,
48
+ FastImage::ImageFetchFailure,
49
+ DirectLink::ErrorNotFound,
50
+ DirectLink::ErrorBadLink,
51
+ ] # all known exceptions that can be raised while using Directlink but not as its fault
52
+
38
53
 
39
54
  def self.google src, width = 0
40
55
  # this can handle links without schema because it's used for parsing community HTML pages
@@ -44,10 +59,10 @@ module DirectLink
44
59
  "#{$1}s#{width}/"
45
60
  when /\A(\/\/lh3\.googleusercontent\.com\/proxy\/[a-zA-Z0-9_-]{66,523}=)(?:w(?:[45]\d\d)-h\d\d\d-[np]|s530-p|s110-p-k)\z/
46
61
  "https:#{$1}s#{width}/"
47
- when /\A(\/\/lh3\.googleusercontent\.com\/cOh2Nsv7EGo0QbuoKxoKZVZO_NcBzufuvPtzirMJfPmAzCzMtnEncfA7zGIDTJfkc1YZFX2MhgKnjA=)w530-h398-p\z/
62
+ when /\A(\/\/lh3\.googleusercontent\.com\/[a-zA-Z0-9]{24}_[a-zA-Z]{30}7zGIDTJfkc1YZFX2MhgKnjA=)w530-h398-p\z/
48
63
  "https:#{$1}s#{width}/"
49
64
  when /\A(\/\/lh3\.googleusercontent\.com\/-[a-zA-Z0-9-]{11}\/[VW][a-zA-Z0-9_-]{9}I\/AAAAAAA[AC][a-zA-Z0-9]{3}\/[a-zA-Z0-9_-]{32}[gwAQ]CJoC\/)w530-h[23]\d\d-p\/[^\/]+\z/,
50
- /\A(?:https?:)?(\/\/[1-4]\.bp\.blogspot\.com\/-[a-zA-Z0-9_-]{11}\/[UVWX][a-zA-Z0-9_-]{9}I\/AAAAAAAA[A-Z][a-zA-Z0-9_-]{2}\/[a-zA-Z0-9_-]{33}C(?:EwYBhgL|(?:Lc|Kg)BGAs)\/)(?:s640|w\d\d\d?-h\d\d\d?-p(?:-k-no-nu)?)\/[^\/]+\z/,
65
+ /\A(?:https?:)?(\/\/[1-4]\.bp\.blogspot\.com\/-[a-zA-Z0-9_-]{11}\/[UVWX][a-zA-Z0-9_-]{9}I\/AAAAAAAA[A-Z][a-zA-Z0-9_-]{2}\/[a-zA-Z0-9_-]{33}C(?:EwYBhgL|(?:Lc|Kg)BGAs(?:YHQ)?)\/)(?:s640|w\d{2,4}-h\d\d\d?-p(?:-k-no-nu)?)\/[^\/]+\z/,
51
66
  /\A(?:https?:)?(\/\/[1-4]\.bp\.blogspot\.com\/-[a-zA-Z0-9-]{11}\/[UV][a-zA-Z0-9_-]{9}I\/AAAAAAAA[A-Z][a-zA-Z0-9]{2}\/[a-zA-Z0-9-]{11}\/)w72-h72-p-k-no-nu\/[^\/]+\z/
52
67
  "https:#{$1}s#{width}/"
53
68
  when /\A(https:\/\/lh3\.googleusercontent\.com\/-[a-zA-Z0-9_]{11}\/AAAAAAAAAAI\/AAAAAAAAAAQ\/[a-zA-Z0-9_]{11}\/)w530-h[13]\d\d-n\/[^\/]+\z/,
@@ -65,18 +80,24 @@ module DirectLink
65
80
  /\A(https:\/\/lh[356]\.googleusercontent\.com\/-[a-zA-Z0-9]{11}\/AAAAAAAAAAI\/AAAAAAAAAAA\/[a-zA-Z0-9_]{34}\/)s(?:46|64)-c(?:-k(?:-no)?)?-mo\/photo\.jpg\z/
66
81
  "#{$1}s#{width}/"
67
82
  # Google Keep
68
- when /\A(https:\/\/lh\d\.googleusercontent\.com\/[a-zA-Z0-9_-]{104,106}=s)\d\d\d\d?\Z/
83
+ when /\A(https:\/\/lh\d\.googleusercontent\.com\/[a-zA-Z0-9_-]{104,106}=s)\d\d\d\d?\z/
69
84
  "#{$1}#{width}"
85
+ # opensea
86
+ when /\A(https:\/\/lh3\.googleusercontent\.com\/[a-zA-Z0-9]{78}-nGx_jf_XGqqiVANe_Jr8u2g=)w1400-k\z/
87
+ "#{$1}s#{width}"
70
88
  # mp4
71
89
  when /\A(https:\/\/lh3\.googleusercontent\.com\/-[a-zA-Z]{11}\/W[a-zA-Z0-9]{9}I\/AAAAAAAAODw\/[a-zA-Z0-9]{32}QCJoC\/)w530-h883-n-k-no\/[^\/]+\.mp4\z/
72
90
  "#{$1}s#{width}/"
91
+ # something else
92
+ when /\A(https:\/\/lh3\.googleusercontent\.com\/-[a-zA-Z0-9_]{11}\/X-[a-zA-Z0-9]{8}I\/AAAAAAAAALE\/[a-zA-Z0-9]{23}_[a-zA-Z0-9]{19}\/)w1200-h630-p-k-no-nu\/[\d-]+\.png\z/
93
+ "#{$1}s#{width}/"
73
94
  else
74
95
  raise ErrorBadLink.new src
75
96
  end
76
97
  end
77
98
 
99
+
78
100
  require "json"
79
- require "nethttputils"
80
101
 
81
102
  # TODO make the timeout handling respect the way the Directlink method works with timeouts
82
103
  def self.imgur link, timeout = 1000
@@ -88,7 +109,7 @@ module DirectLink
88
109
  NetHTTPUtils.request_data url, header: { Authorization: "Client-ID #{ENV["IMGUR_CLIENT_ID"]}" }
89
110
  rescue NetHTTPUtils::Error => e
90
111
  raise ErrorNotFound.new url.inspect if 404 == e.code
91
- if t < timeout && [400, 500, 503].include?(e.code)
112
+ if t < timeout && [400, 500, 502, 503].include?(e.code)
92
113
  logger.error "retrying in #{t} seconds because of Imgur HTTP ERROR #{e.code}"
93
114
  sleep t
94
115
  t *= 2
@@ -107,16 +128,16 @@ module DirectLink
107
128
  elsif data["images"]
108
129
  raise ErrorNotFound.new link.inspect if data["images"].empty?
109
130
  data["images"]
110
- elsif data["type"] && data["type"].start_with?("image/")
131
+ elsif data["type"] && %w{ image/jpeg image/png image/gif video/mp4 }.include?(data["type"])
111
132
  # TODO check if this branch is possible at all
112
133
  [ data ]
113
134
  # elsif data["comment"]
114
135
  # fi["https://imgur.com/" + data["image_id"]]
115
136
  else
116
137
  # one day single-video item should hit this but somehow it didn't yet
117
- raise ErrorAssert.new "unknown data format #{data.inspect} for #{link}"
138
+ raise ErrorAssert.new "unknown data format #{json} for #{link}"
118
139
  end
119
- when /\Ahttps?:\/\/(?:(?:i|m|www)\.)?imgur\.com\/([a-zA-Z0-9]{7,8})(?:\.(?:gifv|jpg(?:\?fb)?|png))?\z/,
140
+ when /\Ahttps?:\/\/(?:(?:i|m|www)\.)?imgur\.com\/([a-zA-Z0-9]{7,8})(?:\.(?:gifv|jpe?g(?:\?fb)?|png))?\z/,
120
141
  /\Ahttps?:\/\/(?:(?:i|m|www)\.)?imgur\.com\/([a-zA-Z0-9]{5})\.mp4\z/,
121
142
  /\Ahttps?:\/\/imgur\.com\/([a-zA-Z0-9]{5}(?:[a-zA-Z0-9]{2})?)\z/,
122
143
  /\Ahttps?:\/\/imgur\.com\/([a-zA-Z0-9]{7})(?:\?\S+)?\z/,
@@ -128,7 +149,7 @@ module DirectLink
128
149
  raise ErrorBadLink.new link
129
150
  end.map do |image|
130
151
  case image["type"]
131
- when "image/jpeg", "image/png", "image/gif", "video/mp4"
152
+ when *%w{ image/jpeg image/png image/gif video/mp4 }
132
153
  image.values_at "link", "width", "height", "type"
133
154
  else
134
155
  raise ErrorAssert.new "unknown type of #{link}: #{image}"
@@ -137,9 +158,8 @@ module DirectLink
137
158
  end
138
159
 
139
160
  def self._500px link
140
- raise ErrorBadLink.new link unless %r{\Ahttps://500px\.com/photo/(?<id>[^/]+)/[^/]+\z} =~ link
161
+ raise ErrorBadLink.new link unless %r{\Ahttps://500px\.com/photo/(?<id>[^/]+)/[-[a-zA-Z0-9]%]+\/?\z} =~ link
141
162
  require "nokogiri"
142
- resp = NetHTTPUtils.request_data link
143
163
  f = lambda do |form|
144
164
  JSON.load(NetHTTPUtils.request_data "https://api.500px.com/v1/photos", form: form).fetch("photos").values.first
145
165
  end
@@ -191,11 +211,11 @@ module DirectLink
191
211
  attr_accessor :reddit_bot
192
212
  end
193
213
  def self.reddit link, timeout = 1000
194
- unless id = URI(link).path[/\A(?:\/r\/[0-9a-zA-Z_]+)?(?:\/comments|\/duplicates)?\/([0-9a-z]{5,6})(?:\/|\z)/, 1]
195
- raise DirectLink::ErrorBadLink.new link unless URI(link).host &&
196
- URI(link).host.split(?.) == %w{ i redd it } &&
197
- URI(link).path[/\A\/[a-z0-9]{12,13}\.(gif|jpg)\z/]
198
- return [true, link]
214
+ return [true, link] if URI(link).host &&
215
+ URI(link).host.split(?.) == %w{ i redd it } &&
216
+ URI(link).path[/\A\/[a-z0-9]{12,13}\.(gif|jpg)\z/]
217
+ unless id = link[/\Ahttps:\/\/www\.reddit\.com\/gallery\/([0-9a-z]{5,6})\z/, 1]
218
+ raise DirectLink::ErrorBadLink.new link unless id = URI(link).path[/\A(?:\/r\/[0-9a-zA-Z_]+)?(?:\/comments|\/duplicates)?\/([0-9a-z]{5,6})(?:\/|\z)/, 1]
199
219
  end
200
220
  retry_on_json_parseerror = lambda do |&b|
201
221
  t = 1
@@ -221,14 +241,21 @@ module DirectLink
221
241
  raise ErrorAssert.new "our knowledge about Reddit API seems to be outdated" unless json.size == 2
222
242
  json.find{ |_| _["data"]["children"].first["kind"] == "t3" }
223
243
  end
244
+ # TODO: do we handle linking Imgur albums?
224
245
  data = json["data"]["children"].first["data"]
225
- if data["media"]["reddit_video"]
226
- return [true, data["media"]["reddit_video"]["fallback_url"]]
227
- else
246
+ if data["media"]
247
+ return [true, data["media"]["reddit_video"]["fallback_url"]] if data["media"]["reddit_video"]
228
248
  raise ErrorAssert.new "our knowledge about Reddit API seems to be outdated" unless data["media"].keys.sort == %w{ oembed type } && %w{ youtube.com gfycat.com imgur.com }.include?(data["media"]["type"])
229
249
  return [true, data["media"]["oembed"]["thumbnail_url"]]
230
- end if data["media"]
231
- return [true, data["url"]] if data["crosspost_parent"]
250
+ end
251
+ if data["media_metadata"]
252
+ return [true, data["media_metadata"].values.map do |media|
253
+ next if media == {"status"=>"failed"} || media == {"status"=>"unprocessed"}
254
+ raise ErrorAssert.new "our knowledge about Reddit API seems to be outdated (media == #{media.inspect})" unless media["status"] == "valid"
255
+ [media["m"], *media["s"].values_at("x", "y"), CGI.unescapeHTML(media["s"][media["m"]=="image/gif" ? "gif" : "u"])]
256
+ end.compact]
257
+ end
258
+ return [true, "#{"https://www.reddit.com" if /\A\/r\/[0-9a-zA-Z_]+\/comments\/[0-9a-z]{5,6}\// =~ data["url"]}#{data["url"]}"] if data["crosspost_parent"]
232
259
  return [true, data["url"]] unless data["is_self"]
233
260
  raise ErrorAssert.new "our knowledge about Reddit API seems to be outdated" if data["url"] != "https://www.reddit.com" + data["permalink"]
234
261
  return [false, data["selftext"]]
@@ -236,46 +263,54 @@ module DirectLink
236
263
 
237
264
  def self.vk link
238
265
  id, mtd, field, f = case link
239
- when %r{\Ahttps://vk\.com/id(?<user_id>\d+)\?z=photo(?<id>\k<user_id>_\d+)(%2F(album\k<user_id>_0|photos\k<user_id>))\z},
266
+ when %r{\Ahttps://vk\.com/id(?<user_id>\d+)\?z=photo(?<id>\k<user_id>_\d+)(%2F(album\k<user_id>_0|photos\k<user_id>))?\z},
240
267
  %r{\Ahttps://vk\.com/[a-z_]+\?z=photo(?<_>)(?<id>(?<user_id>\d+)_\d+)%2Fphotos\k<user_id>\z},
241
- %r{\Ahttps://vk\.com/photo(?<_>)(?<id>-?\d+_\d+)(\?all=1)?\z},
268
+ %r{\Ahttps://vk\.com/photo(?<_>)(?<id>-?\d+_\d+)(\?(?:all|rev)=1)?\z},
242
269
  %r{\Ahttps://vk\.com/feed\?section=likes&z=photo(?<_>)(?<id>-(?<user_id>\d+)_\d+)%2F(liked\d+|album\k<user_id>_0)\z},
243
270
  %r{\Ahttps://vk\.com/[a-z_]+\?z=photo(?<_>)(?<id>(?<user_id>-\d+)_\d+)%2F(wall\k<user_id>_\d+|album\k<user_id>_0)\z},
244
271
  %r{\Ahttps://vk\.com/wall(?<user_id>-\d+)_\d+\?z=photo(?<id>\k<user_id>_\d+)%2F(wall\k<user_id>_\d+|album\k<user_id>_00%2Frev|\d+)\z}
245
272
  [$2, :photos, :photos, lambda do |t|
246
273
  raise ErrorAssert.new "our knowledge about VK API seems to be outdated" unless 1 == t.size
247
- t.first
274
+ t
248
275
  end ]
249
- when %r{\Ahttps://vk\.com/wall(?<id>-\d+_\d+)\z}
276
+ when %r{\Ahttps://vk\.com/wall(?<id>-?\d+_\d+)\z},
277
+ %r{\Ahttps://vk\.com/[a-z\.]+\?w=wall(?<id>-?\d+_\d+)\z}
250
278
  [$1, :wall, :posts, lambda do |t|
251
- t.first.fetch("attachments").tap do |t|
252
- raise ErrorAssert.new "our knowledge about VK API seems to be outdated" unless 1 == t.size
253
- end.first.tap do |t|
254
- raise ErrorAssert.new "our knowledge about VK API seems to be outdated" unless %w{ type photo } == t.keys
255
- end.fetch("photo")
279
+ t.first.fetch("attachments").select do |item|
280
+ case item.keys
281
+ when %w{ type photo }
282
+ raise ErrorAssert.new "our knowledge about VK API seems to be outdated" unless item["type"] == "photo"
283
+ next true
284
+ when %w{ type audio }
285
+ raise ErrorAssert.new "our knowledge about VK API seems to be outdated" unless item["type"] == "audio"
286
+ else
287
+ raise ErrorAssert.new "our knowledge about VK API seems to be outdated"
288
+ end
289
+ end.map{ |i| i.fetch "photo" }
256
290
  end ]
257
291
  else
258
292
  raise ErrorBadLink.new link
259
293
  end
260
294
  raise ErrorMissingEnvVar.new "define VK_ACCESS_TOKEN and VK_CLIENT_SECRET env vars" unless ENV["VK_ACCESS_TOKEN"] && ENV["VK_CLIENT_SECRET"]
261
- sleep 0.25 # "error_msg"=>"Too many requests per second"
295
+ sleep 0.25 unless ENV["CI"] # "error_msg"=>"Too many requests per second"
262
296
  f.call( JSON.load( NetHTTPUtils.request_data "https://api.vk.com/method/#{mtd}.getById",
263
297
  :POST, form: { field => id, :access_token => ENV["VK_ACCESS_TOKEN"], :client_secret => ENV["VK_CLIENT_SECRET"], :v => "5.101" }
264
- ).fetch("response") ).fetch("sizes").map do |s|
265
- s.values_at("width", "height", "url").tap do |whu|
266
- w, h, u = whu
267
- whu[0, 2] = FastImage.new(u, raise_on_failure: true).size if [w, h].include? 0
268
- end
269
- end.max_by{ |w, h, u| w * h }
298
+ ).fetch("response") ).map do |photos|
299
+ photos.fetch("sizes").map do |size|
300
+ size.values_at("width", "height", "url").tap do |whu|
301
+ w, h, u = whu
302
+ whu[0, 2] = FastImage.new(u, raise_on_failure: true).size if [w, h].include? 0 # wtf?
303
+ end
304
+ end.max_by{ |w, h, u| w * h }
305
+ end
270
306
  end
271
307
 
272
308
  class_variable_set :@@directlink, Struct.new(:url, :width, :height, :type)
273
309
  end
274
310
 
275
311
 
276
- require "fastimage"
277
-
278
- def DirectLink link, timeout = nil, giveup: false, ignore_meta: false
312
+ def DirectLink link, timeout = nil, proxy = nil, giveup: false, ignore_meta: false
313
+ timeout ||= DirectLink.timeout
279
314
  ArgumentError.new("link should be a <String>, not <#{link.class}>") unless link.is_a? String
280
315
  begin
281
316
  URI link
@@ -313,15 +348,16 @@ def DirectLink link, timeout = nil, giveup: false, ignore_meta: false
313
348
  **( %w{ reddit com } == URI(link).host.split(?.).last(2) ||
314
349
  %w{ redd it } == URI(link).host.split(?.) ? {Cookie: "over18=1"} : {} ),
315
350
  }
316
- head = NetHTTPUtils.request_data link, :head, header: header, **(timeout ? {
351
+ head = NetHTTPUtils.request_data link, :HEAD, header: header, **(proxy ? {proxy: proxy} : {}), **(timeout ? {
317
352
  timeout: timeout,
318
353
  max_start_http_retry_delay: timeout,
319
- max_read_retry_delay: timeout
354
+ max_read_retry_delay: timeout,
320
355
  } : {})
321
- rescue Net::ReadTimeout
356
+ rescue Net::ReadTimeout, Errno::ETIMEDOUT
322
357
  rescue NetHTTPUtils::Error => e
323
358
  raise unless 418 == e.code
324
359
  else
360
+ raise DirectLink::ErrorAssert.new "last_response.uri is not set" unless head.instance_variable_get(:@last_response).uri
325
361
  link = head.instance_variable_get(:@last_response).uri.to_s
326
362
  end
327
363
 
@@ -329,10 +365,10 @@ def DirectLink link, timeout = nil, giveup: false, ignore_meta: false
329
365
  # because they can be hidden behind URL shorteners
330
366
  # also it can resolve NetHTTPUtils::Error(404) before trying the adapter
331
367
 
332
- t = google_without_schema_crutch[] and return t
368
+ t = google_without_schema_crutch[] and return t # TODO: why again?
333
369
 
334
370
  begin
335
- imgur = DirectLink.imgur(link).sort_by{ |u, w, h, t| - w * h }.map do |u, w, h, t|
371
+ imgur = DirectLink.imgur(link, timeout).sort_by{ |u, w, h, t| - w * h }.map do |u, w, h, t|
336
372
  struct.new u, w, h, t
337
373
  end
338
374
  # `DirectLink.imgur` return value is always an Array
@@ -370,40 +406,51 @@ def DirectLink link, timeout = nil, giveup: false, ignore_meta: false
370
406
  f = ->_{ _.type == :a ? _.attr["href"] : _.children.flat_map(&f) }
371
407
  require "kramdown"
372
408
  return f[Kramdown::Document.new(u).root].flat_map do |sublink|
373
- DirectLink URI.join(link, sublink).to_s, timeout, giveup: giveup
409
+ DirectLink URI.join(link, sublink).to_s, timeout, giveup: giveup # TODO: maybe subtract from timeout the time we've already wasted
410
+ end
411
+ end
412
+ if u.is_a? Hash
413
+ return struct.new *u.values_at(*%w{ fallback_url width height }), "video"
414
+ elsif u.is_a? Array
415
+ return u.map do |t, x, y, u|
416
+ struct.new u, x, y, t
374
417
  end
375
418
  end
376
- return struct.new *u.values_at(*%w{ fallback_url width height }), "video" if u.is_a? Hash
377
- return DirectLink u
378
- fail if link == u
419
+ raise DirectLink::ErrorNotFound.new link.inspect if link == u
420
+ return DirectLink u, timeout, giveup: giveup
379
421
  rescue DirectLink::ErrorMissingEnvVar
380
422
  end if %w{ reddit com } == URI(link).host.split(?.).last(2) ||
381
423
  %w{ redd it } == URI(link).host.split(?.)
382
424
 
383
425
  begin
384
- w, h, u = DirectLink.vk(link)
385
- return struct.new u, w, h
426
+ return DirectLink.vk(link).map do |w, h, u|
427
+ struct.new u, w, h
428
+ end
386
429
  rescue DirectLink::ErrorMissingEnvVar
387
430
  end if %w{ vk com } == URI(link).host.split(?.)
388
431
 
389
432
  begin
390
- f = FastImage.new(link, raise_on_failure: true, timeout: 5, http_header: {"User-Agent" => "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36"})
433
+ f = FastImage.new link,
434
+ raise_on_failure: true,
435
+ timeout: timeout,
436
+ **(proxy ? {proxy: "http://#{proxy}"} : {}),
437
+ http_header: {"User-Agent" => "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36"}
391
438
  rescue FastImage::UnknownImageType
392
439
  raise if giveup
393
440
  require "nokogiri"
394
- head = NetHTTPUtils.request_data link, :head, header: {"User-Agent" => "Mozilla"},
441
+ head = NetHTTPUtils.request_data link, :HEAD, header: {"User-Agent" => "Mozilla"},
395
442
  max_start_http_retry_delay: timeout,
396
443
  timeout: timeout, # NetHTTPUtild passes this as read_timeout to Net::HTTP.start
397
444
  max_read_retry_delay: timeout # and then compares accumulated delay to this
398
445
  # if we use :get here we will download megabytes of files just to giveup on content_type we can't process
399
- case head.instance_variable_get(:@last_response).content_type
446
+ case head.instance_variable_get(:@last_response).content_type # webmock should provide this
400
447
  when "text/html" ; nil
401
448
  else ; raise
402
449
  end
403
- html = Nokogiri::HTML NetHTTPUtils.request_data link, header: {"User-Agent" => "Mozilla"}
450
+ html = Nokogiri::HTML NetHTTPUtils.request_data link, :GET, header: {"User-Agent" => "Mozilla"}
404
451
  if t = html.at_css("meta[@property='og:image']")
405
452
  begin
406
- return DirectLink URI.join(link, t[:content]), nil, giveup: true
453
+ return DirectLink URI.join(link, t[:content]).to_s, nil, *proxy, giveup: true
407
454
  rescue URI::InvalidURIError
408
455
  end
409
456
  end unless ignore_meta
@@ -419,9 +466,10 @@ def DirectLink link, timeout = nil, giveup: false, ignore_meta: false
419
466
  end
420
467
  end
421
468
  end
422
- l[html].group_by(&:first).map{ |k, v| [k.join(?>), v.map(&:last)] }.tap do |results|
423
- raise if results.empty?
424
- end.max_by{ |_, v| v.map{ |i| i.width * i.height }.inject(:+) / v.size }.last
469
+ l[html].
470
+ tap{ |results| raise if results.empty? }.
471
+ group_by(&:first).map{ |k, v| [k.join(?>), v.map(&:last)] }.
472
+ max_by{ |_, v| v.map{ |i| i.width * i.height }.inject(:+) }.last
425
473
  else
426
474
  # TODO: maybe move this to right before `rescue` line
427
475
  w, h = f.size
data/unit.test.rb ADDED
@@ -0,0 +1,1548 @@
1
+ require "minitest/autorun"
2
+ require "minitest/around/spec"
3
+ require "minitest/mock"
4
+ require "webmock/minitest"
5
+ require_relative "webmock_patch"
6
+
7
+ require_relative "lib/directlink"
8
+ DirectLink.silent = true # TODO: remove?
9
+ DirectLink.timeout = 30 # TODO: tests about this attribute
10
+
11
+ describe DirectLink do
12
+ around{ |test| Timeout.timeout(4){ test.call } }
13
+ before{ WebMock.reset! }
14
+
15
+ describe "./lib" do
16
+
17
+ describe "google" do
18
+
19
+ # TODO: expand this for every branch in the lib
20
+ %w{
21
+ https_long_blogspot https://1.bp.blogspot.com/-__qsdLxNtcQ/XhaOQle-ECI/AAAAAAAABQ4/S_7SGG_abcDE6XIU2wyPvTj9OyBfr_1sQCLcBGAsYHQ/w1200-h630-p-k-no-nu/iceland_poppies_orange_flowers_field-wallpaper-3840x2160.jpg https://1.bp.blogspot.com/-__qsdLxNtcQ/XhaOQle-ECI/AAAAAAAABQ4/S_7SGG_abcDE6XIU2wyPvTj9OyBfr_1sQCLcBGAsYHQ/s0/
22
+ http_short_blogspot http://4.bp.blogspot.com/-poH-QXn7YGg/U-3ZTDkeF_I/AAAAAAAAISE/mabcDE6-v-g/w72-h72-p-k-no-nu/Top-24-Inspired-181.jpg https://4.bp.blogspot.com/-poH-QXn7YGg/U-3ZTDkeF_I/AAAAAAAAISE/mabcDE6-v-g/s0/
23
+ just_gplus https://lh3.googleusercontent.com/-NiGph3ObOPg/XE3DgnavXlI/AAAAAAABvgE/pcPPCe88rsU1r941wwP76TVf_abcDE6kwCJoC/w530-h353-n/DSCF0753.JPG https://lh3.googleusercontent.com/-NiGph3ObOPg/XE3DgnavXlI/AAAAAAABvgE/pcPPCe88rsU1r941wwP76TVf_abcDE6kwCJoC/s0/
24
+ google_keep https://lh5.googleusercontent.com/fRmAL_04p7oomNHCiV4tH4-agHSDBtLaWi_Tb6bgE5ZSHVu5OjQF3iRn06nNwP3ywZwdFP92zWM-abcDE6n6m0tDTBARuO6F9e0wYu_1=s685 https://lh5.googleusercontent.com/fRmAL_04p7oomNHCiV4tH4-agHSDBtLaWi_Tb6bgE5ZSHVu5OjQF3iRn06nNwP3ywZwdFP92zWM-abcDE6n6m0tDTBARuO6F9e0wYu_1=s0
25
+ }.each_slice 3 do |name, link, o|
26
+ it "replaces s0 and schema correctly #{name}" do
27
+ assert_equal o, DirectLink.google(link)
28
+ end
29
+ end
30
+
31
+ describe "does not fail" do
32
+ # TODO: also check the #google is being called
33
+
34
+ "
35
+ 1 //1.bp.blogspot.com/-rYk_u-qROQc/WngolJ8M0LI/AAAAAAAAD-w/abcDE6IVzasBPG5C2T1t-VrWKRd_U6lMgCLcBGAs/w530-h278-p/i-469.jpg
36
+ 2 //4.bp.blogspot.com/-1RlHbRShONs/Wngp9WfxbfI/AAAAAAAAD-8/abcDE6SZdvUMz4VjJPBpprLBrJ5QpBaqACLcBGAs/w530-h278-p/i-468.jpg
37
+ 3 //4.bp.blogspot.com/-5kP8ndL0kuM/Wpt82UCqvmI/AAAAAAAAEjI/abcDE60-kgwRXEJ9JEGioR0bm6U8MOkvQCKgBGAs/w530-h278-p/IMG_20171223_093922.jpg
38
+ 4 //4.bp.blogspot.com/-bS1SZxFkvek/Wnjz3D5Il4I/AAAAAAAAD_I/abcDE6GB7r4NGJ0aR1F-ugsbVm4HUjf5ACLcBGAs/w530-h278-p/i-466.jpg
39
+ 5 //lh3.googleusercontent.com/-1CF2jge1MYI/Wmr_kfCQQUI/AAAAAAAClKs/abcDE64v0q0i5N7rgso6G8SrhJNzsHEagCJoC/w530-h352-p/2809525%2B%25D0%2593%25D0%2590%25D0%259B%25D0%259E%2B%25D0%259F%25D0%2590%25D0%25A0%25D0%2593%25D0%2595%25D0%259B%25D0%2598%25D0%2599%2B-%2B%25D0%25A7%25D0%25A3%25D0%259A%25D0%259E%25D0%25A2%25D0%259A%25D0%2590%2B-%2B%25D0%25AE%25D0%25A0%25D0%2598%25D0%2599%2B%25D0%25A5%25D0%2590%25D0%25A0%25D0%25A7%25D0%2595%25D0%259D%25D0%259A%25D0%259E.jpg
40
+ 6 //lh3.googleusercontent.com/-h1siEcRFqJ8/Wiv1iDQhNVI/AAAAAAAAQV4/abcDE6mYkDMLyVTG_1EBCMmj-UhmclXWwCJoC/w530-h353-p/001
41
+ 7 //lh3.googleusercontent.com/-qnZygRb-Tn8/Wlyj4UuKbaI/AAAAAAAATqE/abcDE6-XBZwsh843xJ3cSd0JPt1LedU9gCJoC/w530-h330-p/001
42
+ 8 //lh3.googleusercontent.com/-rXO9PzbjNmk/WlNfjJkJBmI/AAAAAAAATNY/abcDE6tWBzUKWufNXWkK82etw3RKA6jVgCJoC/w530-h361-p/001
43
+
44
+ 13 //lh3.googleusercontent.com/proxy/2fsWfEOeeCMNEP8MGvHiJPkBlg47Ifaf8DwpgDPJSPSYsh9iomTrfOA9OuwB60laZimtNr4-1GKA5J2csuVB6-1DLNO30K-abcDE656ivICWRFAUuOwzizaztBx5gQ=w530-h707-p
45
+ 14 //lh3.googleusercontent.com/proxy/2qy3gOwhBT77Ie_qa9VgGz6kgN14tnOh1AMmV9FB65oiXOSAIll1_qmOZdupL3QCcJQL4701YskEgeDUnxJTnFGq4mFEzNFvjpNC-QMX9Jg7mdar3e89u_siA4AD-j1SJ6mrkl2MwSJSu1s2guYn0NR5ND1985lK34_iGiwzjoGHG2a65IbxKDSEzgSGbPQ_abcDE6BIvfcgvAxtS6E=s530-p
46
+ 28 //lh3.googleusercontent.com/proxy/7N0QCidzIFysO6OhmrG8twRJ74-sboNenI3Nhy3mIWP8C0SSa8J6xFQU3iyBTw4k3QeleiAgXOME3abYutPvBrHGx-0TBUcI-_abcDE69Zaw6PAGdLndkxp5ReRJnsg=w530-h298-p
47
+ 31 //lh3.googleusercontent.com/proxy/7pN3aTE159nEXXQb_KajU1vcnDDzJVpGOv9vlWWPd3CbVuCKu-v4ndr423Mmhx-Zn5vqkVf6-TfZgPA5NyHECsr4w7kaw1jTmQDJbWO-9jU-Cs7ywrzcIRhyYTTZT-abcDE6wI1oEdbooIk=w464-h640-p
48
+ 48 //lh3.googleusercontent.com/proxy/DZtTi5KL7PqiBwJc8weNGLk_Wi2UTaQH0AC_abcDE6iu0AbwyI2ywOk2XgdAjL7ceg=w530-h354-n
49
+ 74 //lh3.googleusercontent.com/proxy/P4FQdTYwWN8cU_PWmFja1cm3oYu57L_flea7LmKvFjx0Ibdxl3glxcp-abcDE6xMFTQ3gQkWsWzlrkyCm9QrSjCUcGIgnbsYBCXewnhEwSn0ZgT4XDg485GdXMoGWpNETrLYa8IGvG20_iw=w504-h597-p
50
+ 137 //lh3.googleusercontent.com/proxy/kX1ABK6_VjHDU12p-zZN6ciTOzqFcXm7IV41uR9F2sveM2t148nmWBEN1clA-VHvowpkd9T4NOPrAfDaip1o-jJBtBnRBElJuCsKo8FviSIkToZWCY9nQFEj06nHkMNuReBtdbVBlIRSodNJM-_cmqiJ-DnmWNe4dk62EF_kGLS28OWmckDLWVG0YFiZ-lVi8JhgR-ZgRmYWTs8bb_u6v6ucgfo8eh7ewchuZAD9_CoxUlQChAqz0QIfuVuKvmcXE06Ylv2H330btswQotjKDf8AXPe6xcyE9X1JXDYQTsPv-7lftLG9WQp1Mlc9Wu1XbHV7f541L-smt--7YpkW6fbDFYk4_87-pIOcg9UqHKB__DWyMCtH3gs8rv6VCmVwL1a-oAeBWSWk0Tc9R6uSuyCjt0nEBGlZY35aW1Pchz0jXr4lq8Il9m-wbRN9E7Kff1JR89YFOrV2N86w_72YTefFerLFfEsgzmhx3WYwok-abcDE6H7MyiHBQt04uRnb58GA4vl5VBQ=w530-h398-p
51
+
52
+ 245 https://lh3.googleusercontent.com/-2JzV0ErXvv8/Wsdr-m5Q_aI/AAAAAAAA_-I/Lw_e6qEMPUck4yW9yWhkC-abcDE6m8c3QCJoC/w530-h795-n/888052_800n.jpg
53
+ 346 https://lh3.googleusercontent.com/-8YmBt-SUaJo/War0Un_7YTI/AAAAAAAB9xs/kgXp9QrHZX8mpDfyLwuS9cabcDE6oH6NgCL0BGAs/w530-d-h443-n/%25D0%259B%25D0%25B5%25D1%2581%2B%25D0%25BD%25D0%25B0%2B%25D0%25B8%25D1%2581%25D1%2585%25D0%25BE%25D0%25B4%25D0%25B5%2B%25D0%25BB%25D0%25B5%25D1%2582%25D0%25B0.JPG
54
+ 1317 https://lh3.googleusercontent.com/-tGAyJv4S6gM/WSLy4A7AFaI/AAAAAAABxk4/eq0Aprp2koM6GEti9xEIn-abcDE6bRRdQCL0B/w530-d-h660-n/%25D0%2592%25D0%25B5%25D1%2581%25D0%25BD%25D1%258B%2B%25D1%2586%25D0%25B2%25D0%25B5%25D1%2582%25D0%25B5%25D0%25BD%25D0%25B8%25D0%25B5.JPG
55
+
56
+ 178 https://lh3.googleusercontent.com/--Ggbfs9HJZM/Wog1DA2WcFI/AAAAAAAAbdQ/SZKPcnabcDE6Ffl9pddfiLPQnOiJ37muQCJoC/w409-h727-n/11a1c54f37c07d3c04588b40d108b4c2.jpg
57
+ 260 https://lh3.googleusercontent.com/-3Fe6CL9RH4o/WnipWY4889I/AAAAAAAAI4I/CPqdKpabcDE6aBlCqGQ3RdZ9bLJ4nzBwwCJoC/w239-h318-n/gplus1855963830.jpg
58
+ 303 https://lh3.googleusercontent.com/-5vPhLnUILsA/WqFhJhu7VlI/AAAAAAAAAhQ/wWSrC8abcDE6OuzeU3UoXJkNG19tqtAGwCJoC/w265-h353-n/DSC08260.JPG
59
+ 668 https://lh3.googleusercontent.com/-NLW68hNxEzU/WrSNfBxFX2I/AAAAAAAAAT8/7cvRM-abcDE6dlb6CS8pL5-eJ3wbwohlQCJoC/w179-h318-n/23.03.2018%2B-%2B3
60
+ 669 https://lh3.googleusercontent.com/-NVJgqmI_2Is/WqMM2OMYg-I/AAAAAAAALrk/5-p3JLabcDE6o9dOf_p3gpddzqwr3Wp0ACJoC/w424-h318-n/001
61
+ 783 https://lh3.googleusercontent.com/-SqAvt_F__bg/Wq0huHcX2NI/AAAAAAAAVj0/XfnwCUabcDE60Knw8rcXA-bhpKYkI4hdwCJoC/w358-h318-n/IMG_20180317_120218-01-01.jpeg
62
+ 830 https://lh3.googleusercontent.com/-VB3YlLVL_tQ/Wo21yTbKl_I/AAAAAAAAApU/3DURmsabcDE60kif2ZRjhoLG4mTHYf8OwCJoC/w254-h318-n/180218_00001.jpg
63
+ 1002 https://lh3.googleusercontent.com/-d8FkfLWq-_4/WpQXzEfFgBI/AAAAAAAADww/J32xKSabcDE6c3pyrZnNmRec6kjdnJBHgCJoC/w239-h318-n/gplus-485739203.jpg
64
+ ".scan(/(\S+) (\S+)/).each do |i, link| # some pre-May-2018 urls dump from a community
65
+ it "Google Plus community post image ##{i}" do
66
+ assert DirectLink.google link
67
+ end
68
+ end
69
+ %w{
70
+ https://lh3.googleusercontent.com/-abcDE62vWuE/AAAAAAAAAAI/AAAAAAAAAAQ/wVFZagieszU/w530-h176-n/photo.jpg
71
+ //lh3.googleusercontent.com/abcDE6v7EGo0QbuoKxoKZVZO_NcBzufuvPtzirMJfPmAzCzMtnEncfA7zGIDTJfkc1YZFX2MhgKnjA=w530-h398-p
72
+ //lh3.googleusercontent.com/-0Vt5HFpPTVQ/Wnh02Jb4SFI/AAAAAAAAbCU/MtusII6-abcDE6QWofAaWF01IBUVykMyACJoC/w530-h351-p/DSC_3804%2B-%2B%25D0%25BA%25D0%25BE%25D0%25BF%25D0%25B8%25D1%258F.JPG
73
+ https://lh3.googleusercontent.com/-abcDE6-b85c/WvOtHJn-DxI/AAAAAAAAAKg/A_PoO64o0VkX1erPKebiO7Xcs5Iy3idbACJoC/w424-h318-n/IMG_20180510_082023.jpg
74
+ https://lh3.googleusercontent.com/-abcDE6FntJY/Wvl8HLwfF_I/AAAAAAAAA20/mqOtMCFuBkkvtv7jdpVkJv2XSXlOLoXVQCJoC/w212-h318-n/DPP_0006.JPG
75
+ https://lh3.googleusercontent.com/-abcDE64EIiw/WuxqzXHQCHI/AAAAAAAABAI/rA0R2UsbxoswvH6nSePnwa5VHG2f5kNOQCJoC/w398-h318-n/Fox-Desktop-1024x1280.jpg
76
+ https://lh3.googleusercontent.com/-abcDE62-1OA/WvMUBWXI0PI/AAAAAAAAC60/gdtkQz9YI0knYkFB8VjC2CpKOD6-Zs6hQCJoC/w408-h318-n/%25D0%25BF%25D0%25BE%25D1%2581.jpg
77
+ https://lh3.googleusercontent.com/--abcDE6Gk-A/WveQe_YKbAI/AAAAAAAACkQ/eTz2maXvw7A0iKoPjfuwEgfTiZS3a3HSgCJoC/w318-h318-n/gplus1165736192.jpg
78
+ https://lh3.googleusercontent.com/-abcDE6hwMOg/WtOxVJ1DNxI/AAAAAAAAMGs/pJf8awKJcyMFlx2g89p9_QjqlyQDmYMmACJoC/w424-h318-n/DSC03178.JPG
79
+ https://lh3.googleusercontent.com/-abcDE67fY1o/WtaUJCaDGmI/AAAAAAAAmBQ/jZhatiBmHPEiAAABcY2DoJ6KuzVBvqGxQCJoC/w530-h150-n/%25D0%25A1%25D0%25BD%25D0%25B8%25D0%25BC%25D0%25BE%25D0%25BA%2B%25D1%258D%25D0%25BA%25D1%2580%25D0%25B0%25D0%25BD%25D0%25B0%2B2018-04-18%2B%25D0%25B2%2B3.32.42.png
80
+ https://lh3.googleusercontent.com/-40QwR_c58sw/WsLyS3a8uhI/AAAAAAAAAas/ojaQoF1-abcDE6S6c5kLs7bOl_TAOU6oACJoC/w424-h318-n/271091.jpg
81
+ https://lh3.googleusercontent.com/-abcDE6yNBjY/WvOtHOyaj_I/AAAAAAAAAKo/gOAn__a75NwYSgaBaEBGeCTAFI9MyjqlwCJoC/w239-h318-n/IMG_20180510_081956.jpg
82
+ https://lh3.googleusercontent.com/-R19p_abcDE6/Wwma1oEvD6I/AAAAAAAAEX8/tQc4JOq58REEWlukw2jDCTUjH3ejGZI8gCJoC/w486-h864-n/gplus1392977354.jpg
83
+ //2.bp.blogspot.com/-mOnRg-mkojA/W22m79XcT_I/AAAAAAAAHDs/C8yQaOA-ZeEAr3WBG--abcDE65nWV0p7QCEwYBhgL/w530-h353-p/_MG_8809-Edit.jpg
84
+ }.each_with_index do |link, i| # March contenstants
85
+ it "another (March) Google Plus community post image ##{i + 1}" do
86
+ assert DirectLink.google link
87
+ end
88
+ end
89
+ %w{
90
+ https://lh3.googleusercontent.com/-abRcDE6_gic/WwUz3_Www7I/AAAAAAAABio/MNYvB57tBvIl6Bxp0GsKHvg0hSyHyVWhwCJoC/w265-h353-n/IMG_20180522_210620_317.jpg
91
+ https://lh3.googleusercontent.com/-abAcDE61QVc/Wu4Khtduz-I/AAAAAAAAA_A/lvpwTuatrtkhL31co1n9xa7kwKEVwc6IwCJoC/w301-h318-n/gplus662578195.jpg
92
+ https://lh3.googleusercontent.com/-abKcDE6NG2Q/Wvt9mUDC1QI/AAAAAAAANEw/2uWvIzQmG-Y3CGHuB8-UU114z4Zr8lkLQCJoC/w530-h175-n/PANO_20180516_025747.jpg
93
+ https://lh3.googleusercontent.com/-abWcDE66_Ew/WwvJcNQ39BI/AAAAAAAABvI/xb1bdwuEZGM2SmbwamwiMmZKFOW44bhPgCJoC/w239-h318-n/DSC03627.JPG
94
+ https://lh3.googleusercontent.com/-abacDE67WWI/Wu4Khq0IkaI/AAAAAAAAA_A/Dxs9JYJ6f6sv_7jNWMdNl3eo2jh9nYqDwCJoC/w296-h318-n/gplus-328480287.jpg
95
+ https://lh3.googleusercontent.com/-ab3cDE6qjyo/Wu4Khr5ZKhI/AAAAAAAAA_A/725HNeKw2hYtuddakPreAkaucQR6HOYGgCJoC/w424-h318-n/20180317_085037.jpg
96
+ https://lh3.googleusercontent.com/-abYcDE6GABY/Wu4Khov2uGI/AAAAAAAAA_A/xFVaLt1emxUtOvys7Mw8QZIF2599lNV6ACJoC/w424-h318-n/20180317_085508.jpg
97
+ https://lh3.googleusercontent.com/-abMcDE6jtXg/Wu4KhkZD9nI/AAAAAAAAA_A/lBHtrho7HBQgkZNztMTdR3o1RSu47OnigCJoC/w239-h318-n/gplus1795981938.jpg
98
+ https://lh3.googleusercontent.com/-ab-cDE6kT88/Wu4Khs76OSI/AAAAAAAAA_A/2rsXBfNM4Isi3YBNWlgLnPgQpVN72dXiQCJoC/w318-h318-n/gplus-379421668.jpg
99
+ }.each_with_index do |link, i| # April contenstants
100
+ it "another (April) Google Plus community post image ##{i + 1}" do
101
+ assert DirectLink.google link
102
+ end
103
+ end
104
+ %w{
105
+ https://lh3.googleusercontent.com/-abcDE6XoL_k/WvMUBQ6TXMI/AAAAAAAAC60/XRgJlEr5hbgKzYzOQkM8b4_fq2Yp5vNbACJoC/w239-h318-n/%25D0%25B7%25D0%25B0%25D1%2581.jpg
106
+ https://lh3.googleusercontent.com/-abcDE6ZYF4Y/Wvl8HH7crSI/AAAAAAAAA20/0vaXtOyqDL0DLx841Gj45ePol01OCOAewCJoC/w212-h318-n/DPP_0007.JPG
107
+ https://lh3.googleusercontent.com/-abcDE6DAoH8/WyCS3fapzjI/AAAAAAAAB5M/hKzS0cR0MEgnM-YJENsL7dX8Tbch5Z04wCJoC/w265-h353-n/DSC03805.JPG
108
+ https://lh3.googleusercontent.com/-abcDE6hweSI/WvMUBS4uAJI/AAAAAAAAC60/tE7aakvztZgTqvRMcrk2FbrauU8LLhGPgCJoC/w239-h318-n/11.jpg
109
+ https://lh3.googleusercontent.com/-abcDE6T_v6g/WyCS3VJ-hVI/AAAAAAAAB5M/qeUFKiNXnwkJ-q2q2VoM2f9ukTqPOGhAACJoC/w265-h353-n/DSC03807.JPG
110
+ //2.bp.blogspot.com/-abcDE6kbjOk/WwlXLbeiF9I/AAAAAAAAKKE/zT7sAlaNYIc7lTxZMY1tyND0u3UH5DuKACLcBGAs/w530-h398-p/EdnjF3yEMM8.jpg
111
+ https://lh3.googleusercontent.com/-1_J-abcDE6E/Wx9lWOEXUjI/AAAAAAAAB34/183rqDWZipkaSWyV6qF_Fm7_XQrtYiwdACJoC/w265-h353-n/DSC03800.JPG
112
+ https://lh3.googleusercontent.com/-I72-abcDE64/Wx9lWLfINqI/AAAAAAAAB34/EIOySV-4ny8xMECMSC4TvHV43qUn0HLLwCJoC/w265-h353-n/DSC03797.JPG
113
+ https://lh3.googleusercontent.com/-L-RnabcDE6I/WxZnv-NRXWI/AAAAAAAARVw/Z3yVnOEDU7kDZ-WbRoLbEh8Tao1_DtQbACJoC/w179-h318-n/20160305_134926.jpg
114
+ //lh3.googleusercontent.com/-abcDE6HFM6E/WtRMPtqimXI/AAAAAAAACIo/txJyaoEvnEQu7r2-35SgwFYCWHGmXDQ3QCJoC/w530-h353-p/IMG_3695.jpg
115
+ }.each_with_index do |link, i| # May contenstants
116
+ it "another (May) Google Plus community post image ##{i + 1}" do
117
+ assert DirectLink.google link
118
+ end
119
+ end
120
+ %w{
121
+ https://lh3.googleusercontent.com/-dWul1-dnMGE/W1npdlvSenI/AAAAAAAAAJI/abcDE65xS98xvFJDNgMgcxrRzPd6gOp9QCJoC/w208-h318-n/alg3_1520-2.jpg
122
+ https://lh3.googleusercontent.com/-t_ab__91ChA/VeLaObkUlgI/AAAAAAAAL4s/abcDE6_lkRw/w530-h351-n/30.08.15%2B-%2B1
123
+ https://lh3.googleusercontent.com/-Ry3uDLjGG0E/W13DzrEntEI/AAAAAAAAAdY/abcDE6-2JPsnuqYq7qtif_eVWhTd0EmnwCJoC/w480-h853-n/gplus-790559743.jpg
124
+ }.each_with_index do |link, i| # June contenstants
125
+ it "another (June) Google Plus community post image ##{i + 1}" do
126
+ assert DirectLink.google link
127
+ end
128
+ end
129
+ %w{
130
+ https://lh3.googleusercontent.com/-s655sojwyvw/VcNB4YMCz-I/AAAAAAAALqo/abcDE6cJJ0g/w530-h398-n/06.08.15%2B-%2B1
131
+ //4.bp.blogspot.com/-TuMlpg-Q1YY/W3PXkW1lkaI/AAAAAAAAHHg/Bh9IsuLV01kbctIu6lcRJHKkY-abcDE6gCLcBGAs/w530-h353-p/_MG_2688-Edit.jpg
132
+ //lh3.googleusercontent.com/proxy/SEfB6tFuim6X0HdZfEBSxrXtumUdf4Q4y05rUW4wc_clWWVrowuWAGZghx71xwPUmf_abcDE6wnRivsM7PfD2gp3kA=w480-h360-n
133
+ https://lh3.googleusercontent.com/-u3FhiUTmLCY/Vk7dMQnxR2I/AAAAAAAAMc0/I76_abcDE6s/w530-h322-n/Harekosh_A%252520Concert_YkRqQg.jpg
134
+ https://lh3.googleusercontent.com/-t_ab__91ChA/VeLaObkUlgI/AAAAAAAAL4s/abcDE6_lkRw/w530-d-h351-n/30.08.15%2B-%2B1
135
+ //lh3.googleusercontent.com/-u2NzdIQfVyQ/Wy83AzoFT8I/AAAAAAAAh6M/fdpxOUkj5mUIfpvYol_abcDE6nF2nDIEACJoC/w530-h298-p/_DSC9134.jpg
136
+ }.each_with_index do |link, i| # July contenstants
137
+ it "another (July) Google Plus community post image ##{i + 1}" do
138
+ assert DirectLink.google link
139
+ end
140
+ end
141
+ %w{
142
+ https://lh3.googleusercontent.com/-f37xWyiyP8U/WvmxOxCd-0I/AAAAAAAACpw/3A2tRj02oY40MzJqZBJyWabcDE6r0lwMgCJoC/s0/140809%2B029.jpg
143
+ https://lh3.googleusercontent.com/-1s_eiQB4x2k/WvXQEx59z2I/AAAAAAAAcI0/DvKYzWw3g6UNelqAQdOwrabcDE6qKgkxwCJoC/s0/001
144
+ https://lh3.googleusercontent.com/-1Rcbffs4iRI/WvaSsRCrxJI/AAAAAAAAcJs/e-N9tmjaTCIxEBE_jXwFjabcDE6Fwh4owCJoC/s0/001
145
+ https://lh3.googleusercontent.com/-VXUjuSl-dZY/WvK340_E9uI/AAAAAAAAVlg/HqKf1LgUcPUJNrLxHebCMabcDE6q36_bQCJoC/s0/gplus248254159.jpg
146
+ https://lh3.googleusercontent.com/-NlZRwcX_Lj8/WvQTijeAfJI/AAAAAAABNyo/jgoDgbZdTvsnLOGmmYlXMabcDE6ieZV4QCJoC/s0/67u8iii.png
147
+ https://lh3.googleusercontent.com/-8baBz80pf8Y/Wu8KG5lyGhI/AAAAAAACSyU/s3hasZzObK0VlntA1EBj-abcDE6gzRnLQCJoC/s0/%25D0%2592%25D0%25B5%25D1%2581%25D0%25B5%25D0%25BD%25D0%25BD%25D0%25B8%25D0%25B5%2B%25D0%25BA%25D1%2580%25D0%25B0%25D1%2581%25D0%25B0%25D0%25B2%25D1%2586%25D1%258B.JPG
148
+ https://lh3.googleusercontent.com/-BBjhu17YIgg/W-gnZNaZeMI/AAAAAAABA-k/UMlSbNuE0DsSEPV8u3yf_abcDE69vFoBgCJoC/s0/gplus320347186.png
149
+ }.each_with_index do |link, i|
150
+ it "already high res image ##{i + 1}" do
151
+ assert DirectLink.google link
152
+ end
153
+ end
154
+ %w{
155
+ https://lh3.googleusercontent.com/-5USmSTZHWWI/WMi3q1GdgiI/AAAAAAAAQIA/lu9FTcUL6xMw4wdoW_I148Vm1abcDE6mwCJoC/w346-h195-n-k-no/Good%2BJob.gif
156
+ https://lh3.googleusercontent.com/-G8eSL3Xx2Kc/WxWt9M6LptI/AAAAAAAAB_c/KNF9RBxjL04Reb3fBtwYLI2AlabcDE67gCJoC/s0/20180602212016_IMG_0244.JPG
157
+ https://lh3.googleusercontent.com/-aUVoiLNsmAg/WzcsUU2xfNI/AAAAAAAAODw/DOBual6E1rkVLHh3SKZSzbpNQabcDE6OQCJoC/w530-h883-n-k-no/gplus-1797734754.mp4
158
+ //lh3.googleusercontent.com/proxy/hOIoIpMEmoVDSP40VRzM92Zw2AeLvEEhxfyKHCOxiNVPyiGvZik5rMvl3jYISLabcDE6mhZuk8pFEYJhX5BU2wy_dw=w530-h822-p
159
+ https://lh3.googleusercontent.com/-GP3BA3zGR5A/W0IwuVXlfmI/AAAAAAADROs/SH8rRlBDYTsHZiHpM45S3zpEiabcDE6PwCJoC/s0/%25D1%2582%25D0%25B0%25D0%25B4%25D0%25B6%25D0%25B8%25D0%25BA%25D1%2581%25D0%25BA%25D0%25BE%25D0%25B5%2B%25D1%2580%25D0%25B0%25D0%25B7%25D0%25BD%25D0%25BE%25D1%2582%25D1%2580%25D0%25B0%25D0%25B2%25D1%258C%25D0%25B5.png
160
+ https://lh3.googleusercontent.com/-DLODAbD9W7E/W27ob5XGCOI/AAAAAAADV8g/J_6RYR6UkKsc2RJOWRx6Q-NBVabcDE6xwCJoC/s0/1236080.jpg
161
+ https://lh3.googleusercontent.com/-cJajRreI87w/W4gW5uF4Q7I/AAAAAAADZKI/mw1YayYE-MY2-1OCCmjvgM3kbabcDE6ggCJoC/s0/2504855.jpg
162
+ https://lh3.googleusercontent.com/-rm-m1meCOMY/W92GhExMG-I/AAAAAAADsTw/bIAm5-1CIOYEpyPJLnxT8VmI_abcDE6dACJoC/s0/2659806_800n.jpg
163
+ https://lh3.googleusercontent.com/-z1nwsq4NOT4/XETT5qhXP-I/AAAAAAAAC0s/03kJ22drB1EdqZ97gUXCPHkLZabcDE6OACJoC/w530-h942-n/gplus1629127930.jpg
164
+ https://lh3.googleusercontent.com/-NiGph3ObOPg/XE3DgnavXlI/AAAAAAABvgE/pcPPCe88rsU1r941wwP76TVf_abcDE6kwCJoC/w530-h353-n/DSCF0753.JPG
165
+ https://lh3.googleusercontent.com/-QfChCi9Lj6A/XEciVlXmDhI/AAAAAAACuZw/iYzoMIxr7SsGzEFbk1LrqIdCdabcDE6HACJoC/w795-h1193-n-rw/z7765765765757575.jpg
166
+ }.each_with_index do |link, i|
167
+ it "gpluscomm_105636351696833883213_86400 ##{i + 1}" do
168
+ assert DirectLink.google link
169
+ end
170
+ end
171
+ %w{
172
+ https://4.bp.blogspot.com/--L451qLtGq8/W2MiFy8SVCI/AAAAAAAARgM/9qr4fr2PiV8IE4dabcDE6dEnlchx1V6vwCLcBGAs/w72-h72-p-k-no-nu/blood_moon_5k.jpg
173
+ https://3.bp.blogspot.com/-aZETNznngSo/W2xbe673_6I/AAAAAAAARhs/hkXvEk85ol4SqVeabcDE6uDGLyT4V45kACLcBGAs/w72-h72-p-k-no-nu/waterfall-1280x800-ireland-4k-19245.jpg
174
+ https://3.bp.blogspot.com/-ZmCuJkPliO8/W3WjIWvdjXI/AAAAAAAARi4/qO9I8kzJvCMRWsgabcDE6cOCQPCyu_xDQCLcBGAs/w72-h72-p-k-no-nu/ford_mustang_neon_lights_5k.jpg
175
+ https://2.bp.blogspot.com/-rb2PXLGZy0s/W2nQe3mXOSI/AAAAAAAARhQ/P8gV-bMtYbY2xxpabcDE6xu3XDTUaugxQCLcBGAs/w72-h72-p-k-no-nu/beach-bora-bora-clouds-753626.jpg
176
+ https://1.bp.blogspot.com/-FGnZn0040os/W3g7wnT2o-I/AAAAAAAARjE/xfgcR4fyvQgBgV5abcDE6YOVK4d4dTouwCLcBGAs/w72-h72-p-k-no-nu/axl_2018_movie_4k_8k-7680x4320.jpg
177
+ https://1.bp.blogspot.com/-h9kL603XSgY/XCe5rYiOczI/AAAAAAAARmY/qz_kMyR1q-scfs-abcDE6of_QocYK7RegCLcBGAs/w72-h72-p-k-no-nu/spider_man_into_the_spider_verse_hd_4k.jpg
178
+ https://2.bp.blogspot.com/-rb2PXLGZy0s/W2nQe3mXOSI/AAAAAAAARhQ/P8gV-bMtYbY2xxpabcDE6xu3XDTUaugxQCLcBGAs/s640/beach-bora-bora-clouds-753626.jpg
179
+ http://4.bp.blogspot.com/-poH-QXn7YGg/U-3ZTDkeF_I/AAAAAAAAISE/abcDE6b-v-g/w72-h72-p-k-no-nu/Top-24-Inspired-181.jpg
180
+ http://1.bp.blogspot.com/-iSU4orVuR9Y/VFYrwQZ5qYI/AAAAAAAAMnc/abcDE6aeplw/w72-h72-p-k-no-nu/Wolf%2Bphotography2.jpg
181
+ http://1.bp.blogspot.com/-vPQSh6RKijU/VEi7r3D-jJI/AAAAAAAAL2Q/abcDE6oDp5M/w72-h72-p-k-no-nu/Building%2BIn%2BLondon1-4__880.jpeg
182
+ http://1.bp.blogspot.com/-W4xKJSsVf3M/Uz73jPlctbI/AAAAAAAAGz4/abcDE6ILMeY/w72-h72-p-k-no-nu/Beautiful+Japanese+places4.jpg
183
+ https://1.bp.blogspot.com/-__qsdLxNtcQ/XhaOQle-ECI/AAAAAAAABQ4/S_7SGG_K8eQ7abcDE6yPvTj9OyBfr_1sQCLcBGAsYHQ/w1200-h630-p-k-no-nu/iceland_poppies_orange_flowers_field-wallpaper-3840x2160.jpg
184
+ https://lh3.googleusercontent.com/-tV86KJvppss/XE2Nb2Z2aAI/AAAAAAAAGu4/94E_abcDE6AaJ59n43wmmd9rFa--OUuSQCJoC/w530-h338-n/IMG_6845%252C.png
185
+ https://lh3.googleusercontent.com/-cr-2ZSQGMPg/XFWLfetwr7I/AAAAAAAAQQQ/TbwDabcDE6wb4IDDO0SwfArFSZyDG0i0wCJoC/w530-h360-n/DSC07294.JPG
186
+ https://lh3.googleusercontent.com/-7ey25Wg_cQ4/X-Xy8LJPjMI/AAAAAAAAALE/raXQabcDE6EzEDfAg3TcsOU_xc9z4szcwCLcBGAsYHQ/w1200-h630-p-k-no-nu/1608905381407960-0.png
187
+ https://lh3.googleusercontent.com/C84udoMpvJmi95AqshlF9acaR11TCsUgO9rGB9gG1w5Wp3WpB7U52ntft4jBHAyDg2guo6zqI4KZim-nGx_jf_XGqqiVANe_Jr8u2g=w1400-k
188
+ }.each_with_index do |link, i|
189
+ it "largeimages ##{i + 1}" do
190
+ assert DirectLink.google link
191
+ end
192
+ end
193
+ %w{
194
+ //lh3.googleusercontent.com/proxy/S-Z1P92Dd_u0DjYrz5Tb7j0mbZsGjPVffx9lHPQZCqqCFr6vAigCPOG0fEYKU6d-wIvwYr2WriAfh97KjBr9Bq1RKgyHzHq2fpAotTnJYOLd3x_abcDE6GBVuAewE7qp2QDtCYyomyn3dGjZ6cKUnYIC8w=s110-p-k
195
+ }.each_with_index do |link, i|
196
+ it "posted_website_preview_##{i + 1}" do
197
+ assert DirectLink.google link
198
+ end
199
+ end
200
+ %w{
201
+ 0 https://lh3.googleusercontent.com/-okfIabcDE6g/AAAAAAAAAAI/AAAAAAAAZa0/FEv9H8woCBg/s30-p-rw-no/photo.jpg
202
+ 7 https://lh3.googleusercontent.com/-okfIabcDE6g/AAAAAAAAAAI/AAAAAAAAZa0/FEv9H8woCBg/s75-p-rw-no/photo.jpg
203
+ _ https://lh3.googleusercontent.com/-bhgxabcDE6I/AAAAAAAAAAI/AAAAAAAA4MI/_KuKE-Goa7E/s35-p-k-rw-no/photo.jpg
204
+ - https://lh3.googleusercontent.com/-tl9-abcDE6Y/AAAAAAAAAAI/AAAAAAAA8uY/vVeZX8SbTXI/s35-p-k-rw-no/photo.jpg
205
+ 4 https://lh3.googleusercontent.com/-Rb83abcDE64/AAAAAAAAAAI/AAAAAAAAEJc/DawCLQGnaSA/s45-p-k-rw-no/photo.jpg
206
+ }.each_slice 2 do |i, link|
207
+ it "Google Plus userpic #{i}" do
208
+ assert DirectLink.google link
209
+ end
210
+ end
211
+ %w{
212
+ - https://lh3.googleusercontent.com/-abcDE6oWuoU/AAAAAAAAAAI/AAAAAAAAl90/ed314-fNMGg/s20-c-k-no/photo.jpg
213
+ just https://lh5.googleusercontent.com/-abcDE6YpZzU/AAAAAAAAAAI/AAAAAAAAAO4/WISrqFs1vT8/s46-c-k-no/photo.jpg
214
+ no-no https://lh5.googleusercontent.com/-abcDE6MkCWA/AAAAAAAAAAI/AAAAAAAAAAA/4252rscbv5M/s64-c-k/photo.jpg
215
+ no-k-no https://lh3.googleusercontent.com/-abcDE66JAgg/AAAAAAAAAAI/AAAAAAAAAAA/AIcfdXD1lA9yGoSbsihWmGfnl6Z3Rn43WA/s64-c-mo/photo.jpg
216
+ _ https://lh6.googleusercontent.com/-abcDE6PRFk4/AAAAAAAAAAI/AAAAAAAAAAA/AIcfdXDBweE5VzGGy4zMraO_pqiLuFN3yQ/s46-c-k-no-mo/photo.jpg
217
+ }.each_slice 2 do |i, link|
218
+ it "Hangout userpic #{i}" do
219
+ assert DirectLink.google link
220
+ end
221
+ end
222
+ %w{
223
+ https://lh4.googleusercontent.com/abcDE6lyjNKXqSqszuiCgPMi7_oE5fVv1Sw373HXBYlguP8AnIPAqefTS6DbBuurVGYRSxGrtDyKei1hae9Djf6rjiwjJd0aauA5Bg-z=s615
224
+ https://lh5.googleusercontent.com/fRmAL_04p7oomNHCiV4tH4-agHSDBtLaWi_Tb6bgE5ZSHVu5OjQF3iRn06nNwP3ywZwdFP92zWM-abcDE6n6m0tDTBARuO6F9e0wYu_1=s685
225
+ https://lh5.googleusercontent.com/FcYUQBKLXWtFLEvbQduvu7FHUm2f7U_MVdMBVnNbpwfzKHIU-xABkudxw-abcDE60jFYRHedh7Is5Dg6qlgIQF1iSndlWjiKCTTsUo1w=s1080
226
+ https://lh5.googleusercontent.com/gcDb3dxbch9jUKPzKGawJiwtHcNS9wp6o2sc0aJj9wOWQA-u8kXmKIrZ-abcDE6ee6qLaT2p6OK2N7klTJ9CxU66OnpBhYJMg0q9QEdq5Q=s2160
227
+ }.each_with_index do |link, i|
228
+ it "google keep ##{i + 1}" do
229
+ assert DirectLink.google link
230
+ end
231
+ end
232
+
233
+ end
234
+
235
+ end
236
+
237
+ describe "imgur" do
238
+
239
+ %w{
240
+ https://imgur.com/a/badlinkpattern
241
+ http://example.com/
242
+ https://imgur.com/gallery/abcD5.
243
+ }.each_with_index do |url, i|
244
+ it "ErrorBadLink_##{i + 1}" do
245
+ assert_raises DirectLink::ErrorBadLink do
246
+ DirectLink.imgur url
247
+ end
248
+ end
249
+ end
250
+
251
+ it "ErrorNotFound when album is empty" do
252
+ stub_request(:head, "https://api.imgur.com/3/album/abcDEF7/0.json")#.to_return(status: 200, body: "", headers: {})
253
+ stub_request(:get, "https://api.imgur.com/3/album/abcDEF7/0.json").to_return body: {data: {images: {}}}.to_json
254
+ e = assert_raises DirectLink::ErrorNotFound do
255
+ DirectLink.imgur "https://imgur.com/a/abcDEF7"
256
+ end
257
+ assert_nil e.cause if Exception.instance_methods.include? :cause # Ruby 2.1
258
+ end
259
+
260
+ valid_imgur_image_url_direct = "https://i.imgur.com/abcDEF7.jpg"
261
+ valid_imgur_image_url_album = "https://imgur.com/a/abcDEF7"
262
+ [404].each do |error_code|
263
+ it error_code do
264
+ e = assert_raises DirectLink::ErrorNotFound do
265
+ NetHTTPUtils.stub :request_data, ->*{ raise NetHTTPUtils::Error.new "", error_code } do
266
+ DirectLink.imgur valid_imgur_image_url_direct
267
+ end
268
+ end
269
+ assert_equal error_code, e.cause.code if Exception.instance_methods.include? :cause # Ruby 2.1
270
+ end
271
+ end
272
+ [599].each do |error_code|
273
+ it error_code do
274
+ e = assert_raises DirectLink::ErrorAssert do
275
+ NetHTTPUtils.stub :request_data, ->*{ raise NetHTTPUtils::Error.new "", error_code } do
276
+ DirectLink.imgur valid_imgur_image_url_direct
277
+ end
278
+ end
279
+ assert_equal error_code, e.cause.code if Exception.instance_methods.include? :cause # Ruby 2.1
280
+ end
281
+ end
282
+ [400, 500, 502, 503].each do |error_code|
283
+ [
284
+ [valid_imgur_image_url_direct, :direct],
285
+ [valid_imgur_image_url_album, :album],
286
+ ].each do |url, kind|
287
+ it "retries a limited number of times on error #{error_code} (#{kind})" do
288
+ tries = 0
289
+ e = assert_raises DirectLink::ErrorAssert do
290
+ NetHTTPUtils.stub :request_data, ->*{ tries += 1; raise NetHTTPUtils::Error.new "", error_code } do
291
+ DirectLink.imgur url, 1.5
292
+ end
293
+ end
294
+ assert_equal error_code, e.cause.code if Exception.instance_methods.include? :cause # Ruby 2.1
295
+ assert_equal 2, tries
296
+ end
297
+ end
298
+ end
299
+ it "does not throw 400 after a successfull retry" do
300
+ stub_request(:head, "https://api.imgur.com/3/image/abcDEF7/0.json")
301
+ stub_request(:get, "https://api.imgur.com/3/image/abcDEF7/0.json").to_return body: {data: {width: 100, height: 200, type: "image/jpeg", link: "https://i.imgur.com/abcDEF7.jpg"}}.to_json
302
+ f = 0
303
+ m = NetHTTPUtils.method :request_data
304
+ NetHTTPUtils.stub :request_data, lambda{ |*args, **kwargs|
305
+ raise NetHTTPUtils::Error.new "", 400 if 1 == f += 1
306
+ m.call *args, **kwargs
307
+ } do
308
+ assert_equal [[valid_imgur_image_url_direct, 100, 200, "image/jpeg"]],
309
+ DirectLink.imgur(valid_imgur_image_url_direct, 1.5)
310
+ end
311
+ end
312
+
313
+ [
314
+ ["gifv" , "http://i.imgur.com/abcDEF7.gifv", "https://api.imgur.com/3/image/abcDEF7/0.json", {"link"=>"http://i.imgur.com/abcDEF7h.gif", "width"=>100, "height"=>200, "type"=>"image/gif"}],
315
+ ["gif" , "https://i.imgur.com/abcD5.mp4", "https://api.imgur.com/3/image/abcD5/0.json", {"link"=>"https://i.imgur.com/abcD5.gif", "width"=>100, "height"=>200, "type"=>"image/gif"}],
316
+ ["mp4" , "https://i.imgur.com/abcDEF7.png", "https://api.imgur.com/3/image/abcDEF7/0.json", {"link"=>"https://i.imgur.com/abcDEF7.png", "width"=>100, "height"=>200, "type"=>"image/png"}],
317
+ ["fb" , "https://i.imgur.com/abcDEF7.jpg?fb", "https://api.imgur.com/3/image/abcDEF7/0.json", {"link"=>"https://i.imgur.com/abcDEF7.jpg", "width"=>100, "height"=>200, "type"=>"image/jpeg"}],
318
+ ["jpEg" , "https://i.imgur.com/abcDEF7.jpeg", "https://api.imgur.com/3/image/abcDEF7/0.json", {"link"=>"https://i.imgur.com/abcDEF7.jpg", "width"=>100, "height"=>200, "type"=>"image/jpeg"}],
319
+ [nil , "http://m.imgur.com/abcDEF7", "https://api.imgur.com/3/image/abcDEF7/0.json", {"link"=>"https://i.imgur.com/abcDEF7.jpg", "width"=>100, "height"=>200, "type"=>"image/jpeg"}],
320
+ [nil , "https://imgur.com/abcDEF7", "https://api.imgur.com/3/image/abcDEF7/0.json", {"link"=>"https://i.imgur.com/abcDEF7.mp4", "width"=>100, "height"=>200, "type"=>"video/mp4"}],
321
+ [nil , "http://imgur.com/abcDEF7", "https://api.imgur.com/3/image/abcDEF7/0.json", {"link"=>"https://i.imgur.com/abcDEF7.jpg", "width"=>100, "height"=>200, "type"=>"image/jpeg"}],
322
+ ["5 char photo" , "https://imgur.com/abcD5", "https://api.imgur.com/3/image/abcD5/0.json", {"link"=>"https://i.imgur.com/abcD5.jpg", "width"=>100, "height"=>200, "type"=>"image/jpeg"}],
323
+ ["single photo album" , "https://imgur.com/a/abcD5", "https://api.imgur.com/3/album/abcD5/0.json", {"images"=>["link"=>"https://i.imgur.com/abcDEF7.jpg", "width"=>100, "height"=>200, "type"=>"image/jpeg"]}],
324
+ ["5 char https album" , "https://imgur.com/a/abcD5", "https://api.imgur.com/3/album/abcD5/0.json", {"images"=>["link"=>"https://i.imgur.com/abcDEF7.jpg", "width"=>100, "height"=>200, "type"=>"image/jpeg"]*6}, 6],
325
+ ["7 char https album" , "https://imgur.com/a/abcDEF7", "https://api.imgur.com/3/album/abcDEF7/0.json", {"images"=>["link"=>"https://i.imgur.com/abcDEF7.png", "width"=>100, "height"=>200, "type"=>"image/png"]*2}, 2],
326
+ ["album zoomable" , "http://imgur.com/a/abcD5", "https://api.imgur.com/3/album/abcD5/0.json", {"images"=>["link"=>"https://i.imgur.com/abcDEF7.jpg", "width"=>100, "height"=>200, "type"=>"image/jpeg"]*49}, 49],
327
+ ["album not zoomable" , "http://imgur.com/a/abcD5", "https://api.imgur.com/3/album/abcD5/0.json", {"images"=>["link"=>"https://i.imgur.com/abcDEF7.png", "width"=>100, "height"=>200, "type"=>"image/png"]*20}, 20],
328
+ [nil , "http://imgur.com/gallery/abcDEF7/new", "https://api.imgur.com/3/gallery/abcDEF7/0.json", {"link"=>"https://i.imgur.com/abcDEF7.jpg", "width"=>100, "height"=>200, "type"=>"image/jpeg"}],
329
+ [nil , "http://imgur.com/gallery/abcD5", "https://api.imgur.com/3/gallery/abcD5/0.json", {"images"=>["link"=>"https://i.imgur.com/abcDEF7.jpg", "width"=>100, "height"=>200, "type"=>"image/jpeg"]*7}, 7],
330
+ ["single image gallery?" , "http://imgur.com/gallery/abcDEF7", "https://api.imgur.com/3/gallery/abcDEF7/0.json", {"link"=>"https://i.imgur.com/abcDEF7.png", "width"=>100, "height"=>200, "type"=>"image/png"}],
331
+ [nil , "http://imgur.com/gallery/abcD5/new", "https://api.imgur.com/3/gallery/abcD5/0.json", {"link"=>"https://i.imgur.com/abcDEF7.jpg", "width"=>100, "height"=>200, "type"=>"image/jpeg"}],
332
+ ["gallery mp4" , "https://imgur.com/gallery/abcDEF7", "https://api.imgur.com/3/gallery/abcDEF7/0.json", {"link"=>"https://i.imgur.com/abcDEF7.mp4", "width"=>100, "height"=>200, "type"=>"video/mp4"}],
333
+ ["id belongs to an image", "http://imgur.com/gallery/abcDEF7", "https://api.imgur.com/3/gallery/abcDEF7/0.json", {status: [404, "Not Found"]}, DirectLink::ErrorNotFound],
334
+ ["id belongs to an album", "http://imgur.com/gallery/abcD5", "https://api.imgur.com/3/gallery/abcD5/0.json", {status: [404, "Not Found"]}, DirectLink::ErrorNotFound],
335
+ [nil , "http://imgur.com/r/wallpaper/abcDEF7", "https://api.imgur.com/3/image/abcDEF7/0.json", {"link"=>"https://i.imgur.com/abcDEF7.jpg?1", "width"=>100, "height"=>200, "type"=>"image/jpeg"}],
336
+ [nil , "https://imgur.com/abcDEF7?third_party=1#_=_", "https://api.imgur.com/3/image/abcDEF7/0.json", {"link"=>"https://i.imgur.com/abcDEF7.jpg", "width"=>100, "height"=>200, "type"=>"image/jpeg"}],
337
+ ].each_with_index do |t, i|
338
+ desc, url, stub_head, stub, m = t
339
+ it "kinds of post ##{i + 1}#{" (#{desc})" if desc}" do
340
+ case m
341
+ when Class
342
+ stub_request(:head, stub_head).to_return(stub) if stub_head
343
+ assert_raises m do
344
+ DirectLink.imgur url
345
+ end
346
+ when NilClass
347
+ stub_request(:head, stub_head) if stub_head
348
+ stub_request(:get, stub_head).to_return body: {data: stub}.to_json if stub
349
+ real = DirectLink.imgur url
350
+ assert_equal 1, real.size
351
+ assert_equal((stub["images"] ? stub["images"][0].values : stub.values), real.first)
352
+ when Numeric
353
+ stub_request(:head, stub_head) if stub_head
354
+ stub_request(:get, stub_head).to_return body: {data: stub}.to_json if stub
355
+ real = DirectLink.imgur url
356
+ assert_equal m, real.size
357
+ assert_equal stub["images"][0].values, real.first
358
+ else
359
+ fail "bug in tests"
360
+ end
361
+ end
362
+ end
363
+ end
364
+
365
+ describe "_500px" do
366
+ [
367
+ ["https://500px.com/photo/123456789/morning-by", [1200, 800, "https://drscdn.500px.org/photo/123456789/m%3D900/v2?sig=#{Array.new(64){rand(16).to_s(16)}.join}", "jpeg"]],
368
+ ["https://500px.com/photo/1234567890/-flowers-by/", [1819, 2500, "https://drscdn.500px.org/photo/1234567890/m%3D900/v2?sig=#{Array.new(64){rand(16).to_s(16)}.join}", "jpeg"]],
369
+ ["https://500px.com/photo/1234567890/is%E5-by-%E7ku-/", [2048, 2048, "https://drscdn.500px.org/photo/1234567890/m%3D2048/v2?sig=#{Array.new(64){rand(16).to_s(16)}.join}", "jpeg"]]
370
+ ].each_with_index do |(input, (w, h, u, t)), i|
371
+ it "kinds of links" do
372
+ id = URI(input).path[/\d+/]
373
+ stub_request(:head, "https://api.500px.com/v1/photos?ids=#{id}")
374
+ stub_request(:get, "https://api.500px.com/v1/photos?ids=#{id}").to_return body: {"photos"=>{id=>{"width"=>w,"height"=>h}}}.to_json
375
+ stub_request(:head, "https://api.500px.com/v1/photos?ids=#{id}&image_size%5B%5D=#{w}")
376
+ stub_request(:get, "https://api.500px.com/v1/photos?ids=#{id}&image_size%5B%5D=#{w}").to_return body: {"photos"=>{id=>{"images"=>[{"format"=>t,"url"=>u}]}}}.to_json
377
+ result = DirectLink.method(:_500px).call input
378
+ assert_equal [w, h, u, t], result, "#{input} :: #{result.inspect} != #{[w, h, u, t].inspect}"
379
+ end
380
+ end
381
+ end
382
+
383
+ describe "flickr" do
384
+ [
385
+ ["https://www.flickr.com/photos/tomas-/12345678901/", DirectLink::ErrorNotFound],
386
+ ["https://www.flickr.com/photos/12345678@N07/12345678901", DirectLink::ErrorNotFound],
387
+ ["https://www.flickr.com/photos/12345678@N00/12345678901/", [3000, 2000, "https://live.staticflickr.com/7757/12345678901_ed5178cc6a_o.jpg"]], # trailing slash
388
+ ["https://www.flickr.com/photos/jacob_schmidt/12345678901/in/album-12345678901234567/", DirectLink::ErrorNotFound], # username in-album
389
+ ["https://www.flickr.com/photos/tommygi/1234567890/in/dateposted-public/", [1600, 1062, "https://live.staticflickr.com/5249/1234567890_29fae96e38_h.jpg"]], # username in-public
390
+ ["https://www.flickr.com/photos/123456789@N02/12345678901/in/album-12345678901234567/", DirectLink::ErrorNotFound],
391
+ ["https://www.flickr.com/photos/123456789@N03/12345678901/in/dateposted-public/", [4621, 3081, "https://live.staticflickr.com/3796/12345678901_f751b35aeb_o.jpg"]], # userid in-public
392
+ ["https://www.flickr.com/photos/frank3/1234567890/in/photolist#{"-6KVb92"*50}", [4096, 2723, "https://live.staticflickr.com/2499/1234567890_dfa75a41cc_4k.jpg"]],
393
+ ["https://www.flickr.com/photos/patricksloan/12345678901/sizes/l", [2048, 491, "https://live.staticflickr.com/5572/12345678900_fec4783d79_k.jpg"]],
394
+ ["https://flic.kr/p/abcDEF", [5120, 3413, "https://live.staticflickr.com/507/12345678901_1bd49c5ebd_5k.jpg"]],
395
+ ].each_with_index do |(input, expectation), i|
396
+ it "kinds of links" do
397
+ stub_request(:head, /\Ahttps:\/\/api\.flickr\.com\/services\/rest\/\?api_key=#{ENV["FLICKR_API_KEY"]}&format=json&method=flickr\.photos\.getSizes&nojsoncallback=1&photo_id=[\da-zA-Z]+\z/)
398
+ if expectation.is_a? Class
399
+ stub_request(:get, /\Ahttps:\/\/api\.flickr\.com\/services\/rest\/\?api_key=#{ENV["FLICKR_API_KEY"]}&format=json&method=flickr\.photos\.getSizes&nojsoncallback=1&photo_id=[\da-zA-Z]+\z/).to_return body: {"stat"=>"fail", "code"=>1, "message"=>"Photo not found"}.to_json
400
+ assert_raises expectation, input do
401
+ DirectLink.method(:flickr).call input
402
+ end
403
+ else
404
+ w, h, u = expectation
405
+ stub_request(:get, /\Ahttps:\/\/api\.flickr\.com\/services\/rest\/\?api_key=#{ENV["FLICKR_API_KEY"]}&format=json&method=flickr\.photos\.getSizes&nojsoncallback=1&photo_id=[\da-zA-Z]+\z/).to_return body: {"stat"=>"ok", "sizes"=>{ "size"=>[
406
+ {"width"=>w/2, "height"=>h , "source"=>u*2},
407
+ {"width"=>w , "height"=>h , "source"=>u },
408
+ {"width"=>w , "height"=>h/2, "source"=>u*2},
409
+ ] } }.to_json
410
+ result = DirectLink.method(:flickr).call input
411
+ assert_equal expectation, result, "#{input} :: #{result.inspect} != #{expectation.inspect}"
412
+ end
413
+ end
414
+ end
415
+ end
416
+
417
+ describe "wiki" do
418
+ [
419
+ ["https://en.wikipedia.org/wiki/Prostitution_by_country#/media/File:Prostitution_laws_of_the_world.PNG", "https://upload.wikimedia.org/wikipedia/commons/e/e8/Prostitution_laws_of_the_world.PNG"],
420
+ ["https://en.wikipedia.org/wiki/Third_Party_System#/media/File:United_States_presidential_election_results,_1876-1892.svg", DirectLink::ErrorAssert],
421
+ ["http://commons.wikimedia.org/wiki/File:Eduard_Bohlen_anagoria.jpg", "https://upload.wikimedia.org/wikipedia/commons/0/0d/Eduard_Bohlen_anagoria.jpg"],
422
+ ["https://en.wikipedia.org/wiki/Spanish_Civil_War#/media/File:Alfonso_XIIIdeEspa%C3%B1a.jpg", "https://upload.wikimedia.org/wikipedia/commons/f/fb/Alfonso_XIIIdeEspa%C3%B1a.jpg"], # escaped input URI
423
+ ["https://en.wikipedia.org/wiki/File:Tyson_Lewis.jpg", "https://upload.wikimedia.org/wikipedia/en/c/cd/Tyson_Lewis.jpg"], # exists only in en.wiki, not in commons
424
+ ].each_with_index do |(input, expectation), i|
425
+ it "kinds of links" do
426
+ url = URI(input).tap{ |_| _.fragment = nil }.to_s
427
+ stub_request(:head, url)
428
+ stub_request(:head, "https://#{URI(input).hostname}/w/api.php?action=query&format=json&iiprop=url&prop=imageinfo&titles=#{input[/(File:.+)/]}")
429
+ if expectation.is_a? Class
430
+ stub_request(:get, "https://en.wikipedia.org/w/api.php?action=query&format=json&iiprop=url&prop=imageinfo&titles=#{input[/(File:.+)/]}").to_return body: {
431
+ "query"=>{"pages"=>{"-1"=>{}}}
432
+ }.to_json
433
+ assert_raises expectation, input do
434
+ DirectLink.wiki input
435
+ end
436
+ else
437
+ stub_request(:get, "https://#{URI(input).hostname}/w/api.php?action=query&format=json&iiprop=url&prop=imageinfo&titles=#{input[/(File:.+)/]}").to_return body: {
438
+ "query"=>{"pages"=>{"-1"=>{"imageinfo"=>[{"url"=>expectation}]}}}
439
+ }.to_json
440
+ result = DirectLink.wiki input
441
+ assert_equal expectation, result, "#{input} :: #{result.inspect} != #{expectation.inspect}"
442
+ end
443
+ end
444
+ end
445
+ end
446
+
447
+ describe "reddit" do
448
+ [
449
+ ["https://i.redd.it/abcde6k7r5xz.jpg", [true, "https://i.redd.it/abcde6k7r5xz.jpg"]],
450
+ ["https://www.reddit.com/r/cacography/comments/abcde6/c/", [true, "http://i.imgur.com/abcde6Z.jpg"], false, "abcde6"],
451
+ ["http://redd.it/abcde6", [true, "http://i.imgur.com/abcde6Z.jpg"], false, "abcde6"], # TODO maybe check that it calls #imgur recursively
452
+ ["http://redd.it/abcde6", [true, "https://i.redd.it/abcde66ehrg11.jpg"], false, "abcde6"],
453
+ ["https://reddit.com/abcde6", [true, "https://i.ytimg.com/vi/abcde6RbIeU/hqdefault.jpg"],
454
+ false, "abcde6",
455
+ {"type"=>"youtube.com","oembed"=>{"thumbnail_url"=>"https://i.ytimg.com/vi/abcde6RbIeU/hqdefault.jpg"}},
456
+ nil,
457
+ "http://www.youtube.com/watch?v=abcde6RbIeU&amp;feature=g-vrec",
458
+ ],
459
+ ["https://www.reddit.com/r/hangers/comments/abcde6/tara_radovic/", [true, "https://i.imgur.com/abcDE6u.jpg"],
460
+ false, "abcde6",
461
+ nil,
462
+ "t3_abcde7",
463
+ ], # "crossport" from Imgur
464
+ ["https://www.reddit.com/gallery/abcde6",
465
+ [true, [
466
+ ["image/jpg", 1440, 1440, "https://preview.redd.it/abcde6j6vee51.jpg?width=1440&format=pjpg&auto=webp&s=b79952f8364bb98692d978944347f19e28774d1b"],
467
+ ["image/jpg", 2441, 2441, "https://preview.redd.it/abcde6j6vee51.jpg?width=2441&format=pjpg&auto=webp&s=455e669356550351e6b8768d8009de616c11142a"],
468
+ ] ],
469
+ false, "abcde6",
470
+ nil,
471
+ nil,
472
+ "https://www.reddit.com/gallery/abcde6",
473
+ {
474
+ "x31msdjabcde6"=>{
475
+ "status"=>"valid", "m"=>"image/jpg", "s"=>{
476
+ "y"=>1440,
477
+ "x"=>1440,
478
+ "u"=>"https://preview.redd.it/abcde6j6vee51.jpg?width=1440&amp;format=pjpg&amp;auto=webp&amp;s=b79952f8364bb98692d978944347f19e28774d1b",
479
+ },
480
+ },
481
+ "mwkzq6jabcde6"=>{
482
+ "status"=>"valid", "m"=>"image/jpg", "s"=>{
483
+ "y"=>2441,
484
+ "x"=>2441,
485
+ "u"=>"https://preview.redd.it/abcde6j6vee51.jpg?width=2441&amp;format=pjpg&amp;auto=webp&amp;s=455e669356550351e6b8768d8009de616c11142a",
486
+ },
487
+ },
488
+ },
489
+ ],
490
+ ["https://www.reddit.com/abcde6",
491
+ [true, [
492
+ ["image/jpg", 1440, 1440, "https://preview.redd.it/abcde6j6vee51.jpg?width=1440&format=pjpg&auto=webp&s=b79952f8364bb98692d978944347f19e28774d1b"],
493
+ ["image/jpg", 2441, 2441, "https://preview.redd.it/abcde6j6vee51.jpg?width=2441&format=pjpg&auto=webp&s=455e669356550351e6b8768d8009de616c11142a"],
494
+ ] ],
495
+ false, "abcde6",
496
+ nil,
497
+ nil,
498
+ "https://www.reddit.com/gallery/abcde6",
499
+ {
500
+ "x31msdjabcde6"=>{
501
+ "status"=>"valid", "m"=>"image/jpg", "s"=>{
502
+ "y"=>1440,
503
+ "x"=>1440,
504
+ "u"=>"https://preview.redd.it/abcde6j6vee51.jpg?width=1440&amp;format=pjpg&amp;auto=webp&amp;s=b79952f8364bb98692d978944347f19e28774d1b",
505
+ },
506
+ },
507
+ "mwkzq6jabcde6"=>{
508
+ "status"=>"valid", "m"=>"image/jpg", "s"=>{
509
+ "y"=>2441,
510
+ "x"=>2441,
511
+ "u"=>"https://preview.redd.it/abcde6j6vee51.jpg?width=2441&amp;format=pjpg&amp;auto=webp&amp;s=455e669356550351e6b8768d8009de616c11142a",
512
+ },
513
+ },
514
+ },
515
+ ],
516
+ ["https://www.reddit.com/gallery/abcde6", [true, "https://www.reddit.com/gallery/abcde6"], false, "abcde6"], # deleted gallery
517
+ ["https://www.reddit.com/abcde6", [true, "https://www.reddit.com/r/Firewatch/comments/abcde7/new_wallpaper/"],
518
+ false, "abcde6",
519
+ nil,
520
+ "t3_abcde7",
521
+ "/r/Firewatch/comments/abcde7/new_wallpaper/",
522
+ ], # deleted gallery
523
+ ["https://www.reddit.com/abcde6",
524
+ [true, [
525
+ ["image/jpg", 500, 500, "https://preview.redd.it/abcde6eexo461.jpg?width=500&format=pjpg&auto=webp&s=df211fe0699e3970681ffe493ed1af79725857e8"],
526
+ ["image/jpg", 720, 446, "https://preview.redd.it/abcde6hexo461.jpg?width=720&format=pjpg&auto=webp&s=5e34ab0e6d54c0acfdb47f1daaf283087c5ad6a6"],
527
+ ["image/jpg", 713, 588, "https://preview.redd.it/abcde6lexo461.jpg?width=713&format=pjpg&auto=webp&s=969dfb52bedd6f0055249aa8b7454b23adaa946e"],
528
+ ] ],
529
+ false, "abcde6",
530
+ nil,
531
+ nil,
532
+ "https://www.reddit.com/gallery/abcde6",
533
+ {
534
+ "71t8ljeabcde6"=>{
535
+ "status"=>"valid", "m"=>"image/jpg", "s"=>{
536
+ "y"=>500,
537
+ "x"=>500,
538
+ "u"=>"https://preview.redd.it/abcde6eexo461.jpg?width=500&amp;format=pjpg&amp;auto=webp&amp;s=df211fe0699e3970681ffe493ed1af79725857e8",
539
+ },
540
+ },
541
+ "bfmjbtjabcde6"=>{"status"=>"failed"},
542
+ "c11nt7habcde6"=>{
543
+ "status"=>"valid", "m"=>"image/jpg", "s"=>{
544
+ "y"=>446,
545
+ "x"=>720,
546
+ "u"=>"https://preview.redd.it/abcde6hexo461.jpg?width=720&amp;format=pjpg&amp;auto=webp&amp;s=5e34ab0e6d54c0acfdb47f1daaf283087c5ad6a6",
547
+ },
548
+ },
549
+ "67mqvllabcde6"=>{
550
+ "status"=>"valid", "m"=>"image/jpg", "s"=>{
551
+ "y"=>588,
552
+ "x"=>713,
553
+ "u"=>"https://preview.redd.it/abcde6lexo461.jpg?width=713&amp;format=pjpg&amp;auto=webp&amp;s=969dfb52bedd6f0055249aa8b7454b23adaa946e"
554
+ },
555
+ },
556
+ },
557
+ ], # failed media
558
+ ["https://www.reddit.com/r/CatsStandingUp/duplicates/abcde6/cat/",
559
+ [true, "https://v.redd.it/abcde6fb6w721/DASH_2_4_M?source=fallback"],
560
+ false, "abcde6",
561
+ {"reddit_video"=>{"fallback_url"=>"https://v.redd.it/abcde6fb6w721/DASH_2_4_M?source=fallback"}},
562
+ nil,
563
+ "https://v.redd.it/abcde6fb6w721",
564
+ ],
565
+ ["https://www.reddit.com/r/MapPorn/comments/abd123/________aaaabdeeeeeefggghhhiilmmnnoorrrssttuvyyz/",
566
+ [true, [
567
+ ["image/gif", 500, 375, "https://i.redd.it/00146fghhjkox.gif"],
568
+ ["image/jpg", 1000, 600, "https://preview.redd.it/1369aacghkswy.jpg?width=1000&format=pjpg&auto=webp&s=0122334444557777778889aaabbbbcdddddeeeef"],
569
+ ]],
570
+ false, "abd123",
571
+ nil,
572
+ nil,
573
+ "https://www.reddit.com/gallery/abd123",
574
+ {
575
+ "00146fghhjkox"=>{
576
+ "status"=>"valid",
577
+ "e"=>"AnimatedImage",
578
+ "m"=>"image/gif",
579
+ "p"=>[],
580
+ "s"=>{
581
+ "y"=>375,
582
+ "gif"=>"https://i.redd.it/00146fghhjkox.gif",
583
+ "mp4"=>"",
584
+ "x"=>500
585
+ },
586
+ "id"=>"00146fghhjkox"
587
+ },
588
+ "1369aacghkswy"=>{
589
+ "status"=>"valid",
590
+ "e"=>"Image",
591
+ "m"=>"image/jpg",
592
+ "p"=>[],
593
+ "s"=>{
594
+ "y"=>600,
595
+ "x"=>1000,
596
+ "u"=>"https://preview.redd.it/1369aacghkswy.jpg?width=1000&amp;format=pjpg&amp;auto=webp&amp;s=0122334444557777778889aaabbbbcdddddeeeef"
597
+ },
598
+ "id"=>"1369aacghkswy"
599
+ }
600
+ },
601
+ ]
602
+ ].each_with_index do |(input, expectation, is_self, id, media, crosspost_parent, url, media_metadata), i|
603
+ it "kinds of links" do
604
+ unless is_self.nil?
605
+ fail "bug in test: no id" unless id
606
+ stub_request(:post, "https://www.reddit.com/api/v1/access_token").to_return body: {"access_token"=>"123456789012-abcDE6CFsglE63e5F1v8ThrVa9HThg"}.to_json
607
+ stub_request(:get, "https://oauth.reddit.com/by_id/t3_#{id}?api_type=json").to_return body: {
608
+ "data"=>{"children"=>[{"data"=>{
609
+ "is_self"=>is_self,
610
+ "media"=>media,
611
+ "crosspost_parent"=>crosspost_parent,
612
+ "url"=>(url||expectation[1]),
613
+ "media_metadata"=>media_metadata,
614
+ # "permalink"=>"/r/cacography/comments/32tq0i/c/",
615
+ # "selftext"=>"",
616
+ }}]}
617
+ }.to_json
618
+ end
619
+ result = DirectLink.reddit input
620
+ assert_equal expectation, result, "#{input} :: #{result.inspect} != #{expectation.inspect}"
621
+ end
622
+ end
623
+ end
624
+
625
+ describe "vk" do
626
+ [
627
+ ["https://vk.com/wall-123456091_7806", [960, 1280, "https://userapi.com/impf/c123456/v123456900/a72f1/7OZ8ux9Wcwo.jpg"], "wall", [
628
+ [97, 130, "https://sun9-3.userapi.com/impf/c123456/v123456900/a72f1/7OZ8ux9Wcwo.jpg?size=98x130&quality=96&sign=1f2bdc7b856b2a8f60297e3ac2c58689&c_uniq_tag=ggVFjBTdFDFuIz7Ee5o48mEP3fWLixCvjuPmehhyaTI&type=album"],
629
+ [960, 1280, "https://sun9-3.userapi.com/impf/c123456/v123456900/a72f1/7OZ8ux9Wcwo.jpg?size=960x1280&quality=96&proxy=1&sign=985328651fa11a0daef2c2a324f2de09&c_uniq_tag=vKCnEFEDol5QYM0qPrYAkbDwzYo6U_jo0SOR8iQO7HA&type=album"],
630
+ ] ],
631
+ ["https://vk.com/wall-123456611_454?z=photo-123456611_457239340%2Fwall-123456611_454", [1280, 960, "https://userapi.com/impf/c123456/v123456578/1a62f6/VB4SdR1O6Tg.jpg"], "photos", [
632
+ [130, 97, "https://sun9-59.userapi.com/impf/c123456/v123456578/1a62f6/VB4SdR1O6Tg.jpg?size=130x98&quality=96&sign=1e56a7310fd780cd48bed7304a369b1c&c_uniq_tag=xJlpXkJvi0P9WQhcM5acgLHqSUzXK3FGahuZHHncZQY&type=album"],
633
+ [1280, 960, "https://sun9-59.userapi.com/impf/c123456/v123456578/1a62f6/VB4SdR1O6Tg.jpg?size=1280x960&quality=96&proxy=1&sign=f5825d997937393a5c0a95c6775dce19&c_uniq_tag=2ulEOYXMOAN-KJm7HPNZYNcScyikyHdIsRmeqM8i16w&type=album"],
634
+ ] ],
635
+ ["https://vk.com/wall-123456091_7946?z=photo-123456091_457243312%2Falbum-123456091_00%2Frev", [1280, 875, "https://userapi.com/impf/c123456/v123456134/1b6b36/0IsDFb-Hda4.jpg"], "photos", [
636
+ [130, 89, "https://sun9-20.userapi.com/impf/c123456/v123456134/1b6b36/0IsDFb-Hda4.jpg?size=130x89&quality=96&sign=20f98d85da83704a93641c258dd9fb98&c_uniq_tag=PcNYWJFing2At2LiJVOYQOTUd_MbMLxiyPybwnyzaN4&type=album"],
637
+ [1280, 875, "https://sun9-20.userapi.com/impf/c123456/v123456134/1b6b36/0IsDFb-Hda4.jpg?size=1280x875&quality=96&proxy=1&sign=9b1e295c741c53b9485f4156da36ecde&c_uniq_tag=o7eFSi5F74SmkTirdrIS19nh8CEG32Od6yPvX9IPXds&type=album"],
638
+ ] ],
639
+ ["https://vk.com/id12345627?z=photo12345627_456241143%2Falbum12345627_0", [1920, 1440, "https://userapi.com/impf/c123456/v123456944/167836/bP9z41BybhI.jpg"], "photos", [
640
+ [130, 97, "https://sun9-9.userapi.com/impf/c123456/v123456944/167836/bP9z41BybhI.jpg?size=130x98&quality=96&sign=5a7785886e75cde41f7ddc190775c941&c_uniq_tag=uJX7tU35qumnZFlAUe-EhyVVT1FDBoHmp_8Z-dFAH_I&type=album"],
641
+ [1920, 1440, "https://sun9-9.userapi.com/impf/c123456/v123456944/167836/bP9z41BybhI.jpg?size=1920x1440&quality=96&proxy=1&sign=56acc1844d38b491c1b2e4dbedd76ba4&c_uniq_tag=06VziJvbo7gkdo5t0AUEQRs6EX8UrmiT2XkfGRmfRz8&type=album"],
642
+ ] ],
643
+ ["https://vk.com/id12345627?z=photo12345627_456241143", [1920, 1440, "https://userapi.com/impf/c123456/v123456944/167836/bP9z41BybhI.jpg"], "photos", [
644
+ [130, 97, "https://sun9-9.userapi.com/impf/c123456/v123456944/167836/bP9z41BybhI.jpg?size=130x98&quality=96&sign=5a7785886e75cde41f7ddc190775c941&c_uniq_tag=uJX7tU35qumnZFlAUe-EhyVVT1FDBoHmp_8Z-dFAH_I&type=album"],
645
+ [1920, 1440, "https://sun9-9.userapi.com/impf/c123456/v123456944/167836/bP9z41BybhI.jpg?size=1920x1440&quality=96&proxy=1&sign=56acc1844d38b491c1b2e4dbedd76ba4&c_uniq_tag=06VziJvbo7gkdo5t0AUEQRs6EX8UrmiT2XkfGRmfRz8&type=album"],
646
+ ] ],
647
+ ["https://vk.com/photo1_123187843?all=1", [2560, 1913, "https://userapi.com/impf/c123/v123001/6/53_VwoACy4I.jpg"], "photos", [
648
+ [130, 98, "https://sun1-23.userapi.com/impf/c123/v123001/6/53_VwoACy4I.jpg?size=130x97&quality=96&sign=f8f5b706de9bf4cba377c4192ca0faeb&c_uniq_tag=Hq3mDN7NbJvgRd82cKsQpJ45Tze8deKbUX4FZDwVZGQ&type=album"],
649
+ [2560, 1913, "https://sun1-23.userapi.com/impf/c123/v123001/6/53_VwoACy4I.jpg?size=2560x1913&quality=96&proxy=1&sign=c55f340348a35dd86542875a57ad8537&c_uniq_tag=yANq6f3QAVxu7M1xuy6fMu1JgkYOPqB1jBV4bQVjc0Y&type=album"],
650
+ ] ],
651
+ ["https://vk.com/photo123456340_456243948?rev=1", [1583, 1080, "https://userapi.com/impf/c123456/v123456479/321be/9rZaJ2QTdz4.jpg"], "photos", [
652
+ [130, 89, "https://sun9-21.userapi.com/impf/c123456/v123456479/321be/9rZaJ2QTdz4.jpg?size=130x89&quality=96&sign=4095eaa6d55dbb6af3a6d3baed593ecb&c_uniq_tag=9comrJGukZcMLlCDrmQMWhFtne217kVaZcafgoQmCVM&type=album"],
653
+ [1583, 1080, "https://sun9-21.userapi.com/impf/c123456/v123456479/321be/9rZaJ2QTdz4.jpg?size=1583x1080&quality=96&proxy=1&sign=27a9d92fcbd2cf748531a89278281cc3&c_uniq_tag=iwiLvdT52wwR8avHhjDUwPUozsvMpVyuFHHACavY2zA&type=album"],
654
+ ] ],
655
+ ["https://vk.com/photo-123456973_456242404", [1486, 1000, "https://userapi.com/impf/c123456/v123456877/8578e/m6AJWiskiKE.jpg"], "photos", [
656
+ [130, 87, "https://sun9-51.userapi.com/impf/c123456/v123456877/8578e/m6AJWiskiKE.jpg?size=130x87&quality=96&sign=c89c47cebc77fd0063dbe4891245aebe&c_uniq_tag=pSxKInGItqdMMW_83z9SXDzIi74Zl9UJ-P-snVmzHHY&type=album"],
657
+ [1486, 1000, "https://sun9-51.userapi.com/impf/c123456/v123456877/8578e/m6AJWiskiKE.jpg?size=1486x1000&quality=96&proxy=1&sign=f67bdef805a49775776ba9fe3ddddfdd&c_uniq_tag=2IwG6BAja36QQl3mDsUcWS6PC7ozt6TSExLsfRXRyzA&type=album"],
658
+ ] ],
659
+ ["https://vk.com/feed?section=likes&z=photo-123456754_456261460%2Fliked1234566", [1024, 1335, "https://userapi.com/impf/c123456/v123456353/895b6/izQJresLdf0.jpg"], "photos", [
660
+ [100, 130, "https://sun9-55.userapi.com/impf/c123456/v123456353/895b6/izQJresLdf0.jpg?size=100x130&quality=96&sign=56c2f6f2e772c5cac6e891d62c6c563e&c_uniq_tag=HHJ8PsYFgnwmuq9OfND51luLXJve81QbpWvudWXv5aw&type=album"],
661
+ [1024, 1335, "https://sun9-55.userapi.com/impf/c123456/v123456353/895b6/izQJresLdf0.jpg?size=1024x1335&quality=96&proxy=1&sign=b66a4c70eeb4083a05907e36d18cc481&c_uniq_tag=42fraqliOifFUG1CvgahG1lg1txVMF5hbSVlAUkLwf8&type=album"],
662
+ ] ],
663
+ ["https://vk.com/likizimy?z=photo-12345651_456239941%2Fwall-12345651_1908", [1179, 1731, "https://userapi.com/impf/c123456/v123456571/60f7b/ryCPJIMyMkI.jpg"], "photos", [
664
+ [89, 130, "https://sun9-37.userapi.com/impf/c123456/v123456571/60f7b/ryCPJIMyMkI.jpg?size=89x130&quality=96&sign=3ae95211e73168c7b66e12daa854f922&c_uniq_tag=uaDVTYCfixIN4JKSGzgNSTsjiuOUi0658_CgoIF-mqc&type=album"],
665
+ [1179, 1731, "https://sun9-37.userapi.com/impf/c123456/v123456571/60f7b/ryCPJIMyMkI.jpg?size=1179x1731&quality=96&proxy=1&sign=7930a6d1b4a88ce1fa4954fc6643d3e1&c_uniq_tag=rTxHctkqrP4SQsbcECYGDCVt03A43cLYRN8eTnOFtD0&type=album"],
666
+ ] ],
667
+ ["https://vk.com/e_rod?z=photo123456340_457247118%2Fphotos123456340", [1728, 2160, "https://userapi.com/impf/c123456/v123456596/c7714/oImGe4o1ZJI.jpg"], "photos", [
668
+ [104, 130, "https://sun9-53.userapi.com/impf/c123456/v123456596/c7714/oImGe4o1ZJI.jpg?size=104x130&quality=96&sign=6ad356e84dcd6bbf2069d9b869a7bdb1&c_uniq_tag=8KN44nSlDjD-FMDiRhHqk6udwiqZfn5n1qqPNFwu_nI&type=album"],
669
+ [1728, 2160, "https://sun9-53.userapi.com/impf/c123456/v123456596/c7714/oImGe4o1ZJI.jpg?size=1728x2160&quality=96&proxy=1&sign=c7961913ff3efd5064f2ed6c394288f2&c_uniq_tag=CA61KgCRsRXbHm4VJEYYskjpjpSiTHI7UPV4FsCPRmI&type=album"],
670
+ ] ],
671
+ ["https://vk.com/wasserman.anatoliy?w=wall-123457771_353", [864, 1080, "https://userapi.com/impg/13456AABBCDDKKOQSTUWXZbcfghhhjlpqquwyz/z0UtQg2M1s4.jpg"], "wall", [
672
+ [104, 130, "https://sun9-38.userapi.com/impg/13456AABBCDDKKOQSTUWXZbcfghhhjlpqquwyz/z0UtQg2M1s4.jpg?size=104x130&quality=96&sign=a6012bb508e208c7b2b22c42872215e4&c_uniq_tag=qX06fI2p7bmyvnta_Pt2omQ-l5RBLcrG1DBsLWx1jJA&type=album"],
673
+ [864, 1080, "https://sun9-38.userapi.com/impg/13456AABBCDDKKOQSTUWXZbcfghhhjlpqquwyz/z0UtQg2M1s4.jpg?size=864x1080&quality=96&sign=8f02c4c6794efd653ed79609bf0850c8&c_uniq_tag=6WP6TY0RTDiG_8NK1lkYUOge22jtjkzGLLwc6IDxjjg&type=album"],
674
+ ] ],
675
+ ].each_with_index do |(input, expectation, mtd, stub), i|
676
+ it "kinds of links" do
677
+ stub = {sizes: stub.map{ |w,h,u| {width: w, height: h, url: u} }}
678
+ stub_request(:post, "https://api.vk.com/method/#{mtd}.getById").to_return body: {response: [
679
+ mtd == "photos" ? stub : {attachments: [{type: :photo, photo: stub}]}
680
+ ] }.to_json
681
+ result = DirectLink.method(:vk).call input
682
+ assert_equal 1, result.size
683
+ result[0][-1].tap do |url|
684
+ url.replace( URI.parse(url).tap do |_|
685
+ _.host = _.host.split(?.).drop(1).join(?.)
686
+ _.query = nil
687
+ end.to_s )
688
+ end
689
+ assert_equal [expectation], result, "#{input} :: #{result.inspect} != #{expectation.inspect}"
690
+ end
691
+ end
692
+ end
693
+
694
+ describe "google" do
695
+ method = :google
696
+ [
697
+ "https://lh3.googleusercontent.com/-NVJgqmI_2Is/WqMM2OMYg-I/AAAAAAAALrk/5-p3JL3iZt0Ho9dOf_abcDE6zqwr3Wp0ACJoC/w424-h318-n/001",
698
+ "//lh3.googleusercontent.com/proxy/DZtTi5KL7PqiBwJc8weNGLk_Wi2UTaQH0AC_abcDE6iu0AbwyI2ywOk2XgdAjL7ceg=w530-h354-n",
699
+ "//1.bp.blogspot.com/-rYk_u-qROQc/WngolJ8M0LI/AAAAAAAAD-w/woivnaIVzasBPG5C2T1t-VrWKRd_abcDE6LcBGAs/w530-h278-p/i-469.jpg",
700
+ "https://2.bp.blogspot.com/-rb2PXLGZy0s/W2nQe3mXOSI/AAAAAAAARhQ/P8gV-abcDE62xxpTJNcYVxu3XDTUaugxQCLcBGAs/s640/beach-bora-bora-clouds-753626.jpg",
701
+ "http://4.bp.blogspot.com/-poH-QXn7YGg/U-3ZTDkeF_I/AAAAAAAAISE/mabcDE6-v-g/w72-h72-p-k-no-nu/Top-24-Inspired-181.jpg",
702
+ "https://lh5.googleusercontent.com/FcYUQBKLXWtFLEvbQduvu7FHUm2f7U_MVdMBVnNbpwfzKHIU-xABkudxw-abcDE60jFYRHedh7Is5Dg6qlgIQF1iSndlWjiKCTTsUo1w=s1080",
703
+ ].each do |input|
704
+ it "DirectLink() choses a method '#{method}' according to a domain" do
705
+ DirectLink.stub method, ->(link, timeout = nil){
706
+ assert_equal input, link
707
+ throw :_
708
+ } do
709
+ catch :_ do
710
+ DirectLink input
711
+ fail "DirectLink.#{method} was not called"
712
+ end
713
+ end
714
+ end
715
+ end
716
+ end
717
+
718
+ {
719
+ imgur: [
720
+ [%w{ https://imgur.com/abcD5 https://imgur.com/abcD5 }, [
721
+ %w{ https://imgur.com/abcD5 },
722
+ ] ],
723
+ [%w{ https://i.imgur.com/abcD5 https://imgur.com/abcD5 }, [
724
+ [*%w{ https://i.imgur.com/abcD5 https://imgur.com/abcD5 }, [302, "Moved Temporarily"]],
725
+ %w{ https://imgur.com/abcD5 },
726
+ ] ],
727
+ [%w{ https://m.imgur.com/abcD5 https://imgur.com/abcD5 }, [
728
+ %w{ https://m.imgur.com/abcD5 https://imgur.com/abcD5 },
729
+ %w{ https://imgur.com/abcD5 },
730
+ ] ],
731
+ [%w{ https://www.imgur.com/abcD5 https://imgur.com/abcD5 }, [
732
+ [*%w{ https://www.imgur.com/abcD5 https://imgur.com/abcD5 }, [301, "Moved Permanently"]],
733
+ %w{ https://imgur.com/abcD5 },
734
+ ] ],
735
+ [%w{ https://goo.gl/abcDE6 https://i.imgur.com/abcDEFY.png }, [
736
+ %w{ https://goo.gl/abcDE6 https://i.imgur.com/abcDEFY.png },
737
+ %w{ https://i.imgur.com/abcDEFY.png },
738
+ ] ],
739
+ ],
740
+ _500px: [
741
+ [%w{ https://500px.com/photo/123456789/milky-way https://500px.com/photo/123456789/milky-way }, [
742
+ %w{ https://500px.com/photo/123456789/milky-way },
743
+ ] ],
744
+ ],
745
+ flickr: [
746
+ ["https://www.flickr.com/photos/12345678@N07/12345678901/in/dateposted-public/", [
747
+ ["https://www.flickr.com/photos/12345678@N07/12345678901/in/dateposted-public/"],
748
+ ] ],
749
+ [["https://flic.kr/p/abcDEF", "https://www.flickr.com/photos/lopez/12345678901/"], [
750
+ ["https://flic.kr/p/abcDEF", "https://www.flickr.com/photo.gne?short=abcDEF"],
751
+ ["https://www.flickr.com/photo.gne?short=abcDEF", "/photo.gne?rb=1&short=abcDEF"],
752
+ ["https://www.flickr.com/photo.gne?rb=1&short=abcDEF", "/photos/lopez/12345678901/"],
753
+ ["https://www.flickr.com/photos/lopez/12345678901/"],
754
+ ] ],
755
+ ],
756
+ wiki: [
757
+ ["https://en.wikipedia.org/wiki/Third_Party_System#/media/File:United_States_presidential_election_results,_1876-1892.svg", [
758
+ %w{ https://en.wikipedia.org/wiki/Third_Party_System },
759
+ ] ],
760
+ [%w{ http://commons.wikimedia.org/wiki/File:Eduard_Bohlen_anagoria.jpg https://commons.wikimedia.org/wiki/File:Eduard_Bohlen_anagoria.jpg }, [
761
+ %w{ http://commons.wikimedia.org/wiki/File:Eduard_Bohlen_anagoria.jpg https://commons.wikimedia.org/wiki/File:Eduard_Bohlen_anagoria.jpg },
762
+ %w{ https://commons.wikimedia.org/wiki/File:Eduard_Bohlen_anagoria.jpg }
763
+ ] ],
764
+ ],
765
+ reddit: [
766
+ [%w{ https://www.reddit.com/r/abcdef/comments/abcde6/c/ }, [
767
+ %w{ https://www.reddit.com/r/abcdef/comments/abcde6/c/ },
768
+ ] ],
769
+ [%w{ http://redd.it/abcde6 https://www.reddit.com/comments/abcde6 }, [
770
+ [*%w{ http://redd.it/abcde6 https://redd.it/abcde6 }, [301, "Moved Permanently"]],
771
+ [*%w{ https://redd.it/abcde6 https://www.reddit.com/comments/abcde6 }, [302, "Temporarily Moved"]],
772
+ %w{ https://www.reddit.com/comments/abcde6 },
773
+ ] ],
774
+ [%w{ https://reddit.com/123456 https://www.reddit.com/r/funny/comments/123456/im_thinking/ }, [
775
+ [*%w{ https://reddit.com/123456 https://www.reddit.com/123456 }, [301, "Moved Permanently"]],
776
+ [*%w{ https://www.reddit.com/123456 https://www.reddit.com/r/funny/comments/123456/im_thinking/ }, [301, "Moved Permanently"]],
777
+ %w{ https://www.reddit.com/r/funny/comments/123456/im_thinking/ },
778
+ ] ],
779
+ [%w{ https://www.reddit.com/r/abcDEF/comments/abcde6/beard_trimmer/ }, [
780
+ %w{ https://www.reddit.com/r/abcDEF/comments/abcde6/beard_trimmer/ }
781
+ ] ], # NSFW causes redirect to /over_18? if the special cookie not provided # TODO: improve this test
782
+ ],
783
+ vk: [
784
+ ["https://vk.com/id12345678?z=photo12345678_456241143", [
785
+ ["https://vk.com/id12345678?z=photo12345678_456241143", nil, [418, ""]],
786
+ ] ],
787
+ ],
788
+ }.each do |method, tests|
789
+ describe "DirectLink() choses a method '#{method}' according to a domain" do
790
+ tests.each_with_index do |((input, expected), stub), i|
791
+ it "##{i + 1}" do
792
+ DirectLink.stub method, ->(link, timeout = nil, **__){
793
+ assert_equal (expected || input), link
794
+ throw :_
795
+ } do
796
+ (stub || []).each do |u, o, code|
797
+ if o
798
+ stub_request(:head, u).to_return status: (code || [302, "Found"]), headers: {location: o}
799
+ else
800
+ if code
801
+ stub_request(:head, u).to_return status: code
802
+ else
803
+ stub_request(:head, u)
804
+ end
805
+ end
806
+ end
807
+ catch :_ do
808
+ DirectLink input
809
+ fail "DirectLink.#{method} was not called"
810
+ end
811
+ end
812
+ end
813
+ end
814
+ end
815
+ end
816
+
817
+ # TODO: make a Reddit describe
818
+ it "retries limited amount of times on error JSON::ParserError" do
819
+ # TODO: the same but with the error expected to be gone after tries
820
+ stub_request(:post, "https://www.reddit.com/api/v1/access_token").to_return body: '{"access_token": "123456789000-17BDFFGGJJKNPVXZlt-2ACKQZfoswz", "token_type": "bearer", "expires_in": 3600, "scope": "*"}'
821
+ stub_request(:get, "https://oauth.reddit.com/by_id/t3_abcde6?api_type=json").to_return body: "invalid JSON"
822
+ tries = 0
823
+ m = JSON.method :load
824
+ e = assert_raises DirectLink::ErrorBadLink do
825
+ JSON.stub :load, ->*_,**__{ tries += 1; m.call *_,**__ } do
826
+ DirectLink.reddit "https://www.reddit.com/r/gifs/comments/abcde6/", 2.5
827
+ end
828
+ end
829
+ assert_instance_of JSON::ParserError, e.cause if Exception.instance_methods.include? :cause # Ruby 2.1
830
+ assert (3 <= tries)
831
+ assert (tries <= 4)
832
+ end
833
+
834
+ it "Reddit correctly parses out id when no token provided" do
835
+ stub_request(:head, "https://www.reddit.com/r/gifs/comments/9ftc8f/")
836
+ m = NetHTTPUtils.method :request_data
837
+ NetHTTPUtils.stub :request_data, lambda{ |u, *args, **kwargs|
838
+ throw :_ if u == "https://www.reddit.com/9ftc8f.json"
839
+ m.call u, *args, **kwargs
840
+ } do
841
+ t = ENV.delete "REDDIT_SECRETS"
842
+ begin
843
+ catch :_ do
844
+ DirectLink "https://www.reddit.com/r/gifs/comments/9ftc8f/"
845
+ fail
846
+ end
847
+ ensure
848
+ ENV["REDDIT_SECRETS"] = t
849
+ end
850
+ end
851
+ end
852
+
853
+ describe "throws ErrorBadLink if method does not match the link" do
854
+ %i{ google imgur flickr _500px wiki reddit vk }.each do |method|
855
+ ["", "test", "http://example.com/"].each_with_index do |url, i|
856
+ it "#{method} ##{i + 1}" do
857
+ assert_raises DirectLink::ErrorBadLink do
858
+ DirectLink.method(method).call url
859
+ end
860
+ end
861
+ end
862
+ end
863
+ end
864
+
865
+ end
866
+
867
+ describe "DirectLink()" do
868
+
869
+ # thanks to gem addressable
870
+ it "does not throw URI::InvalidURIError if there are brackets" do
871
+ stub_request(:head, "https://www.flickr.com/photos/nakilon/12345678900/%2520%5B2048x1152%5D").to_return status: [404, "Not Found"]
872
+ assert_equal 404, (
873
+ assert_raises NetHTTPUtils::Error do
874
+ DirectLink "https://www.flickr.com/photos/nakilon/12345678900/%20[2048x1152]"
875
+ end.code
876
+ )
877
+ end
878
+
879
+ it "throws ErrorNotFound when Reddit gallery is removed" do
880
+ stub_request(:head, "https://www.reddit.com/gallery/abcde6")
881
+ stub_request(:post, "https://www.reddit.com/api/v1/access_token").to_return body: {"access_token"=>"123456789012-abcde6cOO5V20ZD6J8WC6l36gMYRXQ"}.to_json
882
+ stub_request(:get, "https://oauth.reddit.com/by_id/t3_abcde6?api_type=json").to_return body: {
883
+ "data"=>{
884
+ "children"=>[{
885
+ "data"=>{
886
+ "selftext"=>"[удалено]",
887
+ "media_metadata"=>nil,
888
+ "is_self"=>false,
889
+ "permalink"=>"/r/woahdude/comments/abcde6/crystal_light/",
890
+ "url"=>"https://www.reddit.com/gallery/abcde6",
891
+ "media"=>nil
892
+ }
893
+ }]
894
+ }
895
+ }.to_json
896
+ assert_raises DirectLink::ErrorNotFound do
897
+ DirectLink "https://www.reddit.com/gallery/abcde6"
898
+ end
899
+ end
900
+
901
+ it "follows Reddit crosspost" do
902
+ stub_request(:head, "https://www.reddit.com/abcde6").to_return status: [301, "Moved Permanently"], headers: {location: "https://www.reddit.com/r/wallpapers/comments/abcde6/new_wallpaper/"}
903
+ stub_request(:head, "https://www.reddit.com/r/wallpapers/comments/abcde6/new_wallpaper/")
904
+ stub_request(:post, "https://www.reddit.com/api/v1/access_token").to_return body: {"access_token"=>"123456789012-abcde6WGKuIupoi5M3NtPVdI7bk1jg"}.to_json
905
+ stub_request(:get, "https://oauth.reddit.com/by_id/t3_abcde6?api_type=json").to_return body: {
906
+ "data"=>{
907
+ "children"=>[{
908
+ "data"=>{
909
+ "selftext"=>"",
910
+ "is_self"=>false,
911
+ "crosspost_parent"=>"t3_abcde7",
912
+ "permalink"=>"/r/wallpapers/comments/abcde6/new_wallpaper/",
913
+ "url"=>"/r/Firewatch/comments/abcde7/new_wallpaper/",
914
+ "media"=>nil,
915
+ }
916
+ }]
917
+ }
918
+ }.to_json
919
+ stub_request(:head, "https://www.reddit.com/r/Firewatch/comments/abcde7/new_wallpaper/")
920
+ stub_request(:get, "https://oauth.reddit.com/by_id/t3_abcde7?api_type=json").to_return body: {
921
+ "data"=>{
922
+ "children"=>[{
923
+ "data"=>{
924
+ "selftext"=>"I",
925
+ "media_metadata"=>{
926
+ "abcde62zhek51"=>{
927
+ "status"=>"valid",
928
+ "m"=>"image/png",
929
+ "s"=>{"y"=>1920, "x"=>4920, "u"=>"asd"},
930
+ },
931
+ "abcde72zhek51"=>{
932
+ "status"=>"valid",
933
+ "m"=>"image/png",
934
+ "s"=>{"y"=>1920, "x"=>4920, "u"=>"asd"},
935
+ }
936
+ },
937
+ "is_self"=>true,
938
+ "media"=>nil,
939
+ "permalink"=>"/r/Firewatch/comments/abcde7/new_wallpaper/",
940
+ "url"=>"https://www.reddit.com/r/Firewatch/comments/abcde7/new_wallpaper/",
941
+ }
942
+ }],
943
+ }
944
+ }.to_json
945
+ assert_equal %w{ image/png image/png }, DirectLink("https://www.reddit.com/abcde6").map(&:type)
946
+ end
947
+
948
+ it "throws ErrorBadLink if link is invalid" do
949
+ assert_equal "test".inspect, (
950
+ assert_raises DirectLink::ErrorBadLink do
951
+ DirectLink "test"
952
+ end
953
+ ).message
954
+ end
955
+
956
+ describe "does not shadow the internal exception" do
957
+ [
958
+ SocketError,
959
+ Errno::ECONNRESET,
960
+ ].each do |exception|
961
+ it "raises #{exception} from the redirect resolving stage" do
962
+ assert_raises exception do
963
+ NetHTTPUtils.stub :request_data, ->*{ raise exception.new } do
964
+ DirectLink "http://example.com/404"
965
+ end
966
+ end
967
+ end
968
+ end
969
+ it "raises Net::OpenTimeout -- server side issues can happen (not related to User Agent)" do
970
+ assert_raises Net::OpenTimeout do
971
+ NetHTTPUtils.stub :request_data, ->*{ raise Net::OpenTimeout.new } do
972
+ DirectLink "http://example.com/404"
973
+ end
974
+ end
975
+ end
976
+ it "HEAD Errno::ETIMEDOUT and GET FastImage::ImageFetchFailure are handled by DirectLink::NORMAL_EXCEPTIONS" do
977
+ assert_includes DirectLink::NORMAL_EXCEPTIONS, FastImage::ImageFetchFailure
978
+ assert_raises FastImage::ImageFetchFailure do
979
+ NetHTTPUtils.stub :request_data, ->_,mtd,*{ fail unless mtd == :HEAD; raise Errno::ETIMEDOUT } do
980
+ FastImage.stub :new, ->*{ raise FastImage::ImageFetchFailure } do
981
+ DirectLink "http://www.mesacc.edu/~hello12345/hello/images/photos/aerial/a1234.jpg"
982
+ end
983
+ end
984
+ end
985
+ end
986
+ [ # TODO this URLs may be reused from tests that check that this method calls internal method
987
+ [:google, "//lh3.googleusercontent.com/proxy/578BDGJKLLNPTZceiikqtww_Wi2UTaQH0AC_h2kuURiu0AbwyI2ywOk2XgdAjL7ceg=w530-h354-n"],
988
+ [:imgur, "http://imgur.com/ABCDEFG"],
989
+ [:flickr, "https://www.flickr.com/photos/12345678@N00/00013355778/"],
990
+ [:_500px, "https://500px.com/photo/123456789/milky-way"],
991
+ [:wiki, "http://commons.wikimedia.org/wiki/File:Eduard_Bohlen_anagoria.jpg"],
992
+ [:reddit, "https://www.reddit.com/123456"],
993
+ [:vk, "https://vk.com/"],
994
+ ].each do |method, link|
995
+ it "can otherwise raise DirectLink::ErrorBadLink #{method}" do
996
+ stub_request(:head, link)
997
+ e = assert_raises(DirectLink::ErrorBadLink, link) do
998
+ DirectLink.stub method, ->*{ raise DirectLink::ErrorBadLink.new "test" } do
999
+ DirectLink link
1000
+ end
1001
+ end
1002
+ assert_equal "\"test\" -- if you think this link is valid, please report the issue", e.message
1003
+ end
1004
+ end
1005
+ end
1006
+
1007
+ describe "other domains tests" do
1008
+ [
1009
+ ["http://www.cute.org/wp-content/uploads/2010/10/Niagara.jpg", SocketError, /nodename nor servname provided, or not known|Name or service not known|getaddrinfo: Name does not resolve/, 0],
1010
+ ].each_with_index do |(input, expectation, message_string_or_regex, max_redirect_resolving_retry_delay), i|
1011
+ it "##{i + 1}" do
1012
+ if expectation.is_a? Class
1013
+ # TODO: move the stub to the array above
1014
+ stub_request(:head, input).to_raise expectation.new("Failed to open TCP connection to www.cute.org:80 (getaddrinfo: nodename nor servname provided, or not known)")
1015
+ e = assert_raises expectation, "for #{input}" do
1016
+ DirectLink input, max_redirect_resolving_retry_delay
1017
+ end
1018
+ if message_string_or_regex.is_a? String
1019
+ assert_equal message_string_or_regex, e.message, "for #{input}"
1020
+ else
1021
+ assert_match message_string_or_regex, e.message, "for #{input}"
1022
+ end
1023
+ else
1024
+ u, w, h, t = expectation
1025
+ result = DirectLink input, max_redirect_resolving_retry_delay
1026
+ assert_equal DirectLink.class_variable_get(:@@directlink).new(u, w, h, t), result, "for #{input}"
1027
+ end
1028
+ end
1029
+ end
1030
+ end
1031
+
1032
+ describe "giving up" do
1033
+ [
1034
+ [
1035
+ ["http://example.com", FastImage::UnknownImageType],
1036
+ [
1037
+ [:get, "http://example.com", {body: "<!doctype html><html><head><title>Example Domain</title><meta charset='utf-8'/></head><body><div><h1>Example Domain</h1><p>Lorem ipsum.</p></div></body></html>"}],
1038
+ ],
1039
+ ],
1040
+ [
1041
+ ["http://imgur.com/QWERTYU", FastImage::UnknownImageType, true],
1042
+ [
1043
+ [:get, "http://imgur.com/QWERTYU", {status: [301, "Moved Permanently"], headers: {location: "https://imgur.com/QWERTYU"}}],
1044
+ [:get, "https://imgur.com/QWERTYU", {body: "<html><body></body></html>"}],
1045
+ ],
1046
+ ],
1047
+ [
1048
+ ["http://imgur.com/QWERTYU", "https://i.imgur.com/QWERTYU.jpeg?fb"], # .at_css("meta[@property='og:image']")
1049
+ [
1050
+ [:head, "http://imgur.com/QWERTYU", {status: [301, "Moved Permanently"], headers: {Location: "https://imgur.com/QWERTYU"}}],
1051
+ [:get, "http://imgur.com/QWERTYU", {status: [301, "Moved Permanently"], headers: {Location: "https://imgur.com/QWERTYU"}}],
1052
+ [:head, "https://imgur.com/QWERTYU", {headers: {"Content-Type"=>"text/html"}}],
1053
+ [:get, "https://imgur.com/QWERTYU", {body: <<~HEREDOC
1054
+ <html>
1055
+ <head>
1056
+ <meta property="og:url" data-react-helmet="true" content="https://imgur.com/QWERTYU">
1057
+ <meta name="twitter:image" data-react-helmet="true" content="https://i.imgur.com/QWERTYUh.jpg">
1058
+ <meta property="og:image:width" data-react-helmet="true" content="1">
1059
+ <meta property="og:image:height" data-react-helmet="true" content="2">
1060
+ <meta property="og:image" data-react-helmet="true" content="https://i.imgur.com/QWERTYU.jpeg?fb">
1061
+ <meta property="og:type" data-react-helmet="true" content="article">
1062
+ </head>
1063
+ <body></body>
1064
+ </html>
1065
+ HEREDOC
1066
+ } ],
1067
+ [:head, "https://i.imgur.com/QWERTYU.jpeg?fb"],
1068
+ [:get, "https://i.imgur.com/QWERTYU.jpeg?fb", {body: "\xFF\xD8\xFF\xE0\x00\x10JFIF\x00\x01\x01\x01\x00H\x00H\x00\x00\xFF\xDB\x00C\x00\x03\x02\x02\x02\x02\x02\x03\x02\x02\x02\x03\x03\x03\x03\x04\x06\x04\x04\x04\x04\x04\b\x06\x06\x05\x06\t\b\n\n\t\b\t\t\n\f\x0F\f\n\v\x0E\v\t\t\r\x11\r\x0E\x0F\x10\x10\x11\x10\n\f\x12\x13\x12\x10\x13\x0F\x10\x10\x10\xFF\xC9\x00\v\b\x00\x01\x00\x01\x01\x01\x11\x00\xFF\xCC\x00\x06\x00\x10\x10\x05\xFF\xDA\x00\b\x01\x01\x00\x00?\x00\xD2\xCF \xFF\xD9"}],
1069
+ ],
1070
+ ],
1071
+ [
1072
+ ["https://www.deviantart.com/nakilon/art/Nakilon-123456789", FastImage::UnknownImageType, true],
1073
+ [
1074
+ [:get, "https://www.deviantart.com/nakilon/art/Nakilon-123456789", {body: <<~HEREDOC
1075
+ <html>
1076
+ <head>
1077
+ <meta data-rh="true" property="og:image" content="https://images-wixmp-abc456.wixmp.com/f/abc456-abc456/abc456-abc456.jpg/v1/fill/w_1024,h_732,q_75,strp/nakilon.jpg?token=abCD01.abCD01.abCD01_abCD01_abCD01"/>
1078
+ </head>
1079
+ <body class="theme-dark">
1080
+ <div id="root">
1081
+ <main role="main" class="_1TdPo">
1082
+ <div class="_3tEjH">
1083
+ <div><img alt="Nakilon" aria-hidden="true" class="_1izoQ" src="https://images-wixmp-abc456.wixmp.com/f/abc456-abc456/abc456-abc456.jpg/v1/fill/w_1024,h_732,q_75,strp/nakilon.jpg?token=abCD01.abCD01.abCD01_abCD01_abCD01"/></div>
1084
+ </div>
1085
+ </main>
1086
+ </div>
1087
+ </body>
1088
+ </html>
1089
+ HEREDOC
1090
+ } ],
1091
+ ],
1092
+ ],
1093
+ [
1094
+ ["https://www.deviantart.com/nakilon/art/Nakilon-123456789", "https://images-wixmp-abc456.wixmp.com/f/abc456-abc456/abc456-abc456.jpg/v1/fill/w_1024,h_732,q_75,strp/nakilon.jpg?token=abCD01.abCD01.abCD01_abCD01_abCD01"],
1095
+ [
1096
+ [:head, "https://www.deviantart.com/nakilon/art/Nakilon-123456789", {headers: {"Content-Type"=>"text/html; charset=utf-8"}}],
1097
+ [:get, "https://www.deviantart.com/nakilon/art/Nakilon-123456789", {body: <<~HEREDOC
1098
+ <html>
1099
+ <head>
1100
+ <meta data-rh="true" property="og:image" content="https://images-wixmp-abc456.wixmp.com/f/abc456-abc456/abc456-abc456.jpg/v1/fill/w_1024,h_732,q_75,strp/nakilon.jpg?token=abCD01.abCD01.abCD01_abCD01_abCD01"/>
1101
+ </head>
1102
+ <body class="theme-dark">
1103
+ <div id="root">
1104
+ <main role="main" class="_1TdPo">
1105
+ <div class="_3tEjH">
1106
+ <div><img alt="Nakilon" aria-hidden="true" class="_1izoQ" src="https://images-wixmp-abc456.wixmp.com/f/abc456-abc456/abc456-abc456.jpg/v1/fill/w_1024,h_732,q_75,strp/nakilon.jpg?token=abCD01.abCD01.abCD01_abCD01_abCD01"/></div>
1107
+ </div>
1108
+ </main>
1109
+ </div>
1110
+ </body>
1111
+ </html>
1112
+ HEREDOC
1113
+ } ],
1114
+ [:head, "https://images-wixmp-abc456.wixmp.com/f/abc456-abc456/abc456-abc456.jpg/v1/fill/w_1024,h_732,q_75,strp/nakilon.jpg?token=abCD01.abCD01.abCD01_abCD01_abCD01"],
1115
+ [:get, "https://images-wixmp-abc456.wixmp.com/f/abc456-abc456/abc456-abc456.jpg/v1/fill/w_1024,h_732,q_75,strp/nakilon.jpg?token=abCD01.abCD01.abCD01_abCD01_abCD01", {body: "\xFF\xD8\xFF\xE0\x00\x10JFIF\x00\x01\x01\x01\x00H\x00H\x00\x00\xFF\xDB\x00C\x00\x03\x02\x02\x02\x02\x02\x03\x02\x02\x02\x03\x03\x03\x03\x04\x06\x04\x04\x04\x04\x04\b\x06\x06\x05\x06\t\b\n\n\t\b\t\t\n\f\x0F\f\n\v\x0E\v\t\t\r\x11\r\x0E\x0F\x10\x10\x11\x10\n\f\x12\x13\x12\x10\x13\x0F\x10\x10\x10\xFF\xC9\x00\v\b\x00\x01\x00\x01\x01\x01\x11\x00\xFF\xCC\x00\x06\x00\x10\x10\x05\xFF\xDA\x00\b\x01\x01\x00\x00?\x00\xD2\xCF \xFF\xD9"}],
1116
+ ],
1117
+ ],
1118
+ # TODO: og:image without scheme
1119
+ [
1120
+ ["https://www.reddit.com/r/darksouls3/comments/qwe123/hand/", DirectLink::ErrorBadLink, true],
1121
+ [
1122
+ [:head, "https://www.reddit.com/qwe123.json", {status: [301, "Moved Permanently"], headers: {location: "https://www.reddit.com/r/darksouls3/comments/qwe123/hand/.json"}}],
1123
+ [:head, "https://www.reddit.com/r/darksouls3/comments/qwe123/hand/.json"],
1124
+ [:get, "https://www.reddit.com/qwe123.json", {status: [301, "Moved Permanently"], headers: {"Location"=>"https://www.reddit.com/r/darksouls3/comments/qwe123/hand/.json"}}],
1125
+ [:get, "https://www.reddit.com/r/darksouls3/comments/qwe123/hand/.json", {body: <<~HEREDOC
1126
+ [
1127
+ {
1128
+ "data" : {
1129
+ "before" : null,
1130
+ "children" : [
1131
+ {
1132
+ "data" : {
1133
+ "selftext" : "[A](https://imgur.com/a/abcDEF1). I \\n [Here](https://imgur.com/a/abcDEF2)\\n\\nMobile: https://imgur.com/a/abcDEF3",
1134
+ "id" : "qwe123",
1135
+ "name" : "t3_qwe123",
1136
+ "is_self" : true,
1137
+ "media" : null,
1138
+ "permalink" : "/r/darksouls3/comments/qwe123/hand/",
1139
+ "over_18" : false,
1140
+ "url" : "https://www.reddit.com/r/darksouls3/comments/qwe123/hand/"
1141
+ },
1142
+ "kind" : "t3"
1143
+ }
1144
+ ]
1145
+ }
1146
+ }, {}
1147
+ ]
1148
+ HEREDOC
1149
+ } ],
1150
+ ],
1151
+ ],
1152
+ [
1153
+ ["https://www.reddit.com/r/darksouls3/comments/qwe123/hand/", 6], # TODO: should see the 3rd album too
1154
+ [
1155
+ [:head, "https://www.reddit.com/qwe123.json", {status: [301, "Moved Permanently"], headers: {location: "https://www.reddit.com/r/darksouls3/comments/qwe123/hand/.json"}}],
1156
+ [:head, "https://www.reddit.com/r/darksouls3/comments/qwe123/hand/.json"],
1157
+ [:get, "https://www.reddit.com/qwe123.json", {status: [301, "Moved Permanently"], headers: {Location: "https://www.reddit.com/r/darksouls3/comments/qwe123/hand/.json"}}],
1158
+ [:get, "https://www.reddit.com/r/darksouls3/comments/qwe123/hand/.json", {body: <<~HEREDOC
1159
+ [
1160
+ {
1161
+ "data" : {
1162
+ "before" : null,
1163
+ "children" : [
1164
+ {
1165
+ "data" : {
1166
+ "selftext" : "[A](https://imgur.com/a/abcDEF1). I \\n [Here](https://imgur.com/a/abcDEF2)\\n\\nMobile: https://imgur.com/a/abcDEF3",
1167
+ "id" : "qwe123",
1168
+ "name" : "t3_qwe123",
1169
+ "is_self" : true,
1170
+ "media" : null,
1171
+ "permalink" : "/r/darksouls3/comments/qwe123/hand/",
1172
+ "over_18" : false,
1173
+ "url" : "https://www.reddit.com/r/darksouls3/comments/qwe123/hand/"
1174
+ },
1175
+ "kind" : "t3"
1176
+ }
1177
+ ]
1178
+ }
1179
+ }, {}
1180
+ ]
1181
+ HEREDOC
1182
+ } ],
1183
+ [:head, "https://imgur.com/a/abcDEF1"],
1184
+ [:head, "https://api.imgur.com/3/album/abcDEF1/0.json"],
1185
+ [:get, "https://api.imgur.com/3/album/abcDEF1/0.json", {body: <<~HEREDOC
1186
+ {
1187
+ "status" : 200,
1188
+ "success" : true,
1189
+ "data" : {
1190
+ "id" : "abcDEF1",
1191
+ "link" : "https://imgur.com/a/abcDEF1",
1192
+ "images" : [
1193
+ {
1194
+ "link" : "https://i.imgur.com/abcDEF4.png",
1195
+ "type" : "image/png",
1196
+ "height" : 1080,
1197
+ "width" : 1920
1198
+ },
1199
+ {
1200
+ "link" : "https://i.imgur.com/abcDEF5.png",
1201
+ "type" : "image/png",
1202
+ "height" : 1080,
1203
+ "width" : 1920
1204
+ }
1205
+ ]
1206
+ }
1207
+ }
1208
+ HEREDOC
1209
+ } ],
1210
+ [:head, "https://imgur.com/a/abcDEF2"],
1211
+ [:head, "https://api.imgur.com/3/album/abcDEF2/0.json"],
1212
+ [:get, "https://api.imgur.com/3/album/abcDEF2/0.json", {body: <<~HEREDOC
1213
+ {
1214
+ "status" : 200,
1215
+ "success" : true,
1216
+ "data" : {
1217
+ "id" : "abcDEF2",
1218
+ "link" : "https://imgur.com/a/abcDEF2",
1219
+ "images" : [
1220
+ {
1221
+ "link" : "https://i.imgur.com/abcDEF6.png",
1222
+ "type" : "image/png",
1223
+ "height" : 1080,
1224
+ "width" : 1920
1225
+ },
1226
+ {
1227
+ "link" : "https://i.imgur.com/abcDEF7.jpg",
1228
+ "type" : "image/jpeg",
1229
+ "height" : 1080,
1230
+ "width" : 1920
1231
+ },
1232
+ {
1233
+ "link" : "https://i.imgur.com/abcDEF8.jpg",
1234
+ "type" : "image/jpeg",
1235
+ "height" : 1080,
1236
+ "width" : 1920
1237
+ },
1238
+ {
1239
+ "link" : "https://i.imgur.com/abcDEF9.jpg",
1240
+ "type" : "image/jpeg",
1241
+ "height" : 1080,
1242
+ "width" : 1920
1243
+ }
1244
+ ]
1245
+ }
1246
+ }
1247
+ HEREDOC
1248
+ } ],
1249
+ ],
1250
+ ],
1251
+ ].each do |(input, expectation, giveup), stubs|
1252
+ it "#{URI(input).host} giveup=#{!!giveup}" do
1253
+ stub_request(:head, input)
1254
+ stubs.each do |mtd, link, response|
1255
+ if response
1256
+ stub_request(mtd, link).to_return **response
1257
+ else
1258
+ stub_request(mtd, link)
1259
+ end
1260
+ end
1261
+ ti = ENV.delete "IMGUR_CLIENT_ID" if %w{ imgur com } == URI(input).host.split(?.).last(2)
1262
+ tr = ENV.delete "REDDIT_SECRETS" if %w{ reddit com } == URI(input).host.split(?.).last(2)
1263
+ begin
1264
+ case expectation
1265
+ when Class
1266
+ e = assert_raises expectation, "for #{input} (giveup = #{giveup})" do
1267
+ DirectLink input, 5, giveup: giveup
1268
+ end
1269
+ assert_equal expectation.to_s, e.class.to_s, "for #{input} (giveup = #{giveup})"
1270
+ when String
1271
+ result = DirectLink input, 5, giveup: giveup
1272
+ assert_equal expectation, result.url, "for #{input} (giveup = #{giveup})"
1273
+ else
1274
+ result = DirectLink input, 5, giveup: giveup
1275
+ result = [result] unless result.is_a? Array # we can't do `Array(<Struct>)` because it splats by elements
1276
+ assert_equal expectation, result.size, ->{
1277
+ "for #{input} (giveup = #{giveup}): #{result.map &:url}"
1278
+ }
1279
+ end
1280
+ ensure
1281
+ ENV["IMGUR_CLIENT_ID"] = ti if ti
1282
+ ENV["REDDIT_SECRETS"] = tr if tr
1283
+ end
1284
+ end
1285
+ end
1286
+ end
1287
+
1288
+ end
1289
+
1290
+ describe "./bin" do
1291
+ require "open3"
1292
+
1293
+ describe "shows usage help if misused" do
1294
+ [
1295
+ [/\Ausage: directlink \[--debug\] \[--json\] \[--github\] \[--ignore-meta\] <link1> <link2> <link3> \.\.\.\ndirectlink version \d\.\d\.\d\d?\.\d\d? \(https:\/\/github\.com\/nakilon\/directlink\)\n\z/, nil],
1296
+ [/\Ausage: directlink \[--debug\] \[--json\] \[--github\] \[--ignore-meta\] <link1> <link2> <link3> \.\.\.\ndirectlink version \d\.\d\.\d\d?\.\d\d? \(https:\/\/github\.com\/nakilon\/directlink\)\n\z/, "-h"],
1297
+ [/\Ausage: directlink \[--debug\] \[--json\] \[--github\] \[--ignore-meta\] <link1> <link2> <link3> \.\.\.\ndirectlink version \d\.\d\.\d\d?\.\d\d? \(https:\/\/github\.com\/nakilon\/directlink\)\n\z/, "--help"],
1298
+ [/\Ausage: directlink \[--debug\] \[--json\] \[--github\] \[--ignore-meta\] <link1> <link2> <link3> \.\.\.\ndirectlink version \d\.\d\.\d\d?\.\d\d? \(https:\/\/github\.com\/nakilon\/directlink\)\n\z/, "-v"],
1299
+ [/\Ausage: directlink \[--debug\] \[--json\] \[--github\] \[--ignore-meta\] <link1> <link2> <link3> \.\.\.\ndirectlink version \d\.\d\.\d\d?\.\d\d? \(https:\/\/github\.com\/nakilon\/directlink\)\n\z/, "--version"],
1300
+ ["DirectLink::ErrorBadLink: \"--\"\n", "--"],
1301
+ ["DirectLink::ErrorBadLink: \"-\"\n", "-"],
1302
+ ["DirectLink::ErrorBadLink: \"-\"\n", "- -"],
1303
+ ["DirectLink::ErrorBadLink: \"asd\"\n", "asd"],
1304
+ ].each_with_index do |(expectation, param), i|
1305
+ it "##{i + 1}" do
1306
+ string, status = Open3.capture2e "RUBYOPT='-rbundler/setup' ./bin/directlink #{param}"
1307
+ if expectation.is_a? String
1308
+ assert_equal expectation, string
1309
+ else
1310
+ assert_match expectation, string
1311
+ end
1312
+ assert_equal 1, status.exitstatus
1313
+ end
1314
+ end
1315
+ end
1316
+
1317
+ # that hack around `export` is for crossplatform Travis test, since Ubuntu uses `sh` that can't `source`
1318
+ # TODO: but maybe I could make Open3 use the shell I need, since `source` works fine for `.travis.yaml`'s `:script`
1319
+
1320
+ popen = lambda do |input, stubs, export = nil, param = nil|
1321
+ file = Tempfile.new %w{ directlink_webmock .rb }
1322
+ file.write <<~HEREDOC
1323
+ require "webmock"
1324
+ include WebMock::API
1325
+ WebMock.enable!
1326
+ require "#{__dir__}/webmock_patch"
1327
+ #{stubs.inspect}.each do |mtd, input, response|
1328
+ if response
1329
+ stub_request(mtd, input).to_return **response
1330
+ else
1331
+ stub_request(mtd, input)
1332
+ end
1333
+ end
1334
+ HEREDOC
1335
+ file.flush
1336
+ begin
1337
+ Open3.capture2e "#{"export #{File.read("api_tokens.sh").scan(/(?<=^export )\S+=\S+/).join(" ")} && " if export}RUBYOPT='-r bundler/setup #{$-I.map{ |i| "-I #{i}" }.join " "} -r #{file.path}' ./bin/directlink#{" --#{param}" if param} #{input}"
1338
+ ensure
1339
+ file.close
1340
+ file.unlink
1341
+ end
1342
+ end
1343
+
1344
+ [
1345
+ [1, "http://example.com/", /\AFastImage::UnknownImageType\n\z/, [
1346
+ [:head, "http://example.com/"],
1347
+ [:get, "http://example.com/", {body: "<html></html>"}],
1348
+ ] ],
1349
+ [1, "http://example.com/404", /\ANetHTTPUtils::Error: HTTP error #404 \n\z/, [
1350
+ [:head, "http://example.com/404", {status: [404, "Not Found"]}],
1351
+ ] ],
1352
+
1353
+ # TODO: a test when the giveup=false fails and reraises the DirectLink::ErrorMissingEnvVar
1354
+ # maybe put it to ./lib tests
1355
+
1356
+ # by design it should be impossible to write a test for DirectLink::ErrorAssert
1357
+ [1, "https://flic.kr/p/DirectLinkErrorNotFound", /\ANetHTTPUtils::Error: HTTP error #404 \n\z/, [
1358
+ [:head, "https://flic.kr/p/DirectLinkErrorNotFound", {status: [302, "Found"], headers: {"Location"=>"https://www.flickr.com/photo.gne?short=DirectLinkErrorNotFound"}}],
1359
+ [:head, "https://www.flickr.com/photo.gne?short=DirectLinkErrorNotFound", {status: [302, "Found"], headers: {"Location"=>"/photo.gne?rb=1&short=DirectLinkErrorNotFound"}}],
1360
+ [:head, "https://www.flickr.com/photo.gne?rb=1&short=DirectLinkErrorNotFound", {status: [404, "Not Found"]}],
1361
+ ] ],
1362
+
1363
+ [1, "https://imgur.com/a/badlinkpattern", /\ANetHTTPUtils::Error: HTTP error #404 \n\z/, [
1364
+ [:head, "https://imgur.com/a/badlinkpattern", {status: [404, "Not Found"]}],
1365
+ ] ],
1366
+ # TODO: a test that it appends the `exception.cause`
1367
+ ].each do |expected_exit_code, input, expected_output, stubs|
1368
+ it "fails" do
1369
+ string, status = popen.call(input, stubs, true)
1370
+ assert_equal expected_exit_code, status.exitstatus, "for #{input}"
1371
+ assert string[expected_output], "for #{input}: string=#{string.inspect}"
1372
+ end
1373
+ end
1374
+
1375
+ valid_imgur_image_url1 = "https://goo.gl/ySqUb5"
1376
+ valid_imgur_image_url2 = "https://imgur.com/a/oacI3gl"
1377
+ stubs = [
1378
+ [:head, "https://goo.gl/ySqUb5", {status: [302, "Found"], headers: {"Location"=>"https://i.imgur.com/QpOBvRY.png"}}],
1379
+ [:head, "https://i.imgur.com/QpOBvRY.png"],
1380
+ [:head, "https://api.imgur.com/3/image/QpOBvRY/0.json"],
1381
+ [:get, "https://api.imgur.com/3/image/QpOBvRY/0.json", {body: <<~HEREDOC
1382
+ {
1383
+ "status" : 200,
1384
+ "success" : true,
1385
+ "data" : {
1386
+ "height" : 460,
1387
+ "link" : "https://i.imgur.com/QpOBvRY.png",
1388
+ "type" : "image/png",
1389
+ "width" : 460
1390
+ }
1391
+ }
1392
+ HEREDOC
1393
+ } ],
1394
+ [:head, "https://imgur.com/a/oacI3gl"],
1395
+ [:head, "https://api.imgur.com/3/album/oacI3gl/0.json"],
1396
+ [:get, "https://api.imgur.com/3/album/oacI3gl/0.json", {body: <<~HEREDOC
1397
+ {
1398
+ "status" : 200,
1399
+ "success" : true,
1400
+ "data" : {
1401
+ "id" : "oacI3gl",
1402
+ "link" : "https://imgur.com/a/oacI3gl",
1403
+ "images" : [
1404
+ {
1405
+ "height" : 100,
1406
+ "link" : "https://i.imgur.com/9j4KdkJ.png",
1407
+ "type" : "image/png",
1408
+ "width" : 100
1409
+ },
1410
+ {
1411
+ "height" : 460,
1412
+ "link" : "https://i.imgur.com/QpOBvRY.png",
1413
+ "type" : "image/png",
1414
+ "width" : 460
1415
+ }
1416
+ ]
1417
+ }
1418
+ }
1419
+ HEREDOC
1420
+ } ]
1421
+ ]
1422
+ [
1423
+ [stubs, <<~HEREDOC],
1424
+ <= #{valid_imgur_image_url1}
1425
+ => https://i.imgur.com/QpOBvRY.png
1426
+ image/png 460x460
1427
+ <= #{valid_imgur_image_url2}
1428
+ => https://i.imgur.com/QpOBvRY.png
1429
+ image/png 460x460
1430
+ => https://i.imgur.com/9j4KdkJ.png
1431
+ image/png 100x100
1432
+ HEREDOC
1433
+ [stubs, <<~HEREDOC, "json"]
1434
+ [
1435
+ {
1436
+ "url": "https://i.imgur.com/QpOBvRY.png",
1437
+ "width": 460,
1438
+ "height": 460,
1439
+ "type": "image/png"
1440
+ },
1441
+ [
1442
+ {
1443
+ "url": "https://i.imgur.com/QpOBvRY.png",
1444
+ "width": 460,
1445
+ "height": 460,
1446
+ "type": "image/png"
1447
+ },
1448
+ {
1449
+ "url": "https://i.imgur.com/9j4KdkJ.png",
1450
+ "width": 100,
1451
+ "height": 100,
1452
+ "type": "image/png"
1453
+ }
1454
+ ]
1455
+ ]
1456
+ HEREDOC
1457
+ ].each do |stubs, expected_output, param|
1458
+ it "#{param || "default"} output format" do
1459
+ string, status = popen.call("#{valid_imgur_image_url1} #{valid_imgur_image_url2}", stubs, true, param)
1460
+ assert_equal [0, expected_output], [status.exitstatus, string]
1461
+ end
1462
+ end
1463
+
1464
+ it "reddit_bot gem logger does not flood STDOUT" do
1465
+ string, status = popen.call("http://redd.it/123abc", [
1466
+ [:head, "http://redd.it/123abc", {status: [301, "Moved Permanently"], headers: {"Location"=>"https://redd.it/123abc"}}],
1467
+ [:head, "https://redd.it/123abc", {status: [302, "Temporarily Moved"], headers: {"Location"=>"https://www.reddit.com/comments/123abc"}}],
1468
+ [:head, "https://www.reddit.com/comments/123abc"],
1469
+ [:post, "https://www.reddit.com/api/v1/access_token", {body: {"access_token": "123456789268-NxVk35tst8DttE5uCSD8pPY00XeAXQ"}.to_json}],
1470
+ [:get, "https://oauth.reddit.com/by_id/t3_123abc?api_type=json", {body: <<~HEREDOC
1471
+ {
1472
+ "data" : {
1473
+ "children" : [
1474
+ {
1475
+ "data" : {
1476
+ "id" : "123abc",
1477
+ "is_self" : true,
1478
+ "media" : null,
1479
+ "name" : "t3_123abc",
1480
+ "permalink" : "/r/test___________/comments/123abc/for_the_httpsgithubcomnakilondirectlink_unit_tests/",
1481
+ "selftext" : "https://i.imgur.com/ABCabcd.jpg [test](https://goo.gl/ySqUb5)",
1482
+ "url" : "https://www.reddit.com/r/test___________/comments/123abc/for_the_httpsgithubcomnakilondirectlink_unit_tests/"
1483
+ }
1484
+ }
1485
+ ]
1486
+ }
1487
+ }
1488
+ HEREDOC
1489
+ } ],
1490
+ [:head, "https://goo.gl/ySqUb5", {status: [302, "Found"], headers: {"Location"=>"https://i.imgur.com/QpOBvRY.png"}}],
1491
+ [:head, "https://i.imgur.com/QpOBvRY.png"],
1492
+ [:head, "https://api.imgur.com/3/image/QpOBvRY/0.json"],
1493
+ [:get, "https://api.imgur.com/3/image/QpOBvRY/0.json", {body: <<~HEREDOC
1494
+ {
1495
+ "status" : 200,
1496
+ "success" : true,
1497
+ "data" : {
1498
+ "height" : 460,
1499
+ "width" : 460,
1500
+ "link" : "https://i.imgur.com/QpOBvRY.png",
1501
+ "type" : "image/png"
1502
+ }
1503
+ }
1504
+ HEREDOC
1505
+ } ],
1506
+ ] )
1507
+ assert_equal "<= http://redd.it/123abc\n=> https://i.imgur.com/QpOBvRY.png\n image/png 460x460\n", string
1508
+ end
1509
+
1510
+ # TODO: test about --json
1511
+ it "does not give up" do
1512
+ string, status = popen.call("https://www.kp.ru/daily/123/", [
1513
+ [:head, "https://www.kp.ru/daily/123/", {headers: {"Content-Type"=>"text/html"}}],
1514
+ [:get, "https://www.kp.ru/daily/123/", {body: <<~HEREDOC
1515
+ <!DOCTYPE html>
1516
+ <html xmlns="http://www.w3.org/1999/xhtml">
1517
+ <head>
1518
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
1519
+ <link rel="icon" type="image/png" href="/favicon-16.png" sizes="16x16" />
1520
+ <link rel="icon" type="image/png" href="/favicon-32.png" sizes="32x32" />
1521
+ <link rel="icon" type="image/png" href="/favicon-96.png" sizes="96x96" />
1522
+ <link rel="icon" type="image/png" href="/favicon-128.png" sizes="128x128" />
1523
+ <meta data-react-helmet="true" property="og:image" content="https://s11.stc.all.kpcdn.net/share/20.jpg" />
1524
+ <meta data-react-helmet="true" name="twitter:image:src" content="https://s11.stc.all.kpcdn.net/share/20.jpg" />
1525
+ <link data-react-helmet="true" rel="image_src" href="https://s11.stc.all.kpcdn.net/share/20.jpg" />
1526
+ </head>
1527
+ <body>
1528
+ <div id="app">
1529
+ <div>
1530
+ <a href="/daily/author/1234">
1531
+ <img alt="Александра" src="https://s14.stc.all.kpcdn.net/share/i/3/1234567/avatar.jpg" data-show-on-desktop="false" data-show-on-mobile="false" />
1532
+ Ы
1533
+ </a>
1534
+ </div>
1535
+ </div>
1536
+ </body>
1537
+ </html>
1538
+ HEREDOC
1539
+ } ],
1540
+ [:head, "https://s11.stc.all.kpcdn.net/share/20.jpg"],
1541
+ [:get, "https://s11.stc.all.kpcdn.net/share/20.jpg", {body: "\xFF\xD8\xFF\xE0\x00\x10JFIF\x00\x01\x01\x01\x00H\x00H\x00\x00\xFF\xDB\x00C\x00\x03\x02\x02\x02\x02\x02\x03\x02\x02\x02\x03\x03\x03\x03\x04\x06\x04\x04\x04\x04\x04\b\x06\x06\x05\x06\t\b\n\n\t\b\t\t\n\f\x0F\f\n\v\x0E\v\t\t\r\x11\r\x0E\x0F\x10\x10\x11\x10\n\f\x12\x13\x12\x10\x13\x0F\x10\x10\x10\xFF\xC9\x00\v\b\x00\x01\x00\x01\x01\x01\x11\x00\xFF\xCC\x00\x06\x00\x10\x10\x05\xFF\xDA\x00\b\x01\x01\x00\x00?\x00\xD2\xCF \xFF\xD9"}],
1542
+ ], false, "json")
1543
+ assert_equal [0, "https://s11.stc.all.kpcdn.net/share/20.jpg"], [status.exitstatus, JSON.load(string).fetch("url")]
1544
+ end
1545
+
1546
+ end
1547
+
1548
+ end