mno-enterprise-core 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +1 -0
- data/Rakefile +12 -0
- data/app/assets/images/mno_enterprise/main-logo.png +0 -0
- data/app/controllers/mno_enterprise/application_controller.rb +116 -0
- data/app/helpers/mno_enterprise/application_helper.rb +67 -0
- data/app/helpers/mno_enterprise/impersonate_helper.rb +27 -0
- data/app/models/mno_enterprise/ability.rb +6 -0
- data/app/models/mno_enterprise/app.rb +72 -0
- data/app/models/mno_enterprise/app_instance.rb +36 -0
- data/app/models/mno_enterprise/app_instances_sync.rb +6 -0
- data/app/models/mno_enterprise/arrears_situation.rb +6 -0
- data/app/models/mno_enterprise/audit_event.rb +21 -0
- data/app/models/mno_enterprise/base_resource.rb +228 -0
- data/app/models/mno_enterprise/credit_card.rb +40 -0
- data/app/models/mno_enterprise/deletion_request.rb +35 -0
- data/app/models/mno_enterprise/impac/dashboard.rb +36 -0
- data/app/models/mno_enterprise/impac/dashboard_provisioner.rb +5 -0
- data/app/models/mno_enterprise/impac/kpi.rb +9 -0
- data/app/models/mno_enterprise/impac/widget.rb +13 -0
- data/app/models/mno_enterprise/invoice.rb +53 -0
- data/app/models/mno_enterprise/org_invite.rb +50 -0
- data/app/models/mno_enterprise/organization.rb +33 -0
- data/app/models/mno_enterprise/team.rb +50 -0
- data/app/models/mno_enterprise/tenant.rb +5 -0
- data/app/models/mno_enterprise/tenant_invoice.rb +5 -0
- data/app/models/mno_enterprise/user.rb +183 -0
- data/app/pdf/mno_enterprise/invoice_pdf.rb +516 -0
- data/config/initializers/audit_log.rb +5 -0
- data/config/locales/devise.en.yml +60 -0
- data/config/routes.rb +2 -0
- data/config/styleguide.yml +106 -0
- data/lib/accountingjs_serializer.rb +51 -0
- data/lib/devise/controllers/extension_helpers.rb +52 -0
- data/lib/devise/extension_routes.rb +11 -0
- data/lib/devise/hooks/password_expirable.rb +5 -0
- data/lib/devise/models/password_expirable.rb +28 -0
- data/lib/devise/models/remote_authenticatable.rb +48 -0
- data/lib/devise/strategies/remote_authenticatable.rb +44 -0
- data/lib/devise_extension.rb +36 -0
- data/lib/faraday/adapter/net_http_no_proxy.rb +19 -0
- data/lib/generators/mno_enterprise/database_extension/USAGE +11 -0
- data/lib/generators/mno_enterprise/database_extension/database_extension_generator.rb +36 -0
- data/lib/generators/mno_enterprise/database_extension/templates/model.rb +9 -0
- data/lib/generators/mno_enterprise/dummy/dummy_generator.rb +98 -0
- data/lib/generators/mno_enterprise/dummy/templates/rails/application.rb.erb +9 -0
- data/lib/generators/mno_enterprise/dummy/templates/rails/boot.rb.erb +6 -0
- data/lib/generators/mno_enterprise/dummy/templates/rails/database.yml +22 -0
- data/lib/generators/mno_enterprise/dummy/templates/rails/routes.rb +8 -0
- data/lib/generators/mno_enterprise/dummy/templates/rails/test-env.rb +45 -0
- data/lib/generators/mno_enterprise/install/install_generator.rb +140 -0
- data/lib/generators/mno_enterprise/install/templates/Procfile +1 -0
- data/lib/generators/mno_enterprise/install/templates/config/initializers/mno_enterprise.rb +135 -0
- data/lib/generators/mno_enterprise/install/templates/config/mno_enterprise_styleguide.yml +104 -0
- data/lib/generators/mno_enterprise/install/templates/javascripts/mno_enterprise_extensions.js +7 -0
- data/lib/generators/mno_enterprise/install/templates/stylesheets/main.less_erb +25 -0
- data/lib/generators/mno_enterprise/install/templates/stylesheets/theme.less_erb +59 -0
- data/lib/generators/mno_enterprise/install/templates/stylesheets/variables.less +337 -0
- data/lib/generators/mno_enterprise/install/templates/tasks/sprites.rake +14 -0
- data/lib/generators/mno_enterprise/puma_stack/puma_stack_generator.rb +58 -0
- data/lib/generators/mno_enterprise/templates/scripts/monit/app-server.conf +8 -0
- data/lib/generators/mno_enterprise/templates/scripts/nginx/app +51 -0
- data/lib/generators/mno_enterprise/templates/scripts/puma.rb +25 -0
- data/lib/generators/mno_enterprise/templates/scripts/setup.sh +27 -0
- data/lib/generators/mno_enterprise/templates/scripts/upstart/app-web-hotrestart.conf +26 -0
- data/lib/generators/mno_enterprise/templates/scripts/upstart/app-web-server.conf +34 -0
- data/lib/generators/mno_enterprise/templates/scripts/upstart/app-web.conf +2 -0
- data/lib/generators/mno_enterprise/templates/scripts/upstart/app.conf +11 -0
- data/lib/her_extension/her_orm_adapter.rb +54 -0
- data/lib/her_extension/middleware/mnoe_api_v1_parse_json.rb +54 -0
- data/lib/her_extension/model/associations/association.rb +61 -0
- data/lib/her_extension/model/associations/association_proxy.rb +34 -0
- data/lib/her_extension/model/associations/has_many_association.rb +115 -0
- data/lib/her_extension/model/attributes.rb +43 -0
- data/lib/her_extension/model/orm.rb +59 -0
- data/lib/her_extension/model/parse.rb +40 -0
- data/lib/her_extension/model/relation.rb +92 -0
- data/lib/her_extension/validations/remote_uniqueness_validation.rb +33 -0
- data/lib/html_processor.rb +106 -0
- data/lib/mandrill_client.rb +58 -0
- data/lib/mno-enterprise-core.rb +1 -0
- data/lib/mno_enterprise/concerns.rb +4 -0
- data/lib/mno_enterprise/concerns/controllers.rb +6 -0
- data/lib/mno_enterprise/concerns/controllers/angular_csrf.rb +59 -0
- data/lib/mno_enterprise/concerns/controllers/auth.rb +9 -0
- data/lib/mno_enterprise/concerns/controllers/auth/confirmations_controller.rb +187 -0
- data/lib/mno_enterprise/concerns/controllers/auth/passwords_controller.rb +54 -0
- data/lib/mno_enterprise/concerns/controllers/auth/registrations_controller.rb +136 -0
- data/lib/mno_enterprise/concerns/controllers/auth/sessions_controller.rb +54 -0
- data/lib/mno_enterprise/concerns/controllers/auth/unlocks_controller.rb +50 -0
- data/lib/mno_enterprise/concerns/models.rb +6 -0
- data/lib/mno_enterprise/concerns/models/ability.rb +108 -0
- data/lib/mno_enterprise/concerns/models/app_instance.rb +100 -0
- data/lib/mno_enterprise/concerns/models/organization.rb +102 -0
- data/lib/mno_enterprise/core.rb +279 -0
- data/lib/mno_enterprise/database_extendable.rb +57 -0
- data/lib/mno_enterprise/engine.rb +33 -0
- data/lib/mno_enterprise/testing_support/ability_test_helper.rb +10 -0
- data/lib/mno_enterprise/testing_support/common_rake.rb +19 -0
- data/lib/mno_enterprise/testing_support/factories.rb +13 -0
- data/lib/mno_enterprise/testing_support/factories/app_instances.rb +30 -0
- data/lib/mno_enterprise/testing_support/factories/apps.rb +45 -0
- data/lib/mno_enterprise/testing_support/factories/arrears_situation.rb +14 -0
- data/lib/mno_enterprise/testing_support/factories/audit_event.rb +15 -0
- data/lib/mno_enterprise/testing_support/factories/credit_card.rb +33 -0
- data/lib/mno_enterprise/testing_support/factories/deletion_request.rb +17 -0
- data/lib/mno_enterprise/testing_support/factories/impac/dashboards.rb +15 -0
- data/lib/mno_enterprise/testing_support/factories/impac/kpis.rb +20 -0
- data/lib/mno_enterprise/testing_support/factories/impac/widgets.rb +15 -0
- data/lib/mno_enterprise/testing_support/factories/invoices.rb +51 -0
- data/lib/mno_enterprise/testing_support/factories/org_invite.rb +24 -0
- data/lib/mno_enterprise/testing_support/factories/organizations.rb +25 -0
- data/lib/mno_enterprise/testing_support/factories/team.rb +17 -0
- data/lib/mno_enterprise/testing_support/factories/tenant.rb +12 -0
- data/lib/mno_enterprise/testing_support/factories/tenant_invoice.rb +29 -0
- data/lib/mno_enterprise/testing_support/factories/users.rb +48 -0
- data/lib/mno_enterprise/testing_support/jpi_v1_test_helper.rb +49 -0
- data/lib/mno_enterprise/testing_support/mno_enterprise_api_test_helper.rb +167 -0
- data/lib/mno_enterprise/testing_support/mnoe_faraday_test_adapter.rb +173 -0
- data/lib/mno_enterprise/testing_support/organizations_shared_helpers.rb +175 -0
- data/lib/mno_enterprise/testing_support/user_action_shared.rb +47 -0
- data/lib/mno_enterprise/version.rb +3 -0
- data/lib/tasks/mno_enterprise_tasks.rake +22 -0
- data/spec/controllers/mno_enterprise/angular_csrf_spec.rb +42 -0
- data/spec/lib/her_extension/her_orm_adapter.rb +7 -0
- data/spec/lib/her_extension/model/relation_spec.rb +7 -0
- data/spec/lib/mandrill_client_spec.rb +64 -0
- data/spec/mno_enterprise_spec.rb +79 -0
- data/spec/models/mno_enterprise/app_instance_spec.rb +7 -0
- data/spec/models/mno_enterprise/app_spec.rb +62 -0
- data/spec/models/mno_enterprise/base_resource_spec.rb +28 -0
- data/spec/models/mno_enterprise/deletion_request_spec.rb +26 -0
- data/spec/models/mno_enterprise/invoice_spec.rb +7 -0
- data/spec/models/mno_enterprise/organization_spec.rb +7 -0
- data/spec/models/mno_enterprise/user_spec.rb +44 -0
- data/spec/rails_helper.rb +73 -0
- data/spec/spec_helper.rb +78 -0
- metadata +421 -0
@@ -0,0 +1,17 @@
|
|
1
|
+
# Read about factories at https://github.com/thoughtbot/factory_girl
|
2
|
+
|
3
|
+
FactoryGirl.define do
|
4
|
+
factory :mno_enterprise_team, :class => 'MnoEnterprise::Team' do
|
5
|
+
|
6
|
+
factory :team, class: MnoEnterprise::Team do
|
7
|
+
sequence(:id)
|
8
|
+
sequence(:name) { |n| "Team#{n}" }
|
9
|
+
|
10
|
+
created_at 3.days.ago
|
11
|
+
updated_at 1.hour.ago
|
12
|
+
|
13
|
+
# Properly build the resource with Her
|
14
|
+
initialize_with { new(attributes).tap { |e| e.clear_attribute_changes! } }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
FactoryGirl.define do
|
2
|
+
|
3
|
+
factory :tenant, class: MnoEnterprise::Tenant do
|
4
|
+
last_portfolio_amount Money.new(65644,'AUD')
|
5
|
+
last_customers_invoicing_amount Money.new(687994,'AUD')
|
6
|
+
last_customers_outstanding_amount Money.new(178986,'AUD')
|
7
|
+
last_commission_amount Money.new(412345,'AUD')
|
8
|
+
|
9
|
+
# Properly build the resource with Her
|
10
|
+
initialize_with { new(attributes).tap { |e| e.clear_attribute_changes! } }
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# Read about factories at https://github.com/thoughtbot/factory_girl
|
2
|
+
|
3
|
+
FactoryGirl.define do
|
4
|
+
factory :mno_enterprise_tenant_invoice, :class => 'TenantInvoice' do
|
5
|
+
|
6
|
+
|
7
|
+
factory :tenant_invoice, class: MnoEnterprise::TenantInvoice do
|
8
|
+
sequence(:id)
|
9
|
+
sequence(:slug) { |n| "201504-NU#{n}" }
|
10
|
+
organization_id 265
|
11
|
+
|
12
|
+
started_at 28.days.ago
|
13
|
+
ended_at 3.days.ago
|
14
|
+
created_at 3.days.ago
|
15
|
+
updated_at 1.hour.ago
|
16
|
+
paid_at nil
|
17
|
+
|
18
|
+
total_commission_amount Money.new(0,'AUD')
|
19
|
+
total_portfolio_amount Money.new(0,'AUD')
|
20
|
+
non_commissionable_amount Money.new(0,'AUD')
|
21
|
+
mno_commission_amount Money.new(0,'AUD')
|
22
|
+
|
23
|
+
# Properly build the resource with Her
|
24
|
+
initialize_with { new(attributes).tap { |e| e.clear_attribute_changes! } }
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# Read about factories at https://github.com/thoughtbot/factory_girl
|
2
|
+
|
3
|
+
# This is an API resource factory generating a Hash to be used in API stubs
|
4
|
+
# Use as such: build(:api_user)
|
5
|
+
# See http://stackoverflow.com/questions/10032760/how-to-define-an-array-hash-in-factory-girl
|
6
|
+
FactoryGirl.define do
|
7
|
+
|
8
|
+
factory :user, class: MnoEnterprise::User do
|
9
|
+
sequence(:id)
|
10
|
+
sequence(:uid) { |n| "usr-fda9#{n}" }
|
11
|
+
name "John"
|
12
|
+
surname "Doe"
|
13
|
+
sequence(:email) { |n| "john.doe#{n}@maestrano.com" }
|
14
|
+
company "Doe Inc."
|
15
|
+
phone "449 789 456"
|
16
|
+
phone_country_code "AU"
|
17
|
+
geo_country_code "AU"
|
18
|
+
geo_state_code "NSW"
|
19
|
+
geo_city "Sydney"
|
20
|
+
created_at 2.days.ago
|
21
|
+
updated_at 2.days.ago
|
22
|
+
sso_session "1fdd5sf5a73D7sd1as2a4sd541"
|
23
|
+
admin_role false
|
24
|
+
|
25
|
+
confirmation_sent_at 2.days.ago
|
26
|
+
confirmation_token "wky763pGjtzWR7dP44PD"
|
27
|
+
confirmed_at 1.days.ago
|
28
|
+
|
29
|
+
trait :unconfirmed do
|
30
|
+
confirmed_at nil
|
31
|
+
end
|
32
|
+
|
33
|
+
trait :admin do
|
34
|
+
admin_role true
|
35
|
+
end
|
36
|
+
|
37
|
+
trait :with_deletion_request do
|
38
|
+
deletion_request { build(:deletion_request).attributes }
|
39
|
+
end
|
40
|
+
|
41
|
+
trait :with_organizations do
|
42
|
+
organizations { [build(:organization).attributes] }
|
43
|
+
end
|
44
|
+
|
45
|
+
# Properly build the resource with Her
|
46
|
+
initialize_with { new(attributes).tap { |e| e.clear_attribute_changes! } }
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module MnoEnterprise::TestingSupport::JpiV1TestHelper
|
2
|
+
|
3
|
+
# Expect 'user' to be assigned
|
4
|
+
shared_examples "jpi v1 protected action" do
|
5
|
+
context "with guest user" do
|
6
|
+
it "prevents access" do
|
7
|
+
sign_out user
|
8
|
+
expect(subject).to_not be_successful
|
9
|
+
expect(subject.code).to eq('401')
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'with signed in user' do
|
14
|
+
it "authorizes access" do
|
15
|
+
sign_in user
|
16
|
+
expect(subject).to be_successful
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Expect 'user' to be assigned
|
22
|
+
# Expect 'ability' to be assigned
|
23
|
+
shared_examples "jpi v1 authorizable action" do
|
24
|
+
context "with guest user" do
|
25
|
+
it "prevents access" do
|
26
|
+
sign_out user
|
27
|
+
expect(subject).to_not be_successful
|
28
|
+
expect(subject.code).to eq('401')
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'with unauthorized signed in user' do
|
33
|
+
it "prevents access" do
|
34
|
+
sign_in user
|
35
|
+
allow(ability).to receive(:can?).with(any_args).and_return(false)
|
36
|
+
expect(subject).to_not be_successful
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'with authorized signed in user' do
|
41
|
+
it "authorizes access" do
|
42
|
+
sign_in user
|
43
|
+
allow(ability).to receive(:can?).with(any_args).and_return(true)
|
44
|
+
expect(subject).to be_successful
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
module MnoEnterpriseApiTestHelper
|
2
|
+
|
3
|
+
# Take a resource and transform it into a Hash describing
|
4
|
+
# the resource as if it had been returned by the MnoEnterprise
|
5
|
+
# API server
|
6
|
+
def from_api(res)
|
7
|
+
{ data: serialize_type(res), metadata: {pagination: {count: entity_count(res)}} }
|
8
|
+
end
|
9
|
+
|
10
|
+
def serialize_type(res)
|
11
|
+
case
|
12
|
+
when res.kind_of?(Array)
|
13
|
+
return res.map { |e| serialize_type(e) }
|
14
|
+
when res.kind_of?(MnoEnterprise::BaseResource)
|
15
|
+
hash = res.attributes.dup
|
16
|
+
hash.each do |k,v|
|
17
|
+
hash[k] = serialize_type(v)
|
18
|
+
end
|
19
|
+
return hash
|
20
|
+
when res.kind_of?(Hash)
|
21
|
+
hash = res.dup
|
22
|
+
hash.each do |k,v|
|
23
|
+
hash[k] = serialize_type(v)
|
24
|
+
end
|
25
|
+
return hash
|
26
|
+
when res.kind_of?(Money)
|
27
|
+
return { cents: res.cents, currency: res.currency_as_string }
|
28
|
+
when res.respond_to?(:iso8601)
|
29
|
+
return res.iso8601
|
30
|
+
else
|
31
|
+
return res
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def entity_count(res)
|
36
|
+
case
|
37
|
+
when res.kind_of?(Array)
|
38
|
+
return res.count
|
39
|
+
when res.kind_of?(Hash)
|
40
|
+
return res.count
|
41
|
+
else
|
42
|
+
return 1
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Reset all API stubs.
|
47
|
+
# Called before each test (see spec_helper)
|
48
|
+
def api_stub_reset
|
49
|
+
@_api_stub = nil
|
50
|
+
@_stub_list = {}
|
51
|
+
api_stub_configure(Her::API.new)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Example usage:
|
55
|
+
#
|
56
|
+
# Without opts, it yields a faraday stub object which you can configure
|
57
|
+
# manually:
|
58
|
+
#
|
59
|
+
# You can also pass the response stub via opts
|
60
|
+
# api_stub_for(User,
|
61
|
+
# path: '/users/popular',
|
62
|
+
# response: [{ id: 1, name: "Tobias Fünke" }, { id: 2, name: "Lindsay Fünke" }]
|
63
|
+
# )
|
64
|
+
#
|
65
|
+
# You can also specify the response code:
|
66
|
+
# api_stub_for(User,
|
67
|
+
# path: '/users/popular',
|
68
|
+
# code: 200,
|
69
|
+
# response: [{ id: 1, name: "Tobias Fünke" }, { id: 2, name: "Lindsay Fünke" }]
|
70
|
+
# )
|
71
|
+
def api_stub_for(klass, opts = {})
|
72
|
+
real_opts = klass
|
73
|
+
if klass.is_a?(Class)
|
74
|
+
warn("DEPRECATION WARNING: api_stub_for(MyClass,{ some: 'opts'}) is deprecated. Please use api_stub_for({ some: 'opts' }) from now on")
|
75
|
+
real_opts = opts
|
76
|
+
end
|
77
|
+
|
78
|
+
set_api_stub
|
79
|
+
api_stub_add(real_opts)
|
80
|
+
api_stub_configure(@_api_stub)
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
# Set a stub api on the provider class
|
85
|
+
def set_api_stub
|
86
|
+
return @_api_stub if @_api_stub
|
87
|
+
@_api_stub = Her::API.new
|
88
|
+
allow(MnoEnterprise::BaseResource).to receive(:her_api).and_return(@_api_stub = Her::API.new)
|
89
|
+
@_api_stub
|
90
|
+
end
|
91
|
+
|
92
|
+
# Add a stub to the api
|
93
|
+
# E.g.:
|
94
|
+
# {
|
95
|
+
# path: '/users/popular',
|
96
|
+
# code: 200,
|
97
|
+
# response: [{ id: 1, name: "Tobias Fünke" }, { id: 2, name: "Lindsay Fünke" }]
|
98
|
+
# }
|
99
|
+
def api_stub_add(orig_opts)
|
100
|
+
@_stub_list ||= {}
|
101
|
+
opts = orig_opts.dup
|
102
|
+
|
103
|
+
# Expand options so that: { put: '/path' } becomes { path: '/path', method: :put }
|
104
|
+
unless opts[:method] && opts[:path]
|
105
|
+
[:get,:put,:post,:delete].each do |verb|
|
106
|
+
if path = opts.delete(verb)
|
107
|
+
opts[:path] = path
|
108
|
+
opts[:method] = verb
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
key = opts.to_param
|
114
|
+
@_stub_list[key] = opts
|
115
|
+
end
|
116
|
+
|
117
|
+
# Configure the api and apply a list of stubs
|
118
|
+
def api_stub_configure(api)
|
119
|
+
# This block should match the her.rb initializer
|
120
|
+
api.setup MnoEnterprise.send(:api_options).merge(url: "http://localhost:65000") do |c|
|
121
|
+
# Request
|
122
|
+
c.use Faraday::Request::BasicAuthentication, MnoEnterprise.tenant_id, MnoEnterprise.tenant_key
|
123
|
+
c.use Faraday::Request::UrlEncoded
|
124
|
+
|
125
|
+
# Response
|
126
|
+
c.use Her::Middleware::MnoeApiV1ParseJson
|
127
|
+
|
128
|
+
# Add stubs on the test adapter
|
129
|
+
c.use MnoeFaradayTestAdapter do |receiver|
|
130
|
+
@_stub_list.each do |key,stub|
|
131
|
+
params = stub[:params] && stub[:params].any? ? "?#{stub[:params].to_param}" : ""
|
132
|
+
path = "#{stub[:path]}#{params}"
|
133
|
+
|
134
|
+
receiver.send(stub[:method] || :get,path) { |env|
|
135
|
+
body = Rack::Utils.parse_nested_query(env.body)
|
136
|
+
|
137
|
+
# respond_with takes a model in argument and automatically responds with
|
138
|
+
# a json representation of the model
|
139
|
+
# If the action is an update, it attempts to update the model
|
140
|
+
if model = stub[:respond_with]
|
141
|
+
model.assign_attributes(body['data']) if stub[:method] == :put && model.respond_to?(:assign_attributes) && body['data']
|
142
|
+
resp = from_api(model)
|
143
|
+
else
|
144
|
+
if stub[:response].is_a?(Proc)
|
145
|
+
args = stub[:response].arity > 0 ? [body] : []
|
146
|
+
resp = stub[:response].call(*args)
|
147
|
+
else
|
148
|
+
resp = stub[:response] || {}
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# Response code
|
153
|
+
if stub[:code].is_a?(Proc)
|
154
|
+
args = stub[:code].arity > 0 ? [body] : []
|
155
|
+
resp_code = stub[:code].call(*args)
|
156
|
+
else
|
157
|
+
resp_code = stub[:code] || 200
|
158
|
+
end
|
159
|
+
|
160
|
+
|
161
|
+
[resp_code, {}, resp.to_json]
|
162
|
+
}
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
# Adapter and modified from: https://github.com/lostisland/faraday/blob/master/lib/faraday/adapter/test.rb
|
2
|
+
#
|
3
|
+
#
|
4
|
+
#
|
5
|
+
# test = Faraday::Connection.new do
|
6
|
+
# use MnoeFaradayTestAdapter do |stub|
|
7
|
+
# stub.get '/nigiri/sake.json' do
|
8
|
+
# [200, {}, 'hi world']
|
9
|
+
# end
|
10
|
+
# end
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# resp = test.get '/nigiri/sake.json'
|
14
|
+
# resp.body # => 'hi world'
|
15
|
+
#
|
16
|
+
class MnoeFaradayTestAdapter < Faraday::Adapter
|
17
|
+
attr_accessor :stubs
|
18
|
+
|
19
|
+
class Stubs
|
20
|
+
class NotFound < StandardError
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize
|
24
|
+
# {:get => [Stub, Stub]}
|
25
|
+
@stack, @consumed = {}, {}
|
26
|
+
yield(self) if block_given?
|
27
|
+
end
|
28
|
+
|
29
|
+
def empty?
|
30
|
+
@stack.empty?
|
31
|
+
end
|
32
|
+
|
33
|
+
def match(request_method, path, headers, body)
|
34
|
+
return false if !@stack.key?(request_method)
|
35
|
+
stack = @stack[request_method]
|
36
|
+
consumed = (@consumed[request_method] ||= [])
|
37
|
+
|
38
|
+
if stub = matches?(stack, path, headers, body)
|
39
|
+
consumed << stack.delete(stub)
|
40
|
+
stub
|
41
|
+
else
|
42
|
+
matches?(consumed, path, headers, body)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def get(path, headers = {}, &block)
|
47
|
+
new_stub(:get, path, headers, &block)
|
48
|
+
end
|
49
|
+
|
50
|
+
def head(path, headers = {}, &block)
|
51
|
+
new_stub(:head, path, headers, &block)
|
52
|
+
end
|
53
|
+
|
54
|
+
def post(path, body=nil, headers = {}, &block)
|
55
|
+
new_stub(:post, path, headers, body, &block)
|
56
|
+
end
|
57
|
+
|
58
|
+
def put(path, body=nil, headers = {}, &block)
|
59
|
+
new_stub(:put, path, headers, body, &block)
|
60
|
+
end
|
61
|
+
|
62
|
+
def patch(path, body=nil, headers = {}, &block)
|
63
|
+
new_stub(:patch, path, headers, body, &block)
|
64
|
+
end
|
65
|
+
|
66
|
+
def delete(path, headers = {}, &block)
|
67
|
+
new_stub(:delete, path, headers, &block)
|
68
|
+
end
|
69
|
+
|
70
|
+
def options(path, headers = {}, &block)
|
71
|
+
new_stub(:options, path, headers, &block)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Raises an error if any of the stubbed calls have not been made.
|
75
|
+
def verify_stubbed_calls
|
76
|
+
failed_stubs = []
|
77
|
+
@stack.each do |method, stubs|
|
78
|
+
unless stubs.size == 0
|
79
|
+
failed_stubs.concat(stubs.map {|stub|
|
80
|
+
"Expected #{method} #{stub}."
|
81
|
+
})
|
82
|
+
end
|
83
|
+
end
|
84
|
+
raise failed_stubs.join(" ") unless failed_stubs.size == 0
|
85
|
+
end
|
86
|
+
|
87
|
+
protected
|
88
|
+
|
89
|
+
def new_stub(request_method, path, headers = {}, body=nil, &block)
|
90
|
+
normalized_path = Faraday::Utils.normalize_path(path)
|
91
|
+
(@stack[request_method] ||= []) << Stub.new(normalized_path, headers, body, block)
|
92
|
+
end
|
93
|
+
|
94
|
+
def matches?(stack, path, headers, body)
|
95
|
+
stack.detect { |stub| stub.matches?(path, headers, body) }
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
class Stub < Struct.new(:path, :params, :headers, :body, :block)
|
100
|
+
def initialize(full, headers, body, block)
|
101
|
+
path, query = full.split('?')
|
102
|
+
params = query ?
|
103
|
+
Faraday::Utils.parse_nested_query(query) :
|
104
|
+
{}
|
105
|
+
super(path, params, headers, body, block)
|
106
|
+
end
|
107
|
+
|
108
|
+
def matches?(request_uri, request_headers, request_body)
|
109
|
+
request_path, request_query = request_uri.split('?')
|
110
|
+
request_params = request_query ?
|
111
|
+
Faraday::Utils.parse_nested_query(request_query) :
|
112
|
+
{}
|
113
|
+
request_path == path &&
|
114
|
+
params_match?(request_params) &&
|
115
|
+
(body.to_s.size.zero? || request_body == body) &&
|
116
|
+
headers_match?(request_headers)
|
117
|
+
end
|
118
|
+
|
119
|
+
def params_match?(request_params)
|
120
|
+
params_deep_equal?(params,request_params)
|
121
|
+
# params.keys.all? do |key|
|
122
|
+
# request_params[key] == params[key]
|
123
|
+
# end
|
124
|
+
end
|
125
|
+
|
126
|
+
def params_deep_equal?(src,dst)
|
127
|
+
src.keys.all? do |key|
|
128
|
+
if src[key] && dst[key] && src[key].is_a?(Hash) && dst[key].is_a?(Hash)
|
129
|
+
params_deep_equal?(src[key],dst[key])
|
130
|
+
else
|
131
|
+
(src[key] == '**' && dst[key]) || src[key] == dst[key]
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def headers_match?(request_headers)
|
137
|
+
headers.keys.all? do |key|
|
138
|
+
request_headers[key] == headers[key]
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def to_s
|
143
|
+
"#{path} #{body}"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def initialize(app, stubs=nil, &block)
|
148
|
+
super(app)
|
149
|
+
@stubs = stubs || Stubs.new
|
150
|
+
configure(&block) if block
|
151
|
+
end
|
152
|
+
|
153
|
+
def configure
|
154
|
+
yield(stubs)
|
155
|
+
end
|
156
|
+
|
157
|
+
def call(env)
|
158
|
+
super
|
159
|
+
normalized_path = Faraday::Utils.normalize_path(env[:url])
|
160
|
+
params_encoder = env.request.params_encoder || Faraday::Utils.default_params_encoder
|
161
|
+
|
162
|
+
if stub = stubs.match(env[:method], normalized_path, env.request_headers, env[:body])
|
163
|
+
env[:params] = (query = env[:url].query) ?
|
164
|
+
params_encoder.decode(query) :
|
165
|
+
{}
|
166
|
+
status, headers, body = stub.block.call(env)
|
167
|
+
save_response(env, status, body, headers)
|
168
|
+
else
|
169
|
+
raise Stubs::NotFound, "no stubbed request for #{env[:method]} #{normalized_path} #{env[:body]}"
|
170
|
+
end
|
171
|
+
@app.call(env)
|
172
|
+
end
|
173
|
+
end
|