MrMurano 1.11.3 → 1.12.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/Gemfile +2 -0
- data/README.markdown +12 -14
- data/Rakefile +2 -2
- data/TODO.taskpaper +8 -6
- data/docs/demo.md +109 -0
- data/lib/MrMurano/Account.rb +6 -3
- data/lib/MrMurano/Config.rb +13 -37
- data/lib/MrMurano/Product-1P-Device.rb +2 -0
- data/lib/MrMurano/Product-Resources.rb +5 -4
- data/lib/MrMurano/Product.rb +3 -3
- data/lib/MrMurano/Solution-File.rb +1 -2
- data/lib/MrMurano/Solution-ServiceConfig.rb +11 -1
- data/lib/MrMurano/Solution-Services.rb +24 -7
- data/lib/MrMurano/Solution-Users.rb +6 -4
- data/lib/MrMurano/SyncUpDown.rb +67 -28
- data/lib/MrMurano/commands/config.rb +4 -1
- data/lib/MrMurano/commands/exportImport.rb +3 -0
- data/lib/MrMurano/hash.rb +2 -0
- data/lib/MrMurano/http.rb +3 -3
- data/lib/MrMurano/verbosing.rb +9 -3
- data/lib/MrMurano/version.rb +1 -1
- data/spec/Account_spec.rb +104 -0
- data/spec/Config_spec.rb +202 -57
- data/spec/Http_spec.rb +204 -0
- data/spec/MakePretties_spec.rb +35 -0
- data/spec/Mock_spec.rb +13 -20
- data/spec/ProductBase_spec.rb +2 -0
- data/spec/ProductContent_spec.rb +77 -12
- data/spec/ProductResources_spec.rb +235 -0
- data/spec/Product_1P_Device_spec.rb +62 -0
- data/spec/Product_1P_RPC_spec.rb +2 -0
- data/spec/Product_spec.rb +18 -8
- data/spec/Solution-Cors_spec.rb +28 -1
- data/spec/Solution-Endpoint_spec.rb +250 -33
- data/spec/Solution-File_spec.rb +210 -0
- data/spec/Solution-ServiceConfig_spec.rb +2 -0
- data/spec/Solution-ServiceDevice_spec.rb +174 -0
- data/spec/Solution-ServiceEventHandler_spec.rb +134 -1
- data/spec/Solution-ServiceModules_spec.rb +317 -64
- data/spec/Solution-UsersRoles_spec.rb +207 -0
- data/spec/Solution_spec.rb +90 -0
- data/spec/SyncRoot_spec.rb +52 -46
- data/spec/SyncUpDown_spec.rb +364 -0
- data/spec/Verbosing_spec.rb +279 -0
- data/spec/_workspace.rb +27 -0
- data/spec/fixtures/dumped_config +47 -0
- data/spec/fixtures/product_spec_files/lightbulb-no-state.yaml +11 -0
- data/spec/fixtures/product_spec_files/lightbulb.yaml +2 -0
- data/spec/fixtures/roles-three.yaml +11 -0
- data/spec/spec_helper.rb +9 -0
- metadata +26 -2
data/lib/MrMurano/SyncUpDown.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'pathname'
|
2
2
|
require 'tempfile'
|
3
3
|
require 'shellwords'
|
4
|
+
require 'open3'
|
4
5
|
require 'MrMurano/Config'
|
5
6
|
require 'MrMurano/hash'
|
6
7
|
|
@@ -10,23 +11,52 @@ module MrMurano
|
|
10
11
|
Syncable = Struct.new(:name, :class, :type, :desc, :bydefault) do
|
11
12
|
end
|
12
13
|
|
14
|
+
##
|
15
|
+
# Add a new entry to syncable things
|
16
|
+
# +name+:: The name to use for the long option
|
17
|
+
# +klass+:: The class to instanciate from
|
18
|
+
# +type+:: Single letter for short option and status listing
|
19
|
+
# +desc+:: Summary of what this syncs.
|
20
|
+
# +bydefault+:: Is this part of the default sync group
|
21
|
+
#
|
22
|
+
# returns nil
|
13
23
|
def self.add(name, klass, type, desc, bydefault=false)
|
14
24
|
@@syncset = [] unless defined?(@@syncset)
|
15
25
|
@@syncset << Syncable.new(name.to_s, klass, type, desc, bydefault)
|
26
|
+
nil
|
16
27
|
end
|
17
28
|
|
29
|
+
##
|
30
|
+
# Remove all syncables.
|
18
31
|
def self.reset()
|
19
32
|
@@syncset = []
|
20
33
|
end
|
21
34
|
|
35
|
+
##
|
36
|
+
# Get the list of default syncables.
|
37
|
+
# returns array of names
|
38
|
+
def self.bydefault
|
39
|
+
@@syncset.select{|a| a.bydefault }.map{|a| a.name}
|
40
|
+
end
|
41
|
+
|
42
|
+
##
|
43
|
+
# Iterate over all syncables
|
44
|
+
# +block+:: code to run on each
|
22
45
|
def self.each(&block)
|
23
46
|
@@syncset.each{|a| yield a.name, a.type, a.class }
|
24
47
|
end
|
25
48
|
|
49
|
+
##
|
50
|
+
# Iterate over all syncables with option arguments.
|
51
|
+
# +block+:: code to run on each
|
26
52
|
def self.each_option(&block)
|
27
53
|
@@syncset.each{|a| yield "-#{a.type.downcase}", "--[no-]#{a.name}", a.desc}
|
28
54
|
end
|
29
55
|
|
56
|
+
##
|
57
|
+
# Iterate over just the selected syncables.
|
58
|
+
# +opt+:: Options hash of which to select from
|
59
|
+
# +block+:: code to run on each
|
30
60
|
def self.each_filtered(opt, &block)
|
31
61
|
self.checkSAME(opt)
|
32
62
|
@@syncset.each do |a|
|
@@ -38,13 +68,18 @@ module MrMurano
|
|
38
68
|
|
39
69
|
## Adjust options based on all or none
|
40
70
|
# If none are selected, select the bydefault ones.
|
71
|
+
#
|
72
|
+
# +opt+:: Options hash of which to select from
|
73
|
+
#
|
74
|
+
# returns nil
|
41
75
|
def self.checkSAME(opt)
|
42
76
|
if opt[:all] then
|
43
77
|
@@syncset.each {|a| opt[a.name.to_sym] = true }
|
44
78
|
else
|
45
79
|
any = @@syncset.select {|a| opt[a.name.to_sym] or opt[a.type.to_sym]}
|
46
80
|
if any.empty? then
|
47
|
-
|
81
|
+
bydef = $cfg['sync.bydefault'].split
|
82
|
+
@@syncset.select{|a| bydef.include? a.name }.each{|a| opt[a.name.to_sym] = true}
|
48
83
|
end
|
49
84
|
end
|
50
85
|
|
@@ -72,7 +107,9 @@ module MrMurano
|
|
72
107
|
#
|
73
108
|
# @param itemkey String: The identifying key for this item
|
74
109
|
def remove(itemkey)
|
110
|
+
# :nocov:
|
75
111
|
raise "Forgotten implementation"
|
112
|
+
# :nocov:
|
76
113
|
end
|
77
114
|
|
78
115
|
## Upload local item to remote
|
@@ -83,7 +120,9 @@ module MrMurano
|
|
83
120
|
# @param item Hash: The item details to upload
|
84
121
|
# @param modify Bool: True if item exists already and this is changing it
|
85
122
|
def upload(src, item, modify)
|
123
|
+
# :nocov:
|
86
124
|
raise "Forgotten implementation"
|
125
|
+
# :nocov:
|
87
126
|
end
|
88
127
|
|
89
128
|
##
|
@@ -112,7 +151,7 @@ module MrMurano
|
|
112
151
|
def toRemoteItem(root, path)
|
113
152
|
path = Pathname.new(path) unless path.kind_of? Pathname
|
114
153
|
root = Pathname.new(root) unless root.kind_of? Pathname
|
115
|
-
{:name => path.relative_path_from(root).to_s}
|
154
|
+
{:name => path.realpath.relative_path_from(root.realpath).to_s}
|
116
155
|
end
|
117
156
|
|
118
157
|
##
|
@@ -166,8 +205,7 @@ module MrMurano
|
|
166
205
|
# @param item Hash: The item to download
|
167
206
|
def download(local, item)
|
168
207
|
if item[:bundled] then
|
169
|
-
|
170
|
-
# FIXME don't use say_warning
|
208
|
+
warning "Not downloading into bundled item #{synckey(item)}"
|
171
209
|
return
|
172
210
|
end
|
173
211
|
local.dirname.mkpath
|
@@ -275,7 +313,7 @@ module MrMurano
|
|
275
313
|
::File.fnmatch(i,p)
|
276
314
|
end
|
277
315
|
end.map do |path|
|
278
|
-
path = Pathname.new(path)
|
316
|
+
path = Pathname.new(path).realpath
|
279
317
|
item = toRemoteItem(from, path)
|
280
318
|
if item.kind_of?(Array) then
|
281
319
|
item.compact.map{|i| i[:local_path] = path; i}
|
@@ -289,31 +327,32 @@ module MrMurano
|
|
289
327
|
#######################################################################
|
290
328
|
# Methods that provide the core status/syncup/syncdown
|
291
329
|
|
330
|
+
##
|
331
|
+
# Take a hash or something (a Commander::Command::Options) and return a hash
|
332
|
+
#
|
292
333
|
def elevate_hash(hsh)
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
self[mid]
|
300
|
-
end
|
301
|
-
end
|
334
|
+
# Commander::Command::Options stripped all of the methods from parent
|
335
|
+
# objects. I have not nice thoughts about that.
|
336
|
+
begin
|
337
|
+
hsh = hsh.__hash__
|
338
|
+
rescue NoMethodError
|
339
|
+
# swallow this.
|
302
340
|
end
|
303
|
-
|
341
|
+
# build a hash where the default is 'false' instead of 'nil'
|
342
|
+
Hash.new(false).merge(Hash.transform_keys_to_symbols(hsh))
|
304
343
|
end
|
305
344
|
private :elevate_hash
|
306
345
|
|
307
346
|
def syncup(options={})
|
308
347
|
options = elevate_hash(options)
|
309
348
|
itemkey = @itemkey.to_sym
|
310
|
-
options
|
349
|
+
options[:asdown] = false
|
311
350
|
dt = status(options)
|
312
351
|
toadd = dt[:toadd]
|
313
352
|
todel = dt[:todel]
|
314
353
|
tomod = dt[:tomod]
|
315
354
|
|
316
|
-
if options
|
355
|
+
if options[:delete] then
|
317
356
|
todel.each do |item|
|
318
357
|
verbose "Removing item #{item[:synckey]}"
|
319
358
|
unless $cfg['tool.dry'] then
|
@@ -321,7 +360,7 @@ module MrMurano
|
|
321
360
|
end
|
322
361
|
end
|
323
362
|
end
|
324
|
-
if options
|
363
|
+
if options[:create] then
|
325
364
|
toadd.each do |item|
|
326
365
|
verbose "Adding item #{item[:synckey]}"
|
327
366
|
unless $cfg['tool.dry'] then
|
@@ -329,7 +368,7 @@ module MrMurano
|
|
329
368
|
end
|
330
369
|
end
|
331
370
|
end
|
332
|
-
if options
|
371
|
+
if options[:update] then
|
333
372
|
tomod.each do |item|
|
334
373
|
verbose "Updating item #{item[:synckey]}"
|
335
374
|
unless $cfg['tool.dry'] then
|
@@ -341,14 +380,14 @@ module MrMurano
|
|
341
380
|
|
342
381
|
def syncdown(options={})
|
343
382
|
options = elevate_hash(options)
|
344
|
-
options
|
383
|
+
options[:asdown] = true
|
345
384
|
dt = status(options)
|
346
385
|
into = @locationbase + @location ###
|
347
386
|
toadd = dt[:toadd]
|
348
387
|
todel = dt[:todel]
|
349
388
|
tomod = dt[:tomod]
|
350
389
|
|
351
|
-
if options
|
390
|
+
if options[:delete] then
|
352
391
|
todel.each do |item|
|
353
392
|
verbose "Removing item #{item[:synckey]}"
|
354
393
|
unless $cfg['tool.dry'] then
|
@@ -357,7 +396,7 @@ module MrMurano
|
|
357
396
|
end
|
358
397
|
end
|
359
398
|
end
|
360
|
-
if options
|
399
|
+
if options[:create] then
|
361
400
|
toadd.each do |item|
|
362
401
|
verbose "Adding item #{item[:synckey]}"
|
363
402
|
unless $cfg['tool.dry'] then
|
@@ -366,7 +405,7 @@ module MrMurano
|
|
366
405
|
end
|
367
406
|
end
|
368
407
|
end
|
369
|
-
if options
|
408
|
+
if options[:update] then
|
370
409
|
tomod.each do |item|
|
371
410
|
verbose "Updating item #{item[:synckey]}"
|
372
411
|
unless $cfg['tool.dry'] then
|
@@ -398,10 +437,10 @@ module MrMurano
|
|
398
437
|
download(Pathname.new(trmt.path), item)
|
399
438
|
|
400
439
|
cmd = $cfg['diff.cmd'].shellsplit
|
401
|
-
cmd << trmt.path
|
402
|
-
cmd << tlcl.path
|
440
|
+
cmd << trmt.path.gsub(::File::SEPARATOR, ::File::ALT_SEPARATOR || ::File::SEPARATOR)
|
441
|
+
cmd << tlcl.path.gsub(::File::SEPARATOR, ::File::ALT_SEPARATOR || ::File::SEPARATOR)
|
403
442
|
|
404
|
-
|
443
|
+
df, _ = Open3.capture2e(*cmd)
|
405
444
|
ensure
|
406
445
|
trmt.close
|
407
446
|
trmt.unlink
|
@@ -434,7 +473,7 @@ module MrMurano
|
|
434
473
|
todel = []
|
435
474
|
tomod = []
|
436
475
|
unchg = []
|
437
|
-
if options
|
476
|
+
if options[:asdown] then
|
438
477
|
todel = (herebox.keys - therebox.keys).map{|key| herebox[key] }
|
439
478
|
toadd = (therebox.keys - herebox.keys).map{|key| therebox[key] }
|
440
479
|
else
|
@@ -446,7 +485,7 @@ module MrMurano
|
|
446
485
|
mrg = herebox[key].reject{|k,v| k==itemkey}
|
447
486
|
mrg = therebox[key].merge(mrg)
|
448
487
|
if docmp(herebox[key], therebox[key]) then
|
449
|
-
mrg[:diff] = dodiff(mrg) if options
|
488
|
+
mrg[:diff] = dodiff(mrg) if options[:diff]
|
450
489
|
tomod << mrg
|
451
490
|
else
|
452
491
|
unchg << mrg
|
@@ -6,13 +6,16 @@ command :config do |c|
|
|
6
6
|
You can get, set, or query config options with this command. All config
|
7
7
|
options are in a 'section.key' format. There is also a layer of scopes
|
8
8
|
that the keys can be saved in.
|
9
|
+
|
10
|
+
If section is left out, then key is assumed to be in the 'tool' section.
|
11
|
+
|
9
12
|
}
|
10
13
|
|
11
14
|
c.example %{See what the current combined config is}, 'mr config --dump'
|
12
15
|
c.example %{Query a value}, 'mr config solution.id'
|
13
16
|
c.example %{Set a new value; writing to the project config file}, 'mr config solution.id XXXXXXXX'
|
14
17
|
c.example %{Set a new value; writing to the user config file}, 'mr config --user user.name my@email.address'
|
15
|
-
c.example %{Unset a value in a configfile. (lower scopes will become visible
|
18
|
+
c.example %{Unset a value in a configfile. (lower scopes will become visible when unset)},
|
16
19
|
'mr config diff.cmd --unset'
|
17
20
|
|
18
21
|
|
@@ -119,6 +119,8 @@ command 'config import' do |c|
|
|
119
119
|
if routes == '' then
|
120
120
|
acc.verbose "No endpoints to import"
|
121
121
|
elsif File.dirname(routes) == '.' then
|
122
|
+
# TODO: don't need to move this anymore.
|
123
|
+
# Can set location.endpoints and endpoints.searchFor instead.
|
122
124
|
acc.warning "Routes file #{File.basename(routes)} not in endpoints directory"
|
123
125
|
if options.move then
|
124
126
|
acc.warning "Moving it to #{$cfg['location.endpoints']}"
|
@@ -142,6 +144,7 @@ command 'config import' do |c|
|
|
142
144
|
end
|
143
145
|
end
|
144
146
|
|
147
|
+
# TODO: change this to take advantage of searchFor
|
145
148
|
def update_or_stop(paths, cfgkey, what, acc=MrMurano::Account.new)
|
146
149
|
crd = Dir.common_root(paths)
|
147
150
|
acc.debug "crd => #{crd}"
|
data/lib/MrMurano/hash.rb
CHANGED
@@ -3,12 +3,14 @@
|
|
3
3
|
class Hash
|
4
4
|
#take keys of hash and transform those to a symbols
|
5
5
|
def self.transform_keys_to_symbols(value)
|
6
|
+
return value.map{|v| Hash.transform_keys_to_symbols(v)} if value.is_a?(Array)
|
6
7
|
return value if not value.is_a?(Hash)
|
7
8
|
hash = value.inject({}){|memo,(k,v)| memo[k.to_sym] = Hash.transform_keys_to_symbols(v); memo}
|
8
9
|
return hash
|
9
10
|
end
|
10
11
|
#take keys of hash and transform those to strings
|
11
12
|
def self.transform_keys_to_strings(value)
|
13
|
+
return value.map{|v| Hash.transform_keys_to_strings(v)} if value.is_a?(Array)
|
12
14
|
return value if not value.is_a?(Hash)
|
13
15
|
hash = value.inject({}){|memo,(k,v)| memo[k.to_s] = Hash.transform_keys_to_strings(v); memo}
|
14
16
|
return hash
|
data/lib/MrMurano/http.rb
CHANGED
@@ -70,7 +70,7 @@ module MrMurano
|
|
70
70
|
request.each_capitalized{|k,v| puts "> #{k}: #{v}"}
|
71
71
|
if request.body.nil? then
|
72
72
|
else
|
73
|
-
puts "
|
73
|
+
puts ">> #{request.body[0..156]}"
|
74
74
|
end
|
75
75
|
puts "Got #{response.code} #{response.message}"
|
76
76
|
response.each_capitalized{|k,v| puts "< #{k}: #{v}"}
|
@@ -87,7 +87,8 @@ module MrMurano
|
|
87
87
|
else
|
88
88
|
resp << jsn
|
89
89
|
end
|
90
|
-
|
90
|
+
# assuming verbosing was included.
|
91
|
+
error resp
|
91
92
|
end
|
92
93
|
|
93
94
|
def workit(request, &block)
|
@@ -106,7 +107,6 @@ module MrMurano
|
|
106
107
|
end
|
107
108
|
else
|
108
109
|
showHttpError(request, response)
|
109
|
-
raise response
|
110
110
|
end
|
111
111
|
end
|
112
112
|
end
|
data/lib/MrMurano/verbosing.rb
CHANGED
@@ -34,8 +34,8 @@ module MrMurano
|
|
34
34
|
def tabularize(data, ios=nil)
|
35
35
|
fmt = $cfg['tool.outformat']
|
36
36
|
ios = $stdout if ios.nil?
|
37
|
-
cols =
|
38
|
-
rows =
|
37
|
+
cols = nil
|
38
|
+
rows = nil
|
39
39
|
title = nil
|
40
40
|
if data.kind_of?(Hash) then
|
41
41
|
cols = data[:headers] if data.has_key?(:headers)
|
@@ -53,12 +53,18 @@ module MrMurano
|
|
53
53
|
return
|
54
54
|
end
|
55
55
|
if fmt =~ /csv/i then
|
56
|
+
cols = [] if cols.nil?
|
57
|
+
rows = [[]] if rows.nil?
|
56
58
|
CSV(ios, :headers=>cols, :write_headers=>(not cols.empty?)) do |csv|
|
57
59
|
rows.each{|v| csv << v}
|
58
60
|
end
|
59
61
|
else
|
60
62
|
# table.
|
61
|
-
|
63
|
+
table = Terminal::Table.new
|
64
|
+
table.title = title unless title.nil?
|
65
|
+
table.headings = cols unless cols.nil?
|
66
|
+
table.rows = rows unless rows.nil?
|
67
|
+
ios.puts table
|
62
68
|
end
|
63
69
|
end
|
64
70
|
|
data/lib/MrMurano/version.rb
CHANGED
data/spec/Account_spec.rb
CHANGED
@@ -1,8 +1,112 @@
|
|
1
1
|
require 'MrMurano/version'
|
2
2
|
require 'MrMurano/Config'
|
3
3
|
require 'MrMurano/Account'
|
4
|
+
require 'highline/import'
|
5
|
+
require '_workspace'
|
6
|
+
|
7
|
+
RSpec.describe MrMurano::Account, "token" do
|
8
|
+
include_context "WORKSPACE"
|
9
|
+
before(:example) do
|
10
|
+
$cfg = MrMurano::Config.new
|
11
|
+
$cfg.load
|
12
|
+
$cfg['net.host'] = 'bizapi.hosted.exosite.io'
|
13
|
+
$cfg['business.id'] = 'XYZxyz'
|
14
|
+
$cfg['product.id'] = 'XYZ'
|
15
|
+
|
16
|
+
@acc = MrMurano::Account.new
|
17
|
+
end
|
18
|
+
|
19
|
+
after(:example) do
|
20
|
+
@acc.token_reset
|
21
|
+
end
|
22
|
+
|
23
|
+
context "Get login info" do
|
24
|
+
before(:example) do
|
25
|
+
@pswd = instance_double("MrMurano::Passwords")
|
26
|
+
allow(@pswd).to receive(:load).and_return(nil)
|
27
|
+
allow(@pswd).to receive(:save).and_return(nil)
|
28
|
+
allow(MrMurano::Passwords).to receive(:new).and_return(@pswd)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "Asks for nothing" do
|
32
|
+
$cfg['user.name'] = "bob"
|
33
|
+
expect(@pswd).to receive(:get).once.and_return("built")
|
34
|
+
|
35
|
+
ret = @acc._loginInfo
|
36
|
+
expect(ret).to eq({
|
37
|
+
:email => "bob", :password=>"built"
|
38
|
+
})
|
39
|
+
end
|
40
|
+
|
41
|
+
it "Asks for user name" do
|
42
|
+
$cfg['user.name'] = nil
|
43
|
+
expect($terminal).to receive(:ask).once.and_return('bob')
|
44
|
+
expect(@acc).to receive(:error).once
|
45
|
+
expect($cfg).to receive(:set).with('user.name', 'bob', :user).once.and_call_original
|
46
|
+
expect(@pswd).to receive(:get).once.and_return("built")
|
47
|
+
|
48
|
+
ret = @acc._loginInfo
|
49
|
+
expect(ret).to eq({
|
50
|
+
:email => "bob", :password=>"built"
|
51
|
+
})
|
52
|
+
end
|
53
|
+
|
54
|
+
it "Asks for password" do
|
55
|
+
$cfg['user.name'] = "bob"
|
56
|
+
expect(@pswd).to receive(:get).with('bizapi.hosted.exosite.io','bob').once.and_return(nil)
|
57
|
+
expect(@acc).to receive(:error).once
|
58
|
+
expect($terminal).to receive(:ask).once.and_return('dog')
|
59
|
+
expect(@pswd).to receive(:set).once.with('bizapi.hosted.exosite.io','bob','dog')
|
60
|
+
|
61
|
+
ret = @acc._loginInfo
|
62
|
+
expect(ret).to eq({
|
63
|
+
:email => "bob", :password=>"dog"
|
64
|
+
})
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context "token" do
|
69
|
+
before(:example) do
|
70
|
+
allow(@acc).to receive(:_loginInfo).and_return({:email=>'bob',:password=>'v'})
|
71
|
+
end
|
72
|
+
|
73
|
+
it "gets a token" do
|
74
|
+
stub_request(:post, "https://bizapi.hosted.exosite.io/api:1/token/").
|
75
|
+
with(:body => {:email=>'bob', :password=>'v'}.to_json).
|
76
|
+
to_return(body: {:token=>"ABCDEFGHIJKLMNOP"}.to_json )
|
77
|
+
|
78
|
+
ret = @acc.token
|
79
|
+
expect(ret).to eq("ABCDEFGHIJKLMNOP")
|
80
|
+
end
|
81
|
+
|
82
|
+
it "gets an error" do
|
83
|
+
stub_request(:post, "https://bizapi.hosted.exosite.io/api:1/token/").
|
84
|
+
with(:body => {:email=>'bob', :password=>'v'}.to_json).
|
85
|
+
to_return(status: 401, body: {}.to_json )
|
86
|
+
|
87
|
+
expect(@acc).to receive(:error).twice.and_return(nil)
|
88
|
+
ret = @acc.token
|
89
|
+
expect(ret).to be_nil
|
90
|
+
end
|
91
|
+
|
92
|
+
it "uses existing token" do
|
93
|
+
@acc.token_reset("quxx")
|
94
|
+
ret = @acc.token
|
95
|
+
expect(ret).to eq("quxx")
|
96
|
+
end
|
97
|
+
|
98
|
+
it "uses existing token, even with new instance" do
|
99
|
+
@acc.token_reset("quxx")
|
100
|
+
acc = MrMurano::Account.new
|
101
|
+
ret = acc.token
|
102
|
+
expect(ret).to eq("quxx")
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
end
|
4
107
|
|
5
108
|
RSpec.describe MrMurano::Account do
|
109
|
+
include_context "WORKSPACE"
|
6
110
|
before(:example) do
|
7
111
|
$cfg = MrMurano::Config.new
|
8
112
|
$cfg.load
|