# frozen_string_literal: true

require "pending_transaction_repo"

class PendingTransactionRepo
	def setup_mocks
		@redis = Minitest::Mock.new
		@electrum = Minitest::Mock.new
	end
end

FakeElectrumTransaction = Struct.new(:tx_hash, :confirmations, :value) {
	def amount_for(_addr)
		value
	end
}

class TestPendingTransactionRepo < Minitest::Test
	def test_empty_map
		repo = PendingTransactionRepo.new("key")
		repo.setup_mocks
		repo.redis.expect(
			:hgetall,
			[],
			["key"]
		)
		repo.map do |_pending, _customer_id|
			flunk "Shouldn't yield when empty"
		end

		assert_mock repo.redis
		assert_mock repo.electrum
	end

	def test_map
		repo = PendingTransactionRepo.new("key")
		repo.setup_mocks
		repo.redis.expect(
			:hgetall,
			[["tx/addr", "1234"]],
			["key"]
		)
		repo.electrum.expect(
			:gettransaction,
			FakeElectrumTransaction.new("tx", 6, 0.5),
			["tx"]
		)

		v = repo.map { |pending, customer_id|
			"#{pending.value} #{customer_id}"
		}

		assert_equal ["0.5 1234"], v, "Should have returned result of block"

		assert_mock repo.redis
		assert_mock repo.electrum
	end

	def test_error_handler
		repo = PendingTransactionRepo.new("key")
		repo.setup_mocks
		repo.redis.expect(
			:hgetall,
			[["tx/addr", "1234"], ["missing/addr", "1234"]],
			["key"]
		)
		def repo.electrum
			Class.new {
				def gettransaction(txid)
					if txid == "missing"
						raise Electrum::NoTransaction, "Couldn't find"
					end

					FakeElectrumTransaction.new("tx", 6, 0.5)
				end
			}.new
		end

		repo.error_handler do |e|
			case e
			when Electrum::NoTransaction
				true
			end
		end

		v = repo.map { |pending, customer_id|
			"#{pending.value} #{customer_id}"
		}

		assert_equal ["0.5 1234"], v, "Should have returned result of block"

		assert_mock repo.redis
	end

	def test_other_errors
		repo = PendingTransactionRepo.new("key")
		repo.setup_mocks
		repo.redis.expect(
			:hgetall,
			[["tx/addr", "1234"], ["error/addr", "1234"]],
			["key"]
		)
		def repo.electrum
			Class.new {
				def gettransaction(txid)
					raise ArgumentError, "Oh no" if txid == "error"

					FakeElectrumTransaction.new("tx", 6, 0.5)
				end
			}.new
		end

		repo.error_handler do |e|
			case e
			when Electrum::NoTransaction
				true
			end
		end

		assert_raises(ArgumentError) do
			repo.map { |pending, customer_id|
				"#{pending.value} #{customer_id}"
			}
		end

		assert_mock repo.redis
	end

	def test_remove_transaction
		repo = PendingTransactionRepo.new("key")
		repo.setup_mocks

		pending = PendingTransactionRepo::PendingTransaction.new(
			FakeElectrumTransaction.new("tx", 6, 0.5),
			"addr"
		)

		repo.redis.expect(:hdel, nil, ["key", "tx/addr"])

		repo.remove_transaction(pending)

		assert_mock repo.redis
		assert_mock repo.electrum
	end
end
