directlink 0.0.9.2 → 0.0.10.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: f745bed3faf2b74dfe4532357fbb772fe32598b1
4
- data.tar.gz: 203127452f8e51be364fff16a4c1eafa8a0a21df
2
+ SHA256:
3
+ metadata.gz: 22f79345a6e337da21259e7f0a6502b72077862b76cd58c08a199f6fef4ca750
4
+ data.tar.gz: 765af8ad0ea6e047799c33bad9217664ab6cc525ad9b8072ee1f8c3a2f16dc23
5
5
  SHA512:
6
- metadata.gz: 75bd9d5351c48c4f1df45b76f4835abc52bd057eabd01f4615a2056c16f4f9cf57546c3ff7123f479836206d1bb3164a740bebd2d81b84899b5507ee74d0e178
7
- data.tar.gz: 6e20c47d7270b6bf3b0920ea964337b5143e255eb789baac70823218c9b689b13a6b16abdc729a3c134a2b5cf48fccfa074ec64bebac6ed835a32764b8f5726c
6
+ metadata.gz: ed636fbce9c660540e19fcd1ce5888f144a3844ea6de0a73bbe71c7e662abaaea05033697c9d89b601849bca8b4efceb589e64e8f8e639b5fa81c48b2dd293e4
7
+ data.tar.gz: 40d2677c4273a5a28bdaa40466a8546737a26725eeb26b10504939cc44ce5811e5bdaa2778e139605f8047c5fe49c0f0c9c025bd04b2e481595e8a92d3819499
data/bin/directlink CHANGED
@@ -73,4 +73,8 @@ rescue *DirectLink::NORMAL_EXCEPTIONS => e
73
73
  cause = e.cause if e.cause if e.respond_to? :cause
74
74
  c = e.class.to_s
75
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
76
80
  end
data/directlink.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |spec|
2
2
  spec.name = "directlink"
3
- spec.version = "0.0.9.2"
3
+ spec.version = "0.0.10.0"
4
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"
@@ -9,18 +9,21 @@ 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
16
  spec.add_dependency "nethttputils", "~>0.4.1.0"
15
- spec.add_dependency "reddit_bot", "~>1.7.8"
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"
20
+ spec.add_development_dependency "minitest-around"
21
+ spec.add_development_dependency "webmock"
19
22
 
20
23
  spec.require_path = "lib"
21
24
  spec.bindir = "bin"
22
25
  spec.executable = "directlink"
23
- spec.test_file = "test.rb"
26
+ spec.test_file = "unit.test.rb"
24
27
  spec.files = %w{ LICENSE directlink.gemspec lib/directlink.rb bin/directlink }
25
28
 
26
29
  spec.requirements << "you may want to create apps and provide API tokens:"
data/lib/directlink.rb CHANGED
@@ -11,7 +11,7 @@ module DirectLink
11
11
  puts str unless Module.nesting.first.silent
12
12
  end
13
13
 
14
- class ErrorAssert < RuntimeError
14
+ class ErrorAssert < RuntimeError # gem user should not face this error
15
15
  def initialize msg
16
16
  super "#{msg} -- consider reporting this issue to GitHub"
17
17
  end
@@ -57,7 +57,7 @@ module DirectLink
57
57
  "#{$1}s#{width}/"
58
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/
59
59
  "https:#{$1}s#{width}/"
60
- 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/
61
61
  "https:#{$1}s#{width}/"
62
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/,
63
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/,
@@ -83,6 +83,9 @@ module DirectLink
83
83
  # mp4
84
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/
85
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}/"
86
89
  else
87
90
  raise ErrorBadLink.new src
88
91
  end
@@ -91,7 +94,7 @@ module DirectLink
91
94
  require "json"
92
95
 
93
96
  # TODO make the timeout handling respect the way the Directlink method works with timeouts
94
- def self.imgur link, timeout = 2000
97
+ def self.imgur link, timeout = 1000
95
98
  raise ErrorMissingEnvVar.new "define IMGUR_CLIENT_ID env var" unless ENV["IMGUR_CLIENT_ID"]
96
99
 
97
100
  request_data = lambda do |url|
@@ -100,7 +103,7 @@ module DirectLink
100
103
  NetHTTPUtils.request_data url, header: { Authorization: "Client-ID #{ENV["IMGUR_CLIENT_ID"]}" }
101
104
  rescue NetHTTPUtils::Error => e
102
105
  raise ErrorNotFound.new url.inspect if 404 == e.code
103
- if t < timeout && [400, 500, 503].include?(e.code)
106
+ if t < timeout && [400, 500, 502, 503].include?(e.code)
104
107
  logger.error "retrying in #{t} seconds because of Imgur HTTP ERROR #{e.code}"
105
108
  sleep t
106
109
  t *= 2
@@ -151,7 +154,6 @@ module DirectLink
151
154
  def self._500px link
152
155
  raise ErrorBadLink.new link unless %r{\Ahttps://500px\.com/photo/(?<id>[^/]+)/[-[a-zA-Z0-9]%]+\/?\z} =~ link
153
156
  require "nokogiri"
154
- resp = NetHTTPUtils.request_data link
155
157
  f = lambda do |form|
156
158
  JSON.load(NetHTTPUtils.request_data "https://api.500px.com/v1/photos", form: form).fetch("photos").values.first
157
159
  end
@@ -240,11 +242,13 @@ module DirectLink
240
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"])
241
243
  return [true, data["media"]["oembed"]["thumbnail_url"]]
242
244
  end
243
- return [true, data["media_metadata"].values.map do |media|
244
- next if media == {"status"=>"failed"}
245
- raise ErrorAssert.new "our knowledge about Reddit API seems to be outdated" unless media["status"] == "valid"
246
- [media["m"], *media["s"].values_at("x", "y"), CGI.unescapeHTML(media["s"]["u"])]
247
- end.compact] if data["media_metadata"]
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
248
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"]
249
253
  return [true, data["url"]] unless data["is_self"]
250
254
  raise ErrorAssert.new "our knowledge about Reddit API seems to be outdated" if data["url"] != "https://www.reddit.com" + data["permalink"]
@@ -282,7 +286,7 @@ module DirectLink
282
286
  raise ErrorBadLink.new link
283
287
  end
284
288
  raise ErrorMissingEnvVar.new "define VK_ACCESS_TOKEN and VK_CLIENT_SECRET env vars" unless ENV["VK_ACCESS_TOKEN"] && ENV["VK_CLIENT_SECRET"]
285
- sleep 0.25 # "error_msg"=>"Too many requests per second"
289
+ sleep 0.25 unless ENV["CI"] # "error_msg"=>"Too many requests per second"
286
290
  f.call( JSON.load( NetHTTPUtils.request_data "https://api.vk.com/method/#{mtd}.getById",
287
291
  :POST, form: { field => id, :access_token => ENV["VK_ACCESS_TOKEN"], :client_secret => ENV["VK_CLIENT_SECRET"], :v => "5.101" }
288
292
  ).fetch("response") ).map do |photos|
@@ -341,12 +345,13 @@ def DirectLink link, timeout = nil, proxy = nil, giveup: false, ignore_meta: fal
341
345
  head = NetHTTPUtils.request_data link, :HEAD, header: header, **(proxy ? {proxy: proxy} : {}), **(timeout ? {
342
346
  timeout: timeout,
343
347
  max_start_http_retry_delay: timeout,
344
- max_read_retry_delay: timeout
348
+ max_read_retry_delay: timeout,
345
349
  } : {})
346
350
  rescue Net::ReadTimeout
347
351
  rescue NetHTTPUtils::Error => e
348
352
  raise unless 418 == e.code
349
353
  else
354
+ raise DirectLink::ErrorAssert.new "last_response.uri is not set" unless head.instance_variable_get(:@last_response).uri
350
355
  link = head.instance_variable_get(:@last_response).uri.to_s
351
356
  end
352
357
 
@@ -354,10 +359,10 @@ def DirectLink link, timeout = nil, proxy = nil, giveup: false, ignore_meta: fal
354
359
  # because they can be hidden behind URL shorteners
355
360
  # also it can resolve NetHTTPUtils::Error(404) before trying the adapter
356
361
 
357
- t = google_without_schema_crutch[] and return t
362
+ t = google_without_schema_crutch[] and return t # TODO: why again?
358
363
 
359
364
  begin
360
- 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|
361
366
  struct.new u, w, h, t
362
367
  end
363
368
  # `DirectLink.imgur` return value is always an Array
@@ -432,11 +437,11 @@ def DirectLink link, timeout = nil, proxy = nil, giveup: false, ignore_meta: fal
432
437
  timeout: timeout, # NetHTTPUtild passes this as read_timeout to Net::HTTP.start
433
438
  max_read_retry_delay: timeout # and then compares accumulated delay to this
434
439
  # if we use :get here we will download megabytes of files just to giveup on content_type we can't process
435
- case head.instance_variable_get(:@last_response).content_type
440
+ case head.instance_variable_get(:@last_response).content_type # webmock should provide this
436
441
  when "text/html" ; nil
437
442
  else ; raise
438
443
  end
439
- html = Nokogiri::HTML NetHTTPUtils.request_data link, header: {"User-Agent" => "Mozilla"}
444
+ html = Nokogiri::HTML NetHTTPUtils.request_data link, :GET, header: {"User-Agent" => "Mozilla"}
440
445
  if t = html.at_css("meta[@property='og:image']")
441
446
  begin
442
447
  return DirectLink URI.join(link, t[:content]).to_s, nil, *proxy, giveup: true
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