moose-inventory 0.1.0

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 (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