wreq 1.2.4-aarch64-linux → 1.2.6-aarch64-linux

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 (36) hide show
  1. checksums.yaml +4 -4
  2. data/crates/wreq-util/.github/FUNDING.yml +15 -0
  3. data/crates/wreq-util/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
  4. data/crates/wreq-util/.github/ISSUE_TEMPLATE/custom.md +10 -0
  5. data/crates/wreq-util/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  6. data/crates/wreq-util/.github/dependabot.yml +22 -0
  7. data/crates/wreq-util/.github/workflows/ci.yml +82 -0
  8. data/crates/wreq-util/.github/workflows/release-plz.yml +52 -0
  9. data/crates/wreq-util/.gitignore +24 -0
  10. data/crates/wreq-util/CHANGELOG.md +74 -0
  11. data/crates/wreq-util/LICENSE +201 -0
  12. data/crates/wreq-util/README.md +62 -0
  13. data/crates/wreq-util/release-plz.toml +2 -0
  14. data/crates/wreq-util/rustfmt.toml +5 -0
  15. data/examples/emulate_request.rb +1 -1
  16. data/lib/wreq.rb +72 -45
  17. data/lib/wreq_ruby/3.3/wreq_ruby.so +0 -0
  18. data/lib/wreq_ruby/3.4/wreq_ruby.so +0 -0
  19. data/lib/wreq_ruby/4.0/wreq_ruby.so +0 -0
  20. data/lib/wreq_ruby/body.rb +30 -9
  21. data/lib/wreq_ruby/client.rb +88 -55
  22. data/lib/wreq_ruby/cookie.rb +81 -21
  23. data/lib/wreq_ruby/emulate.rb +77 -7
  24. data/lib/wreq_ruby/error.rb +5 -7
  25. data/lib/wreq_ruby/header.rb +205 -114
  26. data/lib/wreq_ruby/http.rb +74 -0
  27. data/lib/wreq_ruby/response.rb +31 -3
  28. data/script/build_windows_gnu.ps1 +6 -0
  29. data/test/body_sender_test.rb +246 -0
  30. data/test/cookie_test.rb +211 -10
  31. data/test/header_test.rb +228 -192
  32. data/test/json_precision_test.rb +209 -0
  33. data/test/option_validation_test.rb +311 -0
  34. data/test/stream_test.rb +36 -0
  35. data/test/value_semantics_test.rb +238 -0
  36. metadata +19 -2
@@ -64,6 +64,19 @@ unless defined?(Wreq)
64
64
  def content_length
65
65
  end
66
66
 
67
+ # Get the response headers.
68
+ #
69
+ # Header names are case-insensitive. Use {Wreq::Headers#get_all} to get
70
+ # every value when a header appears more than once. Each call returns a
71
+ # fresh, mutable snapshot. Changing that snapshot does not change the
72
+ # response or a later snapshot, and object identity is not guaranteed.
73
+ #
74
+ # @return [Wreq::Headers] Response headers
75
+ # @example
76
+ # response.headers.get("content-type") # => "application/json"
77
+ def headers
78
+ end
79
+
67
80
  # Get the local socket address.
68
81
  #
69
82
  # @return [String, nil] Local address (e.g., "127.0.0.1:54321"), or nil
@@ -80,6 +93,18 @@ unless defined?(Wreq)
80
93
  def remote_addr
81
94
  end
82
95
 
96
+ # Get cookies parsed from the response's `Set-Cookie` headers.
97
+ #
98
+ # Invalid `Set-Cookie` values are skipped.
99
+ #
100
+ # @return [Array<Wreq::Cookie>] Parsed response cookies
101
+ # @example
102
+ # response.cookies.each do |cookie|
103
+ # puts "#{cookie.name}=#{cookie.value}"
104
+ # end
105
+ def cookies
106
+ end
107
+
83
108
  # Get the response bytes as a binary string.
84
109
  # @return [String] Response body as binary data
85
110
  # @example
@@ -91,17 +116,20 @@ unless defined?(Wreq)
91
116
  # Get the response body as text with a specific charset.
92
117
  # This method allows you to specify a default encoding
93
118
  # to use when decoding the response body.
94
- # # @param default_encoding [String] Default encoding to use (e.g., "UTF-8")
95
- # # @return [String] Response body decoded as text using the specified encoding
119
+ # @param default_encoding [String] Default encoding to use (e.g., "UTF-8")
120
+ # @return [String] Response body decoded as text using the specified encoding
96
121
  # @example
97
122
  # html = response.text("ISO-8859-1")
98
123
  # puts html
99
124
  # @raise [Wreq::DecodingError] if body cannot be decoded with the specified encoding
100
- def text(default_encoding: "UTF-8")
125
+ def text(default_encoding = "UTF-8")
101
126
  end
102
127
 
103
128
  # Parse the response body as JSON.
104
129
  #
130
+ # Integral JSON numbers are returned as arbitrary-precision Ruby Integer
131
+ # values. Fractional and exponent-form numbers are returned as Float values.
132
+ #
105
133
  # @return [Object] Parsed JSON (Hash, Array, String, Integer, Float, Boolean, nil)
106
134
  # @raise [Wreq::DecodingError] if body is not valid JSON
107
135
  # @example
@@ -32,7 +32,12 @@ function Invoke-Step {
32
32
  )
33
33
 
34
34
  Write-Host "==> $Name"
35
+ # Native command failures do not honor ErrorActionPreference in Windows PowerShell.
36
+ $global:LASTEXITCODE = 0
35
37
  & $Command
38
+ if ($LASTEXITCODE -ne 0) {
39
+ throw "$Name failed with exit code $LASTEXITCODE."
40
+ }
36
41
  }
37
42
 
38
43
  function Get-MissingUcrtTools {
@@ -122,6 +127,7 @@ Invoke-Step "Check MSYS2 UCRT build tools" {
122
127
  $stillMissing = @(Get-MissingUcrtTools)
123
128
  if ($stillMissing.Count -eq 0) {
124
129
  Write-Warning "pacman returned exit code $LASTEXITCODE, but all required tools are present; continuing."
130
+ $global:LASTEXITCODE = 0
125
131
  return
126
132
  }
127
133
 
@@ -0,0 +1,246 @@
1
+ require "test_helper"
2
+ require "open3"
3
+ require "rbconfig"
4
+ require "socket"
5
+
6
+ class BodySenderTest < Minitest::Test
7
+ def test_valid_capacity_creates_open_sender
8
+ sender = Wreq::BodySender.new(2)
9
+
10
+ refute_predicate sender, :closed?
11
+ ensure
12
+ sender&.close
13
+ end
14
+
15
+ def test_omitted_capacity_defaults_to_eight
16
+ sender = Wreq::BodySender.new
17
+ progress = Queue.new
18
+ producer = Thread.new do
19
+ 9.times do |index|
20
+ sender.push(index.to_s)
21
+ progress << index
22
+ end
23
+ sender.close
24
+ end
25
+ producer.report_on_exception = false
26
+
27
+ assert wait_until { progress.size >= 8 }, "the default channel should buffer eight chunks"
28
+ assert_equal 8, progress.size
29
+ assert_predicate producer, :alive?, "the ninth push should wait for channel capacity"
30
+
31
+ with_request_body_server do |url, request_thread|
32
+ response = Wreq.post(url, body: sender)
33
+
34
+ assert_equal 200, response.code
35
+ assert producer.join(5), "the ninth push should finish once the request drains"
36
+ assert_equal "012345678", request_thread.value
37
+ end
38
+ ensure
39
+ producer&.kill if producer&.alive?
40
+ producer&.join(5)
41
+ sender&.close
42
+ end
43
+
44
+ def test_non_positive_and_excessive_capacities_raise_argument_error
45
+ [0, -1, 2**256].each do |capacity|
46
+ error = assert_raises(ArgumentError) { Wreq::BodySender.new(capacity) }
47
+ assert_match(/capacity/, error.message)
48
+ end
49
+ end
50
+
51
+ def test_non_integer_capacities_raise_type_error
52
+ [1.0, "not an integer"].each do |capacity|
53
+ error = assert_raises(TypeError) { Wreq::BodySender.new(capacity) }
54
+ assert_match(/capacity/, error.message)
55
+ end
56
+ end
57
+
58
+ def test_too_many_constructor_arguments_raise_argument_error
59
+ assert_raises(ArgumentError) { Wreq::BodySender.new(1, 2) }
60
+ end
61
+
62
+ def test_close_is_idempotent_and_updates_closed_state
63
+ sender = Wreq::BodySender.new
64
+
65
+ refute_predicate sender, :closed?
66
+ assert_nil sender.close
67
+ assert_predicate sender, :closed?
68
+ assert_nil sender.close
69
+ end
70
+
71
+ def test_push_after_close_raises_io_error
72
+ sender = Wreq::BodySender.new
73
+ sender.close
74
+
75
+ error = assert_raises(IOError) { sender.push("data") }
76
+ assert_equal "closed body sender", error.message
77
+ end
78
+
79
+ def test_queued_chunks_survive_close_before_request_attachment
80
+ sender = Wreq::BodySender.new(2)
81
+ sender.push("queued-")
82
+ sender.push("body")
83
+ sender.close
84
+
85
+ with_request_body_server do |url, request_thread|
86
+ response = Wreq.post(url, body: sender)
87
+
88
+ assert_equal 200, response.code
89
+ assert_equal "queued-body", request_thread.value
90
+ end
91
+ end
92
+
93
+ def test_valid_capacity_keeps_backpressure_until_request_drains
94
+ sender = Wreq::BodySender.new(1)
95
+ sender.push("first-")
96
+ producer = Thread.new do
97
+ sender.push("second")
98
+ sender.close
99
+ end
100
+ producer.report_on_exception = false
101
+
102
+ sleep 0.05
103
+ assert_predicate producer, :alive?, "the second push should wait for channel capacity"
104
+
105
+ with_request_body_server do |url, request_thread|
106
+ response = Wreq.post(url, body: sender)
107
+
108
+ assert_equal 200, response.code
109
+ assert producer.join(5), "the blocked producer should finish once the request drains"
110
+ assert_equal "first-second", request_thread.value
111
+ end
112
+ ensure
113
+ producer&.kill if producer&.alive?
114
+ producer&.join(5)
115
+ sender&.close
116
+ end
117
+
118
+ def test_receiver_termination_closes_sender
119
+ sender = Wreq::BodySender.new(1)
120
+ sender.push("data")
121
+
122
+ with_reset_server do |url|
123
+ assert_raises(StandardError) { Wreq.post(url, body: sender, timeout: 2) }
124
+ end
125
+
126
+ assert wait_until { sender.closed? }, "sender should close after the request drops its receiver"
127
+ assert_raises(IOError) { sender.push("more") }
128
+ ensure
129
+ sender&.close
130
+ end
131
+
132
+ def test_zero_capacity_regression_exits_subprocess_normally
133
+ lib_dir = File.expand_path("../lib", __dir__)
134
+ script = <<~RUBY
135
+ require "wreq"
136
+
137
+ begin
138
+ Wreq::BodySender.new(0)
139
+ rescue ArgumentError => error
140
+ warn "\#{error.class}: \#{error.message}"
141
+ exit 0
142
+ end
143
+
144
+ warn "zero capacity did not raise"
145
+ exit 2
146
+ RUBY
147
+
148
+ _stdout, stderr, status = Open3.capture3(RbConfig.ruby, "-I", lib_dir, "-e", script)
149
+
150
+ assert status.success?, "subprocess failed with #{status.inspect}: #{stderr}"
151
+ assert_match(/ArgumentError:.*capacity/, stderr)
152
+ refute_match(/panicked|mpsc bounded channel/i, stderr)
153
+ end
154
+
155
+ private
156
+
157
+ def wait_until(timeout: 2)
158
+ deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + timeout
159
+ loop do
160
+ return true if yield
161
+ return false if Process.clock_gettime(Process::CLOCK_MONOTONIC) >= deadline
162
+
163
+ sleep 0.01
164
+ end
165
+ end
166
+
167
+ def with_request_body_server
168
+ server = TCPServer.new("127.0.0.1", 0)
169
+ port = server.addr[1]
170
+ request_thread = Thread.new do
171
+ socket = server.accept
172
+ begin
173
+ socket.gets
174
+ headers = read_headers(socket)
175
+ body = read_request_body(socket, headers)
176
+ socket.write "HTTP/1.1 200 OK\r\n"
177
+ socket.write "Content-Length: 0\r\n"
178
+ socket.write "Connection: close\r\n\r\n"
179
+ body
180
+ ensure
181
+ socket.close unless socket.closed?
182
+ end
183
+ ensure
184
+ server.close unless server.closed?
185
+ end
186
+ request_thread.report_on_exception = false
187
+
188
+ yield "http://127.0.0.1:#{port}/", request_thread
189
+ ensure
190
+ server&.close unless server&.closed?
191
+ request_thread&.join(5)
192
+ end
193
+
194
+ def with_reset_server
195
+ server = TCPServer.new("127.0.0.1", 0)
196
+ port = server.addr[1]
197
+ thread = Thread.new do
198
+ socket = server.accept
199
+ socket.close
200
+ ensure
201
+ server.close unless server.closed?
202
+ end
203
+ thread.report_on_exception = false
204
+
205
+ yield "http://127.0.0.1:#{port}/"
206
+ ensure
207
+ server&.close unless server&.closed?
208
+ thread&.join(5)
209
+ end
210
+
211
+ def read_headers(socket)
212
+ headers = {}
213
+ while (line = socket.gets)
214
+ break if line == "\r\n"
215
+
216
+ name, value = line.split(":", 2)
217
+ headers[name.downcase] = value.strip
218
+ end
219
+ headers
220
+ end
221
+
222
+ def read_request_body(socket, headers)
223
+ if headers.fetch("transfer-encoding", "").downcase.include?("chunked")
224
+ read_chunked_body(socket)
225
+ else
226
+ socket.read(headers.fetch("content-length", "0").to_i)
227
+ end
228
+ end
229
+
230
+ def read_chunked_body(socket)
231
+ body = "".b
232
+ loop do
233
+ size_line = socket.gets or raise EOFError, "missing chunk size"
234
+ size = Integer(size_line.split(";", 2).first, 16)
235
+ break if size.zero?
236
+
237
+ body << socket.read(size)
238
+ raise IOError, "missing chunk terminator" unless socket.read(2) == "\r\n"
239
+ end
240
+
241
+ while (line = socket.gets)
242
+ break if line == "\r\n"
243
+ end
244
+ body
245
+ end
246
+ end
data/test/cookie_test.rb CHANGED
@@ -1,6 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "test_helper"
4
+ require "bigdecimal"
5
+ require "open3"
6
+ require "rbconfig"
4
7
 
5
8
  class CookieTest < Minitest::Test
6
9
  def setup
@@ -41,6 +44,14 @@ class CookieTest < Minitest::Test
41
44
  assert_equal true, (c.secure || c.secure?)
42
45
  end
43
46
 
47
+ def test_add_rejects_invalid_cookie_type
48
+ error = assert_raises(TypeError) do
49
+ @jar.add(Object.new, @base_url)
50
+ end
51
+
52
+ assert_equal "cookie must be a Wreq::Cookie or String", error.message
53
+ end
54
+
44
55
  def test_add_multiple_and_remove
45
56
  @jar.add("a=1; Path=/", @base_url)
46
57
  @jar.add("b=2; Path=/", @base_url)
@@ -84,7 +95,9 @@ class CookieTest < Minitest::Test
84
95
  @jar.add("exp=1; Expires=#{t.gmtime.strftime("%a, %d %b %Y %H:%M:%S GMT")}; Path=/", @base_url)
85
96
  c2 = @jar.get_all.find { |c| c.name == "exp" }
86
97
  assert c2
87
- # expires returns Float (unix seconds) or nil
98
+ # expires_at returns Time. expires remains available for numeric callers.
99
+ assert_kind_of Time, c2.expires_at
100
+ assert_predicate c2.expires_at, :utc?
88
101
  if (e = c2.expires)
89
102
  assert_kind_of Float, e
90
103
  assert_operator e, :>, Time.now.to_f - 1_000_000 # sanity bound
@@ -104,15 +117,18 @@ class CookieTest < Minitest::Test
104
117
  assert_nil c.domain
105
118
  assert_nil c.max_age
106
119
  assert_nil c.expires
120
+ assert_nil c.expires_at
107
121
 
108
122
  assert_equal false, (c.http_only || c.http_only?)
109
123
  assert_equal false, (c.secure || c.secure?)
110
124
  assert_equal false, c.same_site_lax?
111
125
  assert_equal false, c.same_site_strict?
126
+ assert_nil c.same_site
127
+ assert_equal "sid=abc", c.to_s
112
128
  end
113
129
 
114
130
  def test_cookie_new_full_attributes
115
- exp = Time.now.to_f + 7200.0
131
+ exp = Time.utc(2030, 1, 1, 0, 0, Rational(123_456_789, 1_000_000_000))
116
132
  c = Wreq::Cookie.new("sess", "v",
117
133
  domain: "example.com",
118
134
  path: "/",
@@ -130,16 +146,199 @@ class CookieTest < Minitest::Test
130
146
  # Max-Age returns seconds as Integer
131
147
  assert_equal 3600, c.max_age
132
148
 
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
149
+ assert_equal exp, c.expires_at
150
+ assert_predicate c.expires_at, :utc?
151
+ assert_in_delta exp.to_f, c.expires, 1e-6
137
152
 
138
153
  assert_equal true, (c.http_only || c.http_only?)
139
154
  assert_equal true, (c.secure || c.secure?)
140
- # constructor currently sets SameSite to none
141
155
  assert_equal true, c.same_site_lax?
142
156
  assert_equal false, c.same_site_strict?
157
+ assert_equal Wreq::SameSite::Lax, c.same_site
158
+ end
159
+
160
+ def test_same_site_reader_distinguishes_none_from_unset
161
+ cookie = Wreq::Cookie.new("same-site", "value", same_site: Wreq::SameSite::None)
162
+
163
+ assert_equal Wreq::SameSite::None, cookie.same_site
164
+ refute cookie.same_site_lax?
165
+ refute cookie.same_site_strict?
166
+ end
167
+
168
+ def test_cookie_new_uses_shared_keyword_validation
169
+ cookie = Wreq::Cookie.new("sid", "abc", **{"path" => "/"})
170
+ assert_equal "/", cookie.path
171
+
172
+ secret = "must-not-appear"
173
+ unknown_error = assert_raises(ArgumentError) do
174
+ Wreq::Cookie.new("sid", "abc", domian: secret)
175
+ end
176
+ assert_includes unknown_error.message, ":domian"
177
+ refute_includes unknown_error.message, secret
178
+
179
+ duplicate_options = {path: "/one"}
180
+ duplicate_options["path"] = "/two"
181
+ duplicate_error = assert_raises(ArgumentError) do
182
+ Wreq::Cookie.new("sid", "abc", **duplicate_options)
183
+ end
184
+ assert_includes duplicate_error.message, "duplicate option: :path"
185
+ end
186
+
187
+ def test_expires_accepts_past_time
188
+ expiration = Time.at(Rational(-5, 4)).utc
189
+ cookie = Wreq::Cookie.new("past", "value", expires: expiration)
190
+
191
+ assert_equal expiration, cookie.expires_at
192
+ assert_in_delta(-1.25, cookie.expires, 1e-9)
193
+ end
194
+
195
+ def test_expires_accepts_integer_and_fractional_timestamps
196
+ [
197
+ 1_893_456_000,
198
+ 1_893_456_000.125,
199
+ Rational(-5, 4),
200
+ BigDecimal("1893456000.125")
201
+ ].each do |timestamp|
202
+ cookie = Wreq::Cookie.new("timestamp", "value", expires: timestamp)
203
+
204
+ assert_kind_of Time, cookie.expires_at
205
+ assert_predicate cookie.expires_at, :utc?
206
+ assert_in_delta timestamp.to_f, cookie.expires_at.to_f, 1e-6
207
+ assert_in_delta timestamp.to_f, cookie.expires, 1e-6
208
+ end
209
+ end
210
+
211
+ def test_expires_normalizes_time_to_utc_without_losing_precision
212
+ expiration = Time.new(
213
+ 2030,
214
+ 1,
215
+ 1,
216
+ 12,
217
+ 34,
218
+ Rational(123_456_789, 1_000_000_000),
219
+ "+09:00"
220
+ )
221
+ actual = Wreq::Cookie.new("offset", "value", expires: expiration).expires_at
222
+
223
+ assert_predicate actual, :utc?
224
+ assert_equal expiration.to_r, actual.to_r
225
+ end
226
+
227
+ def test_expires_rejects_non_finite_timestamps
228
+ [Float::NAN, Float::INFINITY, -Float::INFINITY].each do |timestamp|
229
+ error = assert_raises(ArgumentError) do
230
+ Wreq::Cookie.new("invalid", "value", expires: timestamp)
231
+ end
232
+
233
+ assert_match(/expires.*finite/, error.message)
234
+ end
235
+ end
236
+
237
+ def test_expires_rejects_unrepresentable_times
238
+ expirations = [
239
+ -(2**63),
240
+ 2**63 - 1,
241
+ -Float::MAX,
242
+ Float::MAX,
243
+ Time.utc(10_000, 1, 1)
244
+ ]
245
+
246
+ expirations.each do |expiration|
247
+ error = assert_raises(RangeError) do
248
+ Wreq::Cookie.new("invalid", "value", expires: expiration)
249
+ end
250
+
251
+ assert_match(/expires.*supported range/, error.message)
252
+ end
253
+ end
254
+
255
+ def test_max_age_accepts_signed_boundaries_without_wrapping
256
+ [-(2**63), -1, 0, 2**63 - 1].each do |max_age|
257
+ cookie = Wreq::Cookie.new("max-age", "value", max_age: max_age)
258
+
259
+ assert_equal max_age, cookie.max_age
260
+ end
261
+
262
+ [-(2**63) - 1, 2**63].each do |max_age|
263
+ assert_raises(RangeError) do
264
+ Wreq::Cookie.new("max-age", "value", max_age: max_age)
265
+ end
266
+ end
267
+ end
268
+
269
+ def test_non_positive_max_age_removes_cookie_from_jar
270
+ [-1, 0].each do |max_age|
271
+ jar = Wreq::Jar.new
272
+ jar.add("session=old; Path=/", @base_url)
273
+ deletion = Wreq::Cookie.new("session", "gone", path: "/", max_age: max_age)
274
+
275
+ jar.add(deletion, @base_url)
276
+
277
+ assert_equal max_age, deletion.max_age
278
+ assert_empty jar.get_all
279
+ end
280
+ end
281
+
282
+ def test_past_expiration_removes_cookie_from_jar
283
+ @jar.add("session=old; Path=/", @base_url)
284
+ deletion = Wreq::Cookie.new(
285
+ "session",
286
+ "gone",
287
+ path: "/",
288
+ expires: Time.at(-1).utc
289
+ )
290
+
291
+ @jar.add(deletion, @base_url)
292
+
293
+ assert_empty @jar.get_all
294
+ end
295
+
296
+ def test_expiration_regressions_exit_subprocess_normally
297
+ lib_dir = File.expand_path("../lib", __dir__)
298
+ script = <<~RUBY
299
+ require "wreq"
300
+
301
+ past = Wreq::Cookie.new("past", "value", expires: -1.0)
302
+ abort "past timestamp was not retained" unless past.expires_at == Time.at(-1).utc
303
+
304
+ [Float::NAN, Float::INFINITY, -Float::INFINITY].each do |timestamp|
305
+ begin
306
+ Wreq::Cookie.new("invalid", "value", expires: timestamp)
307
+ rescue ArgumentError
308
+ next
309
+ end
310
+
311
+ abort "non-finite timestamp did not raise ArgumentError"
312
+ end
313
+
314
+ [Float::MAX, -Float::MAX].each do |timestamp|
315
+ begin
316
+ Wreq::Cookie.new("invalid", "value", expires: timestamp)
317
+ rescue RangeError
318
+ next
319
+ end
320
+
321
+ abort "unrepresentable finite timestamp did not raise RangeError"
322
+ end
323
+
324
+ [-(2**63) - 1, 2**63].each do |max_age|
325
+ begin
326
+ Wreq::Cookie.new("invalid", "value", max_age: max_age)
327
+ rescue RangeError
328
+ next
329
+ end
330
+
331
+ abort "out-of-range Max-Age did not raise RangeError"
332
+ end
333
+
334
+ puts "ok"
335
+ RUBY
336
+
337
+ stdout, stderr, status = Open3.capture3(RbConfig.ruby, "-I", lib_dir, "-e", script)
338
+
339
+ assert status.success?, "subprocess failed with #{status.inspect}: #{stderr}"
340
+ assert_equal "ok\n", stdout
341
+ refute_match(/panicked|fatal|access violation|cannot convert float seconds to Duration/i, stderr)
143
342
  end
144
343
 
145
344
  def test_same_site_flags_from_parsed_header
@@ -148,10 +347,12 @@ class CookieTest < Minitest::Test
148
347
  @jar.add("s2=1; Path=/; SameSite=Lax", @base_url)
149
348
 
150
349
  cookies = @jar.get_all
151
- h = cookies.to_h { |ck| [ck.name, [ck.same_site_strict?, ck.same_site_lax?]] }
350
+ h = cookies.to_h do |cookie|
351
+ [cookie.name, [cookie.same_site, cookie.same_site_strict?, cookie.same_site_lax?]]
352
+ end
152
353
 
153
- assert_equal [true, false], h["s1"]
154
- assert_equal [false, true], h["s2"]
354
+ assert_equal [Wreq::SameSite::Strict, true, false], h["s1"]
355
+ assert_equal [Wreq::SameSite::Lax, false, true], h["s2"]
155
356
  end
156
357
 
157
358
  def test_request_uncompressed_cookies