moose-inventory 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +0 -0
  3. data/.gitignore +17 -0
  4. data/.rubocop.yml +793 -0
  5. data/Gemfile +3 -0
  6. data/Guardfile +38 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +31 -0
  9. data/README.md.orig +35 -0
  10. data/Rakefile +1 -0
  11. data/bin/moose_inventory +10 -0
  12. data/config/dotfiles/coveralls.yml +0 -0
  13. data/config/dotfiles/gitignore +17 -0
  14. data/config/dotfiles/rubocop.yml +793 -0
  15. data/lib/moose/inventory/cli/application.rb +30 -0
  16. data/lib/moose/inventory/cli/formatter.rb +92 -0
  17. data/lib/moose/inventory/cli/group.rb +23 -0
  18. data/lib/moose/inventory/cli/group_add.rb +98 -0
  19. data/lib/moose/inventory/cli/group_addchild.rb +21 -0
  20. data/lib/moose/inventory/cli/group_addhost.rb +97 -0
  21. data/lib/moose/inventory/cli/group_addvar.rb +72 -0
  22. data/lib/moose/inventory/cli/group_get.rb +52 -0
  23. data/lib/moose/inventory/cli/group_list.rb +41 -0
  24. data/lib/moose/inventory/cli/group_rm.rb +77 -0
  25. data/lib/moose/inventory/cli/group_rmchild.rb +20 -0
  26. data/lib/moose/inventory/cli/group_rmhost.rb +89 -0
  27. data/lib/moose/inventory/cli/group_rmvar.rb +65 -0
  28. data/lib/moose/inventory/cli/host.rb +24 -0
  29. data/lib/moose/inventory/cli/host_add.rb +93 -0
  30. data/lib/moose/inventory/cli/host_addgroup.rb +88 -0
  31. data/lib/moose/inventory/cli/host_addvar.rb +76 -0
  32. data/lib/moose/inventory/cli/host_get.rb +59 -0
  33. data/lib/moose/inventory/cli/host_list.rb +40 -0
  34. data/lib/moose/inventory/cli/host_rm.rb +62 -0
  35. data/lib/moose/inventory/cli/host_rmgroup.rb +80 -0
  36. data/lib/moose/inventory/cli/host_rmvar.rb +69 -0
  37. data/lib/moose/inventory/config/config.rb +169 -0
  38. data/lib/moose/inventory/db/db.rb +249 -0
  39. data/lib/moose/inventory/db/exceptions.rb +14 -0
  40. data/lib/moose/inventory/db/models.rb +32 -0
  41. data/lib/moose/inventory/moose_inventory_cli.rb +25 -0
  42. data/lib/moose/inventory/version.rb +7 -0
  43. data/moose-inventory.gemspec +45 -0
  44. data/scripts/guard_quality.sh +3 -0
  45. data/scripts/guard_test.sh +2 -0
  46. data/scripts/reports.sh +4 -0
  47. data/spec/config/config.yml +12 -0
  48. data/spec/lib/moose/inventory/cli/application_spec.rb +15 -0
  49. data/spec/lib/moose/inventory/cli/cli_spec.rb +26 -0
  50. data/spec/lib/moose/inventory/cli/formatter_spec.rb +63 -0
  51. data/spec/lib/moose/inventory/cli/group_add_spec.rb +398 -0
  52. data/spec/lib/moose/inventory/cli/group_addhost_spec.rb +251 -0
  53. data/spec/lib/moose/inventory/cli/group_addvar_spec.rb +235 -0
  54. data/spec/lib/moose/inventory/cli/group_get_spec.rb +107 -0
  55. data/spec/lib/moose/inventory/cli/group_list_spec.rb +79 -0
  56. data/spec/lib/moose/inventory/cli/group_rm_spec.rb +191 -0
  57. data/spec/lib/moose/inventory/cli/group_rmhost_spec.rb +215 -0
  58. data/spec/lib/moose/inventory/cli/group_rmvar_spec.rb +202 -0
  59. data/spec/lib/moose/inventory/cli/group_spec.rb +15 -0
  60. data/spec/lib/moose/inventory/cli/host_add_spec.rb +330 -0
  61. data/spec/lib/moose/inventory/cli/host_addgroup_spec.rb +248 -0
  62. data/spec/lib/moose/inventory/cli/host_addvar_spec.rb +233 -0
  63. data/spec/lib/moose/inventory/cli/host_get_spec.rb +106 -0
  64. data/spec/lib/moose/inventory/cli/host_list_spec.rb +83 -0
  65. data/spec/lib/moose/inventory/cli/host_rm_spec.rb +132 -0
  66. data/spec/lib/moose/inventory/cli/host_rmgroup_spec.rb +245 -0
  67. data/spec/lib/moose/inventory/cli/host_rmvar_spec.rb +206 -0
  68. data/spec/lib/moose/inventory/cli/host_spec.rb +12 -0
  69. data/spec/lib/moose/inventory/config/config_spec.rb +80 -0
  70. data/spec/lib/moose/inventory/db/db_spec.rb +184 -0
  71. data/spec/lib/moose/inventory/db/models_spec.rb +150 -0
  72. data/spec/shared/shared_config_setup.rb +21 -0
  73. data/spec/spec_helper.rb +110 -0
  74. metadata +386 -0
@@ -0,0 +1,106 @@
1
+ require 'spec_helper'
2
+
3
+ # TODO: the usual respond_to? method doesn't seem to work on Thor objects.
4
+ # Why not? For now, we'll check against instance_methods.
5
+
6
+ RSpec.describe Moose::Inventory::Cli::Host do
7
+ before(:all) do
8
+ # Set up the configuration object
9
+ @mockarg_parts = {
10
+ config: File.join(spec_root, 'config/config.yml'),
11
+ format: 'yaml',
12
+ env: 'test'
13
+ }
14
+
15
+ @mockargs = []
16
+ @mockarg_parts.each do |key, val|
17
+ @mockargs << "--#{key}"
18
+ @mockargs << val
19
+ end
20
+
21
+ @config = Moose::Inventory::Config
22
+ @config.init(@mockargs)
23
+ @console = Moose::Inventory::Cli::Formatter
24
+
25
+ @db = Moose::Inventory::DB
26
+ @db.init if @db.db.nil?
27
+
28
+ @host = Moose::Inventory::Cli::Host
29
+ @app = Moose::Inventory::Cli::Application
30
+ end
31
+
32
+ before(:each) do
33
+ @db.reset
34
+ end
35
+
36
+ #=======================
37
+ describe 'get' do
38
+ #---------------------
39
+ it 'Host.get() should be responsive' do
40
+ result = @host.instance_methods(false).include?(:get)
41
+ expect(result).to eq(true)
42
+ end
43
+
44
+ #---------------------
45
+ it 'host get <missing args> ... should abort with an error' do
46
+ # no items in the db
47
+ name = 'not-in-db'
48
+ actual = runner { @app.start(%W(host get)) }
49
+
50
+ desired = {aborted: true}
51
+ desired[:STDERR] = "ERROR: Wrong number of arguments, 0 for 1 or more\n"
52
+
53
+ expected(actual, desired)
54
+ end
55
+
56
+ #---------------------
57
+ it 'host get HOST ... should return an empty set when HOST doesn\'t exist' do
58
+ # no items in the db
59
+ name = 'not-in-db'
60
+ actual = runner { @app.start(%W(host get #{ name })) }
61
+
62
+ desired = {}
63
+ desired[:STDOUT] = {}.to_yaml
64
+
65
+ expected(actual, desired)
66
+ end
67
+
68
+ #---------------------
69
+ it 'host get HOST ... should get a host from the db' do
70
+ name = 'test-host-add'
71
+ runner { @app.start(%W(host add #{ name })) }
72
+
73
+ actual = runner { @app.start(%W(host get #{ name })) }
74
+
75
+ mock = {}
76
+ mock[name.to_sym] = {}
77
+ mock[name.to_sym][:groups] = ['ungrouped']
78
+
79
+ desired = { aborted: false, STDOUT: '', STDERR: '' }
80
+ desired[:STDOUT] = mock.to_yaml
81
+
82
+ expected(actual, desired)
83
+ end
84
+
85
+ #---------------------
86
+ it 'host get HOST ... should display hostvars, if any are set' do
87
+ name = 'test-host-add'
88
+ var = 'foo=bar'
89
+ runner { @app.start(%W(host add #{ name })) }
90
+ runner { @app.start(%W(host addvar #{ name } #{ var })) }
91
+
92
+ actual = runner { @app.start(%W(host get #{ name })) }
93
+
94
+ mock = {}
95
+ mock[name.to_sym] = {}
96
+ mock[name.to_sym][:groups] = ['ungrouped']
97
+ mock[name.to_sym][:hostvars] = {foo: 'bar'}
98
+
99
+ desired = {}
100
+ desired[:STDOUT] = mock.to_yaml
101
+
102
+ expected(actual, desired)
103
+ end
104
+
105
+ end
106
+ end
@@ -0,0 +1,83 @@
1
+ require 'spec_helper'
2
+
3
+ # TODO: the usual respond_to? method doesn't seem to work on Thor objects.
4
+ # Why not? For now, we'll check against instance_methods.
5
+
6
+ RSpec.describe Moose::Inventory::Cli::Host do
7
+ before(:all) do
8
+ # Set up the configuration object
9
+ @mockarg_parts = {
10
+ config: File.join(spec_root, 'config/config.yml'),
11
+ format: 'yaml',
12
+ env: 'test'
13
+ }
14
+
15
+ @mockargs = []
16
+ @mockarg_parts.each do |key, val|
17
+ @mockargs << "--#{key}"
18
+ @mockargs << val
19
+ end
20
+
21
+ @config = Moose::Inventory::Config
22
+ @config.init(@mockargs)
23
+
24
+ @db = Moose::Inventory::DB
25
+ @db.init if @db.db.nil?
26
+
27
+ @host = Moose::Inventory::Cli::Host
28
+ @app = Moose::Inventory::Cli::Application
29
+ end
30
+
31
+ before(:each) do
32
+ @db.reset
33
+ end
34
+
35
+ #====================
36
+ describe 'list' do
37
+ #---------------------
38
+ it 'should be responsive' do
39
+ result = @host.instance_methods(false).include?(:list)
40
+ expect(result).to eq(true)
41
+ end
42
+
43
+ #---------------------
44
+ it 'should return an empty set when no results' do
45
+ # no items in the db
46
+ name = 'not-in-db'
47
+ actual = runner { @app.start(%W(host list)) }
48
+
49
+ desired = { aborted: false, STDOUT: '', STDERR: '' }
50
+ desired[:STDOUT] = {}.to_yaml
51
+
52
+ expected(actual, desired)
53
+ end
54
+
55
+ #---------------------
56
+ it 'should get a list of hosts from the db' do
57
+ var = 'foo=bar'
58
+
59
+ mock = {}
60
+ hosts = %w(host1 host2 host3)
61
+ hosts.each do |name|
62
+ runner { @app.start(%W(host add #{name})) }
63
+ runner { @app.start(%W(host addvar #{ name } foo=bar)) }
64
+ mock[name.to_sym] = {}
65
+ mock[name.to_sym][:groups] = ['ungrouped']
66
+ mock[name.to_sym][:hostvars] = {foo: 'bar'}
67
+ end
68
+
69
+ # items should now be in the db
70
+ actual = runner{ @app.start(%w(host list)) }
71
+
72
+ desired = { aborted: false, STDOUT: '', STDERR: '' }
73
+ desired[:STDOUT] = mock.to_yaml
74
+
75
+ expected(actual, desired)
76
+ end
77
+ #---------------------
78
+ #it 'host list ... should display hostvars, if any are set' do
79
+ # Covered by 'should get a list of hosts from the db'
80
+ #end
81
+
82
+ end
83
+ end
@@ -0,0 +1,132 @@
1
+ require 'spec_helper'
2
+
3
+ # TODO: the usual respond_to? method doesn't seem to work on Thor objects.
4
+ # Why not? For now, we'll check against instance_methods.
5
+
6
+ RSpec.describe Moose::Inventory::Cli::Host do
7
+ before(:all) do
8
+ # Set up the configuration object
9
+ @mockarg_parts = {
10
+ config: File.join(spec_root, 'config/config.yml'),
11
+ format: 'yaml',
12
+ env: 'test'
13
+ }
14
+
15
+ @mockargs = []
16
+ @mockarg_parts.each do |key, val|
17
+ @mockargs << "--#{key}"
18
+ @mockargs << val
19
+ end
20
+
21
+ @config = Moose::Inventory::Config
22
+ @config.init(@mockargs)
23
+
24
+ @db = Moose::Inventory::DB
25
+ @db.init if @db.db.nil?
26
+
27
+ @host = Moose::Inventory::Cli::Host
28
+ @app = Moose::Inventory::Cli::Application
29
+ end
30
+
31
+ before(:each) do
32
+ @db.reset
33
+ end
34
+
35
+ #======================
36
+ describe 'rm' do
37
+ #---------------
38
+ it 'Host.rm() should be responsive' do
39
+ result = @host.instance_methods(false).include?(:rm)
40
+ expect(result).to eq(true)
41
+ end
42
+
43
+ #---------------
44
+ it '<missing argument> ... should abort with an error' do
45
+ actual = runner { @app.start(%w(host rm)) }
46
+
47
+ # Check output
48
+ desired = { aborted: true, STDERR: '', STDOUT: '' }
49
+ desired[:STDERR] = "ERROR: Wrong number of arguments, 0 for 1 or more.\n"
50
+ expected(actual, desired)
51
+ end
52
+
53
+ #---------------
54
+ it 'HOST ... should warn about hosts that don\'t exist' do
55
+ # Rationale:
56
+ # The request implies the desired state is that the host is not present
57
+ # If the host is not present, for whatever reason, then the desired state
58
+ # already exists.
59
+
60
+ # no items in the db
61
+ name = "fake"
62
+ actual = runner { @app.start(%W(host rm #{name})) }
63
+
64
+ desired = {}
65
+ desired[:STDOUT] =
66
+ "Remove host '#{name}':\n"\
67
+ " - Retrieve host '#{name}'...\n"\
68
+ " - No such host, skipping.\n"\
69
+ " - OK\n"\
70
+ " - All OK\n"\
71
+ "Succeeded, with warnings.\n"
72
+ desired[:STDERR] =
73
+ "WARNING: Host '#{name}' does not exist, skipping.\n"
74
+
75
+ expected(actual, desired)
76
+ end
77
+
78
+ #---------------
79
+ it 'HOST ... should remove a host' do
80
+ name = 'test1'
81
+ @db.models[:host].create(name: name)
82
+
83
+ actual = runner { @app.start(%W(host rm #{name})) }
84
+
85
+ # Check output
86
+ desired = {}
87
+ desired[:STDOUT] =
88
+ "Remove host '#{name}':\n"\
89
+ " - Retrieve host '#{name}'...\n"\
90
+ " - OK\n"\
91
+ " - Destroy host '#{name}'...\n"\
92
+ " - OK\n"\
93
+ " - All OK\n"\
94
+ "Succeeded.\n"
95
+
96
+ expected(actual, desired)
97
+
98
+ # Check db
99
+ host = @db.models[:host].find(name: name)
100
+ expect(host).to be_nil
101
+ end
102
+
103
+ #---------------
104
+ it 'HOST1 HOST2 ... should remove multiple hosts' do
105
+ names = %w(host1 host2 host3)
106
+ names.each do |name|
107
+ @db.models[:host].create(name: name)
108
+ end
109
+
110
+ actual = runner { @app.start(%w(host rm) + names) }
111
+
112
+ # Check output
113
+ desired = { aborted: false, STDERR: '', STDOUT: '' }
114
+ names.each do |name|
115
+ desired[:STDOUT] = desired[:STDOUT] +
116
+ "Remove host '#{name}':\n"\
117
+ " - Retrieve host '#{name}'...\n"\
118
+ " - OK\n"\
119
+ " - Destroy host '#{name}'...\n"\
120
+ " - OK\n"\
121
+ " - All OK\n"
122
+ end
123
+ desired[:STDOUT] = desired[:STDOUT] +
124
+ "Succeeded.\n"
125
+ expected(actual, desired)
126
+
127
+ # Check db
128
+ hosts = @db.models[:host].all
129
+ expect(hosts.count).to eq(0)
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,245 @@
1
+ require 'spec_helper'
2
+
3
+ # TODO: the usual respond_to? method doesn't seem to work on Thor objects.
4
+ # Why not? For now, we'll check against instance_methods.
5
+
6
+ RSpec.describe Moose::Inventory::Cli::Host do
7
+ before(:all) do
8
+ # Set up the configuration object
9
+ @mockarg_parts = {
10
+ config: File.join(spec_root, 'config/config.yml'),
11
+ format: 'yaml',
12
+ env: 'test'
13
+ }
14
+
15
+ @mockargs = []
16
+ @mockarg_parts.each do |key, val|
17
+ @mockargs << "--#{key}"
18
+ @mockargs << val
19
+ end
20
+
21
+ @console = Moose::Inventory::Cli::Formatter
22
+ @config = Moose::Inventory::Config
23
+ @config.init(@mockargs)
24
+
25
+ @db = Moose::Inventory::DB
26
+ @db.init if @db.db.nil?
27
+
28
+ @host = Moose::Inventory::Cli::Host
29
+ @app = Moose::Inventory::Cli::Application
30
+ end
31
+
32
+ before(:each) do
33
+ @db.reset
34
+ end
35
+
36
+ #====================
37
+ describe 'rmgroup' do
38
+ #----------------
39
+ it 'should be responsive' do
40
+ result = @host.instance_methods(false).include?(:rmgroup)
41
+ expect(result).to eq(true)
42
+ end
43
+
44
+ #----------------
45
+
46
+ #------------------------
47
+ it 'host rmgroup <missing args> ... should abort with an error' do
48
+ actual = runner do
49
+ @app.start(%w(host rmgroup)) # <- no group given
50
+ end
51
+
52
+ # Check output
53
+ desired = { aborted: true}
54
+ desired[:STDERR] = "ERROR: Wrong number of arguments, 0 for 2 or more.\n"
55
+ expected(actual, desired)
56
+ end
57
+
58
+ #------------------------
59
+ it 'host rmgroup HOST GROUP ... should abort if the host does not exist' do
60
+ host_name = 'not-a-host'
61
+ group_name = 'example'
62
+ actual = runner do
63
+ @app.start(%W(host rmgroup #{host_name} #{group_name}))
64
+ end
65
+
66
+ # Check output
67
+ desired = { aborted: true}
68
+ desired[:STDOUT] =
69
+ "Dissociate host '#{host_name}' from groups '#{group_name}':\n"\
70
+ " - Retrieve host '#{host_name}'...\n"
71
+ desired[:STDERR] =
72
+ "An error occurred during a transaction, any changes have been rolled back.\n"\
73
+ "ERROR: The host '#{host_name}' was not found in the database.\n"
74
+ expected(actual, desired)
75
+ end
76
+
77
+ #------------------------
78
+ it 'host rmgroup HOST GROUP ... should dissociate the host from an existing group' do
79
+ # 1. Should rm the host to the group
80
+ # 2. Should add the host from the 'ungrouped' automatic group
81
+ # if it has no other groups.
82
+
83
+ host_name = 'test1'
84
+ runner { @app.start(%W(host add #{host_name})) }
85
+
86
+ group_names = %w(group1 group2)
87
+ tmp = runner { @app.start(%W(host addgroup #{host_name} #{group_names[0]} #{group_names[1]} )) }
88
+
89
+ #
90
+ # Dissociate from the first group
91
+ # 1. expect that the group association is removed
92
+ # 2. expect that no association with ungrouped is made.
93
+
94
+ actual = runner do
95
+ @app.start(%W(host rmgroup #{host_name} #{group_names[0]} ))
96
+ end
97
+ #@console.dump(actual, 'y')
98
+
99
+ # rubocop:disable Metrics/LineLength
100
+ desired = { aborted: false}
101
+ desired[:STDOUT] =
102
+ "Dissociate host '#{host_name}' from groups '#{group_names[0]}':\n"\
103
+ " - Retrieve host '#{host_name}'...\n"\
104
+ " - OK\n"\
105
+ " - Remove association {host:#{host_name} <-> group:#{group_names[0]}}...\n"\
106
+ " - OK\n"\
107
+ " - All OK\n"\
108
+ "Succeeded\n"
109
+ expected(actual, desired)
110
+ # rubocop:enable Metrics/LineLength
111
+
112
+ # We should have the correct group associations
113
+ host = @db.models[:host].find(name: host_name)
114
+ groups = host.groups_dataset
115
+ expect(groups.count).to eq(1)
116
+ expect(groups[name: group_names[0]]).to be_nil
117
+ expect(groups[name: group_names[1]]).not_to be_nil
118
+ expect(groups[name: 'ungrouped']).to be_nil # redundant, but for clarity!
119
+
120
+ #
121
+ # Remove the second group
122
+ # 1. expect that the group association is removed
123
+ # 2. expect that an association will be made with 'ungrouped'.
124
+ actual = runner do
125
+ @app.start(args = %W(host rmgroup #{host_name} #{group_names[1]} ))
126
+ end
127
+
128
+ # rubocop:disable Metrics/LineLength
129
+ desired = { aborted: false}
130
+ desired[:STDOUT] =
131
+ "Dissociate host '#{host_name}' from groups '#{group_names[1]}':\n"\
132
+ " - Retrieve host '#{host_name}'...\n"\
133
+ " - OK\n"\
134
+ " - Remove association {host:#{host_name} <-> group:#{group_names[1]}}...\n"\
135
+ " - OK\n"\
136
+ " - Add automatic association {host:#{host_name} <-> group:ungrouped}...\n"\
137
+ " - OK\n"\
138
+ " - All OK\n"\
139
+ "Succeeded\n"
140
+ expected(actual, desired)
141
+ # rubocop:enable Metrics/LineLength
142
+
143
+ # We should have the correct group associations
144
+ host = @db.models[:host].find(name: host_name)
145
+ groups = host.groups_dataset
146
+ expect(groups.count).to eq(1)
147
+ expect(groups[name: group_names[0]]).to be_nil
148
+ expect(groups[name: group_names[1]]).to be_nil
149
+ expect(groups[name: 'ungrouped']).not_to be_nil
150
+ end
151
+
152
+ #------------------------
153
+ it 'host rmgroup HOST GROUP ... should warn about non-existing associations' do
154
+ # 1. Should warn that the group doesn't exist.
155
+ # 2. Should complete with success. (desired state == actual state)
156
+
157
+ host_name = 'test1'
158
+ group_name = 'no-group'
159
+ runner { @app.start(%W(host add #{host_name})) }
160
+
161
+ actual = runner do
162
+ @app.start(%W(host rmgroup #{host_name} #{group_name} ))
163
+ end
164
+
165
+ # rubocop:disable Metrics/LineLength
166
+ desired = { aborted: false}
167
+ desired[:STDOUT] =
168
+ "Dissociate host '#{host_name}' from groups '#{group_name}':\n"\
169
+ " - Retrieve host \'#{host_name}\'...\n"\
170
+ " - OK\n"\
171
+ " - Remove association {host:#{host_name} <-> group:#{group_name }}...\n"\
172
+ " - Doesn't exist, skipping.\n"\
173
+ " - OK\n"\
174
+ " - All OK\n"\
175
+ "Succeeded\n"
176
+ desired[:STDERR] = "WARNING: Association {host:#{host_name} <-> group:#{group_name }} doesn't exist, skipping.\n"
177
+
178
+ expected(actual, desired)
179
+ end
180
+
181
+ #------------------------
182
+ it 'host rmgroup HOST \'ungrouped\' ... should abort with an error' do
183
+
184
+ name = 'test1'
185
+ groupname = 'ungrouped'
186
+
187
+ runner { @app.start(%W(host add #{name})) }
188
+
189
+ actual = runner { @app.start(%W(host rmgroup #{name} #{groupname} )) }
190
+
191
+ desired = { aborted: true}
192
+ desired[:STDERR] =
193
+ "ERROR: Cannot manually manipulate the automatic group 'ungrouped'.\n"
194
+ expected(actual, desired)
195
+ end
196
+
197
+ #------------------------
198
+ it 'host rmgroup GROUP1 GROUP1 ... should dissociate the host from'\
199
+ ' multiple groups at once' do
200
+ # 1. Should rm the host to the group
201
+ # 2. Should add the host from the 'ungrouped' automatic group
202
+ # if it has no other groups.
203
+
204
+ host_name = 'test1'
205
+ runner { @app.start(%W(host add #{host_name})) }
206
+
207
+ group_names = %w(group1 group2)
208
+ group_names.each do |group|
209
+ runner { @app.start(%W(host addgroup #{host_name} #{group} )) }
210
+ end
211
+
212
+ actual = runner do
213
+ @app.start(%W(host rmgroup #{host_name}) + group_names)
214
+ end
215
+
216
+ # rubocop:disable Metrics/LineLength
217
+ desired = { aborted: false}
218
+ desired[:STDOUT] =
219
+ "Dissociate host '#{host_name}' from groups '#{group_names.join(',')}':\n"\
220
+ " - Retrieve host \'#{host_name}\'...\n"\
221
+ " - OK\n"
222
+ group_names.each do |group|
223
+ desired[:STDOUT] = desired[:STDOUT] +
224
+ " - Remove association {host:#{host_name} <-> group:#{group}}...\n"\
225
+ " - OK\n"\
226
+ end
227
+ desired[:STDOUT] = desired[:STDOUT] +
228
+ " - Add automatic association {host:#{host_name} <-> group:ungrouped}...\n"\
229
+ " - OK\n"\
230
+ " - All OK\n"\
231
+ "Succeeded\n"
232
+ expected(actual, desired)
233
+ # rubocop:enable Metrics/LineLength
234
+
235
+ # We should have the correct group associations
236
+ host = @db.models[:host].find(name: host_name)
237
+ groups = host.groups_dataset
238
+ expect(groups.count).to eq(1)
239
+ group_names.each do |group|
240
+ expect(groups[name: group]).to be_nil
241
+ end
242
+ expect(groups[name: 'ungrouped']).not_to be_nil # redundant, but for clarity!
243
+ end
244
+ end
245
+ end