MuranoCLI 3.2.0.beta.1 → 3.2.0.beta.5

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 (111) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +4 -1
  3. data/.trustme.plugin +137 -0
  4. data/.trustme.sh +217 -117
  5. data/.trustme.vim +9 -3
  6. data/Gemfile +9 -3
  7. data/MuranoCLI.gemspec +8 -5
  8. data/Rakefile +1 -0
  9. data/dockers/Dockerfile.2.2.9 +6 -3
  10. data/dockers/Dockerfile.2.3.6 +6 -3
  11. data/dockers/Dockerfile.2.4.3 +6 -3
  12. data/dockers/Dockerfile.2.5.0 +6 -3
  13. data/dockers/Dockerfile.GemRelease +10 -8
  14. data/dockers/Dockerfile.m4 +23 -5
  15. data/dockers/docker-test.sh +65 -28
  16. data/docs/completions/murano_completion-bash +751 -57
  17. data/docs/develop.rst +10 -9
  18. data/lib/MrMurano/AccountBase.rb +95 -6
  19. data/lib/MrMurano/Commander-Entry.rb +9 -4
  20. data/lib/MrMurano/Config-Migrate.rb +2 -0
  21. data/lib/MrMurano/Config.rb +94 -26
  22. data/lib/MrMurano/Content.rb +1 -1
  23. data/lib/MrMurano/Exchange.rb +77 -42
  24. data/lib/MrMurano/Gateway.rb +1 -1
  25. data/lib/MrMurano/HttpAuthed.rb +20 -7
  26. data/lib/MrMurano/Logs.rb +10 -1
  27. data/lib/MrMurano/ProjectFile.rb +1 -1
  28. data/lib/MrMurano/ReCommander.rb +129 -73
  29. data/lib/MrMurano/Solution-ServiceConfig.rb +18 -11
  30. data/lib/MrMurano/Solution-Services.rb +78 -50
  31. data/lib/MrMurano/Solution-Users.rb +1 -1
  32. data/lib/MrMurano/Solution.rb +13 -63
  33. data/lib/MrMurano/SyncUpDown-Core.rb +185 -77
  34. data/lib/MrMurano/SyncUpDown-Item.rb +29 -4
  35. data/lib/MrMurano/SyncUpDown.rb +11 -11
  36. data/lib/MrMurano/Webservice-Cors.rb +1 -1
  37. data/lib/MrMurano/Webservice-Endpoint.rb +28 -17
  38. data/lib/MrMurano/Webservice-File.rb +103 -43
  39. data/lib/MrMurano/commands/domain.rb +1 -0
  40. data/lib/MrMurano/commands/element.rb +585 -0
  41. data/lib/MrMurano/commands/exchange.rb +211 -204
  42. data/lib/MrMurano/commands/gb.rb +1 -0
  43. data/lib/MrMurano/commands/globals.rb +17 -7
  44. data/lib/MrMurano/commands/init.rb +115 -101
  45. data/lib/MrMurano/commands/keystore.rb +1 -1
  46. data/lib/MrMurano/commands/logs.rb +2 -1
  47. data/lib/MrMurano/commands/postgresql.rb +17 -7
  48. data/lib/MrMurano/commands/service.rb +572 -0
  49. data/lib/MrMurano/commands/show.rb +7 -3
  50. data/lib/MrMurano/commands/solution.rb +2 -1
  51. data/lib/MrMurano/commands/solution_picker.rb +31 -15
  52. data/lib/MrMurano/commands/status.rb +205 -169
  53. data/lib/MrMurano/commands/sync.rb +70 -38
  54. data/lib/MrMurano/commands/token.rb +59 -14
  55. data/lib/MrMurano/commands/usage.rb +1 -0
  56. data/lib/MrMurano/commands.rb +2 -0
  57. data/lib/MrMurano/hash.rb +91 -0
  58. data/lib/MrMurano/http.rb +55 -6
  59. data/lib/MrMurano/makePretty.rb +47 -0
  60. data/lib/MrMurano/optparse.rb +60 -45
  61. data/lib/MrMurano/variegated/TruthyFalsey.rb +48 -0
  62. data/lib/MrMurano/variegated/ruby_dig.rb +64 -0
  63. data/lib/MrMurano/verbosing.rb +113 -3
  64. data/lib/MrMurano/version.rb +1 -1
  65. data/spec/Account_spec.rb +34 -20
  66. data/spec/Business_spec.rb +12 -9
  67. data/spec/Config_spec.rb +7 -1
  68. data/spec/Content_spec.rb +17 -1
  69. data/spec/GatewayBase_spec.rb +5 -2
  70. data/spec/GatewayDevice_spec.rb +4 -2
  71. data/spec/GatewayResource_spec.rb +4 -1
  72. data/spec/GatewaySettings_spec.rb +4 -1
  73. data/spec/HttpAuthed_spec.rb +73 -0
  74. data/spec/Http_spec.rb +32 -35
  75. data/spec/ProjectFile_spec.rb +1 -1
  76. data/spec/Solution-ServiceConfig_spec.rb +4 -1
  77. data/spec/Solution-ServiceEventHandler_spec.rb +6 -3
  78. data/spec/Solution-ServiceModules_spec.rb +4 -1
  79. data/spec/Solution-UsersRoles_spec.rb +4 -1
  80. data/spec/Solution_spec.rb +4 -1
  81. data/spec/SyncUpDown_spec.rb +1 -1
  82. data/spec/Webservice-Cors_spec.rb +4 -1
  83. data/spec/Webservice-Endpoint_spec.rb +9 -6
  84. data/spec/Webservice-File_spec.rb +17 -4
  85. data/spec/Webservice-Setting_spec.rb +6 -2
  86. data/spec/_workspace.rb +2 -0
  87. data/spec/cmd_common.rb +42 -13
  88. data/spec/cmd_content_spec.rb +17 -7
  89. data/spec/cmd_device_spec.rb +1 -1
  90. data/spec/cmd_domain_spec.rb +2 -2
  91. data/spec/cmd_element_spec.rb +400 -0
  92. data/spec/cmd_exchange_spec.rb +2 -2
  93. data/spec/cmd_init_spec.rb +59 -25
  94. data/spec/cmd_keystore_spec.rb +6 -3
  95. data/spec/cmd_link_spec.rb +10 -5
  96. data/spec/cmd_logs_spec.rb +1 -1
  97. data/spec/cmd_setting_application_spec.rb +18 -15
  98. data/spec/cmd_setting_product_spec.rb +7 -7
  99. data/spec/cmd_status_spec.rb +27 -17
  100. data/spec/cmd_syncdown_application_spec.rb +30 -3
  101. data/spec/cmd_syncdown_both_spec.rb +72 -18
  102. data/spec/cmd_syncup_spec.rb +71 -5
  103. data/spec/cmd_token_spec.rb +2 -2
  104. data/spec/cmd_usage_spec.rb +2 -2
  105. data/spec/dry_run_formatter.rb +27 -0
  106. data/spec/fixtures/dumped_config +8 -0
  107. data/spec/fixtures/exchange_element/element-show.json +1 -0
  108. data/spec/fixtures/exchange_element/swagger-mur-6407__10k.yaml +282 -0
  109. data/spec/fixtures/exchange_element/swagger-mur-6407__20k.yaml +588 -0
  110. data/spec/variegated_TruthyFalsey_spec.rb +29 -0
  111. metadata +51 -25
data/spec/cmd_common.rb CHANGED
@@ -16,6 +16,7 @@ require 'tmpdir'
16
16
  require 'webmock/rspec'
17
17
 
18
18
  require 'MrMurano/Config'
19
+ require 'MrMurano/HttpAuthed'
19
20
 
20
21
  # Prevent Commander from registering its at_exit hook.
21
22
  # - Also print warning if spec exits, which rspec doesn't see as wrong.
@@ -50,6 +51,9 @@ at_exit do
50
51
  warn('¡Unexpected spec exit killed rspec!')
51
52
  warn('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
52
53
  # DEVs: If you see this message, the last test exited unexpectedly.
54
+ warn('')
55
+ warn("Died on: #{$exited_abnormally}")
56
+ warn('')
53
57
  end
54
58
  end
55
59
  alias original_at_exit at_exit unless defined?(original_at_exit)
@@ -96,6 +100,8 @@ RSpec.shared_context 'CI_CMD' do
96
100
  args.push '-c', 'fullerror'
97
101
  # The spinner output would make it hard to write expects().
98
102
  args.push '--no-progress'
103
+ # Save the token.
104
+ args.push '--cache'
99
105
 
100
106
  if Gem.win_platform?
101
107
  cmd = args.map do |i|
@@ -159,7 +165,7 @@ RSpec.shared_context 'CI_CMD' do
159
165
  expect(out).to \
160
166
  eq('').or \
161
167
  eq("No solutions found\n").or \
162
- match(/^Deleted [\d]+ solutions/)
168
+ match(/^Deleted [\d]+ solutions?/)
163
169
  expect(strip_color(err)).to eq('').or eq("No solutions found\n")
164
170
  expect(status.exitstatus).to eq(0).or eq(1)
165
171
  end
@@ -221,7 +227,7 @@ RSpec.shared_context 'CI_CMD' do
221
227
  # Utility fcns: Strings.
222
228
 
223
229
  # rname makes a random Murano-acceptable Solution name.
224
- def rname(name)
230
+ def rname(name='')
225
231
  #"#{name}-#{Random.new.rand.hash.abs.to_s(16)}"
226
232
  # MUR-2454: Product name may only contain letters and numbers.
227
233
  #"#{name}#{Random.new.rand.hash.abs.to_s(16)}"
@@ -272,7 +278,7 @@ RSpec.shared_context 'CI_CMD' do
272
278
 
273
279
  def murano_command_runner(cmd, *args, wont_run: false, wont_parse: false)
274
280
  # This is a functional test, so tell WebMock to back off.
275
- # FIXME: Remember the setting and re-enable.
281
+ allow_net_conn = WebMock::Config.instance.allow_net_connect != false
276
282
  WebMock.allow_net_connect!
277
283
 
278
284
  capture_stdio
@@ -288,25 +294,25 @@ RSpec.shared_context 'CI_CMD' do
288
294
  $stdout = tmpout
289
295
  $stderr = tmperr
290
296
 
291
- $exited_abnormally = true
297
+ $exited_abnormally = "#{cmd} #{args.join(' ')}"
298
+
299
+ MrMurano::HttpAuthed.instance.credentials_reset
292
300
 
293
301
  # When Commander is loaded, it sets an at_exit hook, which we monkey
294
302
  # patch in ReCommander. Since Config.validate_cmd is called before
295
303
  # at_exit, it uses runner.command_exit to tell ReCommander's at_exit
296
304
  # monkey patch not to call Commander.run!. Via rspec, we don't use the
297
305
  # at_exit hook, or ReCommander.
298
- if $cfg.nil?
299
- $cfg = MrMurano::Config.new(::Commander::Runner.instance)
300
- $cfg.load
301
- end
306
+ $cfg = MrMurano::Config.new(::Commander::Runner.instance)
307
+ $cfg.load
302
308
  $cfg['tool.no-progress'] = true
303
- $cfg.validate_cmd(cmd)
309
+ $cfg.validate_cmd
304
310
  runner = ::Commander::Runner.instance
305
311
  unless defined?(runner.command_exit) && runner.command_exit
306
- # Commander's at_exit hook calls runner.run! which runs the command
307
- # that was determined with Commander was loaded. I [lb] tried a few
308
- # different ways to reset Runner.instance, but nothing worked; our
309
- # best bet is to just call the command directly.
312
+ # (lb): Commander's at_exit hook calls runner.run! which runs the
313
+ # command that was determined with Commander was loaded. I tried a
314
+ # few different ways to reset Runner.instance, but nothing worked;
315
+ # our best bet is to just call the command directly.
310
316
  the_cmd = command(cmd.to_sym)
311
317
  when_called = the_cmd.peek_when_called.dup
312
318
 
@@ -342,6 +348,8 @@ RSpec.shared_context 'CI_CMD' do
342
348
  # $stdout, $stderr = STDOUT, STDERR
343
349
  restore_stdio
344
350
 
351
+ WebMock.disable_net_connect! unless allow_net_conn
352
+
345
353
  [strip_color(tmpout.string), strip_color(tmperr.string)]
346
354
  end
347
355
 
@@ -376,11 +384,29 @@ RSpec.shared_context 'CI_CMD' do
376
384
  require 'byebug/settings/histfile'
377
385
  _ignored = ::Byebug::HistfileSetting::DEFAULT
378
386
 
387
+ $exited_abnormally = ex.example.to_s
388
+
379
389
  @testdir = Pathname.new(Dir.pwd).realpath
380
390
  Dir.mktmpdir do |hdir|
391
+ saved_home = Dir.home
381
392
  ENV['HOME'] = hdir
382
393
  Dir.chdir(hdir) do
383
394
  Dir.mkdir('.murano')
395
+
396
+ Dir.chdir('.murano') do
397
+ begin
398
+ FileUtils.ln_s(File.join(saved_home, '.murano', 'tokens'), 'tokens')
399
+ rescue NotImplementedError => err
400
+ require 'rbconfig'
401
+ unless OS.windows?
402
+ warn(
403
+ 'Unexpected: ln_s failed on non-Windows machine / ' \
404
+ "host_os: #{RbConfig::CONFIG['host_os']} / err: #{err}"
405
+ )
406
+ end
407
+ end
408
+ end
409
+
384
410
  @tmpdir = File.join(hdir, 'project')
385
411
  Dir.mkdir(@tmpdir)
386
412
  Dir.chdir(@tmpdir) do
@@ -392,7 +418,10 @@ RSpec.shared_context 'CI_CMD' do
392
418
  rm_symlink
393
419
  end
394
420
  end
421
+ ENV['HOME'] = saved_home
395
422
  end
423
+
424
+ $exited_abnormally = false
396
425
  end
397
426
  end
398
427
 
@@ -15,19 +15,24 @@ RSpec.describe 'murano content', :cmd, :needs_password do
15
15
 
16
16
  before(:example) do
17
17
  @product_name = rname('contestTest')
18
- out, err, status = Open3.capture3(capcmd('murano', 'product', 'create', @product_name, '--save'))
18
+ out, err, status = Open3.capture3(
19
+ capcmd('murano', 'product', 'create', @product_name, '--save')
20
+ )
19
21
  expect(err).to eq('')
20
22
  expect(out.chomp).to match(/^[a-zA-Z0-9]+$/)
21
23
  expect(status.exitstatus).to eq(0)
22
24
  end
25
+
23
26
  after(:example) do
24
- out, err, status = Open3.capture3(capcmd('murano', 'solution', 'delete', '--yes', @product_name))
27
+ out, err, status = Open3.capture3(
28
+ capcmd('murano', 'solution', 'delete', '--yes', @product_name)
29
+ )
25
30
  expect(out).to eq('')
26
31
  expect(err).to eq('')
27
32
  expect(status.exitstatus).to eq(0)
28
33
  end
29
34
 
30
- it 'life cycle' do
35
+ it 'life cycle', :club_10s do
31
36
  out, err, status = Open3.capture3(capcmd('murano', 'content', 'list'))
32
37
  #expect(out.lines).to match(
33
38
  # [
@@ -43,7 +48,9 @@ RSpec.describe 'murano content', :cmd, :needs_password do
43
48
  expect(status.exitstatus).to eq(0)
44
49
 
45
50
  FileUtils.copy(File.join(@testdir, 'spec/fixtures/dumped_config'), 'myFile')
46
- out, err, status = Open3.capture3(capcmd('murano', 'content', 'upload', 'myFile', '--tags', 'random=junk'))
51
+ out, err, status = Open3.capture3(
52
+ capcmd('murano', 'content', 'upload', 'myFile', '--tags', 'random=junk')
53
+ )
47
54
  expect(out).to eq('')
48
55
  expect(err).to eq('')
49
56
  expect(status.exitstatus).to eq(0)
@@ -75,7 +82,9 @@ RSpec.describe 'murano content', :cmd, :needs_password do
75
82
  )
76
83
  expect(status.exitstatus).to eq(0)
77
84
 
78
- out, err, status = Open3.capture3(capcmd('murano', 'content', 'download', 'myFile', '-o', 'testDown'))
85
+ out, err, status = Open3.capture3(
86
+ capcmd('murano', 'content', 'download', 'myFile', '-o', 'testDown')
87
+ )
79
88
  expect(out).to eq('')
80
89
  expect(err).to eq('')
81
90
  expect(status.exitstatus).to eq(0)
@@ -84,7 +93,9 @@ RSpec.describe 'murano content', :cmd, :needs_password do
84
93
  tdf = IO.read('testDown')
85
94
  expect(tdf).to eq(dcf)
86
95
 
87
- out, err, status = Open3.capture3(capcmd('murano', 'content', 'delete', 'myFile', '-y'))
96
+ out, err, status = Open3.capture3(
97
+ capcmd('murano', 'content', 'delete', 'myFile', '-y')
98
+ )
88
99
  expect(out).to eq('')
89
100
  expect(err).to eq('')
90
101
  expect(status.exitstatus).to eq(0)
@@ -105,4 +116,3 @@ RSpec.describe 'murano content', :cmd, :needs_password do
105
116
  end
106
117
  end
107
118
 
108
- # vim: set ai et sw=2 ts=2 :
@@ -84,7 +84,7 @@ RSpec.describe 'murano device', :cmd, :needs_password do
84
84
  expect(status.exitstatus).to eq(0)
85
85
  end
86
86
 
87
- it 'writes and reads' do
87
+ it 'writes and reads', :club_10s do
88
88
  FileUtils.mkpath('specs')
89
89
  FileUtils.copy(
90
90
  File.join(@testdir, 'spec/fixtures/product_spec_files/lightbulb.yaml'),
@@ -35,7 +35,7 @@ RSpec.describe 'murano domain', :cmd, :needs_password do
35
35
  expect(status.exitstatus).to eq(0)
36
36
  end
37
37
 
38
- it 'show domain' do
38
+ it 'show domain', :club_20s, :club_10s do
39
39
  out, err, status = Open3.capture3(capcmd('murano', 'domain'))
40
40
  # 2017-05-31: Previously, the project could be named whatever and
41
41
  # the URI would start with the same.
@@ -46,7 +46,7 @@ RSpec.describe 'murano domain', :cmd, :needs_password do
46
46
  #expect(out.chomp).to end_with('m2.exosite.io')
47
47
  out.lines.each do |line|
48
48
  expect(line).to match(
49
- %r{^(Product|Application): domain[Tt]est[a-z0-9]+ <[a-z0-9]+> https://[.a-z0-9]+$}
49
+ %r{^(Product|Application): domain[Tt]est[a-z0-9]+ <[a-z0-9]+> https://[.\-a-z0-9]+$}
50
50
  )
51
51
  end
52
52
  expect(err).to eq('')
@@ -0,0 +1,400 @@
1
+ # Copyright © 2016-2017 Exosite LLC. All Rights Reserved
2
+ # License: PROPRIETARY. See LICENSE.txt.
3
+ # frozen_string_literal: true
4
+
5
+ # vim:tw=0:ts=2:sw=2:et:ai
6
+ # Unauthorized copying of this file is strictly prohibited.
7
+
8
+ # (lb): 2018-05-01: Whoa. Coverage at 303 of 306 lines! 99.02%!!
9
+
10
+ require 'fileutils'
11
+ require 'json'
12
+ require 'open3'
13
+ require 'pathname'
14
+ require 'tty-editor'
15
+ require 'yaml'
16
+
17
+ require 'cmd_common'
18
+ require 'MrMurano/Config'
19
+
20
+ RSpec.describe 'murano element', :cmd, :needs_password do
21
+ include_context 'CI_CMD'
22
+
23
+ context 'without project' do
24
+ it 'usage' do
25
+ cmd_verify_help('element')
26
+ end
27
+ end
28
+
29
+ def expect_exchange_element_table(stdout, stderr, num_cols: nil)
30
+ expect(stderr).to eq('')
31
+ lines = stdout.lines
32
+ # FIXME/2018-04-30: Is this too much detail??
33
+ # What about running test once, dumping output to file,
34
+ # and expecting same output next time?
35
+ # Outline of table. n columns. '+-----+-----+---...----+\n'
36
+ expect(lines[0]).to match(/^(\+-+){#{num_cols}}\+$/)
37
+ # Header. "... key | value ..."
38
+ expect(lines[1]).to match(/^\| key \+| value \+|$/)
39
+ # Separator.
40
+ expect(lines[2]).to match(/^(\+-+){#{num_cols}}\+$/)
41
+ # Content. Starts with nested.hash.key.
42
+ (3..(lines.length - 2)).to_a.each do |line|
43
+ expect(lines[line]).to match(/^\| [\.0-9a-zA-Z]* +\| /)
44
+ end
45
+ expect(lines[-1]).to match(/^(\+-+){#{num_cols}}\+$/)
46
+ end
47
+
48
+ context 'show' do
49
+ it '--help' do
50
+ stdout, stderr = murano_command_wont_parse('element show', '--help')
51
+ expect(stdout).to a_string_starting_with('Usage:')
52
+ expect(stderr).to eq('')
53
+ end
54
+
55
+ it 'using ambiguous name' do
56
+ # MEH/2018-04-30: This test is dependent on the platform having
57
+ # more than one element with the term 'IoT' in its name!
58
+ # 59bc24836b12c505c98c5a51: ‘Twilio SMS Service’
59
+ # 5adedb7cdcd2dd1d04082fba: ‘Public application for the twilio’
60
+ #
61
+ # FIXME/2018-04-30: Make test-specific elements for this test!
62
+ stdout, stderr = murano_command_exits('element show', 'twilio')
63
+ expect(stdout).to eq('')
64
+ expect(stderr).to a_string_starting_with(
65
+ 'Please be more specific: More than one matching element was found:'
66
+ )
67
+ end
68
+
69
+ it 'as default-table' do
70
+ # FIXME/2018-04-30: Make test-specific elements for this test!
71
+ stdout, stderr = murano_command_run('element show', 'MUR-6407')
72
+ expect_exchange_element_table(stdout, stderr, num_cols: 2)
73
+ expect(stderr).to eq('')
74
+ end
75
+
76
+ it 'as table wrap' do
77
+ # FIXME/2018-04-30: Make test-specific elements for this test!
78
+ stdout, stderr = murano_command_run('element show', 'MUR-6407', '--wrap')
79
+ expect_exchange_element_table(stdout, stderr, num_cols: 2)
80
+ expect(stderr).to eq('')
81
+ end
82
+
83
+ it 'as table truncate' do
84
+ # FIXME/2018-04-30: Make test-specific elements for this test!
85
+ stdout, stderr = murano_command_run('element show', 'MUR-6407', '--truncate')
86
+ expect_exchange_element_table(stdout, stderr, num_cols: 2)
87
+ expect(stderr).to eq('')
88
+ end
89
+
90
+ # (lb): NOTE: This does not add any additional coverage to element.rb:
91
+ it 'as json' do
92
+ # FIXME/2018-04-30: Make test-specific elements for this test!
93
+ stdout, stderr = murano_command_run('element show', '--json', 'MUR-6407')
94
+ expect { JSON.parse(stdout) }.to_not raise_error
95
+ expect(stderr).to eq('')
96
+ end
97
+
98
+ # (lb): NOTE: This does not add any additional coverage to element.rb:
99
+ it 'as yaml' do
100
+ # FIXME/2018-04-30: Make test-specific elements for this test!
101
+ stdout, stderr = murano_command_run('element show', 'MUR-6407', '--yaml')
102
+ expect { YAML.parse(stdout) }.to_not raise_error
103
+ expect(stderr).to eq('')
104
+ end
105
+
106
+ # (lb): This adds 1 line of coverage.
107
+ it 'as csv' do
108
+ # FIXME/2018-04-30: Make test-specific elements for this test!
109
+ stdout, stderr = murano_command_run('element show', 'MUR-6407', '--csv')
110
+ expect(stdout).to_not eq('')
111
+ expect(stderr).to eq('')
112
+ end
113
+ end
114
+
115
+ context 'edit' do
116
+ before(:example) do
117
+ @json_input_file = 'element-show.json'
118
+ @spec_swagger_20k = 'swagger-mur-6407__20k.yaml'
119
+ @spec_swagger_10k = 'swagger-mur-6407__10k.yaml'
120
+ [
121
+ @json_input_file,
122
+ @spec_swagger_20k,
123
+ @spec_swagger_10k,
124
+ ].each do |path|
125
+ FileUtils.copy(
126
+ File.join(@testdir, "spec/fixtures/exchange_element/#{path}"), path
127
+ )
128
+ end
129
+ end
130
+
131
+ def element_editor_expect_edit_json_full
132
+ # Mock the $EDITOR session.
133
+ expect(TTY::Editor).to receive(:open) do |path|
134
+ edit_json_make_unique_contact(path)
135
+ end
136
+ end
137
+
138
+ def edit_json_make_unique_contact(path)
139
+ pname = path.is_a?(Pathname) && path || Pathname.new(path)
140
+ pname.write pname.read.gsub(
141
+ /\n "contact": "[a-f0-9]+",\n/,
142
+ %(\n "contact": "#{rname}",\n)
143
+ )
144
+ end
145
+
146
+ def edit_specs_yaml_make_unique_contact(path)
147
+ pname = path.is_a?(Pathname) && path || Pathname.new(path)
148
+ rname7 = rname[0..6]
149
+ pname.write pname.read.gsub(
150
+ /\n name: Exosite Exosite Exosite Exosite\n/,
151
+ %(\n name: #{rname7} #{rname7} #{rname7} #{rname7}\n)
152
+ )
153
+ end
154
+
155
+ def element_editor_expect_edit_plain_contact
156
+ # Mock the $EDITOR session.
157
+ expect(TTY::Editor).to receive(:open) do |path|
158
+ pname = path.is_a?(Pathname) && path || Pathname.new(path)
159
+ pname.write(rname)
160
+ end
161
+ end
162
+
163
+ # DEL:
164
+ def element_editor_expect_edit_json_from_string
165
+ expect(TTY::Editor).to receive(:open) do |path|
166
+ pname = path.is_a?(Pathname) && path || Pathname.new(path)
167
+ pname.write(
168
+ %({"tiers":[],"bizid":"55cop31gs89","access":"private","attachment":{},"contact":"#{rname}","description":"For testing Murano CLI element-edit.\\n\\nTEST2222\\n\\nTEST MORE","image":{"detail":{"color":"#224123","filename":"","url":""},"thumbnail":{"color":"#224655","filename":"xxx","url":""}},"markdown":"AxxxxAA2333BBBCCC\\n","name":"MUR-6407 Test Element","source":{"from":"service","name":"mur6407testelement","url":"https://testtesttest.apps.exosite-dev.io/swagger-mur-6407.yaml"},"specs":"","type":"service","tags":[],"active":true,"approval":"approved","elementId":"5ae3600e313d01708c2a6e0f"})
169
+ )
170
+ end
171
+ end
172
+
173
+ it '--help' do
174
+ stdout, stderr = murano_command_wont_parse('element edit', '--help')
175
+ expect(stdout).to a_string_starting_with('Usage:')
176
+ expect(stderr).to eq('')
177
+ end
178
+
179
+ it 'as static field option' do
180
+ stdout, stderr = murano_command_run(
181
+ 'element edit', '--contact', rname, 'MUR-6407'
182
+ )
183
+ expect(stdout).to eq('')
184
+ expect(stderr).to eq('')
185
+ end
186
+
187
+ it 'as dynamic field option' do
188
+ stdout, stderr = murano_command_run(
189
+ 'element edit', '-e', "contact=#{rname}", 'MUR-6407'
190
+ )
191
+ expect(stdout).to eq('')
192
+ expect(stderr).to eq('')
193
+ end
194
+
195
+ it 'as dynamic edit empty string without update' do
196
+ # I.e., @edit_fields[''] = nil, meaning should bring up editor.
197
+ # FIXME: Requires (lb)'s fixed commander library.
198
+
199
+ # If we don't change the file, Murano CLI will not PUT to BizAPI.
200
+ expect(TTY::Editor).to receive(:open).once
201
+ stdout, stderr = murano_command_exits(
202
+ 'element edit', '-e', '', 'MUR-6407'
203
+ )
204
+ expect(stdout).to eq('')
205
+ expect(stderr).to eq("No new field values specified to update.\n")
206
+ end
207
+
208
+ it 'as dynamic edit empty string with update' do
209
+ element_editor_expect_edit_json_full
210
+ stdout, stderr = murano_command_run(
211
+ 'element edit', '-e', '', 'MUR-6407'
212
+ )
213
+ expect(stdout).to eq('')
214
+ expect(stderr).to eq('')
215
+ end
216
+
217
+ it 'as dynamic edit no field with update' do
218
+ element_editor_expect_edit_json_full
219
+ stdout, stderr = murano_command_run(
220
+ 'element edit', '-e', '--', 'MUR-6407'
221
+ )
222
+ expect(stdout).to eq('')
223
+ expect(stderr).to eq('')
224
+ end
225
+
226
+ it 'as dynamic edit set empty string' do
227
+ # I.e., @edit_fields[''] = ''
228
+ stdout, stderr = murano_command_run(
229
+ #'element edit', '-e', "''=", 'MUR-6407'
230
+ 'element edit', '-e', '=', 'MUR-6407'
231
+ )
232
+ expect(stdout).to eq('')
233
+ expect(stderr).to eq(
234
+ %(Request Failed: 400: [400] child "type" fails because ["type" is required]\n)
235
+ )
236
+ end
237
+
238
+ it 'as edit with too many fields specified' do
239
+ stdout, stderr = murano_command_exits(
240
+ 'element edit', '-e', "contact=#{rname}", '-e', 'markdown', 'MUR-6407'
241
+ )
242
+ expect(stdout).to eq('')
243
+ expect(stderr).to eq(
244
+ %(Please specify at most one field when not specifing all field values.\n)
245
+ )
246
+ end
247
+
248
+ it 'as edit with no action specified' do
249
+ stdout, stderr = murano_command_exits(
250
+ 'element edit', 'MUR-6407'
251
+ )
252
+ expect(stdout).to eq('')
253
+ expect(stderr).to eq(
254
+ %(Please specify one or more -e/--edit options, or an input file.\n)
255
+ )
256
+ end
257
+
258
+ it 'as edit with input file not found' do
259
+ bad_path = "/path/to/nowhere/#{rname}.json"
260
+ stdout, stderr = murano_command_exits(
261
+ 'element edit', 'MUR-6407', bad_path
262
+ )
263
+ expect(stdout).to eq('')
264
+ expect(stderr).to eq(
265
+ %(Input file not found: #{bad_path}\n)
266
+ )
267
+ end
268
+
269
+ it 'as edit with input file found and valid' do
270
+ path = Pathname.new(@json_input_file)
271
+ path.write path.read.gsub(
272
+ /,"contact":"[a-f0-9]+",/,
273
+ %(,"contact":"#{rname}",)
274
+ )
275
+ stdout, stderr = murano_command_run(
276
+ 'element edit', 'MUR-6407', @json_input_file
277
+ )
278
+ expect(stdout).to eq('')
279
+ expect(stderr).to eq('')
280
+ end
281
+
282
+ it 'as edit with input file and too many fields specified' do
283
+ stdout, stderr = murano_command_exits(
284
+ 'element edit', '-e', 'contact', '-e', 'markdown', 'MUR-6407', @spec_swagger_20k
285
+ )
286
+ expect(stdout).to eq('')
287
+ expect(stderr).to eq(
288
+ %(Please specify at most a single field when specifing an input file.\n)
289
+ )
290
+ end
291
+
292
+ it 'as edit with input file and value specified' do
293
+ stdout, stderr = murano_command_exits(
294
+ 'element edit', '-e', 'contact=XYZ', 'MUR-6407', @spec_swagger_20k
295
+ )
296
+ expect(stdout).to eq('')
297
+ expect(stderr).to eq(
298
+ %(Please do not specify a value when specifing an input file.\n)
299
+ )
300
+ end
301
+
302
+ it 'as edit with input file that is too large and unrecognized field' do
303
+ stdout, stderr = murano_command_run(
304
+ 'element edit', '--edit', "spec=@#{@spec_swagger_20k}", 'MUR-6407'
305
+ )
306
+ expect(stdout).to eq('')
307
+ expect(stderr).to eq(
308
+ %(Request Failed: 400: [400] "spec" is not allowed\n)
309
+ )
310
+ end
311
+
312
+ it 'as edit with input file that should not be parsed' do
313
+ stdout, stderr = murano_command_run(
314
+ 'element edit', '--edit', "specs=@#{@spec_swagger_20k}", 'MUR-6407'
315
+ )
316
+ expect(stdout).to eq('')
317
+ expect(stderr).to eq(
318
+ %(Request Failed: 400: [400] child "specs" fails because ["specs" must be a string]\n)
319
+ )
320
+ end
321
+
322
+ # FIXME/2018-05-01 00:10: How does Tilstra circumvent 20k limit?
323
+ it 'as edit with input file that is too large' do
324
+ stdout, stderr = murano_command_run(
325
+ 'element edit', '--edit', 'specs', '--plain', '--', 'MUR-6407', @spec_swagger_20k
326
+ )
327
+ expect(stdout).to eq('')
328
+ expect(stderr).to eq(
329
+ %(Request Failed: 400: [400] child "specs" fails because ["specs" length must be less than or equal to 10000 characters long]\n)
330
+ )
331
+ end
332
+
333
+ it 'as edit with input file that is just right' do
334
+ edit_specs_yaml_make_unique_contact(@spec_swagger_10k)
335
+ stdout, stderr = murano_command_run(
336
+ 'element edit', '--edit', 'specs', '--plain', '--', 'MUR-6407', @spec_swagger_10k
337
+ )
338
+ expect(stdout).to eq('')
339
+ expect(stderr).to eq('')
340
+
341
+ # Send same file again.
342
+ stdout, stderr = murano_command_exits(
343
+ 'element edit', '--edit', 'specs', '--plain', '--', 'MUR-6407', @spec_swagger_10k
344
+ )
345
+ expect(stdout).to eq('')
346
+ expect(stderr).to eq("No new field values specified to update.\n")
347
+ end
348
+
349
+ it 'as dynamic edit single field no value uses EDITOR' do
350
+ element_editor_expect_edit_plain_contact
351
+ stdout, stderr = murano_command_run(
352
+ 'element edit', '-e', 'contact', '--', 'MUR-6407'
353
+ )
354
+ expect(stdout).to eq('')
355
+ expect(stderr).to eq('')
356
+ end
357
+
358
+ it 'as dynamic edit nested field change value' do
359
+ stdout, stderr = murano_command_run(
360
+ 'element edit', '-e', "image.detail.color=0x#{rname[0..5]}", 'MUR-6407'
361
+ )
362
+ expect(stdout).to eq('')
363
+ expect(stderr).to eq('')
364
+ end
365
+
366
+ # (lb): Get 2 more lines of coverage.
367
+ it 'as dynamic edit no field with update from string' do
368
+ stdout, stderr = murano_command_run(
369
+ 'element edit',
370
+ '-e',
371
+ %(={"bizid":"55cop31gs89","access":"private","attachment":{},"contact":"#{rname}","description":"For testing Murano CLI element-edit.\\n\\nTEST2222\\n\\nTEST MORE","image":{"detail":{"color":"#224123","filename":"","url":""},"thumbnail":{"color":"#224655","filename":"xxx","url":""}},"markdown":"AxxxxAA2333BBBCCC\\n","name":"MUR-6407 Test Element","source":{"from":"service","name":"mur6407testelement","url":"https://testtesttest.apps.exosite-dev.io/swagger-mur-6407.yaml"},"specs":"","type":"service","tags":[],"active":true,"elementId":"5ae3600e313d01708c2a6e0f"}),
372
+ 'MUR-6407'
373
+ )
374
+ expect(stdout).to eq('')
375
+ expect(stderr).to eq('')
376
+ end
377
+
378
+ # (lb): Get 2 more lines of coverage.
379
+ it 'as dynamic edit no field with update from string not valid Hash' do
380
+ stdout, stderr = murano_command_exits(
381
+ 'element edit', '-e', %(={), 'MUR-6407'
382
+ )
383
+ expect(stdout).to eq('')
384
+ expect(stderr).to eq %(The document object is not a Hash: {\n765: unexpected token at '{'\n)
385
+ end
386
+
387
+ # (lb): No additional coverage, other than integration.
388
+ it 'as dynamic edit no field with update from string fields not allowed' do
389
+ stdout, stderr = murano_command_run(
390
+ 'element edit',
391
+ '-e',
392
+ %(={"tiers":[],"bizid":"55cop31gs89","access":"private","attachment":{},"contact":"#{rname}","description":"For testing Murano CLI element-edit.\\n\\nTEST2222\\n\\nTEST MORE","image":{"detail":{"color":"#224123","filename":"","url":""},"thumbnail":{"color":"#224655","filename":"xxx","url":""}},"markdown":"AxxxxAA2333BBBCCC\\n","name":"MUR-6407 Test Element","source":{"from":"service","name":"mur6407testelement","url":"https://testtesttest.apps.exosite-dev.io/swagger-mur-6407.yaml"},"specs":"","type":"service","tags":[],"active":true,"approval":"approved","elementId":"5ae3600e313d01708c2a6e0f"}),
393
+ 'MUR-6407'
394
+ )
395
+ expect(stdout).to eq('')
396
+ expect(stderr).to eq %(Request Failed: 400: [400] "tiers" is not allowed. "approval" is not allowed\n)
397
+ end
398
+ end
399
+ end
400
+
@@ -39,7 +39,7 @@ RSpec.describe 'murano exchange', :cmd, :needs_password do
39
39
  expect(lines[3]).to match(/^(\+-+){#{num_cols}}\+$/)
40
40
  # Content. Starts with elementId.
41
41
  (4..(lines.length - 2)).to_a.each do |line|
42
- expect(lines[line]).to match(/^\| [0-9a-f]+ \| /)
42
+ expect(lines[line]).to match(/^\| [0-9a-f]+ +\| /)
43
43
  end
44
44
  expect(lines[-1]).to match(/^(\+-+){#{num_cols}}\+$/)
45
45
  end
@@ -51,7 +51,7 @@ RSpec.describe 'murano exchange', :cmd, :needs_password do
51
51
  expect_exchange_element_table(stdout, stderr, num_cols: 4)
52
52
  end
53
53
 
54
- it 'only ids' do
54
+ it 'only ids', :club_20s, :club_10s do
55
55
  stdout, stderr = murano_command_run('exchange list', '--idonly')
56
56
  expect(stderr).to eq('')
57
57
  stdout.lines.each do |line|