moose-inventory 1.0.9 → 2.1

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 (176) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +15 -1
  3. data/.github/workflows/release.yml +60 -0
  4. data/.gitignore +2 -1
  5. data/.gitleaks.toml +9 -0
  6. data/.rubocop.yml +49 -0
  7. data/BACKLOG.md +752 -24
  8. data/Gemfile +2 -0
  9. data/Gemfile.lock +36 -1
  10. data/README.md +340 -44
  11. data/Rakefile +2 -0
  12. data/bin/moose-inventory +2 -1
  13. data/docs/architecture/architecture-and-trust-boundaries.md +444 -0
  14. data/docs/compatibility/cli-output-compatibility.md +76 -0
  15. data/docs/governance/approval-register.md +37 -0
  16. data/docs/maintenance/database-backup-restore-guidance.md +162 -0
  17. data/docs/maintenance/package-maintenance-and-agent-boundaries.md +260 -0
  18. data/docs/process/conformance-gap-analysis-2026-05-28.md +192 -0
  19. data/docs/product/product-brief.md +161 -0
  20. data/docs/product/requirements-baseline.md +477 -0
  21. data/docs/qa/qa-documentation-and-release-gates.md +283 -0
  22. data/docs/release/package-provenance-hardening.md +126 -0
  23. data/docs/release/publishing.md +54 -50
  24. data/docs/release/release-environment-protection.md +70 -0
  25. data/docs/release/release-readiness.md +37 -4
  26. data/docs/security/accepted-risk-register.md +84 -0
  27. data/docs/security/security-privacy-process.md +287 -0
  28. data/docs/security-audit-2026-05-26-rerun.md +75 -0
  29. data/docs/security-audit-2026-05-26.md +63 -0
  30. data/docs/ux/cli-workflow-notes.md +287 -0
  31. data/examples/ansible/ansible.cfg +3 -0
  32. data/examples/ansible/inventory/moose_inventory.yml +5 -0
  33. data/examples/ansible/inventory_plugins/moose_inventory.py +100 -0
  34. data/examples/ci/README.md +16 -0
  35. data/examples/ci/github-actions/inventory-review.yml +38 -0
  36. data/examples/ci/inventory/example-snapshot.yml +19 -0
  37. data/examples/ci/scripts/validate-inventory-snapshot.sh +30 -0
  38. data/lib/moose_inventory/cli/application.rb +133 -5
  39. data/lib/moose_inventory/cli/association_rendering.rb +74 -0
  40. data/lib/moose_inventory/cli/association_rendering_support.rb +89 -0
  41. data/lib/moose_inventory/cli/audit.rb +62 -0
  42. data/lib/moose_inventory/cli/audit_recording.rb +40 -0
  43. data/lib/moose_inventory/cli/child_relation_rendering.rb +110 -0
  44. data/lib/moose_inventory/cli/console.rb +135 -0
  45. data/lib/moose_inventory/cli/db.rb +64 -0
  46. data/lib/moose_inventory/cli/factory.rb +28 -0
  47. data/lib/moose_inventory/cli/formatter.rb +8 -12
  48. data/lib/moose_inventory/cli/group.rb +7 -1
  49. data/lib/moose_inventory/cli/group_add.rb +91 -73
  50. data/lib/moose_inventory/cli/group_addchild.rb +41 -66
  51. data/lib/moose_inventory/cli/group_addhost.rb +33 -71
  52. data/lib/moose_inventory/cli/group_addvar.rb +27 -47
  53. data/lib/moose_inventory/cli/group_get.rb +8 -42
  54. data/lib/moose_inventory/cli/group_list.rb +7 -40
  55. data/lib/moose_inventory/cli/group_listvars.rb +9 -55
  56. data/lib/moose_inventory/cli/group_rm.rb +105 -73
  57. data/lib/moose_inventory/cli/group_rmchild.rb +47 -57
  58. data/lib/moose_inventory/cli/group_rmhost.rb +34 -61
  59. data/lib/moose_inventory/cli/group_rmvar.rb +30 -41
  60. data/lib/moose_inventory/cli/group_tags.rb +33 -0
  61. data/lib/moose_inventory/cli/helpers.rb +143 -0
  62. data/lib/moose_inventory/cli/host.rb +8 -2
  63. data/lib/moose_inventory/cli/host_add.rb +91 -66
  64. data/lib/moose_inventory/cli/host_addgroup.rb +39 -66
  65. data/lib/moose_inventory/cli/host_addvar.rb +28 -52
  66. data/lib/moose_inventory/cli/host_get.rb +9 -37
  67. data/lib/moose_inventory/cli/host_list.rb +24 -21
  68. data/lib/moose_inventory/cli/host_listvars.rb +9 -62
  69. data/lib/moose_inventory/cli/host_rm.rb +60 -42
  70. data/lib/moose_inventory/cli/host_rmgroup.rb +39 -55
  71. data/lib/moose_inventory/cli/host_rmvar.rb +31 -45
  72. data/lib/moose_inventory/cli/host_tags.rb +33 -0
  73. data/lib/moose_inventory/cli/listvars_support.rb +55 -0
  74. data/lib/moose_inventory/cli/plan_rendering.rb +50 -0
  75. data/lib/moose_inventory/cli/relation_transaction_support.rb +51 -0
  76. data/lib/moose_inventory/cli/tag_support.rb +97 -0
  77. data/lib/moose_inventory/cli/variable_rendering.rb +67 -0
  78. data/lib/moose_inventory/config/config.rb +185 -108
  79. data/lib/moose_inventory/db/db.rb +188 -193
  80. data/lib/moose_inventory/db/exceptions.rb +6 -3
  81. data/lib/moose_inventory/db/models.rb +16 -0
  82. data/lib/moose_inventory/db/schema_migrations.rb +248 -0
  83. data/lib/moose_inventory/inventory_context.rb +116 -0
  84. data/lib/moose_inventory/operations/add_associations.rb +131 -0
  85. data/lib/moose_inventory/operations/add_groups.rb +123 -0
  86. data/lib/moose_inventory/operations/add_hosts.rb +123 -0
  87. data/lib/moose_inventory/operations/add_variables.rb +77 -0
  88. data/lib/moose_inventory/operations/entity_variable_operation_support.rb +46 -0
  89. data/lib/moose_inventory/operations/group_child_relations.rb +125 -0
  90. data/lib/moose_inventory/operations/group_cleanup.rb +70 -0
  91. data/lib/moose_inventory/operations/import_inventory_snapshot.rb +41 -0
  92. data/lib/moose_inventory/operations/inventory_doctor.rb +172 -0
  93. data/lib/moose_inventory/operations/inventory_snapshot.rb +60 -0
  94. data/lib/moose_inventory/operations/inventory_snapshot_applier.rb +112 -0
  95. data/lib/moose_inventory/operations/inventory_snapshot_preview.rb +174 -0
  96. data/lib/moose_inventory/operations/inventory_snapshot_validator.rb +134 -0
  97. data/lib/moose_inventory/operations/operation_event_support.rb +27 -0
  98. data/lib/moose_inventory/operations/query_inventory/base_query.rb +24 -0
  99. data/lib/moose_inventory/operations/query_inventory/group_queries.rb +86 -0
  100. data/lib/moose_inventory/operations/query_inventory/host_queries.rb +106 -0
  101. data/lib/moose_inventory/operations/query_inventory.rb +47 -0
  102. data/lib/moose_inventory/operations/remove_associations.rb +113 -0
  103. data/lib/moose_inventory/operations/remove_groups.rb +79 -0
  104. data/lib/moose_inventory/operations/remove_hosts.rb +68 -0
  105. data/lib/moose_inventory/operations/remove_variables.rb +67 -0
  106. data/lib/moose_inventory/runtime_options.rb +31 -0
  107. data/lib/moose_inventory/version.rb +3 -1
  108. data/lib/moose_inventory.rb +10 -7
  109. data/moose-inventory.gemspec +22 -35
  110. data/scripts/check.sh +3 -0
  111. data/scripts/ci/check_generated_artifacts.sh +41 -0
  112. data/scripts/ci/check_permissions.sh +5 -0
  113. data/scripts/ci/check_rubocop.sh +33 -0
  114. data/scripts/ci/check_secrets.sh +26 -0
  115. data/scripts/ci/check_security.sh +18 -0
  116. data/scripts/ci/install_security_tools.sh +47 -0
  117. data/scripts/files.rb +5 -4
  118. data/scripts/install_dependencies.sh +2 -0
  119. data/spec/examples/ci_examples_spec.rb +37 -0
  120. data/spec/lib/moose_inventory/ansible_plugin_examples_spec.rb +29 -0
  121. data/spec/lib/moose_inventory/cli/application_doctor_spec.rb +50 -0
  122. data/spec/lib/moose_inventory/cli/application_import_export_spec.rb +100 -0
  123. data/spec/lib/moose_inventory/cli/application_spec.rb +25 -15
  124. data/spec/lib/moose_inventory/cli/audit_spec.rb +56 -0
  125. data/spec/lib/moose_inventory/cli/cli_spec.rb +15 -19
  126. data/spec/lib/moose_inventory/cli/console_spec.rb +98 -0
  127. data/spec/lib/moose_inventory/cli/factory_spec.rb +27 -0
  128. data/spec/lib/moose_inventory/cli/formatter_spec.rb +95 -3
  129. data/spec/lib/moose_inventory/cli/group_add_spec.rb +140 -116
  130. data/spec/lib/moose_inventory/cli/group_addchild_spec.rb +89 -35
  131. data/spec/lib/moose_inventory/cli/group_addhost_spec.rb +81 -84
  132. data/spec/lib/moose_inventory/cli/group_addvar_spec.rb +65 -68
  133. data/spec/lib/moose_inventory/cli/group_get_spec.rb +17 -33
  134. data/spec/lib/moose_inventory/cli/group_list_spec.rb +16 -38
  135. data/spec/lib/moose_inventory/cli/group_listvar_spec.rb +33 -40
  136. data/spec/lib/moose_inventory/cli/group_rm_spec.rb +165 -85
  137. data/spec/lib/moose_inventory/cli/group_rmchild_spec.rb +100 -30
  138. data/spec/lib/moose_inventory/cli/group_rmhost_spec.rb +76 -78
  139. data/spec/lib/moose_inventory/cli/group_rmvar_spec.rb +57 -63
  140. data/spec/lib/moose_inventory/cli/group_spec.rb +2 -0
  141. data/spec/lib/moose_inventory/cli/helpers_spec.rb +146 -0
  142. data/spec/lib/moose_inventory/cli/host_add_spec.rb +170 -116
  143. data/spec/lib/moose_inventory/cli/host_addgroup_spec.rb +100 -83
  144. data/spec/lib/moose_inventory/cli/host_addvar_spec.rb +92 -74
  145. data/spec/lib/moose_inventory/cli/host_get_spec.rb +14 -33
  146. data/spec/lib/moose_inventory/cli/host_list_spec.rb +41 -33
  147. data/spec/lib/moose_inventory/cli/host_listvar_spec.rb +45 -53
  148. data/spec/lib/moose_inventory/cli/host_rm_spec.rb +66 -48
  149. data/spec/lib/moose_inventory/cli/host_rmgroup_spec.rb +73 -83
  150. data/spec/lib/moose_inventory/cli/host_rmvar_spec.rb +56 -63
  151. data/spec/lib/moose_inventory/cli/host_spec.rb +2 -0
  152. data/spec/lib/moose_inventory/cli/tags_spec.rb +81 -0
  153. data/spec/lib/moose_inventory/config/config_spec.rb +41 -3
  154. data/spec/lib/moose_inventory/db/db_spec.rb +551 -29
  155. data/spec/lib/moose_inventory/db/exceptions_spec.rb +18 -0
  156. data/spec/lib/moose_inventory/db/models_spec.rb +7 -3
  157. data/spec/lib/moose_inventory/db_lifecycle_spec.rb +73 -0
  158. data/spec/lib/moose_inventory/inventory_context_spec.rb +10 -0
  159. data/spec/lib/moose_inventory/operations/add_associations_spec.rb +111 -0
  160. data/spec/lib/moose_inventory/operations/add_groups_spec.rb +80 -0
  161. data/spec/lib/moose_inventory/operations/add_hosts_spec.rb +82 -0
  162. data/spec/lib/moose_inventory/operations/add_variables_spec.rb +103 -0
  163. data/spec/lib/moose_inventory/operations/group_child_relations_spec.rb +122 -0
  164. data/spec/lib/moose_inventory/operations/import_inventory_snapshot_spec.rb +226 -0
  165. data/spec/lib/moose_inventory/operations/inventory_doctor_spec.rb +77 -0
  166. data/spec/lib/moose_inventory/operations/inventory_snapshot_spec.rb +50 -0
  167. data/spec/lib/moose_inventory/operations/operation_event_support_spec.rb +78 -0
  168. data/spec/lib/moose_inventory/operations/query_inventory_spec.rb +146 -0
  169. data/spec/lib/moose_inventory/operations/remove_associations_spec.rb +113 -0
  170. data/spec/lib/moose_inventory/operations/remove_groups_spec.rb +78 -0
  171. data/spec/lib/moose_inventory/operations/remove_hosts_spec.rb +55 -0
  172. data/spec/lib/moose_inventory/operations/remove_variables_spec.rb +83 -0
  173. data/spec/shared/shared_config_setup.rb +4 -3
  174. data/spec/spec_helper.rb +50 -40
  175. data/spec/support/cli_harness.rb +33 -0
  176. metadata +163 -35
@@ -1,50 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Metrics/BlockLength
1
4
  require 'spec_helper'
2
5
 
3
- # TODO: the usual respond_to? method doesn't seem to work on Thor objects.
4
6
  # Why not? For now, we'll check against instance_methods.
5
7
 
6
8
  RSpec.describe Moose::Inventory::Cli::Group do
7
9
  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
- @console = Moose::Inventory::Cli::Formatter
28
- @group = Moose::Inventory::Cli::Group
29
- @app = Moose::Inventory::Cli::Application
10
+ setup_cli_harness(command_class: Moose::Inventory::Cli::Group, command_ivar: :@group)
30
11
  end
31
12
 
32
13
  before(:each) do
33
- @db.reset
14
+ reset_cli_harness
34
15
  end
35
16
 
36
17
  #==================
37
18
  describe 'addvar' do
38
19
  #-----------------
39
20
  it 'should be responsive' do
40
- result = @group.instance_methods(false).include?(:addvar)
21
+ result = @group.method_defined?(:addvar, false)
41
22
  expect(result).to eq(true)
42
23
  end
43
24
 
44
25
  #-----------------
45
26
  it '<missing args> ... should abort with an error' do
46
27
  actual = runner do
47
- @app.start(%w(group addvar)) # <- no group given
28
+ @app.start(%w[group addvar]) # <- no group given
48
29
  end
49
30
  # @console.out(actual, 'y')
50
31
 
@@ -60,16 +41,16 @@ RSpec.describe Moose::Inventory::Cli::Group do
60
41
  group_var = 'foo=bar'
61
42
 
62
43
  actual = runner do
63
- @app.start(%W(group addvar #{group_name} #{group_var}))
44
+ @app.start(%W[group addvar #{group_name} #{group_var}])
64
45
  end
65
46
 
66
47
  # Check output
67
48
  desired = { aborted: true }
68
49
  desired[:STDOUT] =
69
- "Add variables '#{group_var}' to group '#{group_name}':\n"\
70
- " - retrieve group '#{group_name}'...\n"
50
+ "Add variables '#{group_var}' to group '#{group_name}':\n " \
51
+ "- retrieve group '#{group_name}'...\n"
71
52
  desired[:STDERR] =
72
- "An error occurred during a transaction, any changes have been rolled back.\n"\
53
+ "An error occurred during a transaction, any changes have been rolled back.\n" \
73
54
  "ERROR: The group '#{group_name}' does not exist.\n"
74
55
  expected(actual, desired)
75
56
  end
@@ -81,32 +62,30 @@ RSpec.describe Moose::Inventory::Cli::Group do
81
62
 
82
63
  group_name = 'test_group'
83
64
  @db.models[:group].create(name: group_name)
84
-
85
- var = { name: 'var1', value: 'testval' }
86
- cases = %w(
65
+ cases = %w[
87
66
  testvar
88
67
  testvar=
89
68
  =testval
90
69
  testvar=testval=
91
70
  =testvar=testval
92
71
  testvar=testval=extra
93
- )
72
+ ]
94
73
 
95
74
  cases.each do |args|
96
75
  actual = runner do
97
- @app.start(%W(group addvar #{group_name} #{args}))
76
+ @app.start(%W[group addvar #{group_name} #{args}])
98
77
  end
99
78
  # @console.out(actual,'p')
100
79
 
101
80
  desired = { aborted: true }
102
81
  desired[:STDOUT] =
103
- "Add variables '#{args}' to group '#{group_name}':\n"\
104
- " - retrieve group '#{group_name}'...\n"\
105
- " - OK\n"\
106
- " - add variable '#{args}'...\n"
82
+ "Add variables '#{args}' to group '#{group_name}':\n " \
83
+ "- retrieve group '#{group_name}'...\n " \
84
+ "- OK\n " \
85
+ "- add variable '#{args}'...\n"
107
86
 
108
87
  desired[:STDERR] =
109
- "An error occurred during a transaction, any changes have been rolled back.\n"\
88
+ "An error occurred during a transaction, any changes have been rolled back.\n" \
110
89
  "ERROR: Incorrect format in '{#{args}}'. Expected 'key=value'.\n"
111
90
 
112
91
  expected(actual, desired)
@@ -124,18 +103,18 @@ RSpec.describe Moose::Inventory::Cli::Group do
124
103
  @db.models[:group].create(name: group_name)
125
104
 
126
105
  actual = runner do
127
- @app.start(%W(group addvar #{group_name} #{var[:name]}=#{var[:value]}))
106
+ @app.start(%W[group addvar #{group_name} #{var[:name]}=#{var[:value]}])
128
107
  end
129
108
  # @console.out(actual,'p')
130
109
 
131
110
  desired = { aborted: false }
132
111
  desired[:STDOUT] =
133
- "Add variables '#{var[:name]}=#{var[:value]}' to group '#{group_name}':\n"\
134
- " - retrieve group '#{group_name}'...\n"\
135
- " - OK\n"\
136
- " - add variable '#{var[:name]}=#{var[:value]}'...\n"\
137
- " - OK\n"\
138
- " - all OK\n"\
112
+ "Add variables '#{var[:name]}=#{var[:value]}' to group '#{group_name}':\n " \
113
+ "- retrieve group '#{group_name}'...\n " \
114
+ "- OK\n " \
115
+ "- add variable '#{var[:name]}=#{var[:value]}'...\n " \
116
+ "- OK\n " \
117
+ "- all OK\n" \
139
118
  "Succeeded.\n"
140
119
  expected(actual, desired)
141
120
 
@@ -155,43 +134,60 @@ RSpec.describe Moose::Inventory::Cli::Group do
155
134
  group_name = 'test1'
156
135
  varsarray = [
157
136
  { name: 'var1', value: 'val1' },
158
- { name: 'var2', value: 'val2' },
137
+ { name: 'var2', value: 'val2' }
159
138
  ]
160
139
 
161
- vars = []
162
- varsarray.each do |var|
163
- vars << "#{var[:name]}=#{var[:value]}"
140
+ vars = varsarray.map do |var|
141
+ "#{var[:name]}=#{var[:value]}"
164
142
  end
165
143
 
166
144
  @db.models[:group].create(name: group_name)
167
145
 
168
146
  actual = runner do
169
- @app.start(%W(group addvar #{group_name}) + vars)
147
+ @app.start(%W[group addvar #{group_name}] + vars)
170
148
  end
171
149
 
172
150
  # @console.out(actual,'y')
173
151
 
174
152
  desired = { aborted: false }
175
153
  desired[:STDOUT] =
176
- "Add variables '#{vars.join(',')}' to group '#{group_name}':\n"\
177
- " - retrieve group '#{group_name}'...\n"\
178
- " - OK\n"
154
+ "Add variables '#{vars.join(',')}' to group '#{group_name}':\n " \
155
+ "- retrieve group '#{group_name}'...\n " \
156
+ "- OK\n"
179
157
  vars.each do |var|
180
158
  desired[:STDOUT] = desired[:STDOUT] +
181
- " - add variable '#{var}'...\n"\
182
- " - OK\n"
159
+ " - add variable '#{var}'...\n " \
160
+ "- OK\n"
183
161
  end
184
162
  desired[:STDOUT] = desired[:STDOUT] +
185
- " - all OK\n"\
163
+ " - all OK\n" \
186
164
  "Succeeded.\n"
187
165
  expected(actual, desired)
188
166
 
189
167
  # We should have the correct hostvar associations
190
168
  group = @db.models[:group].find(name: group_name)
191
- groupvars = group.groupvars_dataset
169
+ group.groupvars_dataset
192
170
  expect(vars.count).to eq(vars.length)
193
171
  end
194
172
 
173
+ #------------------------
174
+ it 'GROUP key=value --dry-run should not create or update variables' do
175
+ group_name = 'test1'
176
+ @db.models[:group].create(name: group_name)
177
+ runner { @app.start(%W[group addvar #{group_name} var1=old]) }
178
+
179
+ actual = runner do
180
+ @app.start(%W[group addvar #{group_name} var1=new var2=val2 --dry-run])
181
+ end
182
+
183
+ expect(actual[:unexpected]).to eq(false)
184
+ expect(actual[:aborted]).to eq(false)
185
+ expect(actual[:STDOUT]).to include('Dry run complete. No changes applied.')
186
+ group = @db.models[:group].find(name: group_name)
187
+ expect(group.groupvars_dataset[name: 'var1'][:value]).to eq('old')
188
+ expect(group.groupvars_dataset[name: 'var2']).to be_nil
189
+ end
190
+
195
191
  #------------------------
196
192
  it 'GROUP key=value ... should update an already existing association' do
197
193
  # 1. Should add the var to the db
@@ -201,23 +197,23 @@ RSpec.describe Moose::Inventory::Cli::Group do
201
197
  var = { name: 'var1', value: 'testval' }
202
198
 
203
199
  @db.models[:group].create(name: group_name)
204
- runner { @app.start(%W(group addvar #{group_name} #{var[:name]}=#{var[:value]})) }
200
+ runner { @app.start(%W[group addvar #{group_name} #{var[:name]}=#{var[:value]}]) }
205
201
 
206
202
  var[:value] = 'newtestval'
207
203
  actual = runner do
208
- @app.start(%W(group addvar #{group_name} #{var[:name]}=#{var[:value]}))
204
+ @app.start(%W[group addvar #{group_name} #{var[:name]}=#{var[:value]}])
209
205
  end
210
206
  # @console.out(actual,'y')
211
207
 
212
208
  desired = { aborted: false }
213
209
  desired[:STDOUT] =
214
- "Add variables '#{var[:name]}=#{var[:value]}' to group '#{group_name}':\n"\
215
- " - retrieve group '#{group_name}'...\n"\
216
- " - OK\n"\
217
- " - add variable '#{var[:name]}=#{var[:value]}'...\n"\
218
- " - already exists, applying as an update...\n"\
219
- " - OK\n"\
220
- " - all OK\n"\
210
+ "Add variables '#{var[:name]}=#{var[:value]}' to group '#{group_name}':\n " \
211
+ "- retrieve group '#{group_name}'...\n " \
212
+ "- OK\n " \
213
+ "- add variable '#{var[:name]}=#{var[:value]}'...\n " \
214
+ "- already exists, applying as an update...\n " \
215
+ "- OK\n " \
216
+ "- all OK\n" \
221
217
  "Succeeded.\n"
222
218
  expected(actual, desired)
223
219
 
@@ -233,3 +229,4 @@ RSpec.describe Moose::Inventory::Cli::Group do
233
229
  end
234
230
  end
235
231
  end
232
+ # rubocop:enable Metrics/BlockLength
@@ -1,49 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Metrics/BlockLength
1
4
  require 'spec_helper'
2
5
 
3
- # TODO: the usual respond_to? method doesn't seem to work on Thor objects.
4
6
  # Why not? For now, we'll check against instance_methods.
5
7
 
6
8
  RSpec.describe Moose::Inventory::Cli::Group do
7
9
  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
- @group = Moose::Inventory::Cli::Group
29
- @app = Moose::Inventory::Cli::Application
10
+ setup_cli_harness(command_class: Moose::Inventory::Cli::Group, command_ivar: :@group)
30
11
  end
31
12
 
32
13
  before(:each) do
33
- @db.reset
14
+ reset_cli_harness
34
15
  end
35
16
 
36
17
  #=======================
37
18
  describe 'get' do
38
19
  #---------------------
39
20
  it 'should be responsive' do
40
- result = @group.instance_methods(false).include?(:get)
21
+ result = @group.method_defined?(:get, false)
41
22
  expect(result).to eq(true)
42
23
  end
43
24
 
44
25
  #---------------------
45
26
  it '<missing args> ... should abort with an error' do
46
- actual = runner { @app.start(%w(group get)) }
27
+ actual = runner { @app.start(%w[group get]) }
47
28
 
48
29
  # @console.out(actual,'y')
49
30
 
@@ -56,7 +37,7 @@ RSpec.describe Moose::Inventory::Cli::Group do
56
37
  #---------------------
57
38
  it "GROUP ... should return an empty set when GROUP doesn't exist" do
58
39
  group_name = 'does-not-exist'
59
- actual = runner { @app.start(%W(group get #{group_name})) }
40
+ actual = runner { @app.start(%W[group get #{group_name}]) }
60
41
 
61
42
  # @console.out(actual, 'y')
62
43
 
@@ -69,13 +50,15 @@ RSpec.describe Moose::Inventory::Cli::Group do
69
50
  #---------------------
70
51
  it 'GROUP ... should get a group from the db' do
71
52
  name = 'test_group'
72
- runner { @app.start(%W(group add #{name})) }
53
+ runner { @app.start(%W[group add #{name}]) }
73
54
 
74
- actual = runner { @app.start(%W(group get #{name})) }
55
+ actual = runner { @app.start(%W[group get #{name}]) }
75
56
 
76
57
  mock = {}
77
58
  mock[name.to_sym] = {}
78
- # mock[name.to_sym][:hosts] = [] # TODO: Should this be present or not?
59
+ # Contract: `group get` omits empty relationship collections in the default
60
+ # human/data output. `group list --ansible` keeps `hosts: []` because
61
+ # Ansible inventory consumers expect the key to exist.
79
62
 
80
63
  desired = { aborted: false, STDOUT: '', STDERR: '' }
81
64
  desired[:STDOUT] = mock.to_yaml
@@ -87,10 +70,10 @@ RSpec.describe Moose::Inventory::Cli::Group do
87
70
  it 'GROUP ... should display groupvars, if any are set' do
88
71
  name = 'test_group'
89
72
  var = 'foo=bar'
90
- tmp = runner { @app.start(%W(group add #{name})) }
91
- tmp = runner { @app.start(%W(group addvar #{name} #{var})) }
73
+ runner { @app.start(%W[group add #{name}]) }
74
+ runner { @app.start(%W[group addvar #{name} #{var}]) }
92
75
 
93
- actual = runner { @app.start(%W(group get #{name})) }
76
+ actual = runner { @app.start(%W[group get #{name}]) }
94
77
  # @console.out(actual, 'y')
95
78
 
96
79
  mock = {}
@@ -104,3 +87,4 @@ RSpec.describe Moose::Inventory::Cli::Group do
104
87
  end
105
88
  end
106
89
  end
90
+ # rubocop:enable Metrics/BlockLength
@@ -1,48 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Metrics/BlockLength
1
4
  require 'spec_helper'
2
5
 
3
6
  RSpec.describe Moose::Inventory::Cli::Group do
4
7
  before(:all) do
5
- # Set up the configuration object
6
- @mockarg_parts = {
7
- config: File.join(spec_root, 'config/config.yml'),
8
- format: 'yaml',
9
- env: 'test',
10
- }
11
-
12
- @mockargs = []
13
- @mockarg_parts.each do |key, val|
14
- @mockargs << "--#{key}"
15
- @mockargs << val
16
- end
17
-
18
- @config = Moose::Inventory::Config
19
- @config.init(@mockargs)
20
-
21
- @db = Moose::Inventory::DB
22
- @db.init if @db.db.nil?
23
-
24
- @console = Moose::Inventory::Cli::Formatter
25
- @group = Moose::Inventory::Cli::Group
26
- @cli = Moose::Inventory::Cli
27
- @app = Moose::Inventory::Cli::Application
8
+ setup_cli_harness(command_class: Moose::Inventory::Cli::Group, command_ivar: :@group, include_cli: true)
28
9
  end
29
10
 
30
11
  before(:each) do
31
- @db.reset
12
+ reset_cli_harness
32
13
  end
33
14
 
34
15
  #====================
35
16
  describe 'list' do
36
17
  #---------------------
37
18
  it 'should be responsive' do
38
- result = @group.instance_methods(false).include?(:list)
19
+ result = @group.method_defined?(:list, false)
39
20
  expect(result).to eq(true)
40
21
  end
41
22
 
42
23
  #---------------------
43
24
  it 'should return an empty set when no results' do
44
25
  # no items in the db
45
- actual = runner { @app.start(%w(group list)) }
26
+ actual = runner { @app.start(%w[group list]) }
46
27
 
47
28
  desired = { aborted: false, STDOUT: '', STDERR: '' }
48
29
  desired[:STDOUT] = {}.to_yaml
@@ -53,19 +34,18 @@ RSpec.describe Moose::Inventory::Cli::Group do
53
34
  #---------------------
54
35
  it 'should get a list of group from the db' do
55
36
  var = 'foo=bar'
56
- host_name = 'test_host'
57
37
 
58
38
  mock = {}
59
- groups = %w(group1 group2 group3)
39
+ groups = %w[group1 group2 group3]
60
40
  groups.each do |name|
61
- runner { @app.start(%W(group add #{name})) }
62
- runner { @app.start(%W(group addvar #{name} #{var})) }
41
+ runner { @app.start(%W[group add #{name}]) }
42
+ runner { @app.start(%W[group addvar #{name} #{var}]) }
63
43
  mock[name.to_sym] = {}
64
44
  mock[name.to_sym][:groupvars] = { foo: 'bar' }
65
45
  end
66
46
 
67
47
  # items should now be in the db
68
- actual = runner { @app.start(%w(group list)) }
48
+ actual = runner { @app.start(%w[group list]) }
69
49
 
70
50
  desired = { aborted: false, STDOUT: '', STDERR: '' }
71
51
  desired[:STDOUT] = mock.to_yaml
@@ -75,12 +55,10 @@ RSpec.describe Moose::Inventory::Cli::Group do
75
55
 
76
56
  #---------------------
77
57
  it 'should be an alias of --list (i.e. Ansible parameter)' do
78
- host_name = 'test_host'
79
-
80
58
  mock = {}
81
- groups = %w(group1 group2 group3)
59
+ groups = %w[group1 group2 group3]
82
60
  groups.each do |name|
83
- runner { @app.start(%W(group add #{name})) }
61
+ runner { @app.start(%W[group add #{name}]) }
84
62
  mock[name.to_sym] = { hosts: [] }
85
63
  end
86
64
 
@@ -92,10 +70,10 @@ RSpec.describe Moose::Inventory::Cli::Group do
92
70
  # @console.out(actual, 'y')
93
71
 
94
72
  desired = { aborted: false, STDOUT: '', STDERR: '' }
95
- desired[:STDOUT] = mock.to_json + "\n"
73
+ desired[:STDOUT] = "#{mock.to_json}\n"
96
74
 
97
75
  expected(actual, desired)
98
76
  end
99
- end
100
77
  end
101
-
78
+ end
79
+ # rubocop:enable Metrics/BlockLength
@@ -1,50 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Metrics/BlockLength
1
4
  require 'spec_helper'
2
5
 
3
6
  RSpec.describe Moose::Inventory::Cli::Group do
4
7
  before(:all) do
5
- # Set up the configuration object
6
- @mockarg_parts = {
7
- config: File.join(spec_root, 'config/config.yml'),
8
- format: 'yaml',
9
- env: 'test',
10
- }
11
-
12
- @mockargs = []
13
- @mockarg_parts.each do |key, val|
14
- @mockargs << "--#{key}"
15
- @mockargs << val
16
- end
17
-
18
- @config = Moose::Inventory::Config
19
- @config.init(@mockargs)
20
-
21
- @db = Moose::Inventory::DB
22
- @db.init if @db.db.nil?
23
-
24
- @console = Moose::Inventory::Cli::Formatter
25
- @group = Moose::Inventory::Cli::Group
26
- @cli = Moose::Inventory::Cli
27
- @app = Moose::Inventory::Cli::Application
8
+ setup_cli_harness(command_class: Moose::Inventory::Cli::Group, command_ivar: :@group, include_cli: true)
28
9
  end
29
10
 
30
11
  before(:each) do
31
- # We make some @cli calls, which changes config,
32
- # so we must reset config on each pass
33
- @config.init(@mockargs)
34
- @db.reset
12
+ reset_cli_harness(reset_config: true)
35
13
  end
36
14
 
37
15
  #==================
38
16
  describe 'listvar' do
39
17
  #-----------------
40
18
  it 'should be responsive' do
41
- result = @group.instance_methods(false).include?(:listvars)
19
+ result = @group.method_defined?(:listvars, false)
42
20
  expect(result).to eq(true)
43
21
  end
44
22
 
45
23
  #-----------------
46
24
  it '<missing args> ... should abort with an error' do
47
- actual = runner { @app.start(%w(group listvars)) }
25
+ actual = runner { @app.start(%w[group listvars]) }
48
26
 
49
27
  # Check output
50
28
  desired = { aborted: true }
@@ -55,7 +33,7 @@ RSpec.describe Moose::Inventory::Cli::Group do
55
33
  #-----------------
56
34
  it '--ansible <missing args> ... should abort with an error' do
57
35
  args = @mockargs.clone
58
- args.concat(%w(--ansible group listvars)).flatten
36
+ args.push('--ansible', 'group', 'listvars').flatten
59
37
 
60
38
  actual = runner { @cli.start(args) }
61
39
 
@@ -68,13 +46,13 @@ RSpec.describe Moose::Inventory::Cli::Group do
68
46
  #------------------------
69
47
  it 'GROUP ... should return a list of group variables grouped by group' do
70
48
  group_name = 'test_group'
71
- group_vars = %w(foo=bar cow=chicken)
49
+ group_vars = %w[foo=bar cow=chicken]
72
50
 
73
- tmp = runner { @app.start(%W(group add #{group_name})) }
74
- tmp = runner { @app.start(%W(group addvar #{group_name} #{group_vars[0]} #{group_vars[1]})) }
51
+ runner { @app.start(%W[group add #{group_name}]) }
52
+ runner { @app.start(%W[group addvar #{group_name} #{group_vars[0]} #{group_vars[1]}]) }
75
53
 
76
54
  actual = runner do
77
- @app.start(%W(group listvars #{group_name}))
55
+ @app.start(%W[group listvars #{group_name}])
78
56
  end
79
57
 
80
58
  # @console.out(actual, 'y')
@@ -93,15 +71,15 @@ RSpec.describe Moose::Inventory::Cli::Group do
93
71
  end
94
72
 
95
73
  #------------------------
96
- it '--ansible GROUP ... should return a list of group variables, in a style akin to Ansible\'s \'--host HOSTNAME\'' do
74
+ it 'returns group variables in an Ansible-style hostvars payload' do
97
75
  group_name = 'test_group'
98
- group_vars = %w(foo=bar cow=chicken)
76
+ group_vars = %w[foo=bar cow=chicken]
99
77
 
100
- tmp = runner { @app.start(%W(group add #{group_name})) }
101
- tmp = runner { @app.start(%W(group addvar #{group_name} #{group_vars[0]} #{group_vars[1]})) }
78
+ runner { @app.start(%W[group add #{group_name}]) }
79
+ runner { @app.start(%W[group addvar #{group_name} #{group_vars[0]} #{group_vars[1]}]) }
102
80
 
103
81
  actual = runner do
104
- @cli.start(%W(--config #{@mockarg_parts[:config]} --ansible group listvars #{group_name}))
82
+ @cli.start(%W[--config #{@mockarg_parts[:config]} --ansible group listvars #{group_name}])
105
83
  end
106
84
 
107
85
  # @console.out(actual, 'y')
@@ -114,8 +92,23 @@ RSpec.describe Moose::Inventory::Cli::Group do
114
92
  end
115
93
 
116
94
  desired = {}
117
- desired[:STDOUT] = mock.to_json + "\n"
95
+ desired[:STDOUT] = "#{mock.to_json}\n"
96
+ expected(actual, desired)
97
+ end
98
+
99
+ #------------------------
100
+ it 'warns when an Ansible-mode group does not exist' do
101
+ group_name = 'missing_group'
102
+
103
+ actual = runner do
104
+ @cli.start(%W[--config #{@mockarg_parts[:config]} --ansible group listvars #{group_name}])
105
+ end
106
+
107
+ desired = {}
108
+ desired[:STDOUT] = "{}\n"
109
+ desired[:STDERR] = "WARNING: The Group #{group_name} does not exist."
118
110
  expected(actual, desired)
119
111
  end
120
112
  end
121
113
  end
114
+ # rubocop:enable Metrics/BlockLength