From 2c81dfb3ca6b9c65c2d430c088d8801dcbcb94ec Mon Sep 17 00:00:00 2001 From: Alastair Pharo Date: Sat, 3 Aug 2024 00:16:13 +1000 Subject: [PATCH] Add tests for some transforms --- spec/features/serializer.rb | 43 +++++++++++++++++++ spec/moneta/transforms/base64_spec.rb | 59 ++++++++++++++++++++++++++ spec/moneta/transforms/bencode_spec.rb | 23 ++++++++++ spec/moneta/transforms/bert_spec.rb | 23 ++++++++++ spec/moneta/transforms/bson_spec.rb | 23 ++++++++++ spec/moneta/transforms/bzip2_spec.rb | 15 +++++++ spec/moneta/transforms/city128_spec.rb | 9 ++++ spec/moneta/transforms/city32_spec.rb | 9 ++++ spec/moneta/transforms/city64_spec.rb | 9 ++++ spec/moneta/transforms/escape_spec.rb | 17 ++++++++ spec/moneta/transforms/hex_spec.rb | 17 ++++++++ 11 files changed, 247 insertions(+) create mode 100644 spec/features/serializer.rb create mode 100644 spec/moneta/transforms/base64_spec.rb create mode 100644 spec/moneta/transforms/bencode_spec.rb create mode 100644 spec/moneta/transforms/bert_spec.rb create mode 100644 spec/moneta/transforms/bson_spec.rb create mode 100644 spec/moneta/transforms/bzip2_spec.rb create mode 100644 spec/moneta/transforms/city128_spec.rb create mode 100644 spec/moneta/transforms/city32_spec.rb create mode 100644 spec/moneta/transforms/city64_spec.rb create mode 100644 spec/moneta/transforms/escape_spec.rb create mode 100644 spec/moneta/transforms/hex_spec.rb diff --git a/spec/features/serializer.rb b/spec/features/serializer.rb new file mode 100644 index 00000000..efd4d6b5 --- /dev/null +++ b/spec/features/serializer.rb @@ -0,0 +1,43 @@ +RSpec.shared_examples "serializer" do + it "serializes non-string values" do + property_of { dict { [string, choose(string, integer)] } }.check do |hash| + expect(subject.encode(hash)).to be_a String + end + end + + it "serializes arrays to strings" do + property_of { array { choose(string, integer) } }.check do |arr| + expect(subject.encode(arr)).to be_a String + end + end + + it "serializes integers to strings" do + property_of { integer }.check do |i| + expect(subject.encode(i)).to be_a String + end + end +end + +RSpec.shared_examples "serializer only" do + it_behaves_like "serializer" + + it "refuses to decode strings" do + expect { subject.decode("test") }.to raise_error(NotImplementedError) + end +end + +RSpec.shared_examples "serializer and deserializer" do + it_behaves_like "serializer" + + it "deserializes serialized hashes" do + property_of { dict { [string, choose(string, integer)] } }.check do |hash| + expect(subject.decode(subject.encode(hash))).to eq(hash) + end + end + + it "deserializes serialized arrays" do + property_of { array { choose(string, integer) } }.check do |arr| + expect(subject.decode(subject.encode(arr))).to eq(arr) + end + end +end diff --git a/spec/moneta/transforms/base64_spec.rb b/spec/moneta/transforms/base64_spec.rb new file mode 100644 index 00000000..9068f5aa --- /dev/null +++ b/spec/moneta/transforms/base64_spec.rb @@ -0,0 +1,59 @@ +RSpec.describe Moneta::Transforms::Base64 do + context 'with url_safe: false, strict: false' do + subject { described_class.new(url_safe: false, strict: false) } + + it 'encodes strings to RFC 2045 base64' do + expect(subject.encode('x' * 50)).to eq <<~BASE64 + eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4 + eHh4eHg= + BASE64 + end + + it 'decodes base64 strings with line feeds' do + expect(subject.decode(<<~BASE64)).to eq('x' * 50) + eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4 + eHh4eHg= + BASE64 + end + end + + context 'with url_safe: false, strict: true' do + subject { described_class.new(url_safe: false, strict: true) } + + it 'encodes strings to RFC 4648 base64' do + expect(subject.encode('x' * 50)).to eq <<~BASE64.chomp + eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHg= + BASE64 + end + + it 'decodes base64 strings without line feeds' do + expect(subject.decode(<<~BASE64.chomp)).to eq('x' * 50) + eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHg= + BASE64 + end + + it 'raises when decoding base64 strings with line feeds' do + expect { subject.decode(<<~BASE64) }.to raise_error('invalid base64') + eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4 + eHh4eHg= + BASE64 + end + end + + context 'with url_safe: true, padding: false' do + subject { described_class.new(url_safe: true, padding: false) } + + it 'encodes URL-safe base64 without padding' do + expect(subject.encode('¿español?')).to eq 'wr9lc3Bhw7FvbD8' + end + end + + context 'with url_safe: true, padding: true' do + subject { described_class.new(url_safe: true, padding: true) } + + it 'encodes URL-safe base64 with padding' do + expect(subject.encode('¿español?')).to eq 'wr9lc3Bhw7FvbD8=' + end + end +end + diff --git a/spec/moneta/transforms/bencode_spec.rb b/spec/moneta/transforms/bencode_spec.rb new file mode 100644 index 00000000..633f9fea --- /dev/null +++ b/spec/moneta/transforms/bencode_spec.rb @@ -0,0 +1,23 @@ +RSpec.describe Moneta::Transforms::BEncode do + context "with serialize_unless_string: true" do + subject { described_class.new(serialize_unless_string: true) } + + it_behaves_like "serializer only" + + it "passes through string inputs unchanged" do + expect(subject.encode("testing")).to eq "testing" + end + + it "refuses to decode strings" do + expect { subject.decode("test") }.to raise_error(NotImplementedError) + end + end + + context "with serialize_unless_string: false" do + it_behaves_like "serializer and deserializer" + + it "serializes strings to bencode format" do + expect(subject.encode("test")).to eq "4:test" + end + end +end diff --git a/spec/moneta/transforms/bert_spec.rb b/spec/moneta/transforms/bert_spec.rb new file mode 100644 index 00000000..65765f3a --- /dev/null +++ b/spec/moneta/transforms/bert_spec.rb @@ -0,0 +1,23 @@ +RSpec.describe Moneta::Transforms::BERT do + context "with serialize_unless_string: true" do + subject { described_class.new(serialize_unless_string: true) } + + it_behaves_like "serializer only" + + it "passes through string inputs unchanged" do + expect(subject.encode("testing")).to eq "testing" + end + + it "refuses to decode strings" do + expect { subject.decode("test") }.to raise_error(NotImplementedError) + end + end + + context "with serialize_unless_string: false" do + it_behaves_like "serializer and deserializer" + + it "serializes strings to BERT format" do + expect(subject.encode("test")).to eq ::BERT.encode('test') + end + end +end diff --git a/spec/moneta/transforms/bson_spec.rb b/spec/moneta/transforms/bson_spec.rb new file mode 100644 index 00000000..c21fce3d --- /dev/null +++ b/spec/moneta/transforms/bson_spec.rb @@ -0,0 +1,23 @@ +RSpec.describe Moneta::Transforms::BSON do + context "with serialize_unless_string: true" do + subject { described_class.new(serialize_unless_string: true) } + + it_behaves_like "serializer only" + + it "passes through string inputs unchanged" do + expect(subject.encode("testing")).to eq "testing" + end + + it "refuses to decode strings" do + expect { subject.decode("test") }.to raise_error(NotImplementedError) + end + end + + context "with serialize_unless_string: false" do + it_behaves_like "serializer and deserializer" + + it "serializes strings to BSON format" do + expect(subject.encode("test")).to eq ::BSON::Document['v' => 'test'].to_bson.to_s + end + end +end diff --git a/spec/moneta/transforms/bzip2_spec.rb b/spec/moneta/transforms/bzip2_spec.rb new file mode 100644 index 00000000..fb650b5f --- /dev/null +++ b/spec/moneta/transforms/bzip2_spec.rb @@ -0,0 +1,15 @@ +RSpec.describe Moneta::Transforms::Bzip2 do + subject { described_class.new } + + it 'encodes strings to bzip2' do + property_of { string }.check do |str| + expect(subject.encode(str)).to start_with 'BZ' + end + end + + it 'decodes bzip2-encoded strings' do + property_of { string }.check do |str| + expect(subject.decode(subject.encode(str))).to eq str + end + end +end diff --git a/spec/moneta/transforms/city128_spec.rb b/spec/moneta/transforms/city128_spec.rb new file mode 100644 index 00000000..05a8fad7 --- /dev/null +++ b/spec/moneta/transforms/city128_spec.rb @@ -0,0 +1,9 @@ +RSpec.describe Moneta::Transforms::City128 do + subject { described_class.new } + + it 'hashes strings' do + property_of { string }.check do |str| + expect(subject.encode(str)).to match(/^[0-9a-f]{16,33}$/) + end + end +end diff --git a/spec/moneta/transforms/city32_spec.rb b/spec/moneta/transforms/city32_spec.rb new file mode 100644 index 00000000..956a7567 --- /dev/null +++ b/spec/moneta/transforms/city32_spec.rb @@ -0,0 +1,9 @@ +RSpec.describe Moneta::Transforms::City32 do + subject { described_class.new } + + it 'hashes strings' do + property_of { string }.check do |str| + expect(subject.encode(str)).to match(/^[0-9a-f]{4,8}$/) + end + end +end diff --git a/spec/moneta/transforms/city64_spec.rb b/spec/moneta/transforms/city64_spec.rb new file mode 100644 index 00000000..4fd58f08 --- /dev/null +++ b/spec/moneta/transforms/city64_spec.rb @@ -0,0 +1,9 @@ +RSpec.describe Moneta::Transforms::City64 do + subject { described_class.new } + + it 'hashes strings' do + property_of { string }.check do |str| + expect(subject.encode(str)).to match(/^[0-9a-f]{8,16}$/) + end + end +end diff --git a/spec/moneta/transforms/escape_spec.rb b/spec/moneta/transforms/escape_spec.rb new file mode 100644 index 00000000..0d0be26e --- /dev/null +++ b/spec/moneta/transforms/escape_spec.rb @@ -0,0 +1,17 @@ +RSpec.describe Moneta::Transforms::Escape do + subject { described_class.new } + + describe '#encode' do + it 'escapes strings' do + expect(subject.encode('This "string", containing punctuation?')).to eq "This%20%22string%22%2C%20containing%20punctuation%3F" + end + end + + describe '#decode' do + it 'unescapes escaped strings' do + property_of { string }.check do |str| + expect(subject.decode(subject.encode(str))).to eq str + end + end + end +end diff --git a/spec/moneta/transforms/hex_spec.rb b/spec/moneta/transforms/hex_spec.rb new file mode 100644 index 00000000..fc7c8f5b --- /dev/null +++ b/spec/moneta/transforms/hex_spec.rb @@ -0,0 +1,17 @@ +RSpec.describe Moneta::Transforms::Hex do + subject { described_class.new } + + describe '#encode' do + it 'converts strings to hexadecimal' do + expect(subject.encode('testing 1 2 3')).to eq('74657374696e67203120322033') + end + end + + describe '#decode' do + it 'converts hexadecimal to strings' do + property_of { string }.check do |str| + expect(subject.decode(subject.encode(str))).to eq str + end + end + end +end