M .builds/debian-stable.yml => .builds/debian-stable.yml +4 -1
@@ 13,7 13,10 @@ environment:
tasks:
- dependencies: |
cd jmp-pay
- bundle install --path=.gems
+ bundle install --without=development --path=.gems
- lint: |
cd jmp-pay
rubocop
+- test: |
+ cd jmp-pay
+ RANTLY_COUNT=100 bundle exec rake test
M Gemfile => Gemfile +9 -0
@@ 12,5 12,14 @@ gem "roda"
gem "slim"
group(:development) do
+ gem "pry-reload"
+ gem "pry-rescue"
+ gem "pry-stack_explorer"
gem "roda-bin"
end
+
+group(:test) do
+ gem "minitest"
+ gem "rantly"
+ gem "webmock"
+end
A Rakefile => Rakefile +22 -0
@@ 0,0 1,22 @@
+# frozen_string_literal: true
+
+require "rake/testtask"
+require "rubocop/rake_task"
+
+Rake::TestTask.new(:test) do |t|
+ ENV["RANTLY_VERBOSE"] = "0" unless ENV["RANTLY_VERBOSE"]
+ ENV["RANTLY_COUNT"] = "10" unless ENV["RANTLY_COUNT"]
+
+ t.libs << "test"
+ t.libs << "lib"
+ t.test_files = FileList["test/**/test_*.rb"]
+ t.warning = false
+end
+
+RuboCop::RakeTask.new(:lint)
+
+task :entr do
+ sh "sh", "-c", "git ls-files | entr -s 'rubocop && rake test'"
+end
+
+task default: :test
M config.ru => config.ru +5 -0
@@ 7,6 7,11 @@ require "pg"
require "redis"
require "roda"
+if ENV["RACK_ENV"] == "development"
+ require "pry-rescue"
+ use PryRescue::Rack
+end
+
require_relative "lib/electrum"
REDIS = Redis.new
A test/test_electrum.rb => test/test_electrum.rb +102 -0
@@ 0,0 1,102 @@
+# frozen_string_literal: true
+
+require "test_helper"
+require "electrum"
+
+class ElectrumTest < Minitest::Test
+ RPC_URI = "http://example.com"
+
+ def setup
+ @electrum = Electrum.new(
+ rpc_uri: RPC_URI,
+ rpc_username: "username",
+ rpc_password: "password"
+ )
+ end
+
+ def stub_rpc(method, params)
+ stub_request(:post, RPC_URI).with(
+ headers: {"Content-Type" => "application/json"},
+ basic_auth: ["username", "password"],
+ body: hash_including(
+ method: method,
+ params: params
+ )
+ )
+ end
+
+ property(:getaddresshistory) { string(:alnum) }
+ def getaddresshistory(address)
+ req =
+ stub_rpc("getaddresshistory", address: address)
+ .to_return(body: {result: "result"}.to_json)
+ assert_equal "result", @electrum.getaddresshistory(address)
+ assert_requested(req)
+ end
+
+ property(:get_tx_status) { string(:alnum) }
+ def get_tx_status(tx_hash)
+ req =
+ stub_rpc("get_tx_status", txid: tx_hash)
+ .to_return(body: {result: "result"}.to_json)
+ assert_equal "result", @electrum.get_tx_status(tx_hash)
+ assert_requested(req)
+ end
+
+ property(:gettransaction) { [string(:alnum), string(:xdigit)] }
+ def gettransaction(tx_hash, dummy_tx)
+ req1 =
+ stub_rpc("gettransaction", txid: tx_hash)
+ .to_return(body: {result: dummy_tx}.to_json)
+ req2 =
+ stub_rpc("deserialize", [dummy_tx])
+ .to_return(body: {result: {outputs: []}}.to_json)
+ assert_kind_of Electrum::Transaction, @electrum.gettransaction(tx_hash)
+ assert_requested(req1)
+ assert_requested(req2)
+ end
+
+ class TransactionTest < Minitest::Test
+ def transaction(outputs=[])
+ electrum_mock = Minitest::Mock.new("Electrum")
+ [
+ electrum_mock,
+ Electrum::Transaction.new(
+ electrum_mock,
+ "txhash",
+ "outputs" => outputs
+ )
+ ]
+ end
+
+ def test_confirmations
+ electrum_mock, tx = transaction
+ electrum_mock.expect(
+ :get_tx_status,
+ {"confirmations" => 1234},
+ ["txhash"]
+ )
+ assert_equal 1234, tx.confirmations
+ end
+
+ def test_amount_for_empty
+ _, tx = transaction
+ assert_equal 0, tx.amount_for
+ end
+
+ def test_amount_for_address_not_present
+ _, tx = transaction([{"address" => "address", "value_sats" => 1}])
+ assert_equal 0, tx.amount_for("other_address")
+ end
+
+ def test_amount_for_address_present
+ _, tx = transaction([{"address" => "address", "value_sats" => 1}])
+ assert_equal 0.00000001, tx.amount_for("address")
+ end
+
+ def test_amount_for_one_of_address_present
+ _, tx = transaction([{"address" => "address", "value_sats" => 1}])
+ assert_equal 0.00000001, tx.amount_for("boop", "address", "lol")
+ end
+ end
+end
A test/test_helper.rb => test/test_helper.rb +22 -0
@@ 0,0 1,22 @@
+# frozen_string_literal: true
+
+require "minitest/autorun"
+require "rantly/minitest_extensions"
+require "webmock/minitest"
+begin
+ require "pry-rescue/minitest"
+ require "pry-reload"
+rescue LoadError
+ # Just helpers for dev, no big deal if missing
+ nil
+end
+
+module Minitest
+ class Test
+ def self.property(m, &block)
+ define_method("test_#{m}") do
+ property_of(&block).check { |args| send(m, *args) }
+ end
+ end
+ end
+end