foreman_scc_manager 1.6.1 → 1.6.2
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.
- checksums.yaml +4 -4
- data/app/controllers/scc_accounts_controller.rb +17 -11
- data/app/lib/actions/scc_manager/subscribe_product.rb +1 -0
- data/app/lib/actions/scc_manager/sync.rb +1 -0
- data/app/lib/actions/scc_manager/sync_plan_account_repositories.rb +38 -0
- data/app/lib/actions/scc_manager/sync_repositories.rb +3 -2
- data/app/lib/scc_manager.rb +1 -0
- data/app/models/concerns/recurring_logic_extensions.rb +9 -0
- data/app/models/scc_account.rb +123 -1
- data/app/models/scc_account_sync_plan_task_group.rb +7 -0
- data/app/models/scc_product.rb +1 -0
- data/app/views/scc_account_sync_plan_task_groups/_scc_account_sync_plan_task_groups.html.erb +4 -0
- data/app/views/scc_accounts/_form.html.erb +4 -0
- data/config/routes.rb +1 -0
- data/db/migrate/20190417202427_add_recurring_sync.foreman_scc_manager.rb +26 -0
- data/lib/foreman_scc_manager/engine.rb +4 -0
- data/lib/foreman_scc_manager/version.rb +1 -1
- data/lib/tasks/rubocop.rake +32 -0
- data/lib/tasks/test.rake +26 -0
- data/test/features/data_products_page1.json +10468 -0
- data/test/features/data_products_page2.json +11277 -0
- data/test/features/data_subscriptions.json +88 -0
- data/test/features/sync_test.rb +141 -0
- data/test/fixtures/models/scc_accounts.yml +24 -0
- data/test/fixtures/models/scc_products.yml +21 -0
- data/test/models/scc_account_test.rb +74 -0
- data/test/models/scc_product_test.rb +32 -0
- data/test/support/fixtures_support.rb +12 -0
- data/test/test_plugin_helper.rb +54 -0
- metadata +43 -5
- data/lib/tasks/foreman_scc_manager_tasks.rake +0 -45
@@ -0,0 +1,88 @@
|
|
1
|
+
[
|
2
|
+
{
|
3
|
+
"id": 1,
|
4
|
+
"regcode": "0123456789ABCD",
|
5
|
+
"name": "Expired Product number one",
|
6
|
+
"type": "evaluation",
|
7
|
+
"status": "EXPIRED",
|
8
|
+
"starts_at": "2014-11-13T00:00:00.000Z",
|
9
|
+
"expires_at": "2015-01-12T00:00:00.000Z",
|
10
|
+
"system_limit": 1,
|
11
|
+
"systems_count": 3,
|
12
|
+
"virtual_count": null,
|
13
|
+
"product_classes": [
|
14
|
+
"HPC-X86",
|
15
|
+
"7261"
|
16
|
+
],
|
17
|
+
"product_ids": [
|
18
|
+
1234,
|
19
|
+
5678
|
20
|
+
],
|
21
|
+
"skus": [],
|
22
|
+
"systems": [
|
23
|
+
{
|
24
|
+
"id": 3,
|
25
|
+
"login": "SCC_0123456789abcdef0123456789abcdef",
|
26
|
+
"password": "fedcba9876543210",
|
27
|
+
"last_seen_at": null
|
28
|
+
},
|
29
|
+
{
|
30
|
+
"id": 4,
|
31
|
+
"login": "SCC_123456789abcdef0123456789abcdef0",
|
32
|
+
"password": "edcba9876543210f",
|
33
|
+
"last_seen_at": null
|
34
|
+
},
|
35
|
+
{
|
36
|
+
"id": 5,
|
37
|
+
"login": "SCC_23456789abcdef0123456789abcdef01",
|
38
|
+
"password": "dcba9876543210fe",
|
39
|
+
"last_seen_at": null
|
40
|
+
}
|
41
|
+
]
|
42
|
+
},
|
43
|
+
{
|
44
|
+
"id": 2,
|
45
|
+
"regcode": "123456789ABCDE",
|
46
|
+
"name": "Some other Product which is ACTIVE",
|
47
|
+
"type": "full",
|
48
|
+
"status": "ACTIVE",
|
49
|
+
"starts_at": "2014-03-13T00:00:00.000Z",
|
50
|
+
"expires_at": "2020-01-31T00:00:00.000Z",
|
51
|
+
"system_limit": 1,
|
52
|
+
"systems_count": 0,
|
53
|
+
"virtual_count": null,
|
54
|
+
"product_classes": [
|
55
|
+
"VMDP"
|
56
|
+
],
|
57
|
+
"product_ids": [
|
58
|
+
1337
|
59
|
+
],
|
60
|
+
"skus": [],
|
61
|
+
"systems": []
|
62
|
+
},
|
63
|
+
{
|
64
|
+
"id": 3,
|
65
|
+
"regcode": "23456789ABCDEF",
|
66
|
+
"name": "Yet Another ACTIVE SUSE Product",
|
67
|
+
"type": "full",
|
68
|
+
"status": "ACTIVE",
|
69
|
+
"starts_at": "2014-03-13T00:00:00.000Z",
|
70
|
+
"expires_at": "2020-01-31T00:00:00.000Z",
|
71
|
+
"system_limit": 1,
|
72
|
+
"systems_count": 0,
|
73
|
+
"virtual_count": null,
|
74
|
+
"product_classes": [
|
75
|
+
"HPC-X86",
|
76
|
+
"SUSE_RT",
|
77
|
+
"7261",
|
78
|
+
"13319"
|
79
|
+
],
|
80
|
+
"product_ids": [
|
81
|
+
123,
|
82
|
+
456,
|
83
|
+
789
|
84
|
+
],
|
85
|
+
"skus": [],
|
86
|
+
"systems": []
|
87
|
+
}
|
88
|
+
]
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'test_plugin_helper'
|
2
|
+
|
3
|
+
class SccAccountSyncTest < ActiveSupport::TestCase
|
4
|
+
# rubocop:disable Metrics/MethodLength
|
5
|
+
def setup
|
6
|
+
# test_connection:
|
7
|
+
stub_request(:get, 'https://scc.example.com/connect/organizations/subscriptions')
|
8
|
+
.with(
|
9
|
+
headers: {
|
10
|
+
'Accept' => 'application/vnd.scc.suse.com.v4+json',
|
11
|
+
'Accept-Encoding' => 'gzip, deflate',
|
12
|
+
'Authorization' => 'Basic b25ldXNlcjpvbmVwYXNz',
|
13
|
+
'Host' => 'scc.example.com'
|
14
|
+
}
|
15
|
+
).to_return(
|
16
|
+
status: 200,
|
17
|
+
body: Zlib.gzip(File.read("#{File.dirname(__FILE__)}/data_subscriptions.json")),
|
18
|
+
headers: {
|
19
|
+
server: 'nginx',
|
20
|
+
date: 'Tue, 05 Mar 2019 15:07:38 GMT',
|
21
|
+
content_type: 'application/json; charset=utf-8',
|
22
|
+
transfer_encoding: 'chunked',
|
23
|
+
connection: 'keep-alive',
|
24
|
+
vary: 'Accept-Encoding',
|
25
|
+
x_frame_options: 'SAMEORIGIN',
|
26
|
+
x_xss_protection: '1; mode=block',
|
27
|
+
x_content_type_options: 'nosniff',
|
28
|
+
per_page: '10',
|
29
|
+
total: '3',
|
30
|
+
scc_api_version: 'v4',
|
31
|
+
etag: 'W/"0123456789abcdef0123456789abcdef"',
|
32
|
+
cache_control: 'max-age=0, private, must-revalidate',
|
33
|
+
set_cookie: [
|
34
|
+
'XSRF-TOKEN=TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBp%0Ac2NpbmcgZWxpdCwgc2VkIGRvIGVpdXNtb2QgdGVtcG9yIGluY2l; path=/; secure',
|
35
|
+
'Uy#u~osh#oh3ahv.op0OII; Expires=Fri, 02-Mar-2029 15:07:20 GMT; Path=/'
|
36
|
+
],
|
37
|
+
x_request_id: '67450237-e4aa-4994-a47d-ed3ce142555b',
|
38
|
+
x_runtime: '0.144083',
|
39
|
+
strict_transport_security: 'max-age=15552000, max-age=300',
|
40
|
+
content_encoding: 'gzip'
|
41
|
+
}
|
42
|
+
)
|
43
|
+
############
|
44
|
+
# Products #
|
45
|
+
############
|
46
|
+
# products page1
|
47
|
+
stub_request(:get, 'https://scc.example.com/connect/organizations/products')
|
48
|
+
.with(
|
49
|
+
headers: {
|
50
|
+
'Accept' => 'application/vnd.scc.suse.com.v4+json',
|
51
|
+
'Accept-Encoding' => 'gzip, deflate',
|
52
|
+
'Authorization' => 'Basic b25ldXNlcjpvbmVwYXNz',
|
53
|
+
'Host' => 'scc.example.com'
|
54
|
+
}
|
55
|
+
).to_return(
|
56
|
+
status: 200,
|
57
|
+
body: Zlib.gzip(File.read("#{File.dirname(__FILE__)}/data_products_page1.json")),
|
58
|
+
headers: {
|
59
|
+
server: 'nginx',
|
60
|
+
date: 'Mon, 11 Mar 2019 15:37:00 GMT',
|
61
|
+
content_type: 'application/json; charset=utf-8',
|
62
|
+
transfer_encoding: 'chunked',
|
63
|
+
connection: 'keep-alive',
|
64
|
+
vary: 'Accept-Encoding',
|
65
|
+
x_frame_options: 'SAMEORIGIN',
|
66
|
+
x_xss_protection: '1; mode=block',
|
67
|
+
x_content_type_options: 'nosniff',
|
68
|
+
link: '<https://scc.example.com/connect/organizations/products?page=2>; rel="last", <https://scc.example.com/connect/organizations/products?page=2>; rel="next"',
|
69
|
+
per_page: 25,
|
70
|
+
total: 50,
|
71
|
+
scc_api_version: 'v4',
|
72
|
+
etag: '57fbfddfb5cc165b2581d297cad27a53',
|
73
|
+
cache_control: 'max-age=0, private, must-revalidate',
|
74
|
+
set_cookie: [
|
75
|
+
'XSRF-TOKEN=EABKsiefcpa7dMNEXRixmihKeUfIvXF4AwmNQt2wZG5Fm%2FPKvR0%2FMBDVV5lZJ3p4waUAcds2xWv42vbKg9GQhg%3D%3D; path=/; secure',
|
76
|
+
'TbBx+jfg=v1jitvAA@@UII; Expires=Thu, 08-Mar-2029 15:37:15 GMT; Path=/'
|
77
|
+
],
|
78
|
+
x_request_id: 'd2797941-1aed-499c-8e06-b4cb52515443',
|
79
|
+
x_runtime: '6.671012',
|
80
|
+
strict_transport_security: 'max-age=15552000, max-age=300',
|
81
|
+
content_encoding: 'gzip'
|
82
|
+
}
|
83
|
+
)
|
84
|
+
# products page2
|
85
|
+
stub_request(:get, 'https://scc.example.com/connect/organizations/products?page=2')
|
86
|
+
.with(
|
87
|
+
headers: {
|
88
|
+
'Accept' => 'application/vnd.scc.suse.com.v4+json',
|
89
|
+
'Accept-Encoding' => 'gzip, deflate',
|
90
|
+
'Authorization' => 'Basic b25ldXNlcjpvbmVwYXNz',
|
91
|
+
'Host' => 'scc.example.com'
|
92
|
+
}
|
93
|
+
).to_return(
|
94
|
+
status: 200,
|
95
|
+
body: Zlib.gzip(File.read("#{File.dirname(__FILE__)}/data_products_page2.json")),
|
96
|
+
headers: {
|
97
|
+
server: 'nginx',
|
98
|
+
date: 'Mon, 11 Mar 2019 15:37:19 GMT',
|
99
|
+
content_type: 'application/json; charset=utf-8',
|
100
|
+
transfer_encoding: 'chunked',
|
101
|
+
connection: 'keep-alive',
|
102
|
+
vary: 'Accept-Encoding',
|
103
|
+
x_frame_options: 'SAMEORIGIN',
|
104
|
+
x_xss_protection: '1; mode=block',
|
105
|
+
x_content_type_options: 'nosniff',
|
106
|
+
link: '<https://scc.example.com/connect/organizations/products?page=1>; rel="first", <https://scc.example.com/connect/organizations/products?page=1>; rel="prev"',
|
107
|
+
per_page: 3,
|
108
|
+
total: 2,
|
109
|
+
scc_api_version: 'v4',
|
110
|
+
etag: '3fb638e3ab553dc6c88ef9914540b4bd',
|
111
|
+
cache_control: 'max-age=0, private, must-revalidate',
|
112
|
+
set_cookie: [
|
113
|
+
'XSRF-TOKEN=z3bGc45lQxf%2FXq7qN7cwzJrK1zcw4e7uuskVCPejeN0zv3ExUcb8ev3jhGnDGJaSz3ZwV7Dk0SdLII%2FOcI2eEw%3D%3D; path=/; secure',
|
114
|
+
'TbBx+jfg=v1oytvAA@@I73; Expires=Thu, 08-Mar-2029 15:37:23 GMT; Path=/'
|
115
|
+
],
|
116
|
+
x_request_id: '17e6707a-1134-403d-a49c-7344442446c1',
|
117
|
+
x_runtime: '6.671012',
|
118
|
+
strict_transport_security: 'max-age=15552000, max-age=300',
|
119
|
+
content_encoding: 'gzip'
|
120
|
+
}
|
121
|
+
)
|
122
|
+
end
|
123
|
+
# rubocop:enable Metrics/MethodLength
|
124
|
+
|
125
|
+
test 'SCC server connection-test' do
|
126
|
+
assert scc_accounts(:one).test_connection
|
127
|
+
end
|
128
|
+
|
129
|
+
test 'SCC server sync products new' do
|
130
|
+
scc_account = scc_accounts(:one)
|
131
|
+
assert scc_account
|
132
|
+
products = ::SccManager.get_scc_data(
|
133
|
+
scc_account.base_url,
|
134
|
+
'/connect/organizations/products',
|
135
|
+
scc_account.login,
|
136
|
+
scc_account.password
|
137
|
+
)
|
138
|
+
assert_not_nil(products)
|
139
|
+
assert_equal(50, products.length)
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
---
|
2
|
+
one:
|
3
|
+
login: oneuser
|
4
|
+
password: onepass
|
5
|
+
base_url: https://scc.example.com
|
6
|
+
name: onename
|
7
|
+
interval: never
|
8
|
+
organization_id: <%= ActiveRecord::FixtureSet.identify(:empty_organization) %>
|
9
|
+
|
10
|
+
two:
|
11
|
+
login: twouser
|
12
|
+
password: twopass
|
13
|
+
base_url: https://scc.example.com
|
14
|
+
name: twoname
|
15
|
+
interval: never
|
16
|
+
organization_id: <%= ActiveRecord::FixtureSet.identify(:empty_organization) %>
|
17
|
+
|
18
|
+
account_missing_url:
|
19
|
+
login: fakeuser1
|
20
|
+
password: fakepass1
|
21
|
+
name: fake1
|
22
|
+
interval: never
|
23
|
+
organization_id: <%= ActiveRecord::FixtureSet.identify(:empty_organization) %>
|
24
|
+
...
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
|
2
|
+
|
3
|
+
one:
|
4
|
+
scc_account_id: test_account1
|
5
|
+
scc_id: 111
|
6
|
+
name: one
|
7
|
+
version: 1
|
8
|
+
arch: x86_128
|
9
|
+
friendly_name: number one
|
10
|
+
description: lorem ipsum dolor sit amet
|
11
|
+
product_type: base
|
12
|
+
|
13
|
+
two:
|
14
|
+
scc_account_id: test_account1
|
15
|
+
scc_id: 222
|
16
|
+
name: two
|
17
|
+
version: 2
|
18
|
+
arch: x86_128
|
19
|
+
friendly_name: number two
|
20
|
+
description: lorem ipsum dolor sit amet
|
21
|
+
product_type: extras
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'test_plugin_helper'
|
2
|
+
|
3
|
+
class SccAccountCreateTest < ActiveSupport::TestCase
|
4
|
+
def setup
|
5
|
+
@account = scc_accounts(:one)
|
6
|
+
end
|
7
|
+
|
8
|
+
test 'create' do
|
9
|
+
assert @account.save
|
10
|
+
refute_empty SccAccount.where(:id => @account.id)
|
11
|
+
end
|
12
|
+
|
13
|
+
test 'create default url' do
|
14
|
+
account = scc_accounts(:account_missing_url)
|
15
|
+
assert account.save
|
16
|
+
assert_equal account.base_url, 'https://scc.suse.com'
|
17
|
+
refute_empty SccAccount.where(:id => @account.id)
|
18
|
+
end
|
19
|
+
|
20
|
+
test 'create missing value' do
|
21
|
+
list = {
|
22
|
+
name: 'Not Working',
|
23
|
+
organization: get_organization,
|
24
|
+
# base_url has a default value set in DB
|
25
|
+
# base_url: 'https://scc.example.org',
|
26
|
+
login: 'account1',
|
27
|
+
password: 'secret'
|
28
|
+
}
|
29
|
+
|
30
|
+
# for every key in hash try to create account without it set
|
31
|
+
list.each_key do |k|
|
32
|
+
l = list.clone
|
33
|
+
l.delete(k)
|
34
|
+
assert_raises ActiveRecord::RecordInvalid do
|
35
|
+
SccAccount.new(l).save!
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
test 'create wrong interval-value' do
|
41
|
+
account = scc_accounts(:two)
|
42
|
+
account.interval = 'gazillion'
|
43
|
+
assert_not account.save
|
44
|
+
end
|
45
|
+
|
46
|
+
test 'password is saved encrypted when updated' do
|
47
|
+
assert SccAccount.encrypts? :password
|
48
|
+
@account.expects(:encryption_key).at_least_once.returns('25d224dd383e92a7e0c82b8bf7c985e815f34cf5')
|
49
|
+
@account.password = '123456'
|
50
|
+
as_admin do
|
51
|
+
assert @account.save
|
52
|
+
end
|
53
|
+
assert_equal @account.password, '123456'
|
54
|
+
refute_equal @account.password_in_db, '123456'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class SccAccountSearchTest < ActiveSupport::TestCase
|
59
|
+
test 'default ordered by login' do
|
60
|
+
assert_equal SccAccount.all.pluck(:login), ['oneuser', 'twouser', 'fakeuser1'].sort
|
61
|
+
end
|
62
|
+
|
63
|
+
test 'search login' do
|
64
|
+
one = scc_accounts(:one)
|
65
|
+
accounts = SccAccount.search_for("login = \"#{one.login}\"")
|
66
|
+
assert_includes accounts, one
|
67
|
+
refute_includes accounts, scc_accounts(:two)
|
68
|
+
|
69
|
+
empty = SccAccount.search_for('login = "nobody"')
|
70
|
+
assert_empty empty
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# FIXME: test cascaded delete
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'test_plugin_helper'
|
2
|
+
|
3
|
+
class SccProductCreateTest < ActiveSupport::TestCase
|
4
|
+
def setup
|
5
|
+
@product = scc_products(:one)
|
6
|
+
end
|
7
|
+
|
8
|
+
test 'create' do
|
9
|
+
assert @product.save
|
10
|
+
refute_empty SccProduct.where(id: @product.id)
|
11
|
+
end
|
12
|
+
|
13
|
+
test 'uniq_name' do
|
14
|
+
assert_equal @product.uniq_name, '111 number one'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class SccProductSearchTest < ActiveSupport::TestCase
|
19
|
+
test 'default ordered by name' do
|
20
|
+
assert_equal SccProduct.all.pluck(:name), ['one', 'two'].sort
|
21
|
+
end
|
22
|
+
|
23
|
+
test 'search name' do
|
24
|
+
one = scc_products(:one)
|
25
|
+
products = SccProduct.search_for("name = \"#{one.name}\"")
|
26
|
+
assert_includes products, one
|
27
|
+
refute_includes products, scc_products(:two)
|
28
|
+
|
29
|
+
empty = SccProduct.search_for('name = "nothing"')
|
30
|
+
assert_empty empty
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module ForemanSccManager
|
2
|
+
module FixturesSupport
|
3
|
+
FIXTURE_CLASSES = {
|
4
|
+
scc_accounts: ForemanSccManager::SccAccount,
|
5
|
+
scc_products: ForemanSccManager::SccProduct
|
6
|
+
}.freeze
|
7
|
+
|
8
|
+
def self.set_fixture_classes(test_class)
|
9
|
+
FIXTURE_CLASSES.each { |k, v| test_class.set_fixture_class(k => v) }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
data/test/test_plugin_helper.rb
CHANGED
@@ -1,2 +1,56 @@
|
|
1
1
|
# This calls the main test_helper in Foreman-core
|
2
2
|
require 'test_helper'
|
3
|
+
|
4
|
+
require 'foreman_tasks/test_helpers'
|
5
|
+
require "#{ForemanSccManager::Engine.root}/test/support/fixtures_support"
|
6
|
+
|
7
|
+
module FixtureTestCase
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
included do
|
11
|
+
extend ActiveRecord::TestFixtures
|
12
|
+
|
13
|
+
self.use_instantiated_fixtures = false
|
14
|
+
self.pre_loaded_fixtures = true
|
15
|
+
|
16
|
+
ForemanSccManager::FixturesSupport.set_fixture_classes(self)
|
17
|
+
|
18
|
+
# Fixtures are copied into a separate path to combine with Foreman fixtures. This directory
|
19
|
+
# is kept out of version control.
|
20
|
+
self.fixture_path = "#{Rails.root}/tmp/combined_fixtures/"
|
21
|
+
FileUtils.rm_rf(self.fixture_path) if File.directory?(self.fixture_path)
|
22
|
+
Dir.mkdir(self.fixture_path)
|
23
|
+
FileUtils.cp(Dir.glob("#{ForemanSccManager::Engine.root}/test/fixtures/models/*"), self.fixture_path)
|
24
|
+
FileUtils.cp(Dir.glob("#{Rails.root}/test/fixtures/*"), self.fixture_path)
|
25
|
+
fixtures(:all)
|
26
|
+
FIXTURES = load_fixtures(ActiveRecord::Base)
|
27
|
+
|
28
|
+
Setting::Content.load_defaults
|
29
|
+
|
30
|
+
User.current = ::User.unscoped.find(FIXTURES['users']['admin']['id'])
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class ActiveSupport::TestCase
|
35
|
+
include FactoryBot::Syntax::Methods
|
36
|
+
include FixtureTestCase
|
37
|
+
include ForemanTasks::TestHelpers::WithInThreadExecutor
|
38
|
+
|
39
|
+
before do
|
40
|
+
Setting::Content.load_defaults
|
41
|
+
end
|
42
|
+
|
43
|
+
def get_organization(org = nil)
|
44
|
+
saved_user = User.current
|
45
|
+
User.current = User.unscoped.find(users(:admin).id)
|
46
|
+
org = org.nil? ? :empty_organization : org
|
47
|
+
organization = Organization.find(taxonomies(org.to_sym).id)
|
48
|
+
organization.stubs(:label_not_changed).returns(true)
|
49
|
+
organization.setup_label_from_name
|
50
|
+
location = Location.where(name: 'Location 1').first
|
51
|
+
organization.locations << location
|
52
|
+
organization.save!
|
53
|
+
User.current = saved_user
|
54
|
+
organization
|
55
|
+
end
|
56
|
+
end
|