diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml index aa89e72..c475264 100644 --- a/.github/workflows/rspec.yml +++ b/.github/workflows/rspec.yml @@ -19,19 +19,21 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - ruby-version: ['2.6', '2.7', '3.0'] + ruby-version: ['3.0', '3.1', '3.2'] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Ruby # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby, # change this to (see https://github.com/ruby/setup-ruby#versioning): # uses: ruby/setup-ruby@v1 - uses: ruby/setup-ruby@473e4d8fe5dd94ee328fdfca9f8c9c7afc9dae5e + uses: ruby/setup-ruby@master with: ruby-version: ${{ matrix.ruby-version }} bundler-cache: true # runs 'bundle install' and caches installed gems automatically - name: Run tests run: bundle exec rake - env: - CODECOV_TOKEN: 4b75ccca-3149-4610-99fb-3a5aeff4ff00 + - uses: codecov/codecov-action@v3 + with: + token: 4b75ccca-3149-4610-99fb-3a5aeff4ff00 + diff --git a/.github/workflows/rubocop.yml b/.github/workflows/rubocop.yml index d201e1d..81db61f 100644 --- a/.github/workflows/rubocop.yml +++ b/.github/workflows/rubocop.yml @@ -8,10 +8,10 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Set up Ruby 2.7 + - name: Set up Ruby 3.0 uses: ruby/setup-ruby@v1 with: - ruby-version: 2.7 + ruby-version: 3.0 - name: Cache gems uses: actions/cache@v1 with: @@ -22,7 +22,6 @@ jobs: - name: Install gems run: | bundle config path vendor/bundle - bundle config set without 'default doc job cable storage ujs test db' bundle install --jobs 4 --retry 3 - name: Run RuboCop run: bundle exec rubocop --parallel diff --git a/.github/workflows/yardoc.yml b/.github/workflows/yardoc.yml index f5351a4..f94c080 100644 --- a/.github/workflows/yardoc.yml +++ b/.github/workflows/yardoc.yml @@ -11,7 +11,7 @@ jobs: - name: Set up Ruby 2.7 uses: ruby/setup-ruby@v1 with: - ruby-version: 2.7 + ruby-version: 3.2 - name: Cache gems uses: actions/cache@v1 with: @@ -22,7 +22,6 @@ jobs: - name: Install gems run: | bundle config path vendor/bundle - bundle config set without 'default doc job cable storage ujs test db' bundle install --jobs 4 --retry 3 - name: Run yard stats run: bundle exec yard stats --list-undoc --no-cache --fail-on-warning diff --git a/.rubocop.yml b/.rubocop.yml index ffcd4cb..ba763ad 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -9,7 +9,7 @@ Metrics/BlockLength: AllCops: NewCops: enable - TargetRubyVersion: 2.6 + TargetRubyVersion: 3.0 Exclude: - vendor/bundle/**/* # exclude protoc generated code @@ -20,4 +20,6 @@ AllCops: - 'lib/orderer/*' - 'lib/peer/*' - +RSpec/SpecFilePathFormat: + Exclude: + - spec/fabric/entities/* diff --git a/.ruby-version b/.ruby-version index a603bb5..be94e6f 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.7.5 +3.2.2 diff --git a/Gemfile b/Gemfile index 013b495..b42ed4a 100644 --- a/Gemfile +++ b/Gemfile @@ -7,3 +7,12 @@ gemspec gem 'rake', '~> 12.3.3' gem 'rspec', '~> 3.0' + +gem 'factory_bot', '~> 6.3.0' +gem 'grpc-tools', '~> 1.59.0' +gem 'rubocop', '~> 1.57', '>= 1.57.1' +gem 'rubocop-rspec', '~> 2.24.1' +gem 'simplecov', '~> 0.22.0' +gem 'simplecov-cobertura', '~> 2.1' +gem 'timecop', '~> 0.9.8' +gem 'yard', '~> 0.9.34' diff --git a/Rakefile b/Rakefile index 564b8d2..82bb534 100644 --- a/Rakefile +++ b/Rakefile @@ -2,7 +2,6 @@ require 'bundler/gem_tasks' require 'rspec/core/rake_task' -require 'rake/notes/rake_task' RSpec::Core::RakeTask.new(:spec) diff --git a/fabric-gateway.gemspec b/fabric-gateway.gemspec index 57b6aae..4b79fd1 100644 --- a/fabric-gateway.gemspec +++ b/fabric-gateway.gemspec @@ -12,7 +12,7 @@ Gem::Specification.new do |spec| spec.description = 'Ruby port of the Hyperledger Fabric Gateway SDK' spec.homepage = 'https://github.com/ethicalidentity/fabric-gateway-ruby' spec.license = 'MIT' - spec.required_ruby_version = Gem::Requirement.new('>= 2.6.0') + spec.required_ruby_version = Gem::Requirement.new('>= 3.0') spec.metadata['allowed_push_host'] = 'https://rubygems.org' @@ -28,17 +28,8 @@ Gem::Specification.new do |spec| spec.bindir = 'exe' spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.require_paths = ['lib'] - spec.add_dependency('google-protobuf', '>= 3.19.1') + spec.add_dependency('google-protobuf', '~> 3.24', '>= 3.24.4') spec.add_dependency('grpc', '~> 1.42') - spec.add_development_dependency('codecov', '~> 0.6.0') - spec.add_development_dependency('factory_bot', '~> 6.2.0') - spec.add_development_dependency('grpc-tools', '~> 1.46.2') - spec.add_development_dependency('rake-notes', '~> 0.2.0') - spec.add_development_dependency('rubocop', '~> 1.23.0') - spec.add_development_dependency('rubocop-rspec', '~> 2.6.0') - spec.add_development_dependency('simplecov', '~> 0.21.2') - spec.add_development_dependency('timecop', '~> 0.9.4') - spec.add_development_dependency('yard', '~> 0.9.27') spec.metadata = { 'rubygems_mfa_required' => 'true' } diff --git a/lib/fabric/ec_crypto_suite.rb b/lib/fabric/ec_crypto_suite.rb index 5c644b8..6957ae1 100644 --- a/lib/fabric/ec_crypto_suite.rb +++ b/lib/fabric/ec_crypto_suite.rb @@ -40,7 +40,7 @@ def sign(private_key, message) def verify(public_key, message, signature) digest = digest message - openssl_pkey = openssl_pkey_from_public_key public_key + openssl_pkey = pkey_from_public_key public_key sequence = OpenSSL::ASN1.decode signature return false unless check_malleability sequence, openssl_pkey.group.order @@ -48,8 +48,7 @@ def verify(public_key, message, signature) end def generate_private_key - key = OpenSSL::PKey::EC.new curve - key.generate_key! + key = OpenSSL::PKey::EC.generate(curve) key.private_key.to_s(16).downcase end @@ -133,15 +132,35 @@ def decrypt(secret, data) aes.update(encrypted_data) + aes.final end - def pkey_pem_from_private_key(private_key) + # when https://github.com/ruby/openssl/pull/555 gets merged, consider refactoring + # the code here + # rubocop:disable Metrics/AbcSize + # rubocop:disable Metrics/MethodLength + def pkey_from_private_key(private_key) public_key = restore_public_key private_key - key = OpenSSL::PKey::EC.new curve - key.private_key = OpenSSL::BN.new private_key, 16 - key.public_key = OpenSSL::PKey::EC::Point.new key.group, - OpenSSL::BN.new(public_key, 16) - pkey = OpenSSL::PKey::EC.new(key.public_key.group) - pkey.public_key = key.public_key + group = OpenSSL::PKey::EC::Group.new(curve) + + private_key_bn = OpenSSL::BN.new(private_key, 16) + public_key_bn = OpenSSL::BN.new(public_key, 16) + public_key_point = OpenSSL::PKey::EC::Point.new(group, public_key_bn) + + asn1 = OpenSSL::ASN1::Sequence( + [ + OpenSSL::ASN1::Integer.new(1), + OpenSSL::ASN1::OctetString(private_key_bn.to_s(2)), + OpenSSL::ASN1::ObjectId(curve, 0, :EXPLICIT), + OpenSSL::ASN1::BitString(public_key_point.to_octet_string(:uncompressed), 1, :EXPLICIT) + ] + ) + + OpenSSL::PKey::EC.new(asn1.to_der) + end + # rubocop:enable Metrics/AbcSize + # rubocop:enable Metrics/MethodLength + + def pem_from_private_key(private_key) + pkey = pkey_from_private_key(private_key) pkey.to_pem end @@ -151,29 +170,33 @@ def key_from_pem(pem) key.private_key.to_s(16).downcase end - def pkey_from_x509_certificate(certificate) + def public_key_from_x509_certificate(certificate) cert = OpenSSL::X509::Certificate.new(certificate) cert.public_key.public_key.to_bn.to_s(16).downcase end - def openssl_pkey_from_public_key(public_key) - pkey = OpenSSL::PKey::EC.new curve - pkey.public_key = OpenSSL::PKey::EC::Point.new(pkey.group, OpenSSL::BN.new(public_key, 16)) + # rubocop:disable Metrics/MethodLength + def pkey_from_public_key(public_key) + group = OpenSSL::PKey::EC::Group.new(curve) - pkey - end + public_key_bn = OpenSSL::BN.new(public_key, 16) + public_key_point = OpenSSL::PKey::EC::Point.new(group, public_key_bn) - private - - def pkey_from_private_key(private_key) - public_key = restore_public_key private_key - key = OpenSSL::PKey::EC.new curve - key.private_key = OpenSSL::BN.new private_key, 16 - key.public_key = OpenSSL::PKey::EC::Point.new key.group, - OpenSSL::BN.new(public_key, 16) + asn1 = OpenSSL::ASN1::Sequence.new( + [ + OpenSSL::ASN1::Sequence.new([ + OpenSSL::ASN1::ObjectId.new('id-ecPublicKey'), + OpenSSL::ASN1::ObjectId.new(group.curve_name) + ]), + OpenSSL::ASN1::BitString.new(public_key_point.to_octet_string(:uncompressed)) + ] + ) - key + OpenSSL::PKey::EC.new(asn1.to_der) end + # rubocop:enable Metrics/MethodLength + + private # barely understand this code - this link provides a good explanation: # http://coders-errand.com/malleability-ecdsa-signatures/ diff --git a/lib/fabric/entities/identity.rb b/lib/fabric/entities/identity.rb index 2656f1f..7f41a7d 100644 --- a/lib/fabric/entities/identity.rb +++ b/lib/fabric/entities/identity.rb @@ -42,7 +42,7 @@ def initialize(private_key: nil, public_key: nil, certificate: nil, msp_id: nil, # @return [boolean] true if valid, false otherwise # def validate_key_integrity - cert_pubkey = @crypto_suite.pkey_from_x509_certificate(certificate) + cert_pubkey = @crypto_suite.public_key_from_x509_certificate(certificate) priv_pubkey = @crypto_suite.restore_public_key(@private_key) @public_key == cert_pubkey && @public_key == priv_pubkey diff --git a/lib/fabric/entities/status.rb b/lib/fabric/entities/status.rb index 2069d9c..6b73a40 100644 --- a/lib/fabric/entities/status.rb +++ b/lib/fabric/entities/status.rb @@ -6,9 +6,9 @@ module Fabric # class Status TRANSACTION_STATUSES = ::Protos::TxValidationCode.constants.map(&::Protos::TxValidationCode.method(:const_get)) - .collect do |i| + .to_h do |i| [::Protos::TxValidationCode.lookup(i), i] - end.to_h + end # @return [Integer] Block number in which the transaction committed. attr_reader :block_number diff --git a/spec/fabric/client_spec.rb b/spec/fabric/client_spec.rb index 42aafa6..de79c26 100644 --- a/spec/fabric/client_spec.rb +++ b/spec/fabric/client_spec.rb @@ -33,11 +33,7 @@ context 'when passing invalid client_opts' do let(:expected_message) do - if RUBY_VERSION.start_with?('2.6') - 'unknown keyword: bad_arg' - else - 'unknown keyword: :bad_arg' - end + 'unknown keyword: :bad_arg' end it 'raises an error' do @@ -57,11 +53,7 @@ context 'when grpc_client host and creds are passed' do let(:expected_args) do - if RUBY_VERSION.start_with?('2.6') - ['localhost:1234', :this_channel_is_insecure, {}] - else - ['localhost:1234', :this_channel_is_insecure] - end + ['localhost:1234', :this_channel_is_insecure] end before do diff --git a/spec/fabric/contract_spec.rb b/spec/fabric/contract_spec.rb index 53a570e..7155001 100644 --- a/spec/fabric/contract_spec.rb +++ b/spec/fabric/contract_spec.rb @@ -176,8 +176,8 @@ end describe '#submit' do - let(:proposal_double) { instance_double('Proposal') } - let(:transaction_double) { instance_double('Transaction') } + let(:proposal_double) { instance_double(Fabric::Proposal) } + let(:transaction_double) { instance_double(Fabric::Transaction) } before do allow(transaction_double).to receive(:result).and_return('mocked result') @@ -229,8 +229,8 @@ end describe '#submit_transaction' do - let(:proposal_double) { instance_double('Proposal') } - let(:transaction_double) { instance_double('Transaction') } + let(:proposal_double) { instance_double(Fabric::Proposal) } + let(:transaction_double) { instance_double(Fabric::Transaction) } before do allow(transaction_double).to receive(:result).and_return('mocked result') diff --git a/spec/fabric/ec_crypto_suite_spec.rb b/spec/fabric/ec_crypto_suite_spec.rb index 4fb7f64..76ffcbe 100644 --- a/spec/fabric/ec_crypto_suite_spec.rb +++ b/spec/fabric/ec_crypto_suite_spec.rb @@ -1,21 +1,284 @@ # frozen_string_literal: true RSpec.describe Fabric::ECCryptoSuite do - describe '#openssl_pkey_from_public_key' do - subject(:crypto_suite) { described_class.new } + subject(:crypto_suite) { described_class.new } + let(:private_key) { 'd62e76ab4a907d7634ada0d9709b4ed2bfc7c51c421127b7fc93c0141e461797' } + let(:public_key) do + '04a01f01fa942d2233a64aebe0b36c16ebdfd1c453ac5297591f20e2bfaba869e17e15f5f7367ee6f1' \ + '6121c64cac3ecdd517920a36f5145dc2a881ae9371873ac6' + end + let(:random_secret) { 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' } + + describe '#initialize' do + context 'when no parameters are passed' do + it 'utilizes default key_size' do + expect(crypto_suite.key_size).to be(256) + end + + it 'utilizes default digest_algorithm' do + expect(crypto_suite.digest_algorithm).to eql('SHA256') + end + + it 'utilizes default curve' do + expect(crypto_suite.curve).to eql('prime256v1') + end + + it 'utilizes default cipher' do + expect(crypto_suite.cipher).to eql('aes-256-cbc') + end + end + + context 'when options are passed' do + subject(:crypto_suite) do + described_class.new( + { + key_size: 384, + digest_algorithm: 'SHA224', + cipher: 'aes-128-cbc' + } + ) + end + + it 'utilizes key_size option passed' do + expect(crypto_suite.key_size).to be(384) + end + + it 'utilizes digest_algorithm option passed' do + expect(crypto_suite.digest_algorithm).to eql('SHA224') + end + + it 'utilizes curve option passed' do + expect(crypto_suite.curve).to eql('secp384r1') + end + + it 'utilizes cipher option passed' do + expect(crypto_suite.cipher).to eql('aes-128-cbc') + end + end + end + + describe '#sign' do + it 'creates a valid signature' do + signature = crypto_suite.sign(private_key, 'this is a test') + expect(crypto_suite.verify(public_key, 'this is a test', signature)).to be(true) + end + end + + describe '#verify' do + context 'when the signature is invalid' do + it 'raises an error' do + expect { crypto_suite.verify(public_key, 'this is a test', 'invalid') }.to raise_error(OpenSSL::ASN1::ASN1Error) + end + end + + context 'when the signature matches the key and the message' do + it 'returns true' do + signature = crypto_suite.sign(private_key, 'this is a test') + expect(crypto_suite.verify(public_key, 'this is a test', signature)).to be(true) + end + end + + context 'when the signature does not match the key and the message' do + it 'return false' do + signature = crypto_suite.sign(private_key, 'this is a test') + expect(crypto_suite.verify(public_key, 'this is not a test', signature)).to be(false) + end + end + end + + describe '#generate_private_key' do + it 'generates a private_key string' do + private_key = crypto_suite.generate_private_key + expect(private_key).to be_a(String) + end + + it 'generates a private_key' do + private_key = crypto_suite.generate_private_key + pkey = crypto_suite.pkey_from_private_key(private_key) + expect(pkey.private?).to be(true) + end + + it 'matches itself' do + private_key = crypto_suite.generate_private_key + pkey = crypto_suite.pkey_from_private_key(private_key) + expect(pkey.private_key.to_s(16).downcase).to eql(private_key) + end + end + + describe '#generate_csr' do + subject(:response_value) { crypto_suite.generate_csr(private_key) } + + it 'generates a OpenSSL::X509::Request' do + expect(response_value).to be_a(OpenSSL::X509::Request) + end + + it 'returns the proper value' do + expect(response_value.public_key.private_key.to_s(16).downcase).to eql(private_key) + end + end + + describe '#generate_nonce' do + context 'when no parameters pass' do + it 'generates a random 24 byte string' do + expect(crypto_suite.generate_nonce.length).to be(24) + end + end + + context 'when passing byte length' do + it 'generates a random byte string the size passed in' do + expect(crypto_suite.generate_nonce(50).length).to be(50) + end + end + end + + describe '#hexdigest' do + it 'generates a hexdigest' do + expected_digest = 'b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9' + expect(crypto_suite.hexdigest('hello world')).to eql(expected_digest) + end + end + + describe '#digest' do + it 'generates a digest' do + digest = crypto_suite.digest('hello world') + expected_digest = ['b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9'].pack('H*') + expect(digest).to eql(expected_digest) + end + end + + describe '#encode_hex' do + it 'converts bytes to hex' do + expect(crypto_suite.encode_hex('a')).to eql('61') + end + end + + describe '#decode_hex' do + it 'converts bytes to hex' do + expect(crypto_suite.decode_hex('61')).to eql('a') + end + end + + describe '#restore_public_key' do + it 'regenerates the public key from the private key' do + expect(crypto_suite.restore_public_key(private_key)).to eql(public_key) + end + end + + describe '#address_from_public_key' do + it 'returns the address from the public key' do + expect(crypto_suite.address_from_public_key(public_key)).to eql('5de210d00aa3614d0e99ff84fe380bd34835f66e') + end + end + + # TODO: - it might be useful to test that the shared key can be properly utilized as well + describe '#build_shared_key' do + let(:random_public_key) do + '04293ed1ea547c079f06f7bc6aa8adec39fd465ba839323a262fc7abab7714ba6' \ + 'e680305dcfdf97043bfb1817a932cd7f4883d255b03ef303cf6651d765b9b3418' + end + + it 'returns a shared key' do + shared_key = crypto_suite.build_shared_key(private_key, random_public_key) + expect(shared_key).to eql('f1388005817ef6c5f0e8d4f655b000c083a67926c991eaea3da4adf1fc20ceb5') + end + end + + describe '#decrypt' do + context 'when data is nil' do + it 'returns nil' do + expect(crypto_suite.decrypt(random_secret, nil)).to be_nil + end + end + end + + describe '#encrypt/#decrypt' do + it 'properly able to encrypte and decrypt strings' do + expect(crypto_suite.decrypt(random_secret, + crypto_suite.encrypt(random_secret, 'this is a test'))).to eql('this is a test') + end + end + + describe '#pkey_from_private_key' do + subject(:return_value) { crypto_suite.pkey_from_private_key(private_key) } + + it 'converts a private key into a OpenSSL::PKey::EC' do + expect(return_value).to be_a(OpenSSL::PKey::EC) + end + + it 'private_key is expected to equal the original private key' do + expect(return_value.private_key).to eql(OpenSSL::BN.new(private_key, 16)) + end + end + + describe '#pem_from_private_key' do + let(:expected_pem) do + "-----BEGIN EC PRIVATE KEY-----\n" \ + "MHcCAQEEINYudqtKkH12NK2g2XCbTtK/x8UcQhEnt/yTwBQeRheXoAoGCCqGSM49\n" \ + "AwEHoUQDQgAEoB8B+pQtIjOmSuvgs2wW69/RxFOsUpdZHyDiv6uoaeF+FfX3Nn7m\n" \ + "8WEhxkysPs3VF5IKNvUUXcKoga6TcYc6xg==\n" \ + "-----END EC PRIVATE KEY-----\n" + end + + it 'returns a pem from private key' do + pem = crypto_suite.pem_from_private_key(private_key) + expect(pem).to eql(expected_pem) + end + end + + describe '#public_key_from_x509_certificate' do + let(:random_certificate) do + "-----BEGIN CERTIFICATE-----\n" \ + "MIIBHjCBxaADAgECAgEBMAoGCCqGSM49BAMCMBcxFTATBgNVBAoTDERvY2tlciwg\n" \ + "SW5jLjAeFw0xMzA3MjUwMTEwMjRaFw0xNTA3MjUwMTEwMjRaMBcxFTATBgNVBAoT\n" \ + "DERvY2tlciwgSW5jLjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABMolCWAO0iP7\n" \ + "tkX/KLjQ9CKeOoHYynBgfFcd1ZGoxcefmIbWjHx29eWI3xlhbjS6ssSxhrw1Kuh5\n" \ + "RrASfUCHD7SjAjAAMAoGCCqGSM49BAMCA0gAMEUCIQDRLQTSSeqjsxsb+q4exLSt\n" \ + "EM7f7/ymBzoUzbXU7wI9AgIgXCWaI++GkopGT8T2qV/3+NL0U+fYM0ZjSNSiwaK3\n" \ + "+kA=\n" \ + '-----END CERTIFICATE-----' + end + let(:random_certificate_public_key) do + '04ca2509600ed223fbb645ff28b8d0f4229e3a81d8ca70607c571dd591a8c5c79f9886d' \ + '68c7c76f5e588df19616e34bab2c4b186bc352ae87946b0127d40870fb4' + end + + it 'returns public_key from x509 certificate' do + public_key = crypto_suite.public_key_from_x509_certificate(random_certificate) + expect(public_key).to eql(random_certificate_public_key) + end + end + + describe '#pkey_from_public_key' do let(:random_public_key) do '04293ed1ea547c079f06f7bc6aa8adec39fd465ba839323a262fc7abab7714ba6' \ 'e680305dcfdf97043bfb1817a932cd7f4883d255b03ef303cf6651d765b9b3418' end it 'returns an OpenSSL::PKey::EC object' do - expect(crypto_suite.openssl_pkey_from_public_key(random_public_key)).to be_a(OpenSSL::PKey::EC) + expect(crypto_suite.pkey_from_public_key(random_public_key)).to be_a(OpenSSL::PKey::EC) end it 'generates a matching public key' do - expect(crypto_suite.openssl_pkey_from_public_key(random_public_key).public_key.to_bn.to_s(16).downcase) + expect(crypto_suite.pkey_from_public_key(random_public_key).public_key.to_bn.to_s(16).downcase) .to eql(random_public_key) end + + it 'is a public key' do + expect(crypto_suite.pkey_from_public_key(random_public_key).public?).to be(true) + end + + it 'is not a private key' do + expect(crypto_suite.pkey_from_public_key(random_public_key).private?).to be(false) + end + + it 'creates a matching public key' do + private_pkey = OpenSSL::PKey::EC.generate(crypto_suite.curve) + public_pem = private_pkey.public_to_pem + + public_key = crypto_suite.restore_public_key(private_pkey.private_key.to_s(16).downcase) + + expect(public_pem).to eql(crypto_suite.pkey_from_public_key(public_key).to_pem) + end end end diff --git a/spec/fabric/entities/chaincode_events_request_spec.rb b/spec/fabric/entities/chaincode_events_request_spec.rb index d9fd0ed..1a4dcb9 100644 --- a/spec/fabric/entities/chaincode_events_request_spec.rb +++ b/spec/fabric/entities/chaincode_events_request_spec.rb @@ -269,7 +269,7 @@ expect(contract.client).to have_received(:chaincode_events) .with(chaincode_events_request.signed_request, {}) do |&block| - expect(block).to be(nil) + expect(block).to be_nil end end end diff --git a/spec/fabric/entities/proposal_spec.rb b/spec/fabric/entities/proposal_spec.rb index 07b421d..24a41b9 100644 --- a/spec/fabric/entities/proposal_spec.rb +++ b/spec/fabric/entities/proposal_spec.rb @@ -326,7 +326,7 @@ let!(:response) { proposal.new_evaluate_request } it 'returns an EvaluateRequest' do - expect(response).to be_a(::Gateway::EvaluateRequest) + expect(response).to be_a(Gateway::EvaluateRequest) end it 'sets the channel_id' do @@ -346,7 +346,7 @@ let!(:response) { proposal.new_endorse_request } it 'returns an EndorseRequest' do - expect(response).to be_a(::Gateway::EndorseRequest) + expect(response).to be_a(Gateway::EndorseRequest) end it 'sets the channel_id' do @@ -367,7 +367,7 @@ let!(:response) { proposal.new_prepared_transaction(expected_envelope) } it 'returns a PreparedTransaction' do - expect(response).to be_a(::Gateway::PreparedTransaction) + expect(response).to be_a(Gateway::PreparedTransaction) end it 'sets the transaction_id' do diff --git a/spec/fabric/entities/proposed_transaction_spec.rb b/spec/fabric/entities/proposed_transaction_spec.rb index 60c2717..9675250 100644 --- a/spec/fabric/entities/proposed_transaction_spec.rb +++ b/spec/fabric/entities/proposed_transaction_spec.rb @@ -46,7 +46,7 @@ end it 'produces a proposed_transaction' do - expect(proposed_transaction.proposed_transaction).to be_a(::Gateway::ProposedTransaction) + expect(proposed_transaction.proposed_transaction).to be_a(Gateway::ProposedTransaction) end it 'produces a valid proposed_transaction' do diff --git a/spec/fabric/fabric_spec.rb b/spec/fabric/fabric_spec.rb index 5d7acea..fb6c0c9 100644 --- a/spec/fabric/fabric_spec.rb +++ b/spec/fabric/fabric_spec.rb @@ -2,10 +2,10 @@ RSpec.describe Fabric do it 'has a version number' do - expect(Fabric::VERSION).not_to be nil + expect(Fabric::VERSION).not_to be_nil end - it { expect(Fabric::Error.new).to be_a_kind_of(StandardError) } - it { expect(Fabric::InvalidArgument.new).to be_a_kind_of(Fabric::Error) } - it { expect(Fabric::NotYetImplemented.new).to be_a_kind_of(Fabric::Error) } + it { expect(Fabric::Error.new).to be_a(StandardError) } + it { expect(Fabric::InvalidArgument.new).to be_a(Fabric::Error) } + it { expect(Fabric::NotYetImplemented.new).to be_a(Fabric::Error) } end diff --git a/spec/fabric/network_spec.rb b/spec/fabric/network_spec.rb index 8970283..7a6f466 100644 --- a/spec/fabric/network_spec.rb +++ b/spec/fabric/network_spec.rb @@ -68,7 +68,7 @@ describe '#new_chaincode_events' do let(:contract) { build(:contract) } - let(:spied_chaincode_events_request) { instance_double('ChaincodeEventsRequest') } + let(:spied_chaincode_events_request) { instance_double(Fabric::ChaincodeEventsRequest) } before do allow(Fabric::ChaincodeEventsRequest).to receive(:new).and_return(spied_chaincode_events_request) diff --git a/spec/factories.rb b/spec/factories.rb index 29039cf..2b51a71 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -30,8 +30,8 @@ factory :gateway, class: 'Fabric::Gateway' do transient do - signer { build(:identity) } - client { build(:simple_client) } + signer { association :identity } + client { association :simple_client } end initialize_with { Fabric::Gateway.new(signer, client) } @@ -39,7 +39,7 @@ factory :network, class: 'Fabric::Network' do transient do - gateway { build(:gateway) } + gateway { association :gateway } name { 'testnet' } end @@ -48,7 +48,7 @@ factory :contract, class: 'Fabric::Contract' do transient do - network { build(:network) } + network { association :network } chaincode_name { 'testchaincode' } contract_name { 'testcontract' } end @@ -58,7 +58,7 @@ factory :proposed_transaction, class: 'Fabric::ProposedTransaction' do transient do - contract { build(:contract) } + contract { association :contract } transaction_name { 'testtransaction' } arguments { [] } transient_data { {} } diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index dde51a5..d3ae375 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -8,8 +8,8 @@ require 'simplecov' SimpleCov.start - require 'codecov' - SimpleCov.formatter = SimpleCov::Formatter::Codecov + require 'simplecov-cobertura' + SimpleCov.formatter = SimpleCov::Formatter::CoberturaFormatter end require 'fabric' diff --git a/spec/support/shared_context/accessor_shared_contexts.rb b/spec/support/shared_context/accessor_shared_contexts.rb index 8044676..90bd9b9 100644 --- a/spec/support/shared_context/accessor_shared_contexts.rb +++ b/spec/support/shared_context/accessor_shared_contexts.rb @@ -3,13 +3,13 @@ RSpec.shared_examples 'a gateway accessor' do describe '#gateway' do it 'returns the gateway' do - expect(subject.gateway).to be_a(::Fabric::Gateway) + expect(subject.gateway).to be_a(Fabric::Gateway) end end describe '#client' do it 'returns a Fabric::Client' do - expect(subject.client).to be_a(::Fabric::Client) + expect(subject.client).to be_a(Fabric::Client) end it 'returns the client from gateway' do @@ -19,7 +19,7 @@ describe '#signer' do it 'returns a Fabric::Identity' do - expect(subject.signer).to be_a(::Fabric::Identity) + expect(subject.signer).to be_a(Fabric::Identity) end it 'returns the signer from the gateway' do @@ -39,7 +39,7 @@ describe '#gateway' do it 'returns the gateway' do - expect(subject.gateway).to be_a(::Fabric::Gateway) + expect(subject.gateway).to be_a(Fabric::Gateway) end it 'returns the gateway from the network' do @@ -53,7 +53,7 @@ describe '#network' do it 'returns the network' do - expect(subject.network).to be_a(::Fabric::Network) + expect(subject.network).to be_a(Fabric::Network) end it 'returns the network from the contract' do diff --git a/spec/support/shared_context/client_mocks.rb b/spec/support/shared_context/client_mocks.rb index ca9e7cc..f217f02 100644 --- a/spec/support/shared_context/client_mocks.rb +++ b/spec/support/shared_context/client_mocks.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # rubocop:disable Metrics/BlockLength -RSpec.shared_context 'client mocks' do # rubocop:disable Rspec/ContextWording +RSpec.shared_context 'client mocks' do # rubocop:disable RSpec/ContextWording attr_reader :sent_evaluate_request, :sent_endorse_request, :sent_submit_request attr_reader :sent_proposal attr_reader :sent_chaincode_proposal_payload @@ -14,15 +14,15 @@ def sent_chaincode_input_args attr_reader :sent_call_options def decode_proposal(request) - @sent_proposal = ::Protos::Proposal.decode(request) - @sent_chaincode_proposal_payload = ::Protos::ChaincodeProposalPayload.decode(sent_proposal.payload) - @sent_chaincode_proposal_input = ::Protos::ChaincodeInvocationSpec.decode(sent_chaincode_proposal_payload.input) + @sent_proposal = Protos::Proposal.decode(request) + @sent_chaincode_proposal_payload = Protos::ChaincodeProposalPayload.decode(sent_proposal.payload) + @sent_chaincode_proposal_input = Protos::ChaincodeInvocationSpec.decode(sent_chaincode_proposal_payload.input) end def mock_evaluate_response(return_payload) - mock_protos_response = object_double(::Protos::Response.new) + mock_protos_response = object_double(Protos::Response.new) allow(mock_protos_response).to receive(:payload).and_return(return_payload) - mock_evaluate_response = object_double(::Gateway::EvaluateResponse.new) + mock_evaluate_response = object_double(Gateway::EvaluateResponse.new) allow(mock_evaluate_response).to receive(:result).and_return(mock_protos_response) mock_evaluate_response @@ -33,7 +33,7 @@ def mock_evaluate_response(return_payload) # end def mock_endorse_response(return_payload) - mock_endorse_response = object_double(::Gateway::EndorseResponse.new) + mock_endorse_response = object_double(Gateway::EndorseResponse.new) allow(mock_endorse_response).to receive(:prepared_transaction).and_return(return_payload) mock_endorse_response @@ -41,7 +41,7 @@ def mock_endorse_response(return_payload) def setup_evaluate_mock(client, return_payload) allow(client).to receive(:evaluate) do |arg, arg2| - expect(arg).to be_a(::Gateway::EvaluateRequest) + expect(arg).to be_a(Gateway::EvaluateRequest) decode_proposal(arg.proposed_transaction.proposal_bytes) @sent_evaluate_request = arg @sent_call_options = arg2 @@ -58,7 +58,7 @@ def setup_evaluate_mock(client, return_payload) def setup_endorse_mock(client, return_payload) allow(client).to receive(:endorse) do |arg, arg2| - expect(arg).to be_a(::Gateway::EndorseRequest) + expect(arg).to be_a(Gateway::EndorseRequest) @sent_endorse_request = arg @sent_call_options = arg2 end.and_return(mock_endorse_response(return_payload))