diff --git a/lib/xendit_api/api/split_rule.rb b/lib/xendit_api/api/split_rule.rb new file mode 100644 index 0000000..d9cc1b8 --- /dev/null +++ b/lib/xendit_api/api/split_rule.rb @@ -0,0 +1,16 @@ +require 'xendit_api/api/base' +require 'xendit_api/model/split_rule' + +module XenditApi + module Api + class SplitRule < XenditApi::Api::Base + PATH = '/split_rules'.freeze + + def create(params) + headers = { 'Path-Group' => PATH } + response = client.post(PATH, params, headers) + XenditApi::Model::SplitRule.new(response) + end + end + end +end diff --git a/lib/xendit_api/client.rb b/lib/xendit_api/client.rb index 52b2dba..bc6778b 100644 --- a/lib/xendit_api/client.rb +++ b/lib/xendit_api/client.rb @@ -13,6 +13,7 @@ require 'xendit_api/api/transfer' require 'xendit_api/api/transaction' require 'xendit_api/api/fee_rule' +require 'xendit_api/api/split_rule' require 'xendit_api/api/report' require 'logger' @@ -88,6 +89,10 @@ def fee_rule @fee_rule || XenditApi::Api::FeeRule.new(self) end + def split_rule + @split_rule ||= XenditApi::Api::SplitRule.new(self) + end + def report @report || XenditApi::Api::Report.new(self) end diff --git a/lib/xendit_api/model/split_rule.rb b/lib/xendit_api/model/split_rule.rb new file mode 100644 index 0000000..d7633fa --- /dev/null +++ b/lib/xendit_api/model/split_rule.rb @@ -0,0 +1,14 @@ +require 'xendit_api/model/base' + +module XenditApi + module Model + class SplitRule < XenditApi::Model::Base + attr_accessor :id, + :name, + :description, + :created, + :updated, + :routes + end + end +end diff --git a/spec/xendit_api/api/split_rule_spec.rb b/spec/xendit_api/api/split_rule_spec.rb new file mode 100644 index 0000000..0e88b3f --- /dev/null +++ b/spec/xendit_api/api/split_rule_spec.rb @@ -0,0 +1,103 @@ +require 'spec_helper' + +RSpec.describe XenditApi::Api::SplitRule do + let(:client) { XenditApi::Client.new('FILTERED_AUTH_KEY') } + let(:split_rule_api) { described_class.new(client) } + + describe 'PATH constant' do + it 'has the correct path' do + expect(described_class::PATH).to eq('/split_rules') + end + end + + describe 'inheritance' do + it 'inherits from Base' do + expect(described_class.superclass).to eq(XenditApi::Api::Base) + end + end + + describe '#create' do + let(:params) do + { + name: 'Test Split Rule', + description: 'A test split rule', + routes: [ + { + flat_amount: 1000, + currency: 'IDR', + destination: { + type: 'MERCHANT', + account_code: 'MERCHANT_001' + } + } + ] + } + end + + let(:expected_response) do + { + id: 'sr_123456789', + name: 'Test Split Rule', + description: 'A test split rule', + created: '2023-10-08T10:00:00.000Z', + updated: '2023-10-08T10:00:00.000Z', + routes: [ + { + flat_amount: 1000, + currency: 'IDR', + destination: { + type: 'MERCHANT', + account_code: 'MERCHANT_001' + } + } + ] + } + end + + it 'creates a split rule and returns a SplitRule model' do + allow(client).to receive(:post).with('/split_rules', params, { 'Path-Group' => '/split_rules' }) + .and_return(expected_response) + + result = split_rule_api.create(params) + + expect(result).to be_a(XenditApi::Model::SplitRule) + expect(result.id).to eq('sr_123456789') + expect(result.name).to eq('Test Split Rule') + expect(result.description).to eq('A test split rule') + end + + it 'sends correct headers with Path-Group' do + allow(client).to receive(:post).and_return(expected_response) + + split_rule_api.create(params) + + expect(client).to have_received(:post).with( + '/split_rules', + params, + { 'Path-Group' => '/split_rules' } + ) + end + + it 'passes through the correct parameters' do + allow(client).to receive(:post).and_return(expected_response) + + split_rule_api.create(params) + + expect(client).to have_received(:post).with('/split_rules', params, anything) + end + + context 'when client raises an error' do + it 'propagates the error' do + allow(client).to receive(:post).and_raise(StandardError, 'Network error') + + expect { split_rule_api.create(params) }.to raise_error(StandardError, 'Network error') + end + end + end + + describe '#initialize' do + it 'accepts a client parameter' do + expect(split_rule_api.client).to eq(client) + end + end +end diff --git a/spec/xendit_api/model/split_rule_spec.rb b/spec/xendit_api/model/split_rule_spec.rb new file mode 100644 index 0000000..65bd86c --- /dev/null +++ b/spec/xendit_api/model/split_rule_spec.rb @@ -0,0 +1,270 @@ +require 'spec_helper' + +RSpec.describe XenditApi::Model::SplitRule do + describe 'inheritance' do + it 'inherits from Base' do + expect(described_class.superclass).to eq(XenditApi::Model::Base) + end + end + + describe 'attributes' do + it 'has all expected attribute accessors' do + expect(described_class.new).to respond_to(:id) + expect(described_class.new).to respond_to(:id=) + expect(described_class.new).to respond_to(:name) + expect(described_class.new).to respond_to(:name=) + expect(described_class.new).to respond_to(:description) + expect(described_class.new).to respond_to(:description=) + expect(described_class.new).to respond_to(:created) + expect(described_class.new).to respond_to(:created=) + expect(described_class.new).to respond_to(:updated) + expect(described_class.new).to respond_to(:updated=) + expect(described_class.new).to respond_to(:routes) + expect(described_class.new).to respond_to(:routes=) + end + end + + describe '#initialize' do + context 'when initialized without parameters' do + it 'creates an instance with nil attributes' do + split_rule = described_class.new + + expect(split_rule.id).to be_nil + expect(split_rule.name).to be_nil + expect(split_rule.description).to be_nil + expect(split_rule.created).to be_nil + expect(split_rule.updated).to be_nil + expect(split_rule.routes).to be_nil + end + end + + context 'when initialized with a hash of attributes' do + let(:attributes) do + { + id: 'sr_123456789', + name: 'Test Split Rule', + description: 'A test split rule for payments', + created: '2023-10-08T10:00:00.000Z', + updated: '2023-10-08T10:30:00.000Z', + routes: [ + { + flat_amount: 1000, + currency: 'IDR', + destination: { + type: 'MERCHANT', + account_code: 'MERCHANT_001' + } + }, + { + flat_amount: 500, + currency: 'IDR', + destination: { + type: 'MERCHANT', + account_code: 'MERCHANT_002' + } + } + ] + } + end + + it 'sets all attributes correctly' do + split_rule = described_class.new(attributes) + + expect(split_rule.id).to eq('sr_123456789') + expect(split_rule.name).to eq('Test Split Rule') + expect(split_rule.description).to eq('A test split rule for payments') + expect(split_rule.created).to eq('2023-10-08T10:00:00.000Z') + expect(split_rule.updated).to eq('2023-10-08T10:30:00.000Z') + expect(split_rule.routes).to eq(attributes[:routes]) + end + + it 'returns expected attributes using have_attributes matcher' do + split_rule = described_class.new(attributes) + + expect(split_rule).to have_attributes( + id: 'sr_123456789', + name: 'Test Split Rule', + description: 'A test split rule for payments', + created: '2023-10-08T10:00:00.000Z', + updated: '2023-10-08T10:30:00.000Z', + routes: attributes[:routes] + ) + end + end + + context 'when initialized with string keys' do + let(:attributes) do + { + 'id' => 'sr_987654321', + 'name' => 'String Key Split Rule', + 'description' => 'Split rule with string keys', + 'created' => '2023-10-08T11:00:00.000Z', + 'updated' => '2023-10-08T11:15:00.000Z', + 'routes' => [] + } + end + + it 'sets attributes correctly with string keys' do + split_rule = described_class.new(attributes) + + expect(split_rule.id).to eq('sr_987654321') + expect(split_rule.name).to eq('String Key Split Rule') + expect(split_rule.description).to eq('Split rule with string keys') + expect(split_rule.created).to eq('2023-10-08T11:00:00.000Z') + expect(split_rule.updated).to eq('2023-10-08T11:15:00.000Z') + expect(split_rule.routes).to eq([]) + end + end + + context 'when initialized with mixed symbol and string keys' do + let(:attributes) do + { + :id => 'sr_mixed123', + 'name' => 'Mixed Keys Split Rule', + :description => 'Split rule with mixed keys', + 'created' => '2023-10-08T12:00:00.000Z', + :updated => '2023-10-08T12:05:00.000Z', + 'routes' => [{ flat_amount: 2000 }] + } + end + + it 'sets attributes correctly with mixed keys' do + split_rule = described_class.new(attributes) + + expect(split_rule.id).to eq('sr_mixed123') + expect(split_rule.name).to eq('Mixed Keys Split Rule') + expect(split_rule.description).to eq('Split rule with mixed keys') + expect(split_rule.created).to eq('2023-10-08T12:00:00.000Z') + expect(split_rule.updated).to eq('2023-10-08T12:05:00.000Z') + expect(split_rule.routes).to eq([{ flat_amount: 2000 }]) + end + end + + context 'when initialized with partial attributes' do + let(:partial_attributes) do + { + id: 'sr_partial', + name: 'Partial Split Rule' + } + end + + it 'sets only provided attributes' do + split_rule = described_class.new(partial_attributes) + + expect(split_rule.id).to eq('sr_partial') + expect(split_rule.name).to eq('Partial Split Rule') + expect(split_rule.description).to be_nil + expect(split_rule.created).to be_nil + expect(split_rule.updated).to be_nil + expect(split_rule.routes).to be_nil + end + end + + context 'when initialized with unknown attributes' do + let(:attributes_with_unknown) do + { + id: 'sr_unknown', + name: 'Split Rule with Unknown', + unknown_attribute: 'should be ignored', + another_unknown: 12_345 + } + end + + it 'ignores unknown attributes and sets only known ones' do + split_rule = described_class.new(attributes_with_unknown) + + expect(split_rule.id).to eq('sr_unknown') + expect(split_rule.name).to eq('Split Rule with Unknown') + expect(split_rule).not_to respond_to(:unknown_attribute) + expect(split_rule).not_to respond_to(:another_unknown) + end + end + + context 'when initialized with empty hash' do + it 'creates instance with nil attributes' do + split_rule = described_class.new({}) + + expect(split_rule.id).to be_nil + expect(split_rule.name).to be_nil + expect(split_rule.description).to be_nil + expect(split_rule.created).to be_nil + expect(split_rule.updated).to be_nil + expect(split_rule.routes).to be_nil + end + end + + context 'when initialized with nil' do + it 'creates instance with nil attributes' do + split_rule = described_class.new(nil) + + expect(split_rule.id).to be_nil + expect(split_rule.name).to be_nil + expect(split_rule.description).to be_nil + expect(split_rule.created).to be_nil + expect(split_rule.updated).to be_nil + expect(split_rule.routes).to be_nil + end + end + end + + describe 'attribute assignment after initialization' do + let(:split_rule) { described_class.new } + + it 'allows setting attributes individually' do + split_rule.id = 'sr_individual' + split_rule.name = 'Individual Assignment' + split_rule.description = 'Set individually' + split_rule.created = '2023-10-08T13:00:00.000Z' + split_rule.updated = '2023-10-08T13:01:00.000Z' + split_rule.routes = [{ flat_amount: 3000 }] + + expect(split_rule.id).to eq('sr_individual') + expect(split_rule.name).to eq('Individual Assignment') + expect(split_rule.description).to eq('Set individually') + expect(split_rule.created).to eq('2023-10-08T13:00:00.000Z') + expect(split_rule.updated).to eq('2023-10-08T13:01:00.000Z') + expect(split_rule.routes).to eq([{ flat_amount: 3000 }]) + end + end + + describe 'complex routes data' do + let(:complex_routes) do + [ + { + flat_amount: 1000, + currency: 'IDR', + destination: { + type: 'MERCHANT', + account_code: 'MERCHANT_001' + } + }, + { + percentage: 0.15, + currency: 'IDR', + destination: { + type: 'WALLET', + wallet_id: 'wallet_123' + } + }, + { + flat_amount: 500, + currency: 'USD', + destination: { + type: 'BANK_ACCOUNT', + bank_account_id: 'ba_456' + } + } + ] + end + + it 'handles complex nested route structures' do + split_rule = described_class.new(routes: complex_routes) + + expect(split_rule.routes).to eq(complex_routes) + expect(split_rule.routes.length).to eq(3) + expect(split_rule.routes[0][:flat_amount]).to eq(1000) + expect(split_rule.routes[1][:percentage]).to eq(0.15) + expect(split_rule.routes[2][:destination][:bank_account_id]).to eq('ba_456') + end + end +end