From 3a9d9cf6c7940bfca2fadf93e52c76fb499d8d2f Mon Sep 17 00:00:00 2001 From: ZippyDev Date: Sun, 26 Jul 2020 15:31:22 -0600 Subject: [PATCH] client cleanup. add user endpoint. initial specs. --- Gemfile.lock | 32 +++++--- README.md | 55 ++++++++++++- btcpay.gemspec | 5 +- lib/btcpay.rb | 11 ++- lib/btcpay/client/api/base.rb | 31 ++++++++ lib/btcpay/client/api/users.rb | 30 +++++++ lib/btcpay/client/base.rb | 129 ++++++++++++++++++++++++++++++ lib/btcpay/client/config.rb | 54 +++++++++++++ lib/btcpay/client/result.rb | 76 ++++++++++++++++++ spec/btcpay_spec.rb | 7 ++ spec/client/base_spec.rb | 140 +++++++++++++++++++++++++++++++++ spec/client/config_spec.rb | 56 +++++++++++++ spec/client/result_spec.rb | 90 +++++++++++++++++++++ spec/factories/api/users.rb | 9 +++ spec/factories/config.rb | 21 +++++ spec/spec_helper.rb | 3 + spec/support/factory_bot.rb | 11 +++ 17 files changed, 745 insertions(+), 15 deletions(-) create mode 100644 lib/btcpay/client/api/base.rb create mode 100644 lib/btcpay/client/api/users.rb create mode 100644 lib/btcpay/client/base.rb create mode 100644 lib/btcpay/client/config.rb create mode 100644 lib/btcpay/client/result.rb create mode 100644 spec/client/base_spec.rb create mode 100644 spec/client/config_spec.rb create mode 100644 spec/client/result_spec.rb create mode 100644 spec/factories/api/users.rb create mode 100644 spec/factories/config.rb create mode 100644 spec/support/factory_bot.rb diff --git a/Gemfile.lock b/Gemfile.lock index 99fb92e..a3ee642 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,7 +2,9 @@ PATH remote: . specs: btcpay (0.1.0) - flexirest (~> 1.9, < 2.0) + activesupport (> 5) + multi_json (~> 1.15) + rest-client (~> 2.1) GEM remote: https://rubygems.org/ @@ -22,16 +24,15 @@ GEM safe_yaml (~> 1.0.0) diff-lcs (1.4.4) docile (1.3.2) - faraday (1.0.1) - multipart-post (>= 1.2, < 3) - flexirest (1.9.16) - activesupport - crack - faraday - mime-types - multi_json + domain_name (0.5.20190701) + unf (>= 0.0.5, < 1.0.0) + factory_bot (6.1.0) + activesupport (>= 5.0.0) hashdiff (1.0.1) - i18n (1.8.3) + http-accept (1.7.0) + http-cookie (1.0.3) + domain_name (~> 0.5) + i18n (1.8.5) concurrent-ruby (~> 1.0) method_source (1.0.0) mime-types (3.3.1) @@ -39,7 +40,7 @@ GEM mime-types-data (3.2020.0512) minitest (5.14.1) multi_json (1.15.0) - multipart-post (2.1.1) + netrc (0.11.0) parallel (1.19.2) parser (2.7.1.4) ast (~> 2.4.1) @@ -50,6 +51,11 @@ GEM rainbow (3.0.0) rake (12.3.3) regexp_parser (1.7.1) + rest-client (2.1.0) + http-accept (>= 1.7.0, < 2.0) + http-cookie (>= 1.0.2, < 2.0) + mime-types (>= 1.16, < 4.0) + netrc (~> 0.8) rexml (3.2.4) rspec (3.9.0) rspec-core (~> 3.9.0) @@ -84,6 +90,9 @@ GEM thread_safe (0.3.6) tzinfo (1.2.7) thread_safe (~> 0.1) + unf (0.1.4) + unf_ext + unf_ext (0.0.7.7) unicode-display_width (1.7.0) vcr (6.0.0) webmock (3.8.3) @@ -98,6 +107,7 @@ PLATFORMS DEPENDENCIES btcpay! bundler (~> 2.0) + factory_bot (~> 6.1) pry (> 0) rake (~> 12.0) rspec (~> 3.0) diff --git a/README.md b/README.md index 35f4d27..3726253 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,58 @@ Or install it yourself as: ## Usage -TODO: Write usage instructions here +#### Required Params + +##### Auth Token + +At least one of the following auth tokens are required. Auth tokens can be created via the following: + +1. `auth_token` + - Scoped Api Tokens can be created via `/Manage/APIKeys` + - `BTCPAY_AUTH_TOKEN` environment variable can also be used +1. `basic_auth_token` + - Legacy Api Key can be created per store via `/stores/{store-id}/Tokens` + - `BTCPAY_BASIC_AUTH_TOKEN` environment variable can also be used + +##### Base Url + +A `base_url` is required to interact with the server. + - `BTCPAY_BASE_URL` environment variable can also be used + +```ruby +client = BtcPay.new(auth_token: 'foobar', base_url: 'http://localhost:49392') +``` + +### Response + +A response consists of the following accessible attributes: + +1. `#body` - rubified response body +1. `#code` - response status code +1. `#headers` - response headers +1. `#raw` - unaltered response body +1. `#status` - `:success`/`:failure` + +### Request object types + +All endpoints are accessed via namespaced Api resource. Example: `client.users.create({ email: 'foo@bar.com', password: 'password', isAdministrator: false })` + +#### Users: + +1. `#me(**opts)` + - alias: `get`, `show` +1. `#create(payload, **opts)` + + +### Environment Variables + +`btcpay` can be initialized with either arguments or ENV: + +| Variable | Description | Default | +| --------------------------|:------------------------|:--------:| +| `BTCPAY_AUTH_TOKEN` | BtcPay Auth Token | - | +| `BTCPAY_BASIC_AUTH_TOKEN` | BtcPay Basic Auth Token | - | +| `BTCPAY_BASE_URL` | BtcPay Base Url | - | ### BtcPay Docker Compose @@ -42,7 +93,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To ## Contributing -Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/btcpay. +Bug reports and pull requests are welcome on Gitlab at https://gitlab.com/snogrammer/btcpay. ## License diff --git a/btcpay.gemspec b/btcpay.gemspec index e23f4a4..7d21855 100644 --- a/btcpay.gemspec +++ b/btcpay.gemspec @@ -28,9 +28,12 @@ Gem::Specification.new do |spec| spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.require_paths = ['lib'] - spec.add_dependency 'flexirest', '~> 1.9', '< 2.0' + spec.add_dependency 'activesupport', '> 5' + spec.add_dependency 'multi_json', '~> 1.15' + spec.add_dependency 'rest-client', '~> 2.1' spec.add_development_dependency 'bundler', '~> 2.0' + spec.add_development_dependency 'factory_bot', '~> 6.1' spec.add_development_dependency 'pry', '> 0' spec.add_development_dependency 'rubocop', '> 0' spec.add_development_dependency 'simplecov', '> 0' diff --git a/lib/btcpay.rb b/lib/btcpay.rb index 11bdfab..da32c81 100644 --- a/lib/btcpay.rb +++ b/lib/btcpay.rb @@ -2,7 +2,16 @@ require 'btcpay/version' +require 'btcpay/client/config' +require 'btcpay/client/base' + module BtcPay class Error < StandardError; end - # Your code goes here... + + module_function + + def new(**args) + config = BtcPay::Client::Config.new(**args) + BtcPay::Client::Base.new(config: config, **args) + end end diff --git a/lib/btcpay/client/api/base.rb b/lib/btcpay/client/api/base.rb new file mode 100644 index 0000000..62f449d --- /dev/null +++ b/lib/btcpay/client/api/base.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module BtcPay + module Client + module Api + class Base + def initialize(client:, logger:) + @client = client + @logger = logger + end + + protected + + def base_path + raise NotImplementedError.new + end + + def path(*args) + request_path = args.prepend(base_path.presence).compact.join('/') + request_path[0].eql?('/') ? request_path : '/' + request_path + end + + private + + attr_reader :client, :logger + end + end + end +end + +require_relative './users' diff --git a/lib/btcpay/client/api/users.rb b/lib/btcpay/client/api/users.rb new file mode 100644 index 0000000..39d48df --- /dev/null +++ b/lib/btcpay/client/api/users.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +module BtcPay + module Client + module Api + class Users < Base + PATH = '/users' + + # @see https://docs.btcpayserver.org/API/Greenfield/v1/#operation/Users_GetCurrentUser + def me(**opts) + client.get(path('me'), options: opts) + end + + alias get me + alias show me + + # @see https://docs.btcpayserver.org/API/Greenfield/v1/#tag/Users/paths/~1api~1v1~1users/post + def create(payload, **opts) + client.post(path, payload: payload, options: opts) + end + + protected + + def base_path + PATH + end + end + end + end +end diff --git a/lib/btcpay/client/base.rb b/lib/btcpay/client/base.rb new file mode 100644 index 0000000..7498363 --- /dev/null +++ b/lib/btcpay/client/base.rb @@ -0,0 +1,129 @@ +# frozen_string_literal: true + +require 'active_support' +require 'active_support/core_ext/object' +require 'rest_client' + +require_relative './result' +require_relative './api/base' + +module BtcPay + module Client + class Error < RuntimeError + def initialize(message) + super(message) + end + end + + class Base + API_PATH = '/api/v1' + DEFAULT_TIMEOUT = 10 + + # @param config [BtcPay::Client::Config] + # @param logger [Logger] + # @param timeout [Integer] Defaults to DEFAULT_TIMEOUT + def initialize(config:, logger: Logger.new(STDOUT), timeout: DEFAULT_TIMEOUT, **_kwargs) + @config = config + @logger = logger + @timeout = timeout + end + + # GET request + # + # @param uri [String] + # @param options [Hash] + # @param headers [Hash] + # @return [Result] + def get(uri, options: {}, headers: {}) + request(uri, method: :get, options: options, headers: headers) + end + + # POST request + # + # @param uri [String] + # @param payload [Hash] + # @param options [Hash] + # @param headers [Hash] + # @return [Result] + def post(uri, payload:, options: {}, headers: {}) + data = payload.is_a?(Hash) ? payload.to_json : payload + request(uri, method: :post, payload: data, options: options, headers: headers) + end + + def users + @users ||= Api::Users.new(client: self, logger: logger) + end + + private + + attr_reader :config, :logger + + # @return [Hash] + def default_headers + { + 'Content-Type' => 'application/json', + 'Accept' => 'application/json', + 'User-Agent' => config.user_agent, + 'Accept-Encoding' => 'deflate, gzip', + 'Authorization' => config.authorization + } + end + + # @yield [Result] + def request(uri, method:, payload: nil, options: {}, headers: {}) + options ||= {} + headers ||= {} + + url = URI(config.base_url) + url.path = API_PATH + uri + url.query = CGI.unescape(options.to_query).presence + + params = { + method: method, + url: url.to_s, + payload: payload, + headers: default_headers.merge(headers), + open_timeout: open_timeout, + timeout: timeout + }.compact + + response = RestClient::Request.execute(params) + + logger.debug(message: 'GET Request', url: url, options: options, status: response.code) + result = success?(response.code) ? Result.success(response) : Result.failed(response) + + logger.warn(error: 'Request Error', code: result.code, body: result.raw) if result.failure? + result + rescue ::RestClient::GatewayTimeout + raise Error.new('Gateway timeout') + rescue ::RestClient::RequestTimeout + raise Error.new('Request timeout') + rescue ::RestClient::Exception => e + handle_error(e) + end + + # Handle errors + # @param error [Error] + # @return [Result] + def handle_error(error) + logger.error(error: 'Request Exception', code: error.response.code, message: error.message) + + Result.failed(response) + end + + # @return [Integer] + def open_timeout + @open_timeout || DEFAULT_TIMEOUT + end + + # @return [Integer] + def timeout + @timeout || DEFAULT_TIMEOUT + end + + def success?(response_code) + response_code.in?(200..299) + end + end + end +end diff --git a/lib/btcpay/client/config.rb b/lib/btcpay/client/config.rb new file mode 100644 index 0000000..707a431 --- /dev/null +++ b/lib/btcpay/client/config.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +module BtcPay + module Client + class Config + AUTH_TOKEN_TYPE = 'token' + BASIC_TOKEN_TYPE = 'Basic' + + attr_reader :authorization, :auth_token, :basic_auth_token, :base_url, :user_agent + + def initialize(**kwargs) + @base_url = load_url(kwargs[:base_url]) + @user_agent = kwargs[:user_agent] || "btcpay_ruby/#{BtcPay::VERSION}" + + load_auth_token(kwargs) + set_authorization + end + + def to_h + { + auth_token: auth_token, + basic_auth_token: basic_auth_token, + base_url: base_url, + user_agent: user_agent + }.compact + end + + private + + def load_url(url) + base_url = url || ENV['BTCPAY_BASE_URL'].to_s + uri = URI(base_url) + return uri.to_s if uri.scheme && uri.host + + raise ArgumentError.new('invalid base_url') + end + + def load_auth_token(kwargs) + @auth_token = kwargs[:auth_token] || ENV['BTCPAY_AUTH_TOKEN'] + @basic_auth_token = kwargs[:basic_auth_token] || ENV['BTCPAY_BASIC_AUTH_TOKEN'] + + raise ArgumentError.new('auth_token or basic_auth_token required') unless @auth_token || @basic_auth_token + end + + def set_authorization + @authorization = if @auth_token + "#{AUTH_TOKEN_TYPE} #{@auth_token}" + elsif @basic_auth_token + "#{BASIC_TOKEN_TYPE} #{@basic_auth_token}" + end + end + end + end +end diff --git a/lib/btcpay/client/result.rb b/lib/btcpay/client/result.rb new file mode 100644 index 0000000..7fad6ca --- /dev/null +++ b/lib/btcpay/client/result.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true + +require 'active_support/core_ext/hash/indifferent_access' +require 'multi_json' + +module BtcPay + module Client + ## + # Status object to capture result from an HTTP request + # + # Gives callers context of the result and allows them to + # implement successful strategies to handle success/failure + class Result + def self.success(response) + new(:success, response) + end + + def self.failed(response) + new(:failed, response) + end + + attr_reader :body, :code, :headers, :raw, :status + + def initialize(status, response) + @raw = raw_parse(response.body) + + @body = rubify_body + @code = response.code + @headers = response.headers # e.g. "Content-Type" will become :content_type. + @status = status + end + + def success? + status == :success + end + + def failure? + !success? + end + + def to_h + { + status: status, + headers: headers, + code: code, + body: body + } + end + + alias to_hash to_h + + private + + def method_missing(method, *args, &blk) + to_h.send(method, *args, &blk) || super + end + + def respond_to_missing?(method, include_private = false) + to_h.respond_to?(method) || super + end + + # @param body [JSON] Raw JSON body + def raw_parse(body) + MultiJson.load(body).with_indifferent_access + rescue StandardError => e + raise ResponseBodyParseError.new(error: 'JSON parse error', message: e.message, body: body) + end + + def rubify_body + raw.deep_transform_keys { |key| key.to_s.underscore }.with_indifferent_access + end + + class ResponseBodyParseError < StandardError; end + end + end +end diff --git a/spec/btcpay_spec.rb b/spec/btcpay_spec.rb index 9bc06d6..c6e9b71 100644 --- a/spec/btcpay_spec.rb +++ b/spec/btcpay_spec.rb @@ -4,4 +4,11 @@ RSpec.describe BtcPay do it 'has a version number' do expect(BtcPay::VERSION).to eq('0.1.0') end + + describe '.new' do + it do + expect(described_class.new(auth_token: '123', base_url: 'http://localhost')) + .to be_a(BtcPay::Client::Base) + end + end end diff --git a/spec/client/base_spec.rb b/spec/client/base_spec.rb new file mode 100644 index 0000000..b657df9 --- /dev/null +++ b/spec/client/base_spec.rb @@ -0,0 +1,140 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe BtcPay::Client::Base do + let(:config) { create(:config) } + let(:subject) { described_class.new(config: config) } + + describe '.configuration' do + it 'returns host' do + expect(subject.host).to eq(host) + end + + it 'returns api_key' do + expect(subject.api_key).to eq(api_key) + end + + it 'returns sandbox' do + expect(subject.sandbox).to eq(false) + end + + # skipped due to logger being mocked + xit 'returns default logger' do + expect(subject.logger).to be_a(Logger) + end + + it 'raises error when api_key missing' do + expect { described_class.new }.to raise_error(KeyError, 'key not found: "CMC_PRO_API_KEY"') + end + + context 'host' do + it 'defaults to production environment' do + subject = described_class.new(api_key: api_key) + expect(subject.sandbox).to eq(false) + expect(subject.host).to eq('https://pro-api.coinmarketcap.com/v1') + end + + it 'defaults to production environment' do + subject = described_class.new(api_key: api_key, sandbox: true) + expect(subject.sandbox).to eq(true) + expect(subject.host).to eq('https://sandbox.coinmarketcap.com/v1') + end + end + end + + describe 'services' do + it '.cryptocurrency' do + expect(subject.cryptocurrency).to be_a(CoinMarketPro::Endpoint::Cryptocurrency) + end + + it '.exchange' do + expect(subject.exchange).to be_a(CoinMarketPro::Endpoint::Exchange) + end + + it '.global_metrics' do + expect(subject.global_metrics).to be_a(CoinMarketPro::Endpoint::GlobalMetrics) + end + + it '.tools' do + expect(subject.tools).to be_a(CoinMarketPro::Endpoint::Tools) + end + end + + describe '.default_headers' do + it do + expect(subject.default_headers).to eq( + 'Content-Type' => 'application/json', + 'Accept' => 'application/json', + 'User-Agent' => "coin_market_pro/#{CoinMarketPro::VERSION}", + 'Accept-Encoding' => 'deflate, gzip', + 'X-CMC_PRO_API_KEY' => api_key + ) + end + end + + describe '.open_timeout' do + it { expect(subject.open_timeout).to eq(10) } + end + + describe '.timeout' do + it { expect(subject.timeout).to eq(10) } + end + + describe '.handle_error' do + let(:response) { double('response') } + let(:error) { double('error') } + let(:code) { 400 } + let(:body) do + { + data: { id: 1 }, + status: { code: code } + } + end + + it 'returns failure status response' do + allow(response).to receive(:headers) + allow(response).to receive(:body).and_return(body.to_json) + expect(response).to receive(:code).at_least(:once).and_return(code) + + expect(error).to receive(:message).and_return('message') + expect(error).to receive(:response).and_return(response) + + status = subject.handle_error(error) + + expect(status.failure?).to eq(true) + end + end + + describe '.get' do + let(:response) { double('response') } + let(:body) do + { + data: { id: 1 }, + status: { code: 200 } + } + end + + before do + allow(RestClient::Request).to receive(:execute).and_return(response) + allow(response).to receive(:body).and_return(body.to_json) + allow(response).to receive(:headers) + end + + it 'returns successful status response' do + expect(response).to receive(:code).at_least(:once).and_return(200) + + status = subject.get(path) + expect(status.success?).to eq(true) + end + + context 'error' do + it 'returns failure status response' do + expect(response).to receive(:code).at_least(:once).and_return(400) + + status = subject.get(path) + expect(status.failure?).to eq(true) + end + end + end +end diff --git a/spec/client/config_spec.rb b/spec/client/config_spec.rb new file mode 100644 index 0000000..31f2ee2 --- /dev/null +++ b/spec/client/config_spec.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe BtcPay::Client::Config do + let(:config) { build(:config) } + + describe '#initialize' do + context '#base_url' do + it do + subject = described_class.new(**config.to_h) + expect(subject.base_url).to eq(config.base_url) + end + + it { expect { described_class.new(auth_token: 123) }.to raise_error(ArgumentError, 'invalid base_url') } + end + + context '#authorization' do + it do + expect { described_class.new(base_url: config.base_url) } + .to raise_error(ArgumentError, 'auth_token or basic_auth_token required') + end + + context 'auth_token' do + it do + subject = described_class.new(**config.to_h) + expect(subject.auth_token).to eq(config.auth_token) + expect(subject.authorization).to eq("token #{config.auth_token}") + end + end + + context 'basic_auth_token' do + let(:config) { build(:config, :basic_auth_token) } + + it do + subject = described_class.new(**config.to_h) + expect(subject.basic_auth_token).to eq(config.basic_auth_token) + expect(subject.authorization).to eq("Basic #{config.basic_auth_token}") + end + end + end + end + + describe '#to_h' do + subject { described_class.new(**config.to_h) } + + it 'returns hash' do + expect(subject.to_h).to eq({ + auth_token: config.auth_token, + basic_auth_token: config.basic_auth_token, + base_url: config.base_url, + user_agent: config.user_agent + }.compact) + end + end +end diff --git a/spec/client/result_spec.rb b/spec/client/result_spec.rb new file mode 100644 index 0000000..80a15d3 --- /dev/null +++ b/spec/client/result_spec.rb @@ -0,0 +1,90 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe CoinMarketPro::Client::Result do # rubocop:disable Metrics/BlockLength + let(:response) { double('response') } + let(:code) { double('code') } + let(:body) do + { + data: { + test: 'test' + }, + status: { + code: 0 + } + } + end + let(:headers) do + { + content_type: 'application/json' + } + end + + before(:each) do + allow(response).to receive(:body).and_return(body.to_json) + allow(response).to receive(:code).and_return(code) + allow(response).to receive(:headers).and_return(headers) + end + + describe '.success' do + it 'captures response and sets status to success' do + result = described_class.success(response) + + expect(result).to be_a(CoinMarketPro::Client::Result) + expect(result.success?).to eq(true) + expect(result.failure?).to eq(false) + expect(result.body).to eq(body[:data]) + expect(result.code).to eq(code) + end + end + + describe '.failed' do + it 'captures response and sets status to failed' do + result = described_class.failed(response) + + expect(result).to be_a(CoinMarketPro::Client::Result) + expect(result.success?).to eq(false) + expect(result.failure?).to eq(true) + expect(result.body).to eq(body[:data]) + expect(result.code).to eq(code) + end + end + + describe '#success?' do + let(:result) { described_class.success(response) } + + it 'returns true if success' do + expect(result.success?).to eq(true) + expect(result.failure?).to eq(false) + end + end + + describe '#failure?' do + let(:result) { described_class.failed(response) } + + it 'returns true if failed' do + expect(result.failure?).to eq(true) + expect(result.success?).to eq(false) + end + end + + describe '#to_h' do + it 'returns a hash of values' do + result = described_class.success(response) + expect(result.to_h).to eq(code: code, headers: headers, body: body[:data], status: body[:status].merge(result: :success)) + end + end + + describe '#to_hash' do + it 'returns a hash of values' do + result = described_class.success(response) + expect(result.to_hash).to eq(code: code, headers: headers, body: body[:data], status: body[:status].merge(result: :success)) + end + + it 'can be called implicitly' do + result = described_class.success(response) + expect(result[:code]).to eq code + end + end +end diff --git a/spec/factories/api/users.rb b/spec/factories/api/users.rb new file mode 100644 index 0000000..9edbd55 --- /dev/null +++ b/spec/factories/api/users.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :users_payload, class: Hash do + email { 'foo@bar.com' } + password { 'password' } + isAdministrator { false } + end +end diff --git a/spec/factories/config.rb b/spec/factories/config.rb new file mode 100644 index 0000000..4019562 --- /dev/null +++ b/spec/factories/config.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :config, class: BtcPay::Client::Config do + base_url { 'http://localhost:49392' } + + auth_token # default trait + + initialize_with { new(**attributes) } + + trait :auth_token do + auth_token { '9133b8ef3ae9a4b7f2d9a6efef1d5cf738067c68' } + basic_auth_token { nil } + end + + trait :basic_auth_token do + auth_token { nil } + basic_auth_token { 'TODO' } + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 11c9c8a..cb7952f 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -7,6 +7,9 @@ require 'bundler/setup' require 'btcpay' require 'webmock/rspec' +require 'pry' + +Dir[File.join(__dir__, 'support', '**', '*.rb')].sort.each { |f| require f } RSpec.configure do |config| # Enable flags like --only-failures and --next-failure diff --git a/spec/support/factory_bot.rb b/spec/support/factory_bot.rb new file mode 100644 index 0000000..1ef5853 --- /dev/null +++ b/spec/support/factory_bot.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require 'factory_bot' + +RSpec.configure do |config| + config.include FactoryBot::Syntax::Methods + + config.before(:suite) do + FactoryBot.find_definitions + end +end