directlink 0.0.8.7 → 0.0.10.0

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