wreq 1.2.3-x64-mingw-ucrt
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Cargo.lock +1687 -0
- data/Cargo.toml +54 -0
- data/Gemfile +18 -0
- data/LICENSE +201 -0
- data/README.md +153 -0
- data/Rakefile +90 -0
- data/build.rs +16 -0
- data/docs/windows-gnu-tokio-crash.md +70 -0
- data/examples/body.rb +42 -0
- data/examples/client.rb +33 -0
- data/examples/cookie.rb +24 -0
- data/examples/emulate_request.rb +37 -0
- data/examples/headers.rb +27 -0
- data/examples/proxy.rb +113 -0
- data/examples/send_stream.rb +85 -0
- data/examples/stream.rb +14 -0
- data/examples/thread_interrupt.rb +83 -0
- data/extconf.rb +7 -0
- data/lib/wreq.rb +303 -0
- data/lib/wreq_ruby/3.3/wreq_ruby.so +0 -0
- data/lib/wreq_ruby/3.4/wreq_ruby.so +0 -0
- data/lib/wreq_ruby/4.0/wreq_ruby.so +0 -0
- data/lib/wreq_ruby/body.rb +36 -0
- data/lib/wreq_ruby/client.rb +526 -0
- data/lib/wreq_ruby/cookie.rb +156 -0
- data/lib/wreq_ruby/emulate.rb +232 -0
- data/lib/wreq_ruby/error.rb +157 -0
- data/lib/wreq_ruby/header.rb +205 -0
- data/lib/wreq_ruby/http.rb +160 -0
- data/lib/wreq_ruby/response.rb +209 -0
- data/script/build_platform_gem.rb +34 -0
- data/script/build_windows_gnu.ps1 +257 -0
- data/src/arch.rs +33 -0
- data/src/client/body/form.rs +2 -0
- data/src/client/body/json.rs +16 -0
- data/src/client/body/stream.rs +146 -0
- data/src/client/body.rs +57 -0
- data/src/client/param.rs +19 -0
- data/src/client/query.rs +2 -0
- data/src/client/req.rs +274 -0
- data/src/client/resp.rs +248 -0
- data/src/client.rs +413 -0
- data/src/cookie.rs +312 -0
- data/src/emulate.rs +376 -0
- data/src/error.rs +163 -0
- data/src/extractor.rs +117 -0
- data/src/gvl.rs +154 -0
- data/src/header.rs +245 -0
- data/src/http.rs +142 -0
- data/src/lib.rs +98 -0
- data/src/macros.rs +123 -0
- data/src/rt.rs +46 -0
- data/test/client_cookie_test.rb +46 -0
- data/test/client_test.rb +136 -0
- data/test/cookie_test.rb +182 -0
- data/test/emulation_test.rb +21 -0
- data/test/error_handling_test.rb +92 -0
- data/test/header_test.rb +290 -0
- data/test/inspect_test.rb +125 -0
- data/test/module_methods_test.rb +75 -0
- data/test/orig_header_test.rb +115 -0
- data/test/request_parameters_test.rb +175 -0
- data/test/request_test.rb +244 -0
- data/test/response_test.rb +69 -0
- data/test/stream_test.rb +397 -0
- data/test/test_helper.rb +52 -0
- data/wreq.gemspec +68 -0
- metadata +123 -0
data/test/cookie_test.rb
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "test_helper"
|
|
4
|
+
|
|
5
|
+
class CookieTest < Minitest::Test
|
|
6
|
+
def setup
|
|
7
|
+
@jar = Wreq::Jar.new
|
|
8
|
+
@base_url = "https://example.com"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def test_jar_initially_empty
|
|
12
|
+
assert_instance_of Wreq::Jar, @jar
|
|
13
|
+
cookies = begin
|
|
14
|
+
Wreq::Jar.get_all(@jar)
|
|
15
|
+
rescue
|
|
16
|
+
@jar.get_all
|
|
17
|
+
end # support either binding style
|
|
18
|
+
assert_kind_of Array, cookies
|
|
19
|
+
assert_equal 0, cookies.length
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def test_add_and_get_all
|
|
23
|
+
set_cookie = "sid=abc123; Path=/; Domain=example.com; HttpOnly; Secure"
|
|
24
|
+
@jar.add(set_cookie, @base_url)
|
|
25
|
+
|
|
26
|
+
cookies = @jar.get_all
|
|
27
|
+
assert_kind_of Array, cookies
|
|
28
|
+
assert_equal 1, cookies.length
|
|
29
|
+
|
|
30
|
+
c = cookies.first
|
|
31
|
+
assert_instance_of Wreq::Cookie, c
|
|
32
|
+
assert_equal "sid", c.name
|
|
33
|
+
assert_equal "abc123", c.value
|
|
34
|
+
|
|
35
|
+
# attributes parsed from Set-Cookie
|
|
36
|
+
assert_equal "/", c.path
|
|
37
|
+
assert_equal "example.com", c.domain
|
|
38
|
+
|
|
39
|
+
# predicate-ish flags
|
|
40
|
+
assert_equal true, (c.http_only || c.http_only?)
|
|
41
|
+
assert_equal true, (c.secure || c.secure?)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def test_add_multiple_and_remove
|
|
45
|
+
@jar.add("a=1; Path=/", @base_url)
|
|
46
|
+
@jar.add("b=2; Path=/", @base_url)
|
|
47
|
+
@jar.add("c=3; Path=/", @base_url)
|
|
48
|
+
|
|
49
|
+
cookies = @jar.get_all
|
|
50
|
+
assert_equal 3, cookies.length
|
|
51
|
+
|
|
52
|
+
# remove one by name
|
|
53
|
+
@jar.remove("b", @base_url)
|
|
54
|
+
names = @jar.get_all.map(&:name)
|
|
55
|
+
refute_includes names, "b"
|
|
56
|
+
assert_includes names, "a"
|
|
57
|
+
assert_includes names, "c"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def test_clear
|
|
61
|
+
@jar.add("x=1; Path=/", @base_url)
|
|
62
|
+
@jar.add("y=2; Path=/", @base_url)
|
|
63
|
+
refute_empty @jar.get_all
|
|
64
|
+
|
|
65
|
+
@jar.clear
|
|
66
|
+
assert_empty @jar.get_all
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def test_max_age_and_expires_optional
|
|
70
|
+
# Max-Age only
|
|
71
|
+
@jar.clear
|
|
72
|
+
@jar.add("ma=1; Max-Age=3600; Path=/", @base_url)
|
|
73
|
+
c1 = @jar.get_all.find { |c| c.name == "ma" }
|
|
74
|
+
assert c1
|
|
75
|
+
# can be nil or Integer; just ensure responds and is truthy integer
|
|
76
|
+
if (v = c1.max_age)
|
|
77
|
+
assert_kind_of Integer, v
|
|
78
|
+
assert_operator v, :>=, 0
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Expires only
|
|
82
|
+
@jar.clear
|
|
83
|
+
t = Time.now + 3600
|
|
84
|
+
@jar.add("exp=1; Expires=#{t.gmtime.strftime("%a, %d %b %Y %H:%M:%S GMT")}; Path=/", @base_url)
|
|
85
|
+
c2 = @jar.get_all.find { |c| c.name == "exp" }
|
|
86
|
+
assert c2
|
|
87
|
+
# expires returns Float (unix seconds) or nil
|
|
88
|
+
if (e = c2.expires)
|
|
89
|
+
assert_kind_of Float, e
|
|
90
|
+
assert_operator e, :>, Time.now.to_f - 1_000_000 # sanity bound
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# -------- Wreq::Cookie unit tests --------
|
|
95
|
+
|
|
96
|
+
def test_cookie_new_minimal
|
|
97
|
+
c = Wreq::Cookie.new("sid", "abc")
|
|
98
|
+
|
|
99
|
+
assert_instance_of Wreq::Cookie, c
|
|
100
|
+
assert_equal "sid", c.name
|
|
101
|
+
assert_equal "abc", c.value
|
|
102
|
+
|
|
103
|
+
assert_nil c.path
|
|
104
|
+
assert_nil c.domain
|
|
105
|
+
assert_nil c.max_age
|
|
106
|
+
assert_nil c.expires
|
|
107
|
+
|
|
108
|
+
assert_equal false, (c.http_only || c.http_only?)
|
|
109
|
+
assert_equal false, (c.secure || c.secure?)
|
|
110
|
+
assert_equal false, c.same_site_lax?
|
|
111
|
+
assert_equal false, c.same_site_strict?
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def test_cookie_new_full_attributes
|
|
115
|
+
exp = Time.now.to_f + 7200.0
|
|
116
|
+
c = Wreq::Cookie.new("sess", "v",
|
|
117
|
+
domain: "example.com",
|
|
118
|
+
path: "/",
|
|
119
|
+
max_age: 3600,
|
|
120
|
+
expires: exp,
|
|
121
|
+
http_only: true,
|
|
122
|
+
secure: true,
|
|
123
|
+
same_site: Wreq::SameSite::Lax)
|
|
124
|
+
|
|
125
|
+
assert_equal "sess", c.name
|
|
126
|
+
assert_equal "v", c.value
|
|
127
|
+
assert_equal "example.com", c.domain
|
|
128
|
+
assert_equal "/", c.path
|
|
129
|
+
|
|
130
|
+
# Max-Age returns seconds as Integer
|
|
131
|
+
assert_equal 3600, c.max_age
|
|
132
|
+
|
|
133
|
+
# Expires returns Float seconds-since-epoch (with small tolerance)
|
|
134
|
+
assert c.expires
|
|
135
|
+
assert_kind_of Float, c.expires
|
|
136
|
+
assert_in_delta exp, c.expires, 2.0
|
|
137
|
+
|
|
138
|
+
assert_equal true, (c.http_only || c.http_only?)
|
|
139
|
+
assert_equal true, (c.secure || c.secure?)
|
|
140
|
+
# constructor currently sets SameSite to none
|
|
141
|
+
assert_equal true, c.same_site_lax?
|
|
142
|
+
assert_equal false, c.same_site_strict?
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def test_same_site_flags_from_parsed_header
|
|
146
|
+
@jar.clear
|
|
147
|
+
@jar.add("s1=1; Path=/; SameSite=Strict", @base_url)
|
|
148
|
+
@jar.add("s2=1; Path=/; SameSite=Lax", @base_url)
|
|
149
|
+
|
|
150
|
+
cookies = @jar.get_all
|
|
151
|
+
h = cookies.to_h { |ck| [ck.name, [ck.same_site_strict?, ck.same_site_lax?]] }
|
|
152
|
+
|
|
153
|
+
assert_equal [true, false], h["s1"]
|
|
154
|
+
assert_equal [false, true], h["s2"]
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def test_request_uncompressed_cookies
|
|
158
|
+
client = Wreq::Client.new
|
|
159
|
+
resp = client.get(
|
|
160
|
+
"#{HTTPBIN_URL}/cookies",
|
|
161
|
+
cookies: {"foo" => "bar", "baz" => "qux"}
|
|
162
|
+
)
|
|
163
|
+
json = resp.json
|
|
164
|
+
assert_instance_of Hash, json
|
|
165
|
+
cookies = json.fetch("cookies", json)
|
|
166
|
+
assert_equal "bar", cookies["foo"]
|
|
167
|
+
assert_equal "qux", cookies["baz"]
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def test_request_compressed_cookies
|
|
171
|
+
client = Wreq::Client.new
|
|
172
|
+
resp = client.get(
|
|
173
|
+
"#{HTTPBIN_URL}/cookies",
|
|
174
|
+
cookies: "foo=bar; baz=qux"
|
|
175
|
+
)
|
|
176
|
+
json = resp.json
|
|
177
|
+
assert_instance_of Hash, json
|
|
178
|
+
cookies = json.fetch("cookies", json)
|
|
179
|
+
assert_equal "bar", cookies["foo"]
|
|
180
|
+
assert_equal "qux", cookies["baz"]
|
|
181
|
+
end
|
|
182
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "test_helper"
|
|
4
|
+
|
|
5
|
+
class EmulationTest < Minitest::Test
|
|
6
|
+
def test_all_emulation_device_constants_are_non_nil
|
|
7
|
+
Wreq::Profile.constants.each do |name|
|
|
8
|
+
const = Wreq::Profile.const_get(name)
|
|
9
|
+
assert_instance_of Wreq::Profile, const,
|
|
10
|
+
"#{name} should be Profile, got #{const.inspect}"
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def test_all_emulation_os_constants_are_non_nil
|
|
15
|
+
Wreq::Platform.constants.each do |name|
|
|
16
|
+
const = Wreq::Platform.const_get(name)
|
|
17
|
+
assert_instance_of Wreq::Platform, const,
|
|
18
|
+
"#{name} should be Platform, got #{const.inspect}"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
require "test_helper"
|
|
2
|
+
|
|
3
|
+
class ErrorHandlingTest < Minitest::Test
|
|
4
|
+
def test_network_error_handling
|
|
5
|
+
# Try to connect to a non-existent domain
|
|
6
|
+
response = Wreq.get("https://definitely-not-a-real-domain-12345.com")
|
|
7
|
+
flunk "Expected network error but got response: #{response.code}"
|
|
8
|
+
rescue => e
|
|
9
|
+
assert_instance_of Wreq::ConnectionError, e
|
|
10
|
+
# Network errors should be caught and wrapped appropriately
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def test_invalid_url_handling
|
|
14
|
+
# Invalid URL format
|
|
15
|
+
response = Wreq.get("not-a-valid-url")
|
|
16
|
+
flunk "Expected URL error but got response: #{response.code}"
|
|
17
|
+
rescue => e
|
|
18
|
+
assert_instance_of Wreq::BuilderError, e
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def test_http_error_status_codes
|
|
22
|
+
# These should not raise errors, just return responses with error codes
|
|
23
|
+
[400, 401, 403, 404, 500, 502, 503].each do |status_code|
|
|
24
|
+
response = Wreq.get("#{HTTPBIN_URL}/status/#{status_code}")
|
|
25
|
+
assert_equal status_code, response.code
|
|
26
|
+
# Should not raise an exception for HTTP error codes
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def test_timeout_handling
|
|
31
|
+
# Test timeout with a delay that should definitely cause timeout
|
|
32
|
+
|
|
33
|
+
# Request with a very short timeout that should fail
|
|
34
|
+
response = Wreq.get("#{HTTPBIN_URL}/delay/10", timeout: 1)
|
|
35
|
+
# If we get here, the request didn't timeout (unexpected)
|
|
36
|
+
flunk "Expected timeout error but got response: #{response.code}"
|
|
37
|
+
rescue => e
|
|
38
|
+
# Timeout error is expected
|
|
39
|
+
assert_instance_of Wreq::TimeoutError, e
|
|
40
|
+
# Could also check error message contains timeout-related keywords
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def test_invalid_json_response
|
|
44
|
+
# Get a non-JSON response and try to parse as JSON
|
|
45
|
+
response = Wreq.get("#{HTTPBIN_URL}/html")
|
|
46
|
+
assert_equal 200, response.code
|
|
47
|
+
|
|
48
|
+
# Should raise an error when trying to parse HTML as JSON
|
|
49
|
+
begin
|
|
50
|
+
response.json
|
|
51
|
+
rescue => e
|
|
52
|
+
assert_instance_of Wreq::DecodingError, e
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def test_empty_response_json
|
|
57
|
+
response = Wreq.get("#{HTTPBIN_URL}/status/204")
|
|
58
|
+
assert_equal 204, response.code
|
|
59
|
+
assert_equal "", response.text
|
|
60
|
+
|
|
61
|
+
# Empty body should raise error when parsing as JSON
|
|
62
|
+
begin
|
|
63
|
+
response.json
|
|
64
|
+
rescue => e
|
|
65
|
+
assert_instance_of Wreq::DecodingError, e
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def test_proxy_error_handling
|
|
70
|
+
invalid_proxies = [
|
|
71
|
+
"http://invalid.proxy:8080",
|
|
72
|
+
"https://invalid.proxy:8080",
|
|
73
|
+
"socks4://invalid.proxy:8080",
|
|
74
|
+
"socks4a://invalid.proxy:8080",
|
|
75
|
+
"socks5://invalid.proxy:8080",
|
|
76
|
+
"socks5h://invalid.proxy:8080"
|
|
77
|
+
]
|
|
78
|
+
target_urls = ["https://example.com", "http://example.com"]
|
|
79
|
+
|
|
80
|
+
invalid_proxies.each do |proxy|
|
|
81
|
+
target_urls.each do |url|
|
|
82
|
+
Wreq.get(url, proxy: proxy, timeout: 5)
|
|
83
|
+
flunk "Expected proxy connection error but got response"
|
|
84
|
+
rescue => e
|
|
85
|
+
assert(
|
|
86
|
+
e.is_a?(Wreq::ProxyConnectionError) || e.is_a?(Wreq::RequestError),
|
|
87
|
+
"Expected ProxyConnectionError or RequestError, got #{e.class}: #{e.message}"
|
|
88
|
+
)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
data/test/header_test.rb
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
require "test_helper"
|
|
2
|
+
|
|
3
|
+
class HeadersTest < Minitest::Test
|
|
4
|
+
def setup
|
|
5
|
+
@response = Wreq.get("#{HTTPBIN_URL}/response-headers",
|
|
6
|
+
query: {
|
|
7
|
+
"X-Custom-Header" => "custom-value",
|
|
8
|
+
"X-Multi-Header" => "value1"
|
|
9
|
+
})
|
|
10
|
+
@headers = @response.headers
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def test_headers_class
|
|
14
|
+
assert_instance_of Wreq::Headers, @headers
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def test_initialize
|
|
18
|
+
headers = Wreq::Headers.new
|
|
19
|
+
assert_instance_of Wreq::Headers, headers
|
|
20
|
+
assert headers.empty?
|
|
21
|
+
assert_equal 0, headers.length
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def test_get_existing_header
|
|
25
|
+
# Content-Type should exist in response
|
|
26
|
+
content_type = @headers.get("Content-Type")
|
|
27
|
+
assert_instance_of String, content_type
|
|
28
|
+
refute_nil content_type
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def test_get_case_insensitive
|
|
32
|
+
# Test case-insensitive lookup
|
|
33
|
+
value1 = @headers.get("content-type")
|
|
34
|
+
value2 = @headers.get("Content-Type")
|
|
35
|
+
value3 = @headers.get("CONTENT-TYPE")
|
|
36
|
+
|
|
37
|
+
assert_equal value1, value2
|
|
38
|
+
assert_equal value2, value3
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def test_get_nonexistent_header
|
|
42
|
+
result = @headers.get("X-Nonexistent-Header-12345")
|
|
43
|
+
assert_nil result
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def test_get_all_single_value
|
|
47
|
+
# Most headers have single values
|
|
48
|
+
values = @headers.get_all("Content-Type")
|
|
49
|
+
assert_instance_of Array, values
|
|
50
|
+
assert_equal 1, values.length
|
|
51
|
+
assert_equal @headers.get("Content-Type"), values.first
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def test_get_all_nonexistent
|
|
55
|
+
values = @headers.get_all("X-Nonexistent-Header")
|
|
56
|
+
assert_instance_of Array, values
|
|
57
|
+
assert_equal 0, values.length
|
|
58
|
+
assert_empty values
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def test_set_new_header
|
|
62
|
+
headers = Wreq::Headers.new
|
|
63
|
+
headers.set("X-Test-Header", "test-value")
|
|
64
|
+
|
|
65
|
+
assert_equal "test-value", headers.get("X-Test-Header")
|
|
66
|
+
assert_equal 1, headers.length
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def test_set_replaces_existing
|
|
70
|
+
headers = Wreq::Headers.new
|
|
71
|
+
headers.set("X-Test", "value1")
|
|
72
|
+
headers.set("X-Test", "value2")
|
|
73
|
+
|
|
74
|
+
assert_equal "value2", headers.get("X-Test")
|
|
75
|
+
values = headers.get_all("X-Test")
|
|
76
|
+
assert_equal 1, values.length
|
|
77
|
+
assert_equal "value2", values.first
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def test_append_to_new_header
|
|
81
|
+
headers = Wreq::Headers.new
|
|
82
|
+
headers.append("Accept", "application/json")
|
|
83
|
+
|
|
84
|
+
assert_equal "application/json", headers.get("Accept")
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def test_append_to_existing_header
|
|
88
|
+
headers = Wreq::Headers.new
|
|
89
|
+
headers.set("Accept", "application/json")
|
|
90
|
+
headers.append("Accept", "text/html")
|
|
91
|
+
headers.append("Accept", "application/xml")
|
|
92
|
+
|
|
93
|
+
values = headers.get_all("Accept")
|
|
94
|
+
assert_equal 3, values.length
|
|
95
|
+
assert_includes values, "application/json"
|
|
96
|
+
assert_includes values, "text/html"
|
|
97
|
+
assert_includes values, "application/xml"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def test_remove_existing_header
|
|
101
|
+
headers = Wreq::Headers.new
|
|
102
|
+
headers.set("X-Remove-Me", "value")
|
|
103
|
+
|
|
104
|
+
removed_value = headers.remove("X-Remove-Me")
|
|
105
|
+
assert_equal "value", removed_value
|
|
106
|
+
assert_nil headers.get("X-Remove-Me")
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def test_remove_nonexistent_header
|
|
110
|
+
headers = Wreq::Headers.new
|
|
111
|
+
result = headers.remove("X-Nonexistent")
|
|
112
|
+
assert_nil result
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def test_delete_alias
|
|
116
|
+
headers = Wreq::Headers.new
|
|
117
|
+
headers.set("X-Delete-Me", "value")
|
|
118
|
+
|
|
119
|
+
removed_value = headers.remove("X-Delete-Me")
|
|
120
|
+
assert_equal "value", removed_value
|
|
121
|
+
assert_nil headers.get("X-Delete-Me")
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def test_contains_existing
|
|
125
|
+
assert @headers.contains?("Content-Type")
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def test_contains_nonexistent
|
|
129
|
+
refute @headers.contains?("X-Nonexistent-Header-12345")
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def test_contains_case_insensitive
|
|
133
|
+
# If Content-Type exists
|
|
134
|
+
if @headers.contains?("Content-Type")
|
|
135
|
+
assert @headers.contains?("content-type")
|
|
136
|
+
assert @headers.contains?("CONTENT-TYPE")
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def test_key_alias
|
|
141
|
+
# key? is an alias for contains?
|
|
142
|
+
assert_equal @headers.contains?("Content-Type"), @headers.key?("Content-Type")
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def test_length
|
|
146
|
+
headers = Wreq::Headers.new
|
|
147
|
+
assert_equal 0, headers.length
|
|
148
|
+
|
|
149
|
+
headers.set("Header1", "value1")
|
|
150
|
+
assert_equal 1, headers.length
|
|
151
|
+
|
|
152
|
+
headers.set("Header2", "value2")
|
|
153
|
+
assert_equal 2, headers.length
|
|
154
|
+
|
|
155
|
+
# Setting same header shouldn't increase length
|
|
156
|
+
headers.set("Header1", "new-value")
|
|
157
|
+
assert_equal 2, headers.length
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def test_empty_on_new_headers
|
|
161
|
+
headers = Wreq::Headers.new
|
|
162
|
+
assert headers.empty?
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def test_empty_on_headers_with_data
|
|
166
|
+
refute @headers.empty?
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def test_clear
|
|
170
|
+
headers = Wreq::Headers.new
|
|
171
|
+
headers.set("Header1", "value1")
|
|
172
|
+
headers.set("Header2", "value2")
|
|
173
|
+
|
|
174
|
+
refute headers.empty?
|
|
175
|
+
headers.clear
|
|
176
|
+
assert headers.empty?
|
|
177
|
+
assert_equal 0, headers.length
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def test_keys
|
|
181
|
+
headers = Wreq::Headers.new
|
|
182
|
+
headers.set("Content-Type", "application/json")
|
|
183
|
+
headers.set("Authorization", "Bearer token")
|
|
184
|
+
|
|
185
|
+
keys = headers.keys
|
|
186
|
+
assert_instance_of Array, keys
|
|
187
|
+
assert_equal 2, keys.length
|
|
188
|
+
assert_includes keys, "content-type"
|
|
189
|
+
assert_includes keys, "authorization"
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def test_keys_are_lowercase
|
|
193
|
+
headers = Wreq::Headers.new
|
|
194
|
+
headers.set("Content-Type", "text/html")
|
|
195
|
+
headers.set("X-Custom-Header", "value")
|
|
196
|
+
|
|
197
|
+
keys = headers.keys
|
|
198
|
+
keys.each do |key|
|
|
199
|
+
assert_equal key, key.downcase
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def test_values
|
|
204
|
+
headers = Wreq::Headers.new
|
|
205
|
+
headers.set("Content-Type", "application/json")
|
|
206
|
+
headers.set("Authorization", "Bearer token")
|
|
207
|
+
|
|
208
|
+
values = headers.values
|
|
209
|
+
assert_instance_of Array, values
|
|
210
|
+
assert_equal 2, values.length
|
|
211
|
+
assert_includes values, "application/json"
|
|
212
|
+
assert_includes values, "Bearer token"
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def test_each_with_block
|
|
216
|
+
headers = Wreq::Headers.new
|
|
217
|
+
headers.set("Header1", "value1")
|
|
218
|
+
headers.set("Header2", "value2")
|
|
219
|
+
|
|
220
|
+
collected = {}
|
|
221
|
+
headers.each do |name, value|
|
|
222
|
+
assert_instance_of String, name
|
|
223
|
+
assert_instance_of String, value
|
|
224
|
+
collected[name] = value
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
assert_equal 2, collected.length
|
|
228
|
+
assert_equal "value1", collected["header1"]
|
|
229
|
+
assert_equal "value2", collected["header2"]
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def test_multiple_operations_sequence
|
|
233
|
+
headers = Wreq::Headers.new
|
|
234
|
+
|
|
235
|
+
# Add headers
|
|
236
|
+
headers.set("Content-Type", "application/json")
|
|
237
|
+
headers.set("Accept", "application/json")
|
|
238
|
+
|
|
239
|
+
assert_equal 2, headers.length
|
|
240
|
+
|
|
241
|
+
# Append to Accept
|
|
242
|
+
headers.append("Accept", "text/html")
|
|
243
|
+
assert_equal 2, headers.get_all("Accept").length
|
|
244
|
+
|
|
245
|
+
# Clear all
|
|
246
|
+
headers.clear
|
|
247
|
+
assert headers.empty?
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def test_special_characters_in_header_values
|
|
251
|
+
headers = Wreq::Headers.new
|
|
252
|
+
special_value = "Bearer token-123_abc/xyz+456=789"
|
|
253
|
+
headers.set("Authorization", special_value)
|
|
254
|
+
|
|
255
|
+
assert_equal special_value, headers.get("Authorization")
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def test_response_headers_integration
|
|
259
|
+
# Test that headers from actual HTTP response work correctly
|
|
260
|
+
assert_instance_of Wreq::Headers, @headers
|
|
261
|
+
refute @headers.empty?
|
|
262
|
+
|
|
263
|
+
# Should have common HTTP headers
|
|
264
|
+
assert @headers.length > 0
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def test_response_headers_each
|
|
268
|
+
# Test iteration over real response headers
|
|
269
|
+
count = 0
|
|
270
|
+
@headers.each do |name, value|
|
|
271
|
+
assert_instance_of String, name
|
|
272
|
+
assert_instance_of String, value
|
|
273
|
+
count += 1
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
assert count > 0
|
|
277
|
+
assert_equal @headers.length, count
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
def test_headers_immutability_across_instances
|
|
281
|
+
headers1 = Wreq::Headers.new
|
|
282
|
+
headers2 = Wreq::Headers.new
|
|
283
|
+
|
|
284
|
+
headers1.set("X-Test", "value1")
|
|
285
|
+
headers2.set("X-Test", "value2")
|
|
286
|
+
|
|
287
|
+
assert_equal "value1", headers1.get("X-Test")
|
|
288
|
+
assert_equal "value2", headers2.get("X-Test")
|
|
289
|
+
end
|
|
290
|
+
end
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "test_helper"
|
|
4
|
+
|
|
5
|
+
class InspectTest < Minitest::Test
|
|
6
|
+
# ---- Headers ----
|
|
7
|
+
|
|
8
|
+
def test_headers_inspect_empty
|
|
9
|
+
headers = Wreq::Headers.new
|
|
10
|
+
assert_equal "#<Wreq::Headers [0 headers]>", headers.inspect
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def test_headers_inspect_with_entries
|
|
14
|
+
headers = Wreq::Headers.new
|
|
15
|
+
headers.set("Content-Type", "text/html")
|
|
16
|
+
headers.set("Accept", "application/json")
|
|
17
|
+
assert_equal "#<Wreq::Headers [2 headers]>", headers.inspect
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# ---- Cookie ----
|
|
21
|
+
|
|
22
|
+
def test_cookie_inspect_minimal
|
|
23
|
+
c = Wreq::Cookie.new("sid", "secret123")
|
|
24
|
+
result = c.inspect
|
|
25
|
+
assert_includes result, "#<Wreq::Cookie"
|
|
26
|
+
assert_includes result, "sid"
|
|
27
|
+
refute_includes result, "secret123"
|
|
28
|
+
assert result.end_with?(">")
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def test_cookie_inspect_with_domain_and_path
|
|
32
|
+
c = Wreq::Cookie.new("sid", "val",
|
|
33
|
+
domain: "example.com",
|
|
34
|
+
path: "/app")
|
|
35
|
+
result = c.inspect
|
|
36
|
+
assert_includes result, "domain=example.com"
|
|
37
|
+
assert_includes result, "path=/app"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def test_cookie_inspect_with_flags
|
|
41
|
+
c = Wreq::Cookie.new("sid", "val",
|
|
42
|
+
secure: true,
|
|
43
|
+
http_only: true)
|
|
44
|
+
result = c.inspect
|
|
45
|
+
assert_includes result, "secure"
|
|
46
|
+
assert_includes result, "http_only"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def test_cookie_inspect_omits_nil_attributes
|
|
50
|
+
c = Wreq::Cookie.new("sid", "val")
|
|
51
|
+
result = c.inspect
|
|
52
|
+
refute_includes result, "domain="
|
|
53
|
+
refute_includes result, "path="
|
|
54
|
+
refute_includes result, "secure"
|
|
55
|
+
refute_includes result, "http_only"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# ---- Jar ----
|
|
59
|
+
|
|
60
|
+
def test_jar_inspect_empty
|
|
61
|
+
jar = Wreq::Jar.new
|
|
62
|
+
assert_equal "#<Wreq::Jar [0 cookies]>", jar.inspect
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def test_jar_inspect_with_cookies
|
|
66
|
+
jar = Wreq::Jar.new
|
|
67
|
+
jar.add("a=1; Path=/", "https://example.com")
|
|
68
|
+
jar.add("b=2; Path=/", "https://example.com")
|
|
69
|
+
assert_equal "#<Wreq::Jar [2 cookies]>", jar.inspect
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# ---- Client ----
|
|
73
|
+
|
|
74
|
+
def test_client_inspect
|
|
75
|
+
client = Wreq::Client.new
|
|
76
|
+
assert_equal "#<Wreq::Client>", client.inspect
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def test_client_inspect_with_options
|
|
80
|
+
client = Wreq::Client.new(timeout: 30, gzip: true)
|
|
81
|
+
assert_equal "#<Wreq::Client>", client.inspect
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# ---- Response ----
|
|
85
|
+
|
|
86
|
+
def test_response_to_s_returns_body
|
|
87
|
+
response = Wreq.get("#{HTTPBIN_URL}/json")
|
|
88
|
+
assert_equal response.text, response.to_s
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def test_response_inspect_format
|
|
92
|
+
response = Wreq.get("#{HTTPBIN_URL}/json")
|
|
93
|
+
result = response.inspect
|
|
94
|
+
assert result.start_with?("#<Wreq::Response")
|
|
95
|
+
assert_includes result, "200"
|
|
96
|
+
assert result.end_with?(">")
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# ---- StatusCode ----
|
|
100
|
+
|
|
101
|
+
def test_status_code_inspect
|
|
102
|
+
response = Wreq.get("#{HTTPBIN_URL}/status/200")
|
|
103
|
+
result = response.status.inspect
|
|
104
|
+
assert result.start_with?("#<Wreq::StatusCode")
|
|
105
|
+
assert_includes result, response.status.to_s
|
|
106
|
+
assert result.end_with?(">")
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# ---- Version ----
|
|
110
|
+
|
|
111
|
+
def test_version_inspect_from_constant
|
|
112
|
+
v = Wreq::Version::HTTP_11
|
|
113
|
+
result = v.inspect
|
|
114
|
+
assert result.start_with?("#<Wreq::Version")
|
|
115
|
+
assert_includes result, v.to_s
|
|
116
|
+
assert result.end_with?(">")
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def test_version_inspect_from_response
|
|
120
|
+
response = Wreq.get("#{HTTPBIN_URL}/get")
|
|
121
|
+
result = response.version.inspect
|
|
122
|
+
assert result.start_with?("#<Wreq::Version")
|
|
123
|
+
assert result.end_with?(">")
|
|
124
|
+
end
|
|
125
|
+
end
|