oj 3.13.12 → 3.13.15

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "abstract_unit"
4
+ require "active_support/json"
5
+ require "active_support/time"
6
+ require_relative "time_zone_test_helpers"
7
+
8
+ class TestJSONDecoding < ActiveSupport::TestCase
9
+ include TimeZoneTestHelpers
10
+
11
+ class Foo
12
+ def self.json_create(object)
13
+ "Foo"
14
+ end
15
+ end
16
+
17
+ TESTS = {
18
+ %q({"returnTo":{"\/categories":"\/"}}) => { "returnTo" => { "/categories" => "/" } },
19
+ %q({"return\\"To\\":":{"\/categories":"\/"}}) => { "return\"To\":" => { "/categories" => "/" } },
20
+ %q({"returnTo":{"\/categories":1}}) => { "returnTo" => { "/categories" => 1 } },
21
+ %({"returnTo":[1,"a"]}) => { "returnTo" => [1, "a"] },
22
+ %({"returnTo":[1,"\\"a\\",", "b"]}) => { "returnTo" => [1, "\"a\",", "b"] },
23
+ %({"a": "'", "b": "5,000"}) => { "a" => "'", "b" => "5,000" },
24
+ %({"a": "a's, b's and c's", "b": "5,000"}) => { "a" => "a's, b's and c's", "b" => "5,000" },
25
+ # multibyte
26
+ %({"matzue": "松江", "asakusa": "浅草"}) => { "matzue" => "松江", "asakusa" => "浅草" },
27
+ %({"a": "2007-01-01"}) => { "a" => Date.new(2007, 1, 1) },
28
+ %({"a": "2007-01-01 01:12:34 Z"}) => { "a" => Time.utc(2007, 1, 1, 1, 12, 34) },
29
+ %(["2007-01-01 01:12:34 Z"]) => [Time.utc(2007, 1, 1, 1, 12, 34)],
30
+ %(["2007-01-01 01:12:34 Z", "2007-01-01 01:12:35 Z"]) => [Time.utc(2007, 1, 1, 1, 12, 34), Time.utc(2007, 1, 1, 1, 12, 35)],
31
+ # no time zone
32
+ %({"a": "2007-01-01 01:12:34"}) => { "a" => Time.new(2007, 1, 1, 1, 12, 34, "-05:00") },
33
+ # invalid date
34
+ %({"a": "1089-10-40"}) => { "a" => "1089-10-40" },
35
+ # xmlschema date notation
36
+ %({"a": "2009-08-10T19:01:02"}) => { "a" => Time.new(2009, 8, 10, 19, 1, 2, "-04:00") },
37
+ %({"a": "2009-08-10T19:01:02Z"}) => { "a" => Time.utc(2009, 8, 10, 19, 1, 2) },
38
+ %({"a": "2009-08-10T19:01:02+02:00"}) => { "a" => Time.utc(2009, 8, 10, 17, 1, 2) },
39
+ %({"a": "2009-08-10T19:01:02-05:00"}) => { "a" => Time.utc(2009, 8, 11, 00, 1, 2) },
40
+ # needs to be *exact*
41
+ %({"a": " 2007-01-01 01:12:34 Z "}) => { "a" => " 2007-01-01 01:12:34 Z " },
42
+ %({"a": "2007-01-01 : it's your birthday"}) => { "a" => "2007-01-01 : it's your birthday" },
43
+ %({"a": "Today is:\\n2020-05-21"}) => { "a" => "Today is:\n2020-05-21" },
44
+ %({"a": "2007-01-01 01:12:34 Z\\nwas my birthday"}) => { "a" => "2007-01-01 01:12:34 Z\nwas my birthday" },
45
+ %([]) => [],
46
+ %({}) => {},
47
+ %({"a":1}) => { "a" => 1 },
48
+ %({"a": ""}) => { "a" => "" },
49
+ %({"a":"\\""}) => { "a" => "\"" },
50
+ %({"a": null}) => { "a" => nil },
51
+ %({"a": true}) => { "a" => true },
52
+ %({"a": false}) => { "a" => false },
53
+ '{"bad":"\\\\","trailing":""}' => { "bad" => "\\", "trailing" => "" },
54
+ %q({"a": "http:\/\/test.host\/posts\/1"}) => { "a" => "http://test.host/posts/1" },
55
+ %q({"a": "\u003cunicode\u0020escape\u003e"}) => { "a" => "<unicode escape>" },
56
+ '{"a": "\\\\u0020skip double backslashes"}' => { "a" => "\\u0020skip double backslashes" },
57
+ %q({"a": "\u003cbr /\u003e"}) => { "a" => "<br />" },
58
+ %q({"b":["\u003ci\u003e","\u003cb\u003e","\u003cu\u003e"]}) => { "b" => ["<i>", "<b>", "<u>"] },
59
+ # test combination of dates and escaped or unicode encoded data in arrays
60
+ %q([{"d":"1970-01-01", "s":"\u0020escape"},{"d":"1970-01-01", "s":"\u0020escape"}]) =>
61
+ [{ "d" => Date.new(1970, 1, 1), "s" => " escape" }, { "d" => Date.new(1970, 1, 1), "s" => " escape" }],
62
+ %q([{"d":"1970-01-01","s":"http:\/\/example.com"},{"d":"1970-01-01","s":"http:\/\/example.com"}]) =>
63
+ [{ "d" => Date.new(1970, 1, 1), "s" => "http://example.com" },
64
+ { "d" => Date.new(1970, 1, 1), "s" => "http://example.com" }],
65
+ # tests escaping of "\n" char with Yaml backend
66
+ %q({"a":"\n"}) => { "a" => "\n" },
67
+ %q({"a":"\u000a"}) => { "a" => "\n" },
68
+ %q({"a":"Line1\u000aLine2"}) => { "a" => "Line1\nLine2" },
69
+ # prevent json unmarshalling
70
+ '{"json_class":"TestJSONDecoding::Foo"}' => { "json_class" => "TestJSONDecoding::Foo" },
71
+ # json "fragments" - these are invalid JSON, but ActionPack relies on this
72
+ '"a string"' => "a string",
73
+ "1.1" => 1.1,
74
+ "1" => 1,
75
+ "-1" => -1,
76
+ "true" => true,
77
+ "false" => false,
78
+ "null" => nil
79
+ }
80
+
81
+ TESTS.each_with_index do |(json, expected), index|
82
+ fail_message = "JSON decoding failed for #{json}"
83
+
84
+ test "json decodes #{index}" do
85
+ with_tz_default "Eastern Time (US & Canada)" do
86
+ with_parse_json_times(true) do
87
+ silence_warnings do
88
+ if expected.nil?
89
+ assert_nil ActiveSupport::JSON.decode(json), fail_message
90
+ else
91
+ assert_equal expected, ActiveSupport::JSON.decode(json), fail_message
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+ test "json decodes time json with time parsing disabled" do
100
+ with_parse_json_times(false) do
101
+ expected = { "a" => "2007-01-01 01:12:34 Z" }
102
+ assert_equal expected, ActiveSupport::JSON.decode(%({"a": "2007-01-01 01:12:34 Z"}))
103
+ end
104
+ end
105
+
106
+ def test_failed_json_decoding
107
+ assert_raise(ActiveSupport::JSON.parse_error) { ActiveSupport::JSON.decode(%(undefined)) }
108
+ assert_raise(ActiveSupport::JSON.parse_error) { ActiveSupport::JSON.decode(%({a: 1})) }
109
+ assert_raise(ActiveSupport::JSON.parse_error) { ActiveSupport::JSON.decode(%({: 1})) }
110
+ assert_raise(ActiveSupport::JSON.parse_error) { ActiveSupport::JSON.decode(%()) }
111
+ end
112
+
113
+ def test_cannot_pass_unsupported_options
114
+ assert_raise(ArgumentError) { ActiveSupport::JSON.decode("", create_additions: true) }
115
+ end
116
+
117
+ private
118
+ def with_parse_json_times(value)
119
+ old_value = ActiveSupport.parse_json_times
120
+ ActiveSupport.parse_json_times = value
121
+ yield
122
+ ensure
123
+ ActiveSupport.parse_json_times = old_value
124
+ end
125
+ end
@@ -0,0 +1,486 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "securerandom"
4
+ require_relative "abstract_unit"
5
+ require "active_support/core_ext/string/inflections"
6
+ require "active_support/json"
7
+ require "active_support/time"
8
+ require_relative "time_zone_test_helpers"
9
+ require_relative "encoding_test_cases"
10
+
11
+ class TestJSONEncoding < ActiveSupport::TestCase
12
+ include TimeZoneTestHelpers
13
+
14
+ def sorted_json(json)
15
+ if json.start_with?("{") && json.end_with?("}")
16
+ "{" + json[1..-2].split(",").sort.join(",") + "}"
17
+ else
18
+ json
19
+ end
20
+ end
21
+
22
+ JSONTest::EncodingTestCases.constants.each do |class_tests|
23
+ define_method("test_#{class_tests[0..-6].underscore}") do
24
+ prev = ActiveSupport.use_standard_json_time_format
25
+
26
+ standard_class_tests = /Standard/.match?(class_tests)
27
+
28
+ ActiveSupport.escape_html_entities_in_json = !standard_class_tests
29
+ ActiveSupport.use_standard_json_time_format = standard_class_tests
30
+ JSONTest::EncodingTestCases.const_get(class_tests).each do |pair|
31
+ assert_equal pair.last, sorted_json(ActiveSupport::JSON.encode(pair.first))
32
+ end
33
+ ensure
34
+ ActiveSupport.escape_html_entities_in_json = false
35
+ ActiveSupport.use_standard_json_time_format = prev
36
+ end
37
+ end
38
+
39
+ def test_process_status
40
+ rubinius_skip "https://github.com/rubinius/rubinius/issues/3334"
41
+
42
+ # There doesn't seem to be a good way to get a handle on a Process::Status object without actually
43
+ # creating a child process, hence this to populate $?
44
+ system("not_a_real_program_#{SecureRandom.hex}")
45
+ assert_equal %({"exitstatus":#{$?.exitstatus},"pid":#{$?.pid}}), ActiveSupport::JSON.encode($?)
46
+ end
47
+
48
+ def test_hash_encoding
49
+ assert_equal %({\"a\":\"b\"}), ActiveSupport::JSON.encode(a: :b)
50
+ assert_equal %({\"a\":1}), ActiveSupport::JSON.encode("a" => 1)
51
+ assert_equal %({\"a\":[1,2]}), ActiveSupport::JSON.encode("a" => [1, 2])
52
+ assert_equal %({"1":2}), ActiveSupport::JSON.encode(1 => 2)
53
+
54
+ assert_equal %({\"a\":\"b\",\"c\":\"d\"}), sorted_json(ActiveSupport::JSON.encode(a: :b, c: :d))
55
+ end
56
+
57
+ def test_hash_keys_encoding
58
+ ActiveSupport.escape_html_entities_in_json = true
59
+ assert_equal "{\"\\u003c\\u003e\":\"\\u003c\\u003e\"}", ActiveSupport::JSON.encode("<>" => "<>")
60
+ ensure
61
+ ActiveSupport.escape_html_entities_in_json = false
62
+ end
63
+
64
+ def test_utf8_string_encoded_properly
65
+ result = ActiveSupport::JSON.encode("€2.99")
66
+ assert_equal '"€2.99"', result
67
+ assert_equal(Encoding::UTF_8, result.encoding)
68
+
69
+ result = ActiveSupport::JSON.encode("✎☺")
70
+ assert_equal '"✎☺"', result
71
+ assert_equal(Encoding::UTF_8, result.encoding)
72
+ end
73
+
74
+ def test_non_utf8_string_transcodes
75
+ s = "二".encode("Shift_JIS")
76
+ result = ActiveSupport::JSON.encode(s)
77
+ assert_equal '"二"', result
78
+ assert_equal Encoding::UTF_8, result.encoding
79
+ end
80
+
81
+ def test_wide_utf8_chars
82
+ w = "𠜎"
83
+ result = ActiveSupport::JSON.encode(w)
84
+ assert_equal '"𠜎"', result
85
+ end
86
+
87
+ def test_wide_utf8_roundtrip
88
+ hash = { string: "𐒑" }
89
+ json = ActiveSupport::JSON.encode(hash)
90
+ decoded_hash = ActiveSupport::JSON.decode(json)
91
+ assert_equal "𐒑", decoded_hash["string"]
92
+ end
93
+
94
+ def test_hash_key_identifiers_are_always_quoted
95
+ values = { 0 => 0, 1 => 1, :_ => :_, "$" => "$", "a" => "a", :A => :A, :A0 => :A0, "A0B" => "A0B" }
96
+ assert_equal %w( "$" "A" "A0" "A0B" "_" "a" "0" "1" ).sort, object_keys(ActiveSupport::JSON.encode(values))
97
+ end
98
+
99
+ def test_hash_should_allow_key_filtering_with_only
100
+ assert_equal %({"a":1}), ActiveSupport::JSON.encode({ "a" => 1, :b => 2, :c => 3 }, { only: "a" })
101
+ end
102
+
103
+ def test_hash_should_allow_key_filtering_with_except
104
+ assert_equal %({"b":2}), ActiveSupport::JSON.encode({ "foo" => "bar", :b => 2, :c => 3 }, { except: ["foo", :c] })
105
+ end
106
+
107
+ def test_time_to_json_includes_local_offset
108
+ with_standard_json_time_format(true) do
109
+ with_env_tz "US/Eastern" do
110
+ assert_equal %("2005-02-01T15:15:10.000-05:00"), ActiveSupport::JSON.encode(Time.local(2005, 2, 1, 15, 15, 10))
111
+ end
112
+ end
113
+ end
114
+
115
+ def test_hash_with_time_to_json
116
+ with_standard_json_time_format(false) do
117
+ assert_equal '{"time":"2009/01/01 00:00:00 +0000"}', { time: Time.utc(2009) }.to_json
118
+ end
119
+ end
120
+
121
+ def test_nested_hash_with_float
122
+ assert_nothing_raised do
123
+ hash = {
124
+ "CHI" => {
125
+ display_name: "chicago",
126
+ latitude: 123.234
127
+ }
128
+ }
129
+ ActiveSupport::JSON.encode(hash)
130
+ end
131
+ end
132
+
133
+ def test_hash_like_with_options
134
+ h = JSONTest::Hashlike.new
135
+ json = h.to_json only: [:foo]
136
+
137
+ assert_equal({ "foo" => "hello" }, JSON.parse(json))
138
+ end
139
+
140
+ def test_object_to_json_with_options
141
+ obj = Object.new
142
+ obj.instance_variable_set :@foo, "hello"
143
+ obj.instance_variable_set :@bar, "world"
144
+ json = obj.to_json only: ["foo"]
145
+
146
+ assert_equal({ "foo" => "hello" }, JSON.parse(json))
147
+ end
148
+
149
+ def test_struct_to_json_with_options
150
+ struct = Struct.new(:foo, :bar).new
151
+ struct.foo = "hello"
152
+ struct.bar = "world"
153
+ json = struct.to_json only: [:foo]
154
+
155
+ assert_equal({ "foo" => "hello" }, JSON.parse(json))
156
+ end
157
+
158
+ def test_struct_to_json_with_options_nested
159
+ klass = Struct.new(:foo, :bar)
160
+ struct = klass.new "hello", "world"
161
+ parent_struct = klass.new struct, "world"
162
+ json = parent_struct.to_json only: [:foo]
163
+
164
+ assert_equal({ "foo" => { "foo" => "hello" } }, JSON.parse(json))
165
+ end
166
+
167
+ def test_hash_should_pass_encoding_options_to_children_in_as_json
168
+ person = {
169
+ name: "John",
170
+ address: {
171
+ city: "London",
172
+ country: "UK"
173
+ }
174
+ }
175
+ json = person.as_json only: [:address, :city]
176
+
177
+ assert_equal({ "address" => { "city" => "London" } }, json)
178
+ end
179
+
180
+ def test_hash_should_pass_encoding_options_to_children_in_to_json
181
+ person = {
182
+ name: "John",
183
+ address: {
184
+ city: "London",
185
+ country: "UK"
186
+ }
187
+ }
188
+ json = person.to_json only: [:address, :city]
189
+
190
+ assert_equal(%({"address":{"city":"London"}}), json)
191
+ end
192
+
193
+ def test_array_should_pass_encoding_options_to_children_in_as_json
194
+ people = [
195
+ { name: "John", address: { city: "London", country: "UK" } },
196
+ { name: "Jean", address: { city: "Paris", country: "France" } }
197
+ ]
198
+ json = people.as_json only: [:address, :city]
199
+ expected = [
200
+ { "address" => { "city" => "London" } },
201
+ { "address" => { "city" => "Paris" } }
202
+ ]
203
+
204
+ assert_equal(expected, json)
205
+ end
206
+
207
+ def test_array_should_pass_encoding_options_to_children_in_to_json
208
+ people = [
209
+ { name: "John", address: { city: "London", country: "UK" } },
210
+ { name: "Jean", address: { city: "Paris", country: "France" } }
211
+ ]
212
+ json = people.to_json only: [:address, :city]
213
+
214
+ assert_equal(%([{"address":{"city":"London"}},{"address":{"city":"Paris"}}]), json)
215
+ end
216
+
217
+ People = Class.new(BasicObject) do
218
+ include Enumerable
219
+ def initialize
220
+ @people = [
221
+ { name: "John", address: { city: "London", country: "UK" } },
222
+ { name: "Jean", address: { city: "Paris", country: "France" } }
223
+ ]
224
+ end
225
+ def each(*, &blk)
226
+ @people.each do |p|
227
+ yield p if blk
228
+ p
229
+ end.each
230
+ end
231
+ end
232
+
233
+ def test_enumerable_should_generate_json_with_as_json
234
+ json = People.new.as_json only: [:address, :city]
235
+ expected = [
236
+ { "address" => { "city" => "London" } },
237
+ { "address" => { "city" => "Paris" } }
238
+ ]
239
+
240
+ assert_equal(expected, json)
241
+ end
242
+
243
+ def test_enumerable_should_generate_json_with_to_json
244
+ json = People.new.to_json only: [:address, :city]
245
+ assert_equal(%([{"address":{"city":"London"}},{"address":{"city":"Paris"}}]), json)
246
+ end
247
+
248
+ def test_enumerable_should_pass_encoding_options_to_children_in_as_json
249
+ json = People.new.each.as_json only: [:address, :city]
250
+ expected = [
251
+ { "address" => { "city" => "London" } },
252
+ { "address" => { "city" => "Paris" } }
253
+ ]
254
+
255
+ assert_equal(expected, json)
256
+ end
257
+
258
+ def test_enumerable_should_pass_encoding_options_to_children_in_to_json
259
+ json = People.new.each.to_json only: [:address, :city]
260
+
261
+ assert_equal(%([{"address":{"city":"London"}},{"address":{"city":"Paris"}}]), json)
262
+ end
263
+
264
+ class CustomWithOptions
265
+ attr_accessor :foo, :bar
266
+
267
+ def as_json(options = {})
268
+ options[:only] = %w(foo bar)
269
+ super(options)
270
+ end
271
+ end
272
+
273
+ def test_hash_to_json_should_not_keep_options_around
274
+ f = CustomWithOptions.new
275
+ f.foo = "hello"
276
+ f.bar = "world"
277
+
278
+ hash = { "foo" => f, "other_hash" => { "foo" => "other_foo", "test" => "other_test" } }
279
+ assert_equal({ "foo" => { "foo" => "hello", "bar" => "world" },
280
+ "other_hash" => { "foo" => "other_foo", "test" => "other_test" } }, ActiveSupport::JSON.decode(hash.to_json))
281
+ end
282
+
283
+ def test_array_to_json_should_not_keep_options_around
284
+ f = CustomWithOptions.new
285
+ f.foo = "hello"
286
+ f.bar = "world"
287
+
288
+ array = [f, { "foo" => "other_foo", "test" => "other_test" }]
289
+ assert_equal([{ "foo" => "hello", "bar" => "world" },
290
+ { "foo" => "other_foo", "test" => "other_test" }], ActiveSupport::JSON.decode(array.to_json))
291
+ end
292
+
293
+ class OptionsTest
294
+ def as_json(options = :default)
295
+ options
296
+ end
297
+ end
298
+
299
+ def test_hash_as_json_without_options
300
+ json = { foo: OptionsTest.new }.as_json
301
+ assert_equal({ "foo" => :default }, json)
302
+ end
303
+
304
+ def test_array_as_json_without_options
305
+ json = [ OptionsTest.new ].as_json
306
+ assert_equal([:default], json)
307
+ end
308
+
309
+ def test_struct_encoding
310
+ Struct.new("UserNameAndEmail", :name, :email)
311
+ Struct.new("UserNameAndDate", :name, :date)
312
+ Struct.new("Custom", :name, :sub)
313
+ user_email = Struct::UserNameAndEmail.new "David", "sample@example.com"
314
+ user_birthday = Struct::UserNameAndDate.new "David", Date.new(2010, 01, 01)
315
+ custom = Struct::Custom.new "David", user_birthday
316
+
317
+ json_strings = ""
318
+ json_string_and_date = ""
319
+ json_custom = ""
320
+
321
+ assert_nothing_raised do
322
+ json_strings = user_email.to_json
323
+ json_string_and_date = user_birthday.to_json
324
+ json_custom = custom.to_json
325
+ end
326
+
327
+ assert_equal({ "name" => "David",
328
+ "sub" => {
329
+ "name" => "David",
330
+ "date" => "2010-01-01" } }, ActiveSupport::JSON.decode(json_custom))
331
+
332
+ assert_equal({ "name" => "David", "email" => "sample@example.com" },
333
+ ActiveSupport::JSON.decode(json_strings))
334
+
335
+ assert_equal({ "name" => "David", "date" => "2010-01-01" },
336
+ ActiveSupport::JSON.decode(json_string_and_date))
337
+ end
338
+
339
+ def test_nil_true_and_false_represented_as_themselves
340
+ assert_nil nil.as_json
341
+ assert_equal true, true.as_json
342
+ assert_equal false, false.as_json
343
+ end
344
+
345
+ class HashWithAsJson < Hash
346
+ attr_accessor :as_json_called
347
+
348
+ def initialize(*)
349
+ super
350
+ end
351
+
352
+ def as_json(options = {})
353
+ @as_json_called = true
354
+ super
355
+ end
356
+ end
357
+
358
+ def test_json_gem_dump_by_passing_active_support_encoder
359
+ h = HashWithAsJson.new
360
+ h[:foo] = "hello"
361
+ h[:bar] = "world"
362
+
363
+ assert_equal %({"foo":"hello","bar":"world"}), JSON.dump(h)
364
+ assert_nil h.as_json_called
365
+ end
366
+
367
+ def test_json_gem_generate_by_passing_active_support_encoder
368
+ h = HashWithAsJson.new
369
+ h[:foo] = "hello"
370
+ h[:bar] = "world"
371
+
372
+ assert_equal %({"foo":"hello","bar":"world"}), JSON.generate(h)
373
+ assert_nil h.as_json_called
374
+ end
375
+
376
+ def test_json_gem_pretty_generate_by_passing_active_support_encoder
377
+ h = HashWithAsJson.new
378
+ h[:foo] = "hello"
379
+ h[:bar] = "world"
380
+
381
+ assert_equal <<EXPECTED.chomp, JSON.pretty_generate(h)
382
+ {
383
+ "foo": "hello",
384
+ "bar": "world"
385
+ }
386
+ EXPECTED
387
+ assert_nil h.as_json_called
388
+ end
389
+
390
+ def test_twz_to_json_with_use_standard_json_time_format_config_set_to_false
391
+ with_standard_json_time_format(false) do
392
+ zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
393
+ time = ActiveSupport::TimeWithZone.new(Time.utc(2000), zone)
394
+ assert_equal "\"1999/12/31 19:00:00 -0500\"", ActiveSupport::JSON.encode(time)
395
+ end
396
+ end
397
+
398
+ def test_twz_to_json_with_use_standard_json_time_format_config_set_to_true
399
+ with_standard_json_time_format(true) do
400
+ zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
401
+ time = ActiveSupport::TimeWithZone.new(Time.utc(2000), zone)
402
+ assert_equal "\"1999-12-31T19:00:00.000-05:00\"", ActiveSupport::JSON.encode(time)
403
+ end
404
+ end
405
+
406
+ def test_twz_to_json_with_custom_time_precision
407
+ with_standard_json_time_format(true) do
408
+ with_time_precision(0) do
409
+ zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
410
+ time = ActiveSupport::TimeWithZone.new(Time.utc(2000), zone)
411
+ assert_equal "\"1999-12-31T19:00:00-05:00\"", ActiveSupport::JSON.encode(time)
412
+ end
413
+ end
414
+ end
415
+
416
+ def test_time_to_json_with_custom_time_precision
417
+ with_standard_json_time_format(true) do
418
+ with_time_precision(0) do
419
+ assert_equal "\"2000-01-01T00:00:00Z\"", ActiveSupport::JSON.encode(Time.utc(2000))
420
+ end
421
+ end
422
+ end
423
+
424
+ def test_datetime_to_json_with_custom_time_precision
425
+ with_standard_json_time_format(true) do
426
+ with_time_precision(0) do
427
+ assert_equal "\"2000-01-01T00:00:00+00:00\"", ActiveSupport::JSON.encode(DateTime.new(2000))
428
+ end
429
+ end
430
+ end
431
+
432
+ def test_twz_to_json_when_wrapping_a_date_time
433
+ zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
434
+ time = ActiveSupport::TimeWithZone.new(DateTime.new(2000), zone)
435
+ assert_equal '"1999-12-31T19:00:00.000-05:00"', ActiveSupport::JSON.encode(time)
436
+ end
437
+
438
+ def test_exception_to_json
439
+ exception = Exception.new("foo")
440
+ assert_equal '"foo"', ActiveSupport::JSON.encode(exception)
441
+ end
442
+
443
+ class InfiniteNumber
444
+ def as_json(options = nil)
445
+ { "number" => Float::INFINITY }
446
+ end
447
+ end
448
+
449
+ def test_to_json_works_when_as_json_returns_infinite_number
450
+ assert_equal '{"number":null}', InfiniteNumber.new.to_json
451
+ end
452
+
453
+ class NaNNumber
454
+ def as_json(options = nil)
455
+ { "number" => Float::NAN }
456
+ end
457
+ end
458
+
459
+ def test_to_json_works_when_as_json_returns_NaN_number
460
+ assert_equal '{"number":null}', NaNNumber.new.to_json
461
+ end
462
+
463
+ def test_to_json_works_on_io_objects
464
+ assert_equal STDOUT.to_s.to_json, STDOUT.to_json
465
+ end
466
+
467
+ private
468
+ def object_keys(json_object)
469
+ json_object[1..-2].scan(/([^{}:,\s]+):/).flatten.sort
470
+ end
471
+
472
+ def with_standard_json_time_format(boolean = true)
473
+ old, ActiveSupport.use_standard_json_time_format = ActiveSupport.use_standard_json_time_format, boolean
474
+ yield
475
+ ensure
476
+ ActiveSupport.use_standard_json_time_format = old
477
+ end
478
+
479
+ def with_time_precision(value)
480
+ old_value = ActiveSupport::JSON::Encoding.time_precision
481
+ ActiveSupport::JSON::Encoding.time_precision = value
482
+ yield
483
+ ensure
484
+ ActiveSupport::JSON::Encoding.time_precision = old_value
485
+ end
486
+ end