Skip to content

Commit 4e049eb

Browse files
Merge pull request rails#46847 from jonathanhefner/messages-refactor-metadata-tests
Refactor message metadata tests
2 parents c5af4f4 + c103a43 commit 4e049eb

File tree

6 files changed

+234
-219
lines changed

6 files changed

+234
-219
lines changed

activesupport/test/message_encryptor_test.rb

Lines changed: 0 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
require "openssl"
55
require "active_support/time"
66
require "active_support/json"
7-
require_relative "metadata/shared_metadata_tests"
87

98
class MessageEncryptorTest < ActiveSupport::TestCase
109
class JSONSerializer
@@ -404,44 +403,3 @@ def teardown
404403
super
405404
end
406405
end
407-
408-
class MessageEncryptorMetadataTest < ActiveSupport::TestCase
409-
include SharedMessageMetadataTests
410-
411-
setup do
412-
@secret = SecureRandom.random_bytes(32)
413-
@encryptor = ActiveSupport::MessageEncryptor.new(@secret, **encryptor_options)
414-
end
415-
416-
private
417-
def generate(message, **options)
418-
@encryptor.encrypt_and_sign(message, **options)
419-
end
420-
421-
def parse(data, **options)
422-
@encryptor.decrypt_and_verify(data, **options)
423-
end
424-
425-
def encryptor_options; {} end
426-
end
427-
428-
class MessageEncryptorMetadataMarshalTest < MessageEncryptorMetadataTest
429-
private
430-
def encryptor_options
431-
{ serializer: Marshal }
432-
end
433-
end
434-
435-
class MessageEncryptorMetadataJSONTest < MessageEncryptorMetadataTest
436-
private
437-
def encryptor_options
438-
{ serializer: MessageEncryptorTest::JSONSerializer.new }
439-
end
440-
end
441-
442-
class MessageEncryptorMetadataJsonWithMarshalFallbackTest < MessageEncryptorMetadataTest
443-
private
444-
def encryptor_options
445-
{ serializer: ActiveSupport::JsonWithMarshalFallback }
446-
end
447-
end

activesupport/test/message_verifier_test.rb

Lines changed: 0 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
require "openssl"
55
require "active_support/time"
66
require "active_support/json"
7-
require_relative "metadata/shared_metadata_tests"
87

98
class MessageVerifierTest < ActiveSupport::TestCase
109
class JSONSerializer
@@ -289,91 +288,3 @@ def teardown
289288
ActiveSupport::MessageVerifier.default_message_verifier_serializer = @default_verifier
290289
end
291290
end
292-
293-
class MessageVerifierMetadataTest < ActiveSupport::TestCase
294-
include SharedMessageMetadataTests
295-
296-
setup do
297-
@verifier = ActiveSupport::MessageVerifier.new("Hey, I'm a secret!", **verifier_options)
298-
end
299-
300-
def test_verify_raises_when_purpose_differs
301-
assert_raise(ActiveSupport::MessageVerifier::InvalidSignature) do
302-
@verifier.verify(generate(data, purpose: "payment"), purpose: "shipping")
303-
end
304-
end
305-
306-
def test_verify_with_use_standard_json_time_format_as_false
307-
format_before = ActiveSupport.use_standard_json_time_format
308-
ActiveSupport.use_standard_json_time_format = false
309-
assert_equal "My Name", @verifier.verify(generate("My Name"))
310-
ensure
311-
ActiveSupport.use_standard_json_time_format = format_before
312-
end
313-
314-
def test_verify_raises_when_expired
315-
signed_message = generate(data, expires_in: 1.month)
316-
317-
travel 2.months
318-
assert_raise(ActiveSupport::MessageVerifier::InvalidSignature) do
319-
@verifier.verify(signed_message)
320-
end
321-
end
322-
323-
private
324-
def generate(message, **options)
325-
@verifier.generate(message, **options)
326-
end
327-
328-
def parse(message, **options)
329-
@verifier.verified(message, **options)
330-
end
331-
332-
def verifier_options
333-
Hash.new
334-
end
335-
end
336-
337-
class MessageVerifierMetadataMarshalTest < MessageVerifierMetadataTest
338-
private
339-
def verifier_options
340-
{ serializer: Marshal }
341-
end
342-
end
343-
344-
class MessageVerifierMetadataJsonWithMarshalFallbackTest < MessageVerifierMetadataTest
345-
private
346-
def verifier_options
347-
{ serializer: ActiveSupport::JsonWithMarshalFallback }
348-
end
349-
end
350-
351-
class MessageVerifierMetadataJsonTest < MessageVerifierMetadataTest
352-
private
353-
def verifier_options
354-
{ serializer: JSON }
355-
end
356-
end
357-
358-
359-
class MessageVerifierMetadataCustomJSONTest < MessageVerifierMetadataTest
360-
private
361-
def verifier_options
362-
{ serializer: MessageVerifierTest::JSONSerializer.new }
363-
end
364-
end
365-
366-
class MessageEncryptorMetadataNullSerializerTest < MessageVerifierMetadataTest
367-
private
368-
def data
369-
"string message"
370-
end
371-
372-
def null_serializing?
373-
true
374-
end
375-
376-
def verifier_options
377-
{ serializer: ActiveSupport::MessageEncryptor::NullSerializer }
378-
end
379-
end
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# frozen_string_literal: true
2+
3+
require_relative "../abstract_unit"
4+
require_relative "message_metadata_tests"
5+
6+
class MessageEncryptorMetadataTest < ActiveSupport::TestCase
7+
include MessageMetadataTests
8+
9+
private
10+
def make_codec(**options)
11+
@secret ||= SecureRandom.random_bytes(32)
12+
ActiveSupport::MessageEncryptor.new(@secret, **options)
13+
end
14+
15+
def encode(data, encryptor, **options)
16+
encryptor.encrypt_and_sign(data, **options)
17+
end
18+
19+
def decode(message, encryptor, **options)
20+
encryptor.decrypt_and_verify(message, **options)
21+
rescue ActiveSupport::MessageVerifier::InvalidSignature
22+
nil
23+
end
24+
end
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
# frozen_string_literal: true
2+
3+
require "active_support/json"
4+
require "active_support/time"
5+
6+
module MessageMetadataTests
7+
extend ActiveSupport::Concern
8+
9+
included do
10+
test "message :purpose must match specified :purpose" do
11+
each_scenario do |data, codec|
12+
assert_roundtrip data, codec, { purpose: "x" }, { purpose: "x" }
13+
14+
assert_no_roundtrip data, codec, { purpose: "x" }, { purpose: "y" }
15+
assert_no_roundtrip data, codec, { purpose: "x" }, {}
16+
assert_no_roundtrip data, codec, {}, { purpose: "x" }
17+
end
18+
end
19+
20+
test ":purpose can be a symbol" do
21+
each_scenario do |data, codec|
22+
assert_roundtrip data, codec, { purpose: :x }, { purpose: :x }
23+
assert_roundtrip data, codec, { purpose: :x }, { purpose: "x" }
24+
assert_roundtrip data, codec, { purpose: "x" }, { purpose: :x }
25+
end
26+
end
27+
28+
test "message expires with :expires_at" do
29+
freeze_time do
30+
each_scenario do |data, codec|
31+
message = encode(data, codec, expires_at: 1.second.from_now)
32+
33+
travel 0.5.seconds, with_usec: true
34+
assert_equal data, decode(message, codec)
35+
36+
travel 0.5.seconds, with_usec: true
37+
assert_nil decode(message, codec)
38+
end
39+
end
40+
end
41+
42+
test "message expires with :expires_in" do
43+
freeze_time do
44+
each_scenario do |data, codec|
45+
message = encode(data, codec, expires_in: 1.second)
46+
47+
travel 0.5.seconds, with_usec: true
48+
assert_equal data, decode(message, codec)
49+
50+
travel 0.5.seconds, with_usec: true
51+
assert_nil decode(message, codec)
52+
end
53+
end
54+
end
55+
56+
test ":expires_at overrides :expires_in" do
57+
each_scenario do |data, codec|
58+
message = encode(data, codec, expires_at: 1.hour.from_now, expires_in: 1.second)
59+
60+
travel 1.minute
61+
assert_equal data, decode(message, codec)
62+
63+
travel 1.hour
64+
assert_nil decode(message, codec)
65+
end
66+
end
67+
68+
test "messages do not expire by default" do
69+
each_scenario do |data, codec|
70+
message = encode(data, codec, purpose: "x")
71+
72+
travel 1000.years
73+
assert_equal data, decode(message, codec, purpose: "x")
74+
end
75+
end
76+
77+
test "expiration works with ActiveSupport.use_standard_json_time_format = false" do
78+
original_use_standard_json_time_format = ActiveSupport.use_standard_json_time_format
79+
ActiveSupport.use_standard_json_time_format = false
80+
81+
each_scenario do |data, codec|
82+
assert_roundtrip data, codec, { expires_at: 1.hour.from_now }
83+
end
84+
ensure
85+
ActiveSupport.use_standard_json_time_format = original_use_standard_json_time_format
86+
end
87+
88+
test "metadata works with NullSerializer" do
89+
codec = make_codec(serializer: ActiveSupport::MessageEncryptor::NullSerializer)
90+
assert_roundtrip "a string", codec, { purpose: "x", expires_in: 1.year }, { purpose: "x" }
91+
end
92+
end
93+
94+
private
95+
class CustomSerializer
96+
def self.dump(value)
97+
JSON.dump(value) << "!"
98+
end
99+
100+
def self.load(value)
101+
JSON.load(value.chomp!("!"))
102+
end
103+
end
104+
105+
SERIALIZERS = [
106+
Marshal,
107+
JSON,
108+
ActiveSupport::JSON,
109+
ActiveSupport::JsonWithMarshalFallback,
110+
CustomSerializer,
111+
]
112+
113+
DATA = [
114+
"a string",
115+
{ "a_number" => 123, "a_time" => Time.local(2004), "an_object" => { "key" => "value" } },
116+
["a string", 123, Time.local(2004), { "key" => "value" }],
117+
]
118+
119+
def each_scenario
120+
SERIALIZERS.each do |serializer|
121+
codec = make_codec(serializer: serializer)
122+
DATA.each do |data|
123+
yield data, codec
124+
end
125+
end
126+
end
127+
128+
def roundtrip(data, codec, encode_options = {}, decode_options = {})
129+
decode(encode(data, codec, **encode_options), codec, **decode_options)
130+
end
131+
132+
def assert_roundtrip(data, codec, encode_options = {}, decode_options = {})
133+
assert_equal data, roundtrip(data, codec, encode_options, decode_options)
134+
end
135+
136+
def assert_no_roundtrip(data, codec, encode_options = {}, decode_options = {})
137+
assert_nil roundtrip(data, codec, encode_options, decode_options)
138+
end
139+
end
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# frozen_string_literal: true
2+
3+
require_relative "../abstract_unit"
4+
require_relative "message_metadata_tests"
5+
6+
class MessageVerifierMetadataTest < ActiveSupport::TestCase
7+
include MessageMetadataTests
8+
9+
test "#verify raises when :purpose does not match" do
10+
each_scenario do |data, verifier|
11+
assert_equal data, verifier.verify(verifier.generate(data, purpose: "x"), purpose: "x")
12+
13+
assert_raises ActiveSupport::MessageVerifier::InvalidSignature do
14+
verifier.verify(verifier.generate(data, purpose: "x"), purpose: "y")
15+
end
16+
17+
assert_raises ActiveSupport::MessageVerifier::InvalidSignature do
18+
verifier.verify(verifier.generate(data), purpose: "y")
19+
end
20+
21+
assert_raises ActiveSupport::MessageVerifier::InvalidSignature do
22+
verifier.verify(verifier.generate(data, purpose: "x"))
23+
end
24+
end
25+
end
26+
27+
test "#verify raises when message is expired via :expires_at" do
28+
freeze_time do
29+
each_scenario do |data, verifier|
30+
message = verifier.generate(data, expires_at: 1.second.from_now)
31+
32+
travel 0.5.seconds, with_usec: true
33+
assert_equal data, verifier.verify(message)
34+
35+
travel 0.5.seconds, with_usec: true
36+
assert_raises ActiveSupport::MessageVerifier::InvalidSignature do
37+
verifier.verify(message)
38+
end
39+
end
40+
end
41+
end
42+
43+
test "#verify raises when message is expired via :expires_in" do
44+
freeze_time do
45+
each_scenario do |data, verifier|
46+
message = verifier.generate(data, expires_in: 1.second)
47+
48+
travel 0.5.seconds, with_usec: true
49+
assert_equal data, verifier.verify(message)
50+
51+
travel 0.5.seconds, with_usec: true
52+
assert_raises ActiveSupport::MessageVerifier::InvalidSignature do
53+
verifier.verify(message)
54+
end
55+
end
56+
end
57+
end
58+
59+
private
60+
def make_codec(**options)
61+
ActiveSupport::MessageVerifier.new("secret", **options)
62+
end
63+
64+
def encode(data, verifier, **options)
65+
verifier.generate(data, **options)
66+
end
67+
68+
def decode(message, verifier, **options)
69+
verifier.verified(message, **options)
70+
end
71+
end

0 commit comments

Comments
 (0)