magento 0.26.1 → 0.27.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +5 -3
- data/README.md +636 -226
- data/lib/magento/order.rb +39 -2
- data/lib/magento/params/create_image.rb +43 -0
- data/lib/magento/product.rb +88 -0
- data/lib/magento/query.rb +11 -9
- data/lib/magento/version.rb +1 -1
- data/spec/magento/core/model_mapper_spec.rb +74 -0
- data/spec/magento/core/model_spec.rb +71 -0
- data/spec/magento/core/query_spec.rb +207 -0
- data/spec/magento/core/record_collection_spec.rb +116 -0
- data/spec/magento/core/request_spec.rb +141 -0
- data/spec/magento/order_spec.rb +28 -29
- data/spec/{product_spec.rb → magento/product_spec.rb} +21 -0
- data/spec/spec_helper.rb +17 -3
- data/spec/vcr_cassettes/order/send_email.yml +65 -0
- data/spec/vcr_cassettes/product/find.yml +72 -0
- metadata +9 -2
@@ -0,0 +1,116 @@
|
|
1
|
+
RSpec.describe Magento::RecordCollection do
|
2
|
+
subject { Magento::RecordCollection.new(items: []) }
|
3
|
+
|
4
|
+
describe 'read only attributes' do
|
5
|
+
it { is_expected.to respond_to(:items) }
|
6
|
+
it { is_expected.to respond_to(:search_criteria) }
|
7
|
+
it { is_expected.to respond_to(:total_count) }
|
8
|
+
it { is_expected.not_to respond_to(:items=) }
|
9
|
+
it { is_expected.not_to respond_to(:search_criteria=) }
|
10
|
+
it { is_expected.not_to respond_to(:total_count=) }
|
11
|
+
end
|
12
|
+
|
13
|
+
describe '#last_page?' do
|
14
|
+
it do
|
15
|
+
subject = create_subject_with_pagination(total_count: 60, current_page: 6, page_size: 10)
|
16
|
+
expect(subject.last_page?).to be true
|
17
|
+
end
|
18
|
+
|
19
|
+
it do
|
20
|
+
subject = create_subject_with_pagination(total_count: 60, current_page: 5, page_size: 10)
|
21
|
+
expect(subject.last_page?).to be false
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '#next_page' do
|
26
|
+
it 'returns next page number' do
|
27
|
+
subject = create_subject_with_pagination(current_page: 5, total_count: 60, page_size: 10)
|
28
|
+
expect(subject.next_page).to be 6
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'returns nil when current page is the last' do
|
32
|
+
subject = create_subject_with_pagination(current_page: 6, total_count: 60, page_size: 10)
|
33
|
+
expect(subject.next_page).to be nil
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe '.from_magento_response' do
|
38
|
+
let(:response) do
|
39
|
+
{
|
40
|
+
'items' => [
|
41
|
+
{ 'id' => 1, 'name' => 'Product one' },
|
42
|
+
{ 'id' => 2, 'name' => 'Product two' }
|
43
|
+
],
|
44
|
+
'total_count' => 12,
|
45
|
+
'search_criteria' => { 'current_page' => 2, 'page_size' => 10 }
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'create RecordCollection instance from magento response' do
|
50
|
+
records = Magento::RecordCollection.from_magento_response(response, model: Magento::Product)
|
51
|
+
|
52
|
+
expect(records).to all be_a_instance_of(Magento::Product)
|
53
|
+
expect(records.size).to eql(2)
|
54
|
+
expect(records.total_count).to eql(12)
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'allows specify the iterable field' do
|
58
|
+
response['data'] = response.delete 'items'
|
59
|
+
|
60
|
+
records = Magento::RecordCollection.from_magento_response(
|
61
|
+
response,
|
62
|
+
model: Magento::Product,
|
63
|
+
iterable_field: 'data'
|
64
|
+
)
|
65
|
+
|
66
|
+
expect(records.size).to eql(2)
|
67
|
+
expect(records).to all be_a_instance_of(Magento::Product)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe 'delegated methods' do
|
72
|
+
let(:methods) do
|
73
|
+
%i[
|
74
|
+
count
|
75
|
+
length
|
76
|
+
size
|
77
|
+
first
|
78
|
+
last
|
79
|
+
[]
|
80
|
+
find
|
81
|
+
each
|
82
|
+
each_with_index
|
83
|
+
sample
|
84
|
+
map
|
85
|
+
select
|
86
|
+
filter
|
87
|
+
reject
|
88
|
+
collect
|
89
|
+
take
|
90
|
+
take_while
|
91
|
+
sort
|
92
|
+
sort_by
|
93
|
+
reverse_each
|
94
|
+
reverse
|
95
|
+
all?
|
96
|
+
any?
|
97
|
+
none?
|
98
|
+
one?
|
99
|
+
empty?
|
100
|
+
]
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'from #items' do
|
104
|
+
methods.each do |method|
|
105
|
+
expect(subject).to respond_to(method)
|
106
|
+
expect(subject.items).to receive(method)
|
107
|
+
subject.send(method)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def create_subject_with_pagination(total_count:, current_page:, page_size:)
|
113
|
+
search_criteria = double(:search_criteria, current_page: current_page, page_size: page_size)
|
114
|
+
Magento::RecordCollection.new(items: [], total_count: total_count, search_criteria: search_criteria)
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
RSpec.describe Magento::Request do
|
2
|
+
let(:config) do
|
3
|
+
double('Magento::Configuration',
|
4
|
+
url: 'https://site.com.br',
|
5
|
+
token: 'magento-token',
|
6
|
+
store: 'magento-store',
|
7
|
+
timeout: 30,
|
8
|
+
open_timeout: 5
|
9
|
+
)
|
10
|
+
end
|
11
|
+
|
12
|
+
subject { Magento::Request.new(config: config) }
|
13
|
+
|
14
|
+
let(:response) do
|
15
|
+
double('Response', parse: {}, status: double(:status, success?: true))
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '#get' do
|
19
|
+
it 'calls HTTP.get with url' do
|
20
|
+
expect_any_instance_of(HTTP::Client).to receive(:get)
|
21
|
+
.with('https://site.com.br/rest/magento-store/V1/products')
|
22
|
+
.and_return(response)
|
23
|
+
|
24
|
+
subject.get('products')
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#put' do
|
29
|
+
it 'calls HTTP.put with url and body' do
|
30
|
+
body = { product: { price: 22.50 } }
|
31
|
+
|
32
|
+
expect_any_instance_of(HTTP::Client).to receive(:put)
|
33
|
+
.with('https://site.com.br/rest/magento-store/V1/products', { json: body })
|
34
|
+
.and_return(response)
|
35
|
+
|
36
|
+
subject.put('products', body)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe '#post' do
|
41
|
+
it 'calls HTTP.post with url and body' do
|
42
|
+
body = { product: { name: 'Some name', price: 22.50 } }
|
43
|
+
|
44
|
+
expect_any_instance_of(HTTP::Client).to receive(:post)
|
45
|
+
.with('https://site.com.br/rest/magento-store/V1/products', { json: body })
|
46
|
+
.and_return(response)
|
47
|
+
|
48
|
+
subject.post('products', body)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '#delete' do
|
53
|
+
it 'calls HTTP.selete with url' do
|
54
|
+
expect_any_instance_of(HTTP::Client).to receive(:delete)
|
55
|
+
.with('https://site.com.br/rest/magento-store/V1/products/22')
|
56
|
+
.and_return(response)
|
57
|
+
|
58
|
+
subject.delete('products/22')
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context 'private method' do
|
63
|
+
describe '#http_auth' do
|
64
|
+
it 'calls HTTP.auth with token and returns HTTP::Client' do
|
65
|
+
expect(HTTP).to receive(:auth).with("Bearer #{config.token}").and_return(HTTP)
|
66
|
+
result = subject.send(:http_auth)
|
67
|
+
expect(result).to be_a(HTTP::Client)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe '#base_url' do
|
72
|
+
it do
|
73
|
+
base_url = "https://site.com.br/rest/magento-store/V1"
|
74
|
+
expect(subject.send(:base_url)).to eql(base_url)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe '#url' do
|
79
|
+
it 'returns base_url + resource' do
|
80
|
+
url = "https://site.com.br/rest/magento-store/V1/products"
|
81
|
+
expect(subject.send(:url, 'products')).to eql(url)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe '#handle_error' do
|
86
|
+
context 'when success' do
|
87
|
+
it 'does nothing' do
|
88
|
+
subject.send(:handle_error, response)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context 'when status not found' do
|
93
|
+
it 'reises Magento::NotFound error' do
|
94
|
+
allow(response).to receive(:status).and_return(
|
95
|
+
double(:status, success?: false, not_found?: true, code: 404)
|
96
|
+
)
|
97
|
+
|
98
|
+
expect { subject.send(:handle_error, response) }.to raise_error(Magento::NotFound)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
context 'when other status' do
|
103
|
+
it 'reises Magento::MagentoError' do
|
104
|
+
allow(response).to receive(:status).and_return(
|
105
|
+
double(:status, success?: false, not_found?: false, code: 422)
|
106
|
+
)
|
107
|
+
|
108
|
+
expect { subject.send(:handle_error, response) }.to raise_error(Magento::MagentoError)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe '#save_request' do
|
114
|
+
it 'save on instance variable' do
|
115
|
+
body = { quantity: 200, category_ids: [1,3,4] }
|
116
|
+
|
117
|
+
subject.send(:save_request, :post, 'https:someurl.com.br', body)
|
118
|
+
|
119
|
+
expect(subject.instance_variable_get(:@request)).to eql({
|
120
|
+
body: body,
|
121
|
+
method: :post,
|
122
|
+
url: 'https:someurl.com.br',
|
123
|
+
})
|
124
|
+
end
|
125
|
+
|
126
|
+
context 'when body has media_gallery_entries' do
|
127
|
+
it 'removes media_gallery_entries attribute from body' do
|
128
|
+
body = { product: { name: 'Name', price: 99.90, media_gallery_entries: {} } }
|
129
|
+
|
130
|
+
subject.send(:save_request, :post, 'https:someurl.com.br', body)
|
131
|
+
|
132
|
+
expect(subject.instance_variable_get(:@request)).to eql({
|
133
|
+
body: { name: 'Name', price: 99.90 },
|
134
|
+
method: :post,
|
135
|
+
url: 'https:someurl.com.br',
|
136
|
+
})
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
data/spec/magento/order_spec.rb
CHANGED
@@ -1,38 +1,37 @@
|
|
1
|
-
class RequestMock
|
2
|
-
attr_reader :path
|
3
|
-
|
4
|
-
def post(path)
|
5
|
-
@path = path
|
6
|
-
OpenStruct.new(success?: true, parse: true)
|
7
|
-
end
|
8
|
-
end
|
9
|
-
|
10
1
|
RSpec.describe Magento::Order do
|
11
|
-
|
2
|
+
let(:magento_client) { request = Magento::Request.new }
|
12
3
|
|
13
|
-
|
14
|
-
|
15
|
-
request = RequestMock.new
|
16
|
-
allow(Magento::Order).to receive(:request).and_return(request)
|
17
|
-
|
18
|
-
order_id = 25
|
19
|
-
result = Magento::Order.send_email(order_id)
|
20
|
-
|
21
|
-
expect(request.path).to eql("orders/#{order_id}/emails")
|
22
|
-
expect(result).to be true
|
23
|
-
end
|
4
|
+
before do
|
5
|
+
allow(Magento::Order).to receive(:request).and_return(magento_client)
|
24
6
|
end
|
25
7
|
|
26
|
-
describe '
|
27
|
-
|
28
|
-
|
29
|
-
|
8
|
+
describe 'send_email' do
|
9
|
+
let(:order_id) { 11735 }
|
10
|
+
let(:response) { double('Response', parse: true, status: 200) }
|
11
|
+
|
12
|
+
describe 'class method' do
|
13
|
+
it 'should request POST /orders/:id/emails' do
|
14
|
+
expect(magento_client).to receive(:post)
|
15
|
+
.with("orders/#{order_id}/emails")
|
16
|
+
.and_return(response)
|
17
|
+
|
18
|
+
result = Magento::Order.send_email(order_id)
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should return true' do
|
22
|
+
VCR.use_cassette('order/send_email') do
|
23
|
+
expect(Magento::Order.send_email(order_id)).to be(true).or be(false)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
30
27
|
|
31
|
-
|
32
|
-
|
28
|
+
describe 'instance method' do
|
29
|
+
it 'shuld call the class method with order_id' do
|
30
|
+
expect(Magento::Order).to receive(:send_email).with(order_id)
|
33
31
|
|
34
|
-
|
35
|
-
|
32
|
+
order = Magento::Order.build(id: order_id)
|
33
|
+
result = order.send_email
|
34
|
+
end
|
36
35
|
end
|
37
36
|
end
|
38
37
|
end
|
@@ -1,4 +1,25 @@
|
|
1
1
|
RSpec.describe Magento::Product do
|
2
|
+
let(:magento_client) { Magento::Request.new }
|
3
|
+
|
4
|
+
before { allow(Magento::Product).to receive(:request).and_return(magento_client) }
|
5
|
+
|
6
|
+
describe '.find' do
|
7
|
+
it 'request to /prducts/:sku' do
|
8
|
+
response = double('HTTP::Response', parse: {})
|
9
|
+
|
10
|
+
expect(magento_client).to receive(:get).with('products/1243').and_return(response)
|
11
|
+
|
12
|
+
Magento::Product.find('1243')
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'returns a Magento::Product instance' do
|
16
|
+
VCR.use_cassette('product/find') do
|
17
|
+
product = Magento::Product.find('1243')
|
18
|
+
expect(product).to be_an_instance_of(Magento::Product)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
2
23
|
describe '#set_custom_attribute' do
|
3
24
|
let(:product) { Magento::Product.build(
|
4
25
|
sku: 25,
|
data/spec/spec_helper.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
|
-
require
|
1
|
+
require 'bundler/setup'
|
2
2
|
require 'byebug'
|
3
|
-
require
|
3
|
+
require 'magento'
|
4
|
+
require 'vcr'
|
4
5
|
|
5
6
|
RSpec.configure do |config|
|
6
7
|
# Enable flags like --only-failures and --next-failure
|
7
|
-
config.example_status_persistence_file_path =
|
8
|
+
config.example_status_persistence_file_path = '.rspec_status'
|
8
9
|
|
9
10
|
# Disable RSpec exposing methods globally on `Module` and `main`
|
10
11
|
config.disable_monkey_patching!
|
@@ -13,3 +14,16 @@ RSpec.configure do |config|
|
|
13
14
|
c.syntax = :expect
|
14
15
|
end
|
15
16
|
end
|
17
|
+
|
18
|
+
Magento.configure do |config|
|
19
|
+
config.url = 'https://dev.superbomemcasa.com.br'
|
20
|
+
end
|
21
|
+
|
22
|
+
VCR.configure do |c|
|
23
|
+
c.cassette_library_dir = 'spec/vcr_cassettes'
|
24
|
+
c.hook_into :webmock
|
25
|
+
c.configure_rspec_metadata!
|
26
|
+
c.filter_sensitive_data('<MAGENTO_URL>') { Magento.configuration.url }
|
27
|
+
c.filter_sensitive_data('<MAGENTO_DOMAIN>') { Magento.configuration.url.sub(/^http(s)?:\/\//, '') }
|
28
|
+
c.filter_sensitive_data('<MAGENTO_TOKEN>') { Magento.configuration.token }
|
29
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: post
|
5
|
+
uri: "<MAGENTO_URL>/rest/all/V1/orders/11735/emails"
|
6
|
+
body:
|
7
|
+
encoding: UTF-8
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
Authorization:
|
11
|
+
- Bearer <MAGENTO_TOKEN>
|
12
|
+
Connection:
|
13
|
+
- close
|
14
|
+
Host:
|
15
|
+
- "<MAGENTO_DOMAIN>"
|
16
|
+
User-Agent:
|
17
|
+
- http.rb/4.4.1
|
18
|
+
response:
|
19
|
+
status:
|
20
|
+
code: 200
|
21
|
+
message: OK
|
22
|
+
headers:
|
23
|
+
Date:
|
24
|
+
- Thu, 28 Jan 2021 02:19:28 GMT
|
25
|
+
Content-Type:
|
26
|
+
- application/json; charset=utf-8
|
27
|
+
Transfer-Encoding:
|
28
|
+
- chunked
|
29
|
+
Connection:
|
30
|
+
- close
|
31
|
+
Set-Cookie:
|
32
|
+
- PHPSESSID=2smb8fbfrh89ov5l6dsaog9ol3; expires=Thu, 28-Jan-2021 03:19:28 GMT;
|
33
|
+
Max-Age=3600; path=/; domain=<MAGENTO_DOMAIN>; secure; HttpOnly
|
34
|
+
- __cfduid=d6599c746d9235e60e31314f2c23603101611800368; expires=Sat, 27-Feb-21
|
35
|
+
02:19:28 GMT; path=/; domain=.superbomemcasa.com.br; HttpOnly; SameSite=Lax;
|
36
|
+
Secure
|
37
|
+
Vary:
|
38
|
+
- Accept-Encoding
|
39
|
+
Expires:
|
40
|
+
- Thu, 19 Nov 1981 08:52:00 GMT
|
41
|
+
Cache-Control:
|
42
|
+
- no-store, no-cache, must-revalidate
|
43
|
+
Pragma:
|
44
|
+
- no-cache
|
45
|
+
X-Frame-Options:
|
46
|
+
- SAMEORIGIN
|
47
|
+
Cf-Cache-Status:
|
48
|
+
- DYNAMIC
|
49
|
+
Cf-Request-Id:
|
50
|
+
- 07e862ec7d0000f603b22c5000000001
|
51
|
+
Expect-Ct:
|
52
|
+
- max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
|
53
|
+
Report-To:
|
54
|
+
- '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report?s=LcpIGCN14P04w%2B12%2BJip%2BoqrTEEJHVD5vP3%2FqWTDV843xCQbwKwwisiq23jbvZ0fOkDmTOxTTgA804Jq1CCnW9d3I14x%2BUGjfoosFZuRXzpxNONKefOyryVm4KckyBHTJSF1EtaW"}]}'
|
55
|
+
Nel:
|
56
|
+
- '{"max_age":604800,"report_to":"cf-nel"}'
|
57
|
+
Server:
|
58
|
+
- cloudflare
|
59
|
+
Cf-Ray:
|
60
|
+
- 61873a8d9b69f603-GRU
|
61
|
+
body:
|
62
|
+
encoding: UTF-8
|
63
|
+
string: 'false'
|
64
|
+
recorded_at: Thu, 28 Jan 2021 02:19:28 GMT
|
65
|
+
recorded_with: VCR 6.0.0
|