digital_femsa 1.0.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -0
  3. data/Gemfile.lock +104 -0
  4. data/Makefile +14 -0
  5. data/README.md +28 -33
  6. data/VERSION +1 -1
  7. data/config-ruby.json +1 -1
  8. data/digital_femsa.gemspec +1 -1
  9. data/docs/ApiKeyCreateResponse.md +1 -1
  10. data/docs/ApiKeyRequest.md +2 -2
  11. data/docs/ApiKeyResponseOnDelete.md +2 -2
  12. data/docs/ApiKeysApi.md +10 -10
  13. data/docs/BalanceResponse.md +8 -8
  14. data/docs/BalancesApi.md +1 -1
  15. data/docs/ChargeOrderResponse.md +1 -1
  16. data/docs/ChargeOrderResponseChannel.md +24 -0
  17. data/docs/ChargeRequest.md +1 -1
  18. data/docs/ChargeRequestPaymentMethod.md +3 -3
  19. data/docs/ChargeResponse.md +9 -7
  20. data/docs/ChargeResponseChannel.md +3 -3
  21. data/docs/ChargeResponseRefundsData.md +5 -1
  22. data/docs/ChargesApi.md +16 -12
  23. data/docs/ChargesDataResponse.md +9 -7
  24. data/docs/Checkout.md +17 -15
  25. data/docs/CheckoutOrderTemplate.md +3 -3
  26. data/docs/CheckoutResponse.md +1 -1
  27. data/docs/CreateCustomerFiscalEntitiesResponse.md +2 -2
  28. data/docs/Customer.md +13 -19
  29. data/docs/CustomerAddress.md +2 -2
  30. data/docs/CustomerFiscalEntitiesDataResponse.md +2 -2
  31. data/docs/CustomerFiscalEntitiesRequest.md +3 -3
  32. data/docs/CustomerInfo.md +4 -4
  33. data/docs/CustomerPaymentMethodRequest.md +1 -1
  34. data/docs/CustomerResponse.md +17 -21
  35. data/docs/CustomerShippingContactsResponse.md +5 -5
  36. data/docs/CustomersApi.md +15 -15
  37. data/docs/CustomersResponse.md +1 -1
  38. data/docs/DeleteApiKeysResponse.md +1 -1
  39. data/docs/DiscountsApi.md +9 -9
  40. data/docs/EventsApi.md +2 -2
  41. data/docs/EventsResendResponse.md +1 -1
  42. data/docs/GetChargesResponse.md +4 -4
  43. data/docs/GetTransactionsResponse.md +1 -1
  44. data/docs/LogsApi.md +1 -1
  45. data/docs/OrderFiscalEntityRequest.md +4 -4
  46. data/docs/OrderRefundRequest.md +7 -5
  47. data/docs/OrderRequest.md +17 -19
  48. data/docs/OrderResponse.md +37 -35
  49. data/docs/OrderResponseChannel.md +24 -0
  50. data/docs/OrderResponseShippingContact.md +4 -4
  51. data/docs/OrderUpdateFiscalEntityRequest.md +1 -1
  52. data/docs/OrderUpdateRequest.md +19 -15
  53. data/docs/OrdersApi.md +16 -16
  54. data/docs/PaymentLinkApi.md +12 -82
  55. data/docs/PaymentMethodCash.md +4 -4
  56. data/docs/PaymentMethodCashRequest.md +2 -2
  57. data/docs/PaymentMethodsApi.md +5 -5
  58. data/docs/Product.md +10 -12
  59. data/docs/ProductDataResponse.md +9 -11
  60. data/docs/ProductOrderResponse.md +9 -11
  61. data/docs/ProductsApi.md +7 -7
  62. data/docs/ShippingContactsApi.md +9 -9
  63. data/docs/ShippingOrderResponse.md +5 -3
  64. data/docs/ShippingRequest.md +2 -0
  65. data/docs/ShippingsApi.md +7 -7
  66. data/docs/TaxesApi.md +7 -7
  67. data/docs/TransactionResponse.md +20 -14
  68. data/docs/TransactionsApi.md +6 -6
  69. data/docs/TransfersApi.md +10 -10
  70. data/docs/UpdateCustomer.md +17 -21
  71. data/docs/UpdateCustomerFiscalEntitiesResponse.md +2 -2
  72. data/docs/UpdateOrderTaxRequest.md +2 -2
  73. data/docs/UpdateProduct.md +7 -9
  74. data/docs/WebhookKeysApi.md +18 -14
  75. data/docs/WebhooksApi.md +28 -22
  76. data/lib/digital_femsa/api/api_keys_api.rb +8 -8
  77. data/lib/digital_femsa/api/balances_api.rb +2 -2
  78. data/lib/digital_femsa/api/charges_api.rb +13 -9
  79. data/lib/digital_femsa/api/customers_api.rb +12 -12
  80. data/lib/digital_femsa/api/discounts_api.rb +12 -12
  81. data/lib/digital_femsa/api/events_api.rb +4 -4
  82. data/lib/digital_femsa/api/logs_api.rb +2 -2
  83. data/lib/digital_femsa/api/orders_api.rb +20 -20
  84. data/lib/digital_femsa/api/payment_link_api.rb +10 -86
  85. data/lib/digital_femsa/api/payment_methods_api.rb +8 -8
  86. data/lib/digital_femsa/api/products_api.rb +10 -10
  87. data/lib/digital_femsa/api/shipping_contacts_api.rb +8 -8
  88. data/lib/digital_femsa/api/shippings_api.rb +10 -10
  89. data/lib/digital_femsa/api/taxes_api.rb +10 -10
  90. data/lib/digital_femsa/api/transactions_api.rb +6 -6
  91. data/lib/digital_femsa/api/transfers_api.rb +8 -8
  92. data/lib/digital_femsa/api/webhook_keys_api.rb +14 -10
  93. data/lib/digital_femsa/api/webhooks_api.rb +23 -16
  94. data/lib/digital_femsa/api_client.rb +9 -5
  95. data/lib/digital_femsa/models/api_key_create_response.rb +1 -1
  96. data/lib/digital_femsa/models/api_key_request.rb +36 -1
  97. data/lib/digital_femsa/models/balance_response.rb +9 -9
  98. data/lib/digital_femsa/models/charge_order_response.rb +1 -1
  99. data/lib/digital_femsa/models/{order_next_action_response.rb → charge_order_response_channel.rb} +34 -18
  100. data/lib/digital_femsa/models/charge_request_payment_method.rb +3 -2
  101. data/lib/digital_femsa/models/charge_response.rb +15 -4
  102. data/lib/digital_femsa/models/charge_response_refunds_data.rb +23 -4
  103. data/lib/digital_femsa/models/charge_update_request.rb +1 -1
  104. data/lib/digital_femsa/models/charges_data_response.rb +15 -4
  105. data/lib/digital_femsa/models/checkout.rb +97 -81
  106. data/lib/digital_femsa/models/checkout_order_template.rb +4 -4
  107. data/lib/digital_femsa/models/checkout_order_template_customer_info.rb +1 -1
  108. data/lib/digital_femsa/models/checkout_request.rb +1 -1
  109. data/lib/digital_femsa/models/checkout_response.rb +1 -1
  110. data/lib/digital_femsa/models/create_customer_fiscal_entities_response.rb +10 -10
  111. data/lib/digital_femsa/models/customer.rb +57 -95
  112. data/lib/digital_femsa/models/customer_address.rb +7 -0
  113. data/lib/digital_femsa/models/customer_fiscal_entities_data_response.rb +10 -10
  114. data/lib/digital_femsa/models/customer_fiscal_entities_request.rb +13 -13
  115. data/lib/digital_femsa/models/customer_info.rb +13 -20
  116. data/lib/digital_femsa/models/customer_payment_method_request.rb +2 -2
  117. data/lib/digital_femsa/models/customer_payment_sources_inner.rb +104 -0
  118. data/lib/digital_femsa/models/customer_response.rb +122 -113
  119. data/lib/digital_femsa/models/customer_shipping_contacts_response.rb +2 -0
  120. data/lib/digital_femsa/models/customers_response.rb +1 -0
  121. data/lib/digital_femsa/models/events_resend_response.rb +1 -1
  122. data/lib/digital_femsa/models/get_charges_response.rb +21 -21
  123. data/lib/digital_femsa/models/get_transactions_response.rb +1 -1
  124. data/lib/digital_femsa/models/log_response.rb +1 -1
  125. data/lib/digital_femsa/models/logs_response.rb +1 -1
  126. data/lib/digital_femsa/models/order_fiscal_entity_response.rb +1 -1
  127. data/lib/digital_femsa/models/order_refund_request.rb +59 -11
  128. data/lib/digital_femsa/models/order_request.rb +69 -79
  129. data/lib/digital_femsa/models/order_response.rb +187 -119
  130. data/lib/digital_femsa/models/{order_next_action_response_redirect_to_url.rb → order_response_channel.rb} +35 -19
  131. data/lib/digital_femsa/models/order_response_charges.rb +1 -1
  132. data/lib/digital_femsa/models/order_response_checkout.rb +1 -0
  133. data/lib/digital_femsa/models/order_response_customer_info.rb +1 -0
  134. data/lib/digital_femsa/models/order_response_shipping_contact.rb +3 -0
  135. data/lib/digital_femsa/models/order_update_fiscal_entity_request.rb +1 -1
  136. data/lib/digital_femsa/models/order_update_request.rb +106 -65
  137. data/lib/digital_femsa/models/payment_method_cash.rb +1 -1
  138. data/lib/digital_femsa/models/payment_method_cash_request.rb +2 -1
  139. data/lib/digital_femsa/models/product.rb +103 -95
  140. data/lib/digital_femsa/models/product_data_response.rb +103 -95
  141. data/lib/digital_femsa/models/product_order_response.rb +103 -95
  142. data/lib/digital_femsa/models/shipping_order_response.rb +11 -1
  143. data/lib/digital_femsa/models/shipping_request.rb +12 -1
  144. data/lib/digital_femsa/models/transaction_response.rb +172 -99
  145. data/lib/digital_femsa/models/transfers_response.rb +1 -1
  146. data/lib/digital_femsa/models/transfers_response_destination.rb +312 -0
  147. data/lib/digital_femsa/models/update_customer.rb +42 -85
  148. data/lib/digital_femsa/models/update_customer_fiscal_entities_response.rb +10 -10
  149. data/lib/digital_femsa/models/update_order_discount_lines_request.rb +34 -0
  150. data/lib/digital_femsa/models/update_order_tax_request.rb +2 -2
  151. data/lib/digital_femsa/models/update_payment_methods_amount.rb +105 -0
  152. data/lib/digital_femsa/models/update_payment_methods_expires_at.rb +105 -0
  153. data/lib/digital_femsa/models/update_product.rb +85 -58
  154. data/lib/digital_femsa/models/webhook_key_delete_response.rb +1 -1
  155. data/lib/digital_femsa/models/webhook_key_request.rb +1 -0
  156. data/lib/digital_femsa/models/webhook_key_response.rb +1 -1
  157. data/lib/digital_femsa/models/webhook_request.rb +248 -9
  158. data/lib/digital_femsa/models/webhook_response.rb +1 -1
  159. data/lib/digital_femsa/models/webhook_update_request.rb +1 -1
  160. data/lib/digital_femsa/version.rb +1 -1
  161. data/lib/digital_femsa.rb +2 -6
  162. data/spec/api/balances_api_spec.rb +24 -22
  163. data/spec/api/charges_api_spec.rb +92 -49
  164. data/spec/api/companies_api_spec.rb +57 -35
  165. data/spec/api/customers_api_spec.rb +115 -99
  166. data/spec/api/events_api_spec.rb +72 -48
  167. data/spec/api/generated_apis_coverage_spec.rb +94 -0
  168. data/spec/api/logs_api_spec.rb +57 -38
  169. data/spec/api/orders_api_spec.rb +134 -108
  170. data/spec/api/payment_link_api_spec.rb +91 -81
  171. data/spec/api/payment_methods_api_spec.rb +102 -65
  172. data/spec/api/transactions_api_spec.rb +63 -41
  173. data/spec/api/transfers_api_spec.rb +57 -38
  174. data/spec/api/webhook_keys_api_spec.rb +87 -68
  175. data/spec/api/webhooks_api_spec.rb +110 -79
  176. data/spec/api_client_spec.rb +259 -0
  177. data/spec/models/generated_models_coverage_spec.rb +152 -0
  178. data/spec/models/webhook_request_ssrf_protection_spec.rb +275 -0
  179. data/spec/spec_helper.rb +37 -0
  180. data/templates/ruby/api_client.mustache +8 -4
  181. metadata +40 -37
  182. data/docs/CustomerAntifraudInfo.md +0 -20
  183. data/docs/CustomerAntifraudInfoResponse.md +0 -20
  184. data/docs/OrderNextActionResponse.md +0 -20
  185. data/docs/OrderNextActionResponseRedirectToUrl.md +0 -20
  186. data/docs/SmsCheckoutRequest.md +0 -18
  187. data/docs/UpdateCustomerAntifraudInfo.md +0 -20
@@ -13,106 +13,137 @@ Generator version: 7.5.0
13
13
  require 'spec_helper'
14
14
  require 'json'
15
15
 
16
- # Unit tests for DigitalFemsa::WebhooksApi
17
- # Automatically generated by openapi-generator (https://openapi-generator.tech)
18
- # Please update as you see appropriate
19
- describe 'WebhooksApi' do
20
- before do
21
- # run before each test
22
- @api_instance = DigitalFemsa::WebhooksApi.new
23
- end
16
+ RSpec.describe DigitalFemsa::WebhooksApi do
17
+ let(:config) { DigitalFemsa::Configuration.new }
18
+ let(:api_client) { instance_double(DigitalFemsa::ApiClient) }
19
+ let(:api_instance) { described_class.new(api_client) }
24
20
 
25
- after do
26
- # run after each test
21
+ before do
22
+ allow(api_client).to receive(:config).and_return(config)
23
+ allow(api_client).to receive(:select_header_accept).and_return('application/vnd.app-v2.1.0+json')
24
+ allow(api_client).to receive(:select_header_content_type).and_return('application/json')
25
+ allow(api_client).to receive(:object_to_http_body) { |value| value.to_json }
27
26
  end
28
27
 
29
28
  describe 'test an instance of WebhooksApi' do
30
- it 'should create an instance of WebhooksApi' do
31
- expect(@api_instance).to be_instance_of(DigitalFemsa::WebhooksApi)
29
+ it 'creates an instance of WebhooksApi' do
30
+ expect(api_instance).to be_instance_of(DigitalFemsa::WebhooksApi)
32
31
  end
33
32
  end
34
33
 
35
- # unit tests for create_webhook
36
- # Create Webhook
37
- # What we do at Femsa translates into events. For example, an event of interest to us occurs at the time a payment is successfully processed. At that moment we will be interested in doing several things: Send an email to the buyer, generate an invoice, start the process of shipping the product, etc.
38
- # @param webhook_request requested field for webhook
39
- # @param [Hash] opts the optional parameters
40
- # @option opts [String] :accept_language Use for knowing which language to use
41
- # @return [WebhookResponse]
42
- describe 'create_webhook test' do
43
- it 'should work' do
44
- # assertion here. ref: https://rspec.info/features/3-12/rspec-expectations/built-in-matchers/
34
+ describe '#create_webhook_with_http_info' do
35
+ it 'raises when webhook_request is missing' do
36
+ expect do
37
+ api_instance.create_webhook_with_http_info(nil)
38
+ end.to raise_error(ArgumentError, /Missing the required parameter 'webhook_request'/)
45
39
  end
46
- end
47
40
 
48
- # unit tests for delete_webhook
49
- # Delete Webhook
50
- # @param id Identifier of the resource
51
- # @param [Hash] opts the optional parameters
52
- # @option opts [String] :accept_language Use for knowing which language to use
53
- # @return [WebhookResponse]
54
- describe 'delete_webhook test' do
55
- it 'should work' do
56
- # assertion here. ref: https://rspec.info/features/3-12/rspec-expectations/built-in-matchers/
41
+ it 'sends POST request with serialized payload' do
42
+ payload = { url: 'https://example.com/hook', synchronous: false }
43
+
44
+ expect(api_client).to receive(:call_api).with(:POST, '/webhooks', hash_including(
45
+ operation: :'WebhooksApi.create_webhook',
46
+ body: payload.to_json,
47
+ return_type: 'WebhookResponse'
48
+ )).and_return([:created, 201, {}])
49
+
50
+ data = api_instance.create_webhook(payload)
51
+ expect(data).to eq(:created)
57
52
  end
58
53
  end
59
54
 
60
- # unit tests for get_webhook
61
- # Get Webhook
62
- # @param id Identifier of the resource
63
- # @param [Hash] opts the optional parameters
64
- # @option opts [String] :accept_language Use for knowing which language to use
65
- # @option opts [String] :x_child_company_id In the case of a holding company, the company id of the child company to which will process the request.
66
- # @return [WebhookResponse]
67
- describe 'get_webhook test' do
68
- it 'should work' do
69
- # assertion here. ref: https://rspec.info/features/3-12/rspec-expectations/built-in-matchers/
55
+ describe '#delete_webhook_with_http_info' do
56
+ it 'raises when id is missing' do
57
+ expect do
58
+ api_instance.delete_webhook_with_http_info(nil)
59
+ end.to raise_error(ArgumentError, /Missing the required parameter 'id'/)
70
60
  end
71
- end
72
61
 
73
- # unit tests for get_webhooks
74
- # Get List of Webhooks
75
- # Consume the list of webhooks you have, each environment supports 10 webhooks (For production and testing)
76
- # @param [Hash] opts the optional parameters
77
- # @option opts [String] :accept_language Use for knowing which language to use
78
- # @option opts [String] :x_child_company_id In the case of a holding company, the company id of the child company to which will process the request.
79
- # @option opts [Integer] :limit The numbers of items to return, the maximum value is 250
80
- # @option opts [String] :search General order search, e.g. by mail, reference etc.
81
- # @option opts [String] :_next next page
82
- # @option opts [String] :previous previous page
83
- # @return [GetWebhooksResponse]
84
- describe 'get_webhooks test' do
85
- it 'should work' do
86
- # assertion here. ref: https://rspec.info/features/3-12/rspec-expectations/built-in-matchers/
62
+ it 'sends DELETE request with escaped id path' do
63
+ expect(api_client).to receive(:call_api).with(:DELETE, '/webhooks/whk%2F123', hash_including(
64
+ operation: :'WebhooksApi.delete_webhook',
65
+ return_type: 'WebhookResponse'
66
+ )).and_return([:deleted, 200, {}])
67
+
68
+ data = api_instance.delete_webhook('whk/123')
69
+ expect(data).to eq(:deleted)
87
70
  end
88
71
  end
89
72
 
90
- # unit tests for test_webhook
91
- # Test Webhook
92
- # Send a webhook.ping event
93
- # @param id Identifier of the resource
94
- # @param [Hash] opts the optional parameters
95
- # @option opts [String] :accept_language Use for knowing which language to use
96
- # @return [WebhookResponse]
97
- describe 'test_webhook test' do
98
- it 'should work' do
99
- # assertion here. ref: https://rspec.info/features/3-12/rspec-expectations/built-in-matchers/
73
+ describe '#get_webhooks_with_http_info' do
74
+ it 'raises for limit higher than 250' do
75
+ expect do
76
+ api_instance.get_webhooks_with_http_info(limit: 251)
77
+ end.to raise_error(ArgumentError, /must be smaller than or equal to 250/)
78
+ end
79
+
80
+ it 'raises for limit lower than 1' do
81
+ expect do
82
+ api_instance.get_webhooks_with_http_info(limit: 0)
83
+ end.to raise_error(ArgumentError, /must be greater than or equal to 1/)
84
+ end
85
+
86
+ it 'maps filters into query params' do
87
+ expect(api_client).to receive(:call_api).with(:GET, '/webhooks', hash_including(
88
+ operation: :'WebhooksApi.get_webhooks',
89
+ query_params: {
90
+ limit: 25,
91
+ search: 'order',
92
+ url: 'https://example.com/hook',
93
+ next: 'next_token',
94
+ previous: 'prev_token'
95
+ },
96
+ return_type: 'GetWebhooksResponse'
97
+ )).and_return([:list, 200, {}])
98
+
99
+ data = api_instance.get_webhooks(
100
+ limit: 25,
101
+ search: 'order',
102
+ url: 'https://example.com/hook',
103
+ _next: 'next_token',
104
+ previous: 'prev_token'
105
+ )
106
+
107
+ expect(data).to eq(:list)
100
108
  end
101
109
  end
102
110
 
103
- # unit tests for update_webhook
104
- # Update Webhook
105
- # updates an existing webhook
106
- # @param id Identifier of the resource
107
- # @param webhook_update_request requested fields in order to update a webhook
108
- # @param [Hash] opts the optional parameters
109
- # @option opts [String] :accept_language Use for knowing which language to use
110
- # @option opts [String] :x_child_company_id In the case of a holding company, the company id of the child company to which will process the request.
111
- # @return [WebhookResponse]
112
- describe 'update_webhook test' do
113
- it 'should work' do
114
- # assertion here. ref: https://rspec.info/features/3-12/rspec-expectations/built-in-matchers/
111
+ describe '#test_webhook_with_http_info' do
112
+ it 'raises when id is missing' do
113
+ expect do
114
+ api_instance.test_webhook_with_http_info(nil)
115
+ end.to raise_error(ArgumentError, /Missing the required parameter 'id'/)
116
+ end
117
+
118
+ it 'sends POST request to test endpoint' do
119
+ expect(api_client).to receive(:call_api).with(:POST, '/webhooks/whk_123/test', hash_including(
120
+ operation: :'WebhooksApi.test_webhook',
121
+ return_type: 'WebhookResponse'
122
+ )).and_return([:tested, 200, {}])
123
+
124
+ data = api_instance.test_webhook('whk_123')
125
+ expect(data).to eq(:tested)
115
126
  end
116
127
  end
117
128
 
129
+ describe '#update_webhook_with_http_info' do
130
+ it 'raises when webhook_update_request is missing' do
131
+ expect do
132
+ api_instance.update_webhook_with_http_info('whk_123', nil)
133
+ end.to raise_error(ArgumentError, /Missing the required parameter 'webhook_update_request'/)
134
+ end
135
+
136
+ it 'sends PUT request with serialized payload' do
137
+ payload = { url: 'https://example.com/new-hook', disabled: false }
138
+
139
+ expect(api_client).to receive(:call_api).with(:PUT, '/webhooks/whk_123', hash_including(
140
+ operation: :'WebhooksApi.update_webhook',
141
+ body: payload.to_json,
142
+ return_type: 'WebhookResponse'
143
+ )).and_return([:updated, 200, {}])
144
+
145
+ data = api_instance.update_webhook('whk_123', payload)
146
+ expect(data).to eq(:updated)
147
+ end
148
+ end
118
149
  end
@@ -0,0 +1,259 @@
1
+ require 'spec_helper'
2
+ require 'tempfile'
3
+
4
+ RSpec.describe DigitalFemsa::ApiClient do
5
+ RequestOptionsStub = Struct.new(:params_encoder, :timeout)
6
+ RequestStub = Struct.new(:headers, :body, :params, :options, :requested_url) do
7
+ def url(value)
8
+ self.requested_url = value
9
+ end
10
+ end
11
+
12
+ let(:config) { DigitalFemsa::Configuration.new }
13
+ let(:api_client) { described_class.new(config) }
14
+
15
+ describe '#initialize' do
16
+ it 'sets default headers' do
17
+ expect(api_client.default_headers['Content-Type']).to eq('application/json')
18
+ expect(api_client.default_headers['User-Agent']).to include('RubyBindings')
19
+ expect(api_client.default_headers['Spin-Client-User-Agent']).to include('sdk_version=')
20
+ end
21
+ end
22
+
23
+ describe '#json_mime?' do
24
+ it 'returns true for json mime types and wildcard' do
25
+ expect(api_client.json_mime?('application/json')).to be(true)
26
+ expect(api_client.json_mime?('application/json; charset=UTF-8')).to be(true)
27
+ expect(api_client.json_mime?('*/*')).to be(true)
28
+ end
29
+
30
+ it 'returns false for non-json mime types' do
31
+ expect(api_client.json_mime?('text/plain')).to be(false)
32
+ end
33
+ end
34
+
35
+ describe '#select_header_accept' do
36
+ it 'returns nil when accepts are empty' do
37
+ expect(api_client.select_header_accept([])).to be_nil
38
+ end
39
+
40
+ it 'prefers json accept when present' do
41
+ expect(api_client.select_header_accept(%w[text/plain application/json])).to eq('application/json')
42
+ end
43
+
44
+ it 'joins values when json is not present' do
45
+ expect(api_client.select_header_accept(%w[text/plain text/html])).to eq('text/plain,text/html')
46
+ end
47
+ end
48
+
49
+ describe '#select_header_content_type' do
50
+ it 'returns nil when content types are empty' do
51
+ expect(api_client.select_header_content_type([])).to be_nil
52
+ end
53
+
54
+ it 'prefers json content type when present' do
55
+ expect(api_client.select_header_content_type(%w[text/plain application/json])).to eq('application/json')
56
+ end
57
+
58
+ it 'returns the first content type when json is not present' do
59
+ expect(api_client.select_header_content_type(%w[text/plain text/html])).to eq('text/plain')
60
+ end
61
+ end
62
+
63
+ describe '#build_request_body' do
64
+ it 'encodes urlencoded form body' do
65
+ result = api_client.build_request_body(
66
+ { 'Content-Type' => 'application/x-www-form-urlencoded' },
67
+ { 'a' => '1', 'b' => '2' },
68
+ nil
69
+ )
70
+
71
+ expect(result).to eq('a=1&b=2')
72
+ end
73
+
74
+ it 'builds multipart form body preserving arrays and nil values' do
75
+ Tempfile.create('upload') do |file|
76
+ file.write('content')
77
+ file.flush
78
+
79
+ result = api_client.build_request_body(
80
+ { 'Content-Type' => 'multipart/form-data' },
81
+ { file: file, amount: 100, tags: %w[a b], optional: nil },
82
+ nil
83
+ )
84
+
85
+ expect(result[:file]).to be_a(Faraday::FilePart)
86
+ expect(result[:amount]).to eq('100')
87
+ expect(result[:tags]).to eq(%w[a b])
88
+ expect(result[:optional]).to be_nil
89
+ end
90
+ end
91
+
92
+ it 'serializes hash body as json' do
93
+ result = api_client.build_request_body(
94
+ { 'Content-Type' => 'application/json' },
95
+ {},
96
+ { order: 'ord_123' }
97
+ )
98
+
99
+ expect(result).to eq('{"order":"ord_123"}')
100
+ end
101
+
102
+ it 'returns body as-is when already string' do
103
+ result = api_client.build_request_body(
104
+ { 'Content-Type' => 'application/json' },
105
+ {},
106
+ '{"raw":true}'
107
+ )
108
+
109
+ expect(result).to eq('{"raw":true}')
110
+ end
111
+ end
112
+
113
+ describe '#build_request' do
114
+ it 'builds request with auth, query params, body and url' do
115
+ config.access_token = 'token-123'
116
+ config.timeout = 45
117
+
118
+ request = RequestStub.new(nil, nil, nil, RequestOptionsStub.new(nil, nil), nil)
119
+
120
+ api_client.build_request(
121
+ :post,
122
+ 'charges',
123
+ request,
124
+ header_params: { 'Content-Type' => 'application/json' },
125
+ query_params: { limit: 10 },
126
+ form_params: {},
127
+ body: { amount: 1000 },
128
+ auth_names: ['bearerAuth']
129
+ )
130
+
131
+ expect(request.headers['Authorization']).to eq('Bearer token-123')
132
+ expect(request.params).to eq(limit: 10)
133
+ expect(request.body).to eq('{"amount":1000}')
134
+ expect(request.requested_url).to eq('https://api.digitalfemsa.io/charges')
135
+ expect(request.options.timeout).to eq(45)
136
+ end
137
+ end
138
+
139
+ describe '#update_params_for_auth!' do
140
+ it 'supports query auth settings' do
141
+ allow(config).to receive(:auth_settings).and_return(
142
+ 'queryAuth' => {
143
+ in: 'query',
144
+ key: 'api_key',
145
+ value: 'secret'
146
+ }
147
+ )
148
+
149
+ headers = {}
150
+ query = {}
151
+
152
+ api_client.update_params_for_auth!(headers, query, ['queryAuth'])
153
+
154
+ expect(query['api_key']).to eq('secret')
155
+ end
156
+
157
+ it 'raises error for unsupported auth location' do
158
+ allow(config).to receive(:auth_settings).and_return(
159
+ 'badAuth' => {
160
+ in: 'cookie',
161
+ key: 'session',
162
+ value: 'abc'
163
+ }
164
+ )
165
+
166
+ expect do
167
+ api_client.update_params_for_auth!({}, {}, ['badAuth'])
168
+ end.to raise_error(ArgumentError, /Authentication token must be in `query` or `header`/)
169
+ end
170
+ end
171
+
172
+ describe '#sanitize_filename' do
173
+ it 'removes directory traversal from filename' do
174
+ expect(api_client.sanitize_filename('../../tmp/file.txt')).to eq('file.txt')
175
+ expect(api_client.sanitize_filename('C:\\tmp\\file.txt')).to eq('file.txt')
176
+ end
177
+ end
178
+
179
+ describe '#build_collection_param' do
180
+ it 'supports csv and multi formats' do
181
+ expect(api_client.build_collection_param(%w[a b], :csv)).to eq('a,b')
182
+ expect(api_client.build_collection_param(%w[a b], :multi)).to eq(%w[a b])
183
+ end
184
+
185
+ it 'raises for unknown collection format' do
186
+ expect do
187
+ api_client.build_collection_param(%w[a b], :unknown)
188
+ end.to raise_error(RuntimeError, /unknown collection format/)
189
+ end
190
+ end
191
+
192
+ describe '#call_api' do
193
+ let(:opts) do
194
+ {
195
+ header_params: { 'Content-Type' => 'application/json' },
196
+ query_params: {},
197
+ form_params: {},
198
+ auth_names: []
199
+ }
200
+ end
201
+
202
+ it 'raises ApiError on timeout' do
203
+ allow(api_client).to receive(:connection).and_raise(Faraday::TimeoutError)
204
+
205
+ expect do
206
+ api_client.call_api(:get, '/charges', opts)
207
+ end.to raise_error(DigitalFemsa::ApiError, /Connection timed out/)
208
+ end
209
+
210
+ it 'raises ApiError on connection failed' do
211
+ allow(api_client).to receive(:connection).and_raise(Faraday::ConnectionFailed)
212
+
213
+ expect do
214
+ api_client.call_api(:get, '/charges', opts)
215
+ end.to raise_error(DigitalFemsa::ApiError, /Connection failed/)
216
+ end
217
+
218
+ it 'raises ApiError for non-successful responses' do
219
+ response = instance_double(
220
+ 'FaradayResponse',
221
+ success?: false,
222
+ status: 401,
223
+ headers: { 'Content-Type' => 'application/json' },
224
+ body: '{"error":"unauthorized"}',
225
+ reason_phrase: 'Unauthorized'
226
+ )
227
+
228
+ connection = instance_double('FaradayConnection')
229
+ allow(connection).to receive(:get).and_yield(double('request')).and_return(response)
230
+ allow(api_client).to receive(:connection).and_return(connection)
231
+ allow(api_client).to receive(:build_request)
232
+
233
+ expect do
234
+ api_client.call_api(:get, '/charges', opts)
235
+ end.to raise_error(DigitalFemsa::ApiError) { |error| expect(error.code).to eq(401) }
236
+ end
237
+
238
+ it 'returns status and headers on successful responses' do
239
+ response = instance_double(
240
+ 'FaradayResponse',
241
+ success?: true,
242
+ status: 200,
243
+ headers: { 'X-Request-Id' => 'req_123' },
244
+ body: '{"ok":true}'
245
+ )
246
+
247
+ connection = instance_double('FaradayConnection')
248
+ allow(connection).to receive(:get).and_yield(double('request')).and_return(response)
249
+ allow(api_client).to receive(:connection).and_return(connection)
250
+ allow(api_client).to receive(:build_request)
251
+
252
+ data, status, headers = api_client.call_api(:get, '/charges', opts)
253
+
254
+ expect(data).to be_nil
255
+ expect(status).to eq(200)
256
+ expect(headers).to eq('X-Request-Id' => 'req_123')
257
+ end
258
+ end
259
+ end
@@ -0,0 +1,152 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe 'Generated models coverage smoke tests' do
4
+ model_classes = DigitalFemsa.constants.filter_map do |const_name|
5
+ constant = DigitalFemsa.const_get(const_name)
6
+ next unless constant.is_a?(Class)
7
+ next unless constant.respond_to?(:attribute_map)
8
+ next unless constant.respond_to?(:openapi_types)
9
+ next unless constant.respond_to?(:build_from_hash)
10
+ next unless constant.respond_to?(:_deserialize)
11
+
12
+ constant
13
+ rescue NameError
14
+ nil
15
+ end.sort_by(&:name)
16
+
17
+ sample_value_for = lambda do |type, attr_name: nil, depth: 0|
18
+ return nil if depth > 2
19
+
20
+ type_name = type.to_s
21
+
22
+ return 'https://example.com/webhook' if attr_name.to_s.include?('url')
23
+
24
+ case type_name
25
+ when 'String'
26
+ attr_name.to_s.include?('email') ? 'test@example.com' : 'sample'
27
+ when 'Integer'
28
+ 1
29
+ when 'Float'
30
+ 1.5
31
+ when 'Boolean'
32
+ true
33
+ when 'Date'
34
+ '2024-01-01'
35
+ when 'Time'
36
+ '2024-01-01T00:00:00Z'
37
+ when 'Object'
38
+ { 'key' => 'value' }
39
+ when /\AArray<(?<inner_type>.+)>\z/
40
+ [sample_value_for.call(Regexp.last_match[:inner_type], attr_name: attr_name, depth: depth + 1)]
41
+ when /\AHash<(?<k_type>.+?), (?<v_type>.+)>\z/
42
+ {
43
+ sample_value_for.call(Regexp.last_match[:k_type], depth: depth + 1) =>
44
+ sample_value_for.call(Regexp.last_match[:v_type], depth: depth + 1)
45
+ }
46
+ else
47
+ {}
48
+ end
49
+ end
50
+
51
+ candidate_values_for = lambda do |type, attr_name|
52
+ type_name = type.to_s
53
+ attr_name_str = attr_name.to_s
54
+
55
+ candidates = [sample_value_for.call(type, attr_name: attr_name)]
56
+
57
+ if type_name == 'String'
58
+ candidates.concat(
59
+ %w[private public MXN USD es en active inactive pending completed card cash transfer bank_transfer charge order customer webhook]
60
+ )
61
+ candidates << 'https://example.com/webhook' if attr_name_str.include?('url')
62
+ candidates << 'test@example.com' if attr_name_str.include?('email')
63
+ candidates << 'mx' if attr_name_str.include?('country')
64
+ elsif type_name == 'Integer'
65
+ candidates.concat([0, 20, 100, 1_700_000_000])
66
+ elsif type_name == 'Float'
67
+ candidates.concat([0.0, 2.5, 10.0])
68
+ elsif type_name == 'Boolean'
69
+ candidates << false
70
+ elsif type_name.match?(/\AArray<.+>\z/)
71
+ candidates << []
72
+ elsif type_name.match?(/\AHash<.+>\z/)
73
+ candidates << {}
74
+ end
75
+
76
+ candidates.compact.uniq
77
+ end
78
+
79
+ attributes_for = lambda do |klass|
80
+ klass.openapi_types.each_with_object({}) do |(attr, type), attrs|
81
+ assigned = false
82
+
83
+ candidate_values_for.call(type, attr).each do |value|
84
+ begin
85
+ klass.new(attrs.merge(attr => value))
86
+ attrs[attr] = value
87
+ assigned = true
88
+ break
89
+ rescue StandardError
90
+ next
91
+ end
92
+ end
93
+
94
+ next if assigned
95
+
96
+ begin
97
+ klass.new(attrs.merge(attr => nil))
98
+ attrs[attr] = nil
99
+ rescue StandardError
100
+ # Skip attributes that require highly specific values in custom setters.
101
+ end
102
+ end
103
+ end
104
+
105
+ model_classes.each do |klass|
106
+ it "smoke-tests #{klass.name}" do
107
+ attrs = attributes_for.call(klass)
108
+
109
+ begin
110
+ instance = klass.new(attrs)
111
+ another_instance = klass.new(attrs)
112
+ rescue StandardError => e
113
+ skip("Skipped strict model initialization: #{e.message}")
114
+ end
115
+
116
+ expect(klass.acceptable_attributes).to be_a(Array)
117
+ expect(klass.openapi_nullable).to be_a(Set)
118
+
119
+ expect { klass.new('invalid') }.to raise_error(ArgumentError)
120
+ expect { klass.new('__invalid_attribute__' => 'x') }.to raise_error(ArgumentError)
121
+
122
+ expect(instance.send(:==, another_instance)).to be(true)
123
+ expect(instance.send(:eql?, another_instance)).to be(true)
124
+ expect(instance.send(:hash)).to be_a(Integer)
125
+ expect { instance.send(:list_invalid_properties) }.not_to raise_error
126
+ expect { instance.send(:valid?) }.not_to raise_error
127
+
128
+ serialized_hash = instance.send(:to_hash)
129
+ expect(serialized_hash).to be_a(Hash)
130
+ expect(instance.send(:to_body)).to eq(serialized_hash)
131
+ expect(instance.send(:to_s)).to be_a(String)
132
+ expect(instance.send(:_to_hash, [1, nil, 2])).to eq([1, 2])
133
+ expect(instance.send(:_to_hash, { 'a' => 1 })).to eq('a' => 1)
134
+
135
+ rebuilt = klass.build_from_hash(serialized_hash) rescue nil
136
+ expect(rebuilt).to be_a(klass).or be_nil
137
+
138
+ expect(klass._deserialize('String', 1)).to eq('1')
139
+ expect(klass._deserialize('Integer', '1')).to eq(1)
140
+ expect(klass._deserialize('Float', '1.2')).to eq(1.2)
141
+ expect(klass._deserialize('Boolean', 'true')).to be(true)
142
+ expect(klass._deserialize('Boolean', 'false')).to be(false)
143
+ expect(klass._deserialize('Object', { 'a' => 1 })).to eq('a' => 1)
144
+ expect(klass._deserialize('Array<String>', [1, 2])).to eq(%w[1 2])
145
+ expect(klass._deserialize('Hash<String, Integer>', { 'a' => '1' })).to eq('a' => 1)
146
+ expect(klass._deserialize('Date', '2024-01-01')).to be_a(Date)
147
+ expect(klass._deserialize('Time', '2024-01-01T00:00:00Z')).to be_a(Time)
148
+ expect { klass._deserialize(klass.name.split('::').last, {}) rescue nil }.not_to raise_error
149
+
150
+ end
151
+ end
152
+ end