sinew 2.0.5 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +26 -0
  3. data/.rubocop.yml +9 -6
  4. data/.vscode/settings.json +0 -10
  5. data/Gemfile +9 -0
  6. data/README.md +13 -17
  7. data/Rakefile +33 -18
  8. data/bin/sinew +2 -0
  9. data/lib/sinew.rb +0 -1
  10. data/lib/sinew/connection.rb +52 -0
  11. data/lib/sinew/connection/log_formatter.rb +22 -0
  12. data/lib/sinew/connection/rate_limit.rb +29 -0
  13. data/lib/sinew/core_ext.rb +1 -1
  14. data/lib/sinew/dsl.rb +2 -1
  15. data/lib/sinew/main.rb +7 -55
  16. data/lib/sinew/output.rb +7 -23
  17. data/lib/sinew/request.rb +20 -71
  18. data/lib/sinew/response.rb +8 -57
  19. data/lib/sinew/runtime_options.rb +4 -4
  20. data/lib/sinew/version.rb +1 -1
  21. data/sample.sinew +2 -2
  22. data/sinew.gemspec +16 -17
  23. metadata +41 -99
  24. data/.travis.yml +0 -4
  25. data/lib/sinew/cache.rb +0 -79
  26. data/test/legacy/eu.httpbin.org/head/redirect,3 +0 -51
  27. data/test/legacy/eu.httpbin.org/head/status,500 +0 -1
  28. data/test/legacy/eu.httpbin.org/redirect,3 +0 -11
  29. data/test/legacy/eu.httpbin.org/status,500 +0 -1
  30. data/test/legacy/legacy.sinew +0 -2
  31. data/test/recipes/array_header.sinew +0 -6
  32. data/test/recipes/basic.sinew +0 -8
  33. data/test/recipes/dups.sinew +0 -7
  34. data/test/recipes/implicit_header.sinew +0 -5
  35. data/test/recipes/limit.sinew +0 -11
  36. data/test/recipes/noko.sinew +0 -9
  37. data/test/recipes/uri.sinew +0 -11
  38. data/test/recipes/xml.sinew +0 -8
  39. data/test/test.html +0 -45
  40. data/test/test_cache.rb +0 -69
  41. data/test/test_helper.rb +0 -126
  42. data/test/test_legacy.rb +0 -23
  43. data/test/test_main.rb +0 -34
  44. data/test/test_nokogiri_ext.rb +0 -18
  45. data/test/test_output.rb +0 -56
  46. data/test/test_recipes.rb +0 -60
  47. data/test/test_requests.rb +0 -164
  48. data/test/test_utf8.rb +0 -39
data/test/test_legacy.rb DELETED
@@ -1,23 +0,0 @@
1
- require_relative 'test_helper'
2
-
3
- class TestLegacy < MiniTest::Test
4
- def setup
5
- super
6
-
7
- # These are legacy cache files, pulled from an older version of sinew. We
8
- # use them to test our legacy head parsing.
9
- src = 'legacy/eu.httpbin.org'
10
- dst = "#{TMP}/eu.httpbin.org"
11
- FileUtils.cp_r(File.expand_path(src, __dir__), dst)
12
- end
13
-
14
- def test_legacy
15
- assert_output(/failed with 999/) do
16
- sinew.dsl.get('http://eu.httpbin.org/status/500')
17
- assert_equal "\n", sinew.dsl.raw
18
- end
19
-
20
- sinew.dsl.get('http://eu.httpbin.org/redirect/3')
21
- assert_equal 'http://eu.httpbin.org/get', sinew.dsl.url
22
- end
23
- end
data/test/test_main.rb DELETED
@@ -1,34 +0,0 @@
1
- require_relative 'test_helper'
2
-
3
- require 'base64'
4
-
5
- class TestMain < MiniTest::Test
6
- def test_rate_limit
7
- # true network requests call sleep for timeouts, which interferes with our
8
- # instrumentation of Kernel#sleep
9
- skip if test_network?
10
-
11
- slept = false
12
-
13
- # change Kernel#sleep to not really sleep!
14
- Kernel.send(:alias_method, :old_sleep, :sleep)
15
- Kernel.send(:define_method, :sleep) do |_duration|
16
- slept = true
17
- end
18
-
19
- sinew.runtime_options.rate_limit = 1
20
- sinew.dsl.get('http://httpbin.org/html')
21
- sinew.dsl.get('http://httpbin.org/get')
22
- assert(slept)
23
-
24
- # restore old Kernel#sleep
25
- Kernel.send(:alias_method, :sleep, :old_sleep)
26
- Kernel.send(:undef_method, :old_sleep)
27
- end
28
-
29
- def test_gunzip
30
- body = Base64.decode64('H4sICBRI61oAA2d1Yi50eHQASy9N4gIAJlqRYgQAAAA=')
31
- body = Sinew::Response.process_body(OpenStruct.new(body: body))
32
- assert_equal 'gub', body.strip
33
- end
34
- end
@@ -1,18 +0,0 @@
1
- require_relative 'test_helper'
2
-
3
- class TestNokogiriExt < MiniTest::Test
4
- def test_inner_text
5
- assert_equal('hello world', noko.css('li').inner_text)
6
- assert_equal('<li>hello</li> <li>world</li>', noko.css('ul').inner_html.squish)
7
- end
8
-
9
- def test_just_me
10
- assert_equal('a', noko.css('div').text_just_me.squish)
11
- assert_equal('b b', noko.css('p').text_just_me.squish)
12
- end
13
-
14
- def noko
15
- @noko ||= Nokogiri::HTML(HTML).css('#nokogiri_ext')
16
- end
17
- protected :noko
18
- end
data/test/test_output.rb DELETED
@@ -1,56 +0,0 @@
1
- require_relative 'test_helper'
2
-
3
- class TestOutput < MiniTest::Test
4
- def test_filenames
5
- sinew = Sinew::Main.new(recipe: 'gub.sinew')
6
- assert_equal 'gub.csv', sinew.output.filename
7
- sinew = Sinew::Main.new(recipe: 'gub')
8
- assert_equal 'gub.csv', sinew.output.filename
9
- sinew = Sinew::Main.new(recipe: '/somewhere/gub.sinew')
10
- assert_equal '/somewhere/gub.csv', sinew.output.filename
11
- end
12
-
13
- def test_normalization
14
- output = Sinew::Output.new(nil)
15
-
16
- #
17
- # simple types
18
- #
19
-
20
- assert_equal '', output.send(:normalize, nil)
21
- assert_equal '', output.send(:normalize, '')
22
- assert_equal 'text', output.send(:normalize, 'text')
23
- assert_equal '123', output.send(:normalize, 123)
24
- assert_equal('1|2', output.send(:normalize, [ 1, 2 ]))
25
-
26
- #
27
- # nokogiri
28
- #
29
-
30
- noko = Nokogiri::HTML(HTML)
31
-
32
- # node => text
33
- assert_equal('text', output.send(:normalize, noko.css('#element')))
34
- # nodes => text joined with space
35
- assert_equal('text1 text2', output.send(:normalize, noko.css('.e')))
36
-
37
- #
38
- # string cleanups
39
- #
40
-
41
- # strip_html_tags
42
- assert_equal('gub', output.send(:normalize, '<tag>gub</tag>'))
43
- # strip_html_tags and replace with spaces
44
- assert_equal('hello world', output.send(:normalize, '<tag>hello<br>world</tag>'))
45
- # convert_smart_punctuation
46
- assert_equal('"gub"', output.send(:normalize, "\302\223gub\302\224"))
47
- # convert_accented_html_entities
48
- assert_equal('a', output.send(:normalize, '&aacute;'))
49
- # convert_miscellaneous_html_entities
50
- assert_equal('<&>', output.send(:normalize, '&lt;&amp;&gt;'))
51
- # to_ascii
52
- assert_equal('cafe', output.send(:normalize, "caf\xc3\xa9"))
53
- # squish
54
- assert_equal('hello world', output.send(:normalize, "\nhello \t \rworld"))
55
- end
56
- end
data/test/test_recipes.rb DELETED
@@ -1,60 +0,0 @@
1
- require_relative 'test_helper'
2
-
3
- class TestRecipe < MiniTest::Test
4
- DIR = File.expand_path('recipes', __dir__)
5
- TEST_SINEW = "#{TMP}/test.sinew".freeze
6
- TEST_CSV = "#{TMP}/test.csv".freeze
7
-
8
- def test_recipes
9
- Dir.chdir(DIR) do
10
- Dir['*.sinew'].sort.each do |filename|
11
- recipe = IO.read(filename)
12
-
13
- # get ready
14
- IO.write(TEST_SINEW, recipe)
15
- sinew = Sinew::Main.new(cache: TMP, quiet: true, recipe: TEST_SINEW)
16
-
17
- # read OPTIONS
18
- if options = options_from(recipe)
19
- options.each do |key, value|
20
- sinew.options[key] = value
21
- end
22
- end
23
-
24
- # read OUTPUT
25
- output = output_from(recipe, filename)
26
-
27
- # run
28
- sinew.run
29
-
30
- # assert
31
- csv = IO.read(TEST_CSV)
32
- assert_equal(output, csv, "Output didn't match for recipes/#{filename}")
33
- end
34
- end
35
- end
36
-
37
- def options_from(recipe)
38
- if options = recipe[/^#\s*OPTIONS\s*(\{.*\})/, 1]
39
- # rubocop:disable Security/Eval
40
- eval(options)
41
- # rubocop:enable Security/Eval
42
- end
43
- end
44
- protected :options_from
45
-
46
- def output_from(recipe, filename)
47
- lines = recipe.split("\n")
48
- first_line = lines.index { |i| i =~ /^# OUTPUT/ }
49
- if !first_line
50
- raise "# OUTPUT not found in recipes/#{filename}"
51
- end
52
-
53
- output = lines[first_line + 1..-1]
54
- output = output.map { |i| i.gsub(/^# /, '') }
55
- output = output.join("\n")
56
- output += "\n"
57
- output
58
- end
59
- protected :output_from
60
- end
@@ -1,164 +0,0 @@
1
- require_relative 'test_helper'
2
-
3
- class TestRequests < MiniTest::Test
4
- def test_user_agent
5
- sinew.dsl.get('http://httpbin.org/get', a: 1, b: 2)
6
- assert_match(/sinew/, sinew.dsl.json[:headers][:'User-Agent'])
7
- end
8
-
9
- def test_basic_methods
10
- sinew.dsl.get('http://httpbin.org/get', a: 1, b: 2)
11
- assert_equal(200, sinew.dsl.code)
12
- assert_equal({ a: '1', b: '2' }, sinew.dsl.json[:args])
13
-
14
- sinew.dsl.post('http://httpbin.org/post', a: 1, b: 2)
15
- assert_equal({ a: '1', b: '2' }, sinew.dsl.json[:form])
16
-
17
- sinew.dsl.post_json('http://httpbin.org/post', a: 1, b: 2)
18
- assert_equal({ a: 1, b: 2 }, sinew.dsl.json[:json])
19
- end
20
-
21
- def test_custom_headers
22
- sinew.dsl.http('get', 'http://httpbin.org/get', headers: { "User-Agent": '007' })
23
- assert_match(/007/, sinew.dsl.json[:headers][:'User-Agent'])
24
- end
25
-
26
- def test_redirects
27
- # absolute redirect
28
- sinew.dsl.get('http://httpbin.org/redirect/2')
29
- assert_equal 'http://httpbin.org/get', sinew.dsl.url
30
-
31
- # and relative redirect
32
- sinew.dsl.get('http://httpbin.org/relative-redirect/2')
33
- assert_equal 'http://httpbin.org/get', sinew.dsl.url
34
- end
35
-
36
- def test_errors
37
- skip if test_network?
38
-
39
- # 500
40
- assert_output(/failed with 500/) do
41
- sinew.dsl.get('http://httpbin.org/status/500')
42
- assert_equal 500, sinew.dsl.code
43
- assert_equal '500', sinew.dsl.raw
44
- end
45
-
46
- # timeout
47
- assert_output(/failed with 999/) do
48
- sinew.dsl.get('http://httpbin.org/delay/1')
49
- assert_equal 999, sinew.dsl.code
50
- end
51
-
52
- # uncommon errors
53
- errors = [
54
- Errno::ECONNREFUSED,
55
- HTTParty::RedirectionTooDeep.new(nil),
56
- OpenSSL::SSL::SSLError.new,
57
- SocketError.new,
58
- ]
59
- errors.each_with_index do |error, index|
60
- stub_request(:get, %r{http://[^/]+/error#{index}}).to_return { raise error }
61
- assert_output(/failed with 999/) do
62
- sinew.dsl.get("http://httpbin.org/error#{index}")
63
- assert_equal 999, sinew.dsl.code
64
- end
65
- end
66
- end
67
-
68
- def test_retry_timeout
69
- skip if test_network?
70
-
71
- errors = 2
72
- stub_request(:get, %r{http://[^/]+/error}).to_return do
73
- if errors > 0
74
- errors -= 1
75
- raise Timeout::Error
76
- end
77
- { body: 'done', status: 200 }
78
- end
79
- sinew.dsl.get('http://httpbin.org/error')
80
- assert_equal 0, errors
81
- assert_equal 'done', sinew.dsl.raw
82
- end
83
-
84
- def test_retry_500
85
- skip if test_network?
86
-
87
- errors = 2
88
- stub_request(:get, %r{http://[^/]+/error}).to_return do
89
- if errors > 0
90
- errors -= 1
91
- return { status: 500 }
92
- end
93
- { body: 'done', status: 200 }
94
- end
95
- sinew.dsl.get('http://httpbin.org/error')
96
- assert_equal 0, errors
97
- assert_equal 'done', sinew.dsl.raw
98
- end
99
-
100
- def test_cache_key
101
- # empty
102
- req = Sinew::Request.new(sinew, 'get', 'http://host')
103
- assert_equal req.cache_key, 'host/_root_'
104
-
105
- # path
106
- req = Sinew::Request.new(sinew, 'get', 'http://host/path')
107
- assert_equal req.cache_key, 'host/path'
108
-
109
- # query
110
- req = Sinew::Request.new(sinew, 'get', 'http://host/path', query: { a: 'b' })
111
- assert_equal req.cache_key, 'host/path,a=b'
112
-
113
- # post with body
114
- req = Sinew::Request.new(sinew, 'post', 'http://host/path', body: { c: 'd' })
115
- assert_equal req.cache_key, 'host/post,path,c=d'
116
-
117
- # too long should turn into digest
118
- path = 'xyz' * 123
119
- req = Sinew::Request.new(sinew, 'get', "http://host/#{path}")
120
- assert_equal "host/#{Digest::MD5.hexdigest(path)}", req.cache_key
121
- end
122
-
123
- def test_before_generate_cache_key
124
- # arity 1
125
- sinew.runtime_options.before_generate_cache_key = method(:redact_cache_key)
126
- req = Sinew::Request.new(sinew, 'get', 'http://host', query: { secret: 'xyz' })
127
- assert_equal 'host/secret=redacted', req.cache_key
128
-
129
- # arity 2
130
- sinew.runtime_options.before_generate_cache_key = method(:add_scheme)
131
- req = Sinew::Request.new(sinew, 'get', 'https://host/gub')
132
- assert_equal 'host/https,gub', req.cache_key
133
- end
134
-
135
- def test_urls
136
- # simple
137
- req = Sinew::Request.new(sinew, 'get', 'https://host')
138
- assert_equal 'https://host', req.uri.to_s
139
-
140
- # with query
141
- req = Sinew::Request.new(sinew, 'get', 'https://host', query: { a: 1 })
142
- assert_equal 'https://host?a=1', req.uri.to_s
143
-
144
- # entity decoding
145
- req = Sinew::Request.new(sinew, 'get', 'https://host?a=&lt;5')
146
- assert_equal 'https://host?a=%3C5', req.uri.to_s
147
-
148
- # sloppy urls
149
- req = Sinew::Request.new(sinew, 'get', 'https://host?a=b c&d=f\'g')
150
- assert_equal 'https://host?a=b%20c&d=f%27g', req.uri.to_s
151
- end
152
-
153
- def redact_cache_key(key)
154
- key[:query].gsub!(/secret=[^&]+/, 'secret=redacted')
155
- key
156
- end
157
- protected :redact_cache_key
158
-
159
- def add_scheme(key, uri)
160
- key[:scheme] = uri.scheme
161
- key
162
- end
163
- protected :add_scheme
164
- end
data/test/test_utf8.rb DELETED
@@ -1,39 +0,0 @@
1
- require_relative 'test_helper'
2
-
3
- class TestRequests < MiniTest::Test
4
- def test_get
5
- # network (or stub)
6
- sinew.dsl.get('http://httpbin.org/get')
7
- assert_equal 'UTF-8', sinew.dsl.raw.encoding.name
8
-
9
- # disk
10
- sinew.dsl.get('http://httpbin.org/get')
11
- assert_equal 'UTF-8', sinew.dsl.raw.encoding.name
12
- end
13
-
14
- def test_utf8
15
- skip if !test_network?
16
-
17
- # network
18
- sinew.dsl.get('http://httpbin.org/encoding/utf8')
19
- assert_equal 'UTF-8', sinew.dsl.raw.encoding.name
20
- assert_match(/∑/, sinew.dsl.raw)
21
-
22
- # disk
23
- sinew.dsl.get('http://httpbin.org/encoding/utf8')
24
- assert_equal 'UTF-8', sinew.dsl.raw.encoding.name
25
- assert_match(/∑/, sinew.dsl.raw)
26
- end
27
-
28
- def test_encode
29
- skip if !test_network?
30
-
31
- # network
32
- sinew.dsl.get('https://www.google.co.jp')
33
- assert_equal 'UTF-8', sinew.dsl.raw.encoding.name
34
-
35
- # disk
36
- sinew.dsl.get('https://www.google.co.jp')
37
- assert_equal 'UTF-8', sinew.dsl.raw.encoding.name
38
- end
39
- end