MuranoCLI 2.0.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 (151) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +28 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +21 -0
  5. data/Gemfile +27 -0
  6. data/LICENSE.txt +19 -0
  7. data/MuranoCLI.gemspec +50 -0
  8. data/MuranoCLI.iss +50 -0
  9. data/README.markdown +208 -0
  10. data/Rakefile +188 -0
  11. data/TODO.taskpaper +122 -0
  12. data/bin/mr +8 -0
  13. data/bin/murano +84 -0
  14. data/docs/demo.md +109 -0
  15. data/lib/MrMurano/Account.rb +211 -0
  16. data/lib/MrMurano/Config-Migrate.rb +47 -0
  17. data/lib/MrMurano/Config.rb +286 -0
  18. data/lib/MrMurano/Mock.rb +63 -0
  19. data/lib/MrMurano/Product-1P-Device.rb +145 -0
  20. data/lib/MrMurano/Product-Resources.rb +195 -0
  21. data/lib/MrMurano/Product.rb +358 -0
  22. data/lib/MrMurano/ProjectFile.rb +349 -0
  23. data/lib/MrMurano/Solution-Cors.rb +46 -0
  24. data/lib/MrMurano/Solution-Endpoint.rb +177 -0
  25. data/lib/MrMurano/Solution-File.rb +150 -0
  26. data/lib/MrMurano/Solution-ServiceConfig.rb +140 -0
  27. data/lib/MrMurano/Solution-Services.rb +326 -0
  28. data/lib/MrMurano/Solution-Users.rb +129 -0
  29. data/lib/MrMurano/Solution.rb +59 -0
  30. data/lib/MrMurano/SubCmdGroupContext.rb +49 -0
  31. data/lib/MrMurano/SyncUpDown.rb +565 -0
  32. data/lib/MrMurano/commands/assign.rb +57 -0
  33. data/lib/MrMurano/commands/businessList.rb +45 -0
  34. data/lib/MrMurano/commands/completion.rb +152 -0
  35. data/lib/MrMurano/commands/config.rb +67 -0
  36. data/lib/MrMurano/commands/content.rb +130 -0
  37. data/lib/MrMurano/commands/cors.rb +30 -0
  38. data/lib/MrMurano/commands/domain.rb +17 -0
  39. data/lib/MrMurano/commands/gb.rb +33 -0
  40. data/lib/MrMurano/commands/init.rb +138 -0
  41. data/lib/MrMurano/commands/keystore.rb +157 -0
  42. data/lib/MrMurano/commands/logs.rb +78 -0
  43. data/lib/MrMurano/commands/mock.rb +63 -0
  44. data/lib/MrMurano/commands/password.rb +88 -0
  45. data/lib/MrMurano/commands/postgresql.rb +41 -0
  46. data/lib/MrMurano/commands/product.rb +14 -0
  47. data/lib/MrMurano/commands/productCreate.rb +39 -0
  48. data/lib/MrMurano/commands/productDelete.rb +33 -0
  49. data/lib/MrMurano/commands/productDevice.rb +84 -0
  50. data/lib/MrMurano/commands/productDeviceIdCmds.rb +86 -0
  51. data/lib/MrMurano/commands/productList.rb +45 -0
  52. data/lib/MrMurano/commands/productWrite.rb +27 -0
  53. data/lib/MrMurano/commands/show.rb +80 -0
  54. data/lib/MrMurano/commands/solution.rb +14 -0
  55. data/lib/MrMurano/commands/solutionCreate.rb +39 -0
  56. data/lib/MrMurano/commands/solutionDelete.rb +34 -0
  57. data/lib/MrMurano/commands/solutionList.rb +45 -0
  58. data/lib/MrMurano/commands/status.rb +92 -0
  59. data/lib/MrMurano/commands/sync.rb +60 -0
  60. data/lib/MrMurano/commands/timeseries.rb +115 -0
  61. data/lib/MrMurano/commands/tsdb.rb +271 -0
  62. data/lib/MrMurano/commands/usage.rb +23 -0
  63. data/lib/MrMurano/commands/zshcomplete.erb +112 -0
  64. data/lib/MrMurano/commands.rb +32 -0
  65. data/lib/MrMurano/hash.rb +20 -0
  66. data/lib/MrMurano/http.rb +153 -0
  67. data/lib/MrMurano/makePretty.rb +75 -0
  68. data/lib/MrMurano/schema/pf-v1.0.0.yaml +114 -0
  69. data/lib/MrMurano/schema/sf-v0.2.0.yaml +77 -0
  70. data/lib/MrMurano/schema/sf-v0.3.0.yaml +78 -0
  71. data/lib/MrMurano/template/mock.erb +9 -0
  72. data/lib/MrMurano/template/projectFile.murano.erb +81 -0
  73. data/lib/MrMurano/verbosing.rb +99 -0
  74. data/lib/MrMurano/version.rb +4 -0
  75. data/lib/MrMurano.rb +20 -0
  76. data/spec/Account-Passwords_spec.rb +242 -0
  77. data/spec/Account_spec.rb +272 -0
  78. data/spec/ConfigFile_spec.rb +50 -0
  79. data/spec/ConfigMigrate_spec.rb +89 -0
  80. data/spec/Config_spec.rb +409 -0
  81. data/spec/Http_spec.rb +204 -0
  82. data/spec/MakePretties_spec.rb +118 -0
  83. data/spec/Mock_spec.rb +53 -0
  84. data/spec/ProductBase_spec.rb +113 -0
  85. data/spec/ProductContent_spec.rb +162 -0
  86. data/spec/ProductResources_spec.rb +329 -0
  87. data/spec/Product_1P_Device_spec.rb +202 -0
  88. data/spec/Product_1P_RPC_spec.rb +175 -0
  89. data/spec/Product_spec.rb +153 -0
  90. data/spec/ProjectFile_spec.rb +324 -0
  91. data/spec/Solution-Cors_spec.rb +164 -0
  92. data/spec/Solution-Endpoint_spec.rb +581 -0
  93. data/spec/Solution-File_spec.rb +212 -0
  94. data/spec/Solution-ServiceConfig_spec.rb +202 -0
  95. data/spec/Solution-ServiceDevice_spec.rb +176 -0
  96. data/spec/Solution-ServiceEventHandler_spec.rb +385 -0
  97. data/spec/Solution-ServiceModules_spec.rb +465 -0
  98. data/spec/Solution-UsersRoles_spec.rb +207 -0
  99. data/spec/Solution_spec.rb +92 -0
  100. data/spec/SyncRoot_spec.rb +83 -0
  101. data/spec/SyncUpDown_spec.rb +495 -0
  102. data/spec/Verbosing_spec.rb +279 -0
  103. data/spec/_workspace.rb +27 -0
  104. data/spec/cmd_assign_spec.rb +51 -0
  105. data/spec/cmd_business_spec.rb +59 -0
  106. data/spec/cmd_common.rb +72 -0
  107. data/spec/cmd_config_spec.rb +68 -0
  108. data/spec/cmd_content_spec.rb +71 -0
  109. data/spec/cmd_cors_spec.rb +50 -0
  110. data/spec/cmd_device_spec.rb +96 -0
  111. data/spec/cmd_domain_spec.rb +32 -0
  112. data/spec/cmd_init_spec.rb +30 -0
  113. data/spec/cmd_keystore_spec.rb +97 -0
  114. data/spec/cmd_password_spec.rb +62 -0
  115. data/spec/cmd_status_spec.rb +239 -0
  116. data/spec/cmd_syncdown_spec.rb +86 -0
  117. data/spec/cmd_syncup_spec.rb +62 -0
  118. data/spec/cmd_usage_spec.rb +36 -0
  119. data/spec/fixtures/.mrmuranorc +9 -0
  120. data/spec/fixtures/ProjectFiles/invalid.yaml +9 -0
  121. data/spec/fixtures/ProjectFiles/only_meta.yaml +24 -0
  122. data/spec/fixtures/ProjectFiles/with_routes.yaml +27 -0
  123. data/spec/fixtures/SolutionFiles/0.2.0.json +20 -0
  124. data/spec/fixtures/SolutionFiles/0.2.0_invalid.json +18 -0
  125. data/spec/fixtures/SolutionFiles/0.2.json +21 -0
  126. data/spec/fixtures/SolutionFiles/0.3.0.json +20 -0
  127. data/spec/fixtures/SolutionFiles/0.3.0_invalid.json +19 -0
  128. data/spec/fixtures/SolutionFiles/0.3.json +20 -0
  129. data/spec/fixtures/SolutionFiles/basic.json +20 -0
  130. data/spec/fixtures/SolutionFiles/secret.json +6 -0
  131. data/spec/fixtures/configfile +9 -0
  132. data/spec/fixtures/dumped_config +42 -0
  133. data/spec/fixtures/mrmuranorc_deleted_bob +8 -0
  134. data/spec/fixtures/mrmuranorc_tool_bob +3 -0
  135. data/spec/fixtures/product_spec_files/example.exoline.spec.yaml +116 -0
  136. data/spec/fixtures/product_spec_files/example.murano.spec.yaml +14 -0
  137. data/spec/fixtures/product_spec_files/gwe.exoline.spec.yaml +21 -0
  138. data/spec/fixtures/product_spec_files/gwe.murano.spec.yaml +16 -0
  139. data/spec/fixtures/product_spec_files/lightbulb-no-state.yaml +11 -0
  140. data/spec/fixtures/product_spec_files/lightbulb.yaml +14 -0
  141. data/spec/fixtures/roles-three.yaml +11 -0
  142. data/spec/fixtures/syncable_content/assets/icon.png +0 -0
  143. data/spec/fixtures/syncable_content/assets/index.html +0 -0
  144. data/spec/fixtures/syncable_content/assets/js/script.js +0 -0
  145. data/spec/fixtures/syncable_content/modules/table_util.lua +58 -0
  146. data/spec/fixtures/syncable_content/routes/manyRoutes.lua +11 -0
  147. data/spec/fixtures/syncable_content/routes/singleRoute.lua +5 -0
  148. data/spec/fixtures/syncable_content/services/devdata.lua +18 -0
  149. data/spec/fixtures/syncable_content/services/timers.lua +4 -0
  150. data/spec/spec_helper.rb +119 -0
  151. metadata +498 -0
@@ -0,0 +1,207 @@
1
+ require 'MrMurano/version'
2
+ require 'MrMurano/Solution-Users'
3
+ require 'MrMurano/hash'
4
+ require 'tempfile'
5
+ require '_workspace'
6
+
7
+ RSpec.describe MrMurano::Role 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['solution.id'] = 'XYZ'
14
+
15
+ @srv = MrMurano::Role.new
16
+ allow(@srv).to receive(:token).and_return("TTTTTTTTTT")
17
+ end
18
+
19
+ it "initializes" do
20
+ uri = @srv.endPoint('/')
21
+ expect(uri.to_s).to eq("https://bizapi.hosted.exosite.io/api:1/solution/XYZ/role/")
22
+ end
23
+
24
+ it "lists" do
25
+ body = [{:role_id=>"guest", :parameter=>[{:name=>"did"}]},
26
+ {:role_id=>"admin", :parameter=>[{:name=>"enabled"}]},
27
+ {:role_id=>"owns", :parameter=>[{:name=>"did"}]}]
28
+ stub_request(:get, "https://bizapi.hosted.exosite.io/api:1/solution/XYZ/role").
29
+ with(:headers=>{'Authorization'=>'token TTTTTTTTTT',
30
+ 'Content-Type'=>'application/json'}).
31
+ to_return(body: body.to_json)
32
+ ret = @srv.list
33
+ expect(ret).to eq(body)
34
+ end
35
+
36
+ it "fetches" do
37
+ body = {:role_id=>"guest", :parameter=>[{:name=>"did"}]}
38
+ stub_request(:get, "https://bizapi.hosted.exosite.io/api:1/solution/XYZ/role/guest").
39
+ with(:headers=>{'Authorization'=>'token TTTTTTTTTT',
40
+ 'Content-Type'=>'application/json'}).
41
+ to_return(body: body.to_json)
42
+ ret = @srv.fetch('guest')
43
+ expect(ret).to eq(body)
44
+ end
45
+
46
+ it "removes" do
47
+ stub_request(:delete, "https://bizapi.hosted.exosite.io/api:1/solution/XYZ/role/guest").
48
+ with(:headers=>{'Authorization'=>'token TTTTTTTTTT',
49
+ 'Content-Type'=>'application/json'}).
50
+ to_return(status: 200, body: "")
51
+ ret = @srv.remove('guest')
52
+ expect(ret).to eq({})
53
+ end
54
+
55
+ context "uploads" do
56
+ it "updating" do
57
+ body = {:role_id=>"guest", :parameter=>[{:name=>"did"}]}
58
+ stub_request(:delete, "https://bizapi.hosted.exosite.io/api:1/solution/XYZ/role/guest").
59
+ with(:headers=>{'Authorization'=>'token TTTTTTTTTT',
60
+ 'Content-Type'=>'application/json'}).
61
+ to_return(status: 200, body: "")
62
+ stub_request(:post, "https://bizapi.hosted.exosite.io/api:1/solution/XYZ/role/").
63
+ with(:headers=>{'Authorization'=>'token TTTTTTTTTT',
64
+ 'Content-Type'=>'application/json'},
65
+ :body=>body.to_json
66
+ ).
67
+ to_return(status: 205, body: "")
68
+
69
+
70
+ @srv.upload(nil, body, true)
71
+ end
72
+
73
+ it "creating" do
74
+ body = {:role_id=>"guest", :parameter=>[{:name=>"did"}]}
75
+ stub_request(:delete, "https://bizapi.hosted.exosite.io/api:1/solution/XYZ/role/guest").
76
+ with(:headers=>{'Authorization'=>'token TTTTTTTTTT',
77
+ 'Content-Type'=>'application/json'}).
78
+ to_return(status: 404, body: "")
79
+ stub_request(:post, "https://bizapi.hosted.exosite.io/api:1/solution/XYZ/role/").
80
+ with(:headers=>{'Authorization'=>'token TTTTTTTTTT',
81
+ 'Content-Type'=>'application/json'},
82
+ :body=>body.to_json
83
+ ).
84
+ to_return(status: 205, body: "")
85
+
86
+
87
+ @srv.upload(nil, body, false)
88
+ end
89
+
90
+ it "with delete error" do
91
+ body = {:role_id=>"guest", :parameter=>[{:name=>"did"}]}
92
+ stub_request(:delete, "https://bizapi.hosted.exosite.io/api:1/solution/XYZ/role/guest").
93
+ with(:headers=>{'Authorization'=>'token TTTTTTTTTT',
94
+ 'Content-Type'=>'application/json'}).
95
+ to_return(status: 418, body: "I'm a teapot!")
96
+ stub_request(:post, "https://bizapi.hosted.exosite.io/api:1/solution/XYZ/role/").
97
+ with(:headers=>{'Authorization'=>'token TTTTTTTTTT',
98
+ 'Content-Type'=>'application/json'},
99
+ :body=>body.to_json
100
+ ).
101
+ to_return(status: 205, body: "")
102
+
103
+ saved = $stderr
104
+ $stderr = StringIO.new
105
+ @srv.upload(nil, body, false)
106
+ expect($stderr.string).to match(/Request Failed: 418: I'm a teapot/)
107
+ $stderr = saved
108
+ end
109
+ end
110
+
111
+ context "downloads" do
112
+ before(:example) do
113
+ @lry = Pathname.new(@projectDir) + 'roles.yaml'
114
+ @grl = {:role_id=>"guest", :parameter=>[{:name=>"could"}]}
115
+ end
116
+
117
+ it "creates" do
118
+ @srv.download(@lry, @grl)
119
+
120
+ expect(@lry.exist?).to be true
121
+ got = YAML.load(@lry.read)
122
+ expect(got).to include(Hash.transform_keys_to_strings(@grl))
123
+ end
124
+
125
+ it "updates" do
126
+ FileUtils.copy(File.join(@testdir, 'spec/fixtures/roles-three.yaml'), @lry.to_path)
127
+
128
+ @srv.download(@lry, @grl)
129
+ got = YAML.load(@lry.read)
130
+ expect(got).to include(Hash.transform_keys_to_strings(@grl))
131
+ end
132
+ end
133
+
134
+ context "removing local roles" do
135
+ before(:example) do
136
+ @lry = Pathname.new(@projectDir) + 'roles.yaml'
137
+ @grl = {:role_id=>"guest", :parameter=>[{:name=>"could"}]}
138
+ end
139
+
140
+ it "when file missing" do
141
+ @srv.removelocal(@lry, @grl)
142
+ expect(@lry.exist?).to be true
143
+ got = YAML.load(@lry.read)
144
+ expect(got).to eq([])
145
+ end
146
+
147
+ it "when not there" do
148
+ FileUtils.copy(File.join(@testdir, 'spec/fixtures/roles-three.yaml'), @lry.to_path)
149
+ @srv.removelocal(@lry, {:role_id=>"undertow"})
150
+ got = YAML.load(@lry.read)
151
+ rty = Pathname.new(@testdir) + 'spec/fixtures/roles-three.yaml'
152
+ want = YAML.load(rty.read)
153
+ expect(got).to eq(want)
154
+ end
155
+
156
+ it "with matching role" do
157
+ FileUtils.copy(File.join(@testdir, 'spec/fixtures/roles-three.yaml'), @lry.to_path)
158
+ @srv.removelocal(@lry, @grl)
159
+ got = YAML.load(@lry.read)
160
+ expect(got.count).to eq(2)
161
+ expect(got).to contain_exactly(
162
+ a_hash_including('role_id' => 'admin'),
163
+ a_hash_including('role_id' => 'owns')
164
+ )
165
+ end
166
+ end
167
+
168
+ it "tolocalpath is into" do
169
+ lry = Pathname.new(@projectDir) + 'roles.yaml'
170
+ ret = @srv.tolocalpath(lry, {:role_id=>"guest", :parameter=>[{:name=>"could"}]})
171
+ expect(ret).to eq(lry)
172
+ end
173
+
174
+ context "list local items" do
175
+ before(:example) do
176
+ @lry = Pathname.new(@projectDir) + 'roles.yaml'
177
+ end
178
+
179
+ it "when missing" do
180
+ expect(@srv).to receive(:warning).with(/^Skipping missing/)
181
+ ret = @srv.localitems(@lry)
182
+ expect(ret).to eq([])
183
+ end
184
+
185
+ it "when not a file" do
186
+ FileUtils.mkpath(@lry.to_path)
187
+ expect(@srv).to receive(:warning).with(/^Cannot read from/)
188
+ ret = @srv.localitems(@lry)
189
+ expect(ret).to eq([])
190
+ end
191
+
192
+ it "when empty" do
193
+ FileUtils.touch(@lry.to_path)
194
+ ret = @srv.localitems(@lry)
195
+ expect(ret).to eq([])
196
+ end
197
+
198
+ it "with roles" do
199
+ FileUtils.copy(File.join(@testdir, 'spec/fixtures/roles-three.yaml'), @lry.to_path)
200
+ ret = @srv.localitems(@lry)
201
+ expect(ret).to eq([{:role_id=>"guest", :parameter=>[{:name=>"did"}]},
202
+ {:role_id=>"admin", :parameter=>[{:name=>"enabled"}]},
203
+ {:role_id=>"owns", :parameter=>[{:name=>"did"}]}])
204
+ end
205
+ end
206
+ end
207
+ # vim: set ai et sw=2 ts=2 :
@@ -0,0 +1,92 @@
1
+ require 'MrMurano/version'
2
+ require 'MrMurano/Solution-Cors'
3
+ require 'tempfile'
4
+ require 'yaml'
5
+ require '_workspace'
6
+
7
+ RSpec.describe MrMurano::Solution do
8
+ include_context "WORKSPACE"
9
+ before(:example) do
10
+ $cfg = MrMurano::Config.new
11
+ $cfg.load
12
+ $project = MrMurano::ProjectFile.new
13
+ $project.load
14
+ $cfg['net.host'] = 'bizapi.hosted.exosite.io'
15
+ $cfg['solution.id'] = 'XYZ'
16
+
17
+ @srv = MrMurano::Solution.new
18
+ allow(@srv).to receive(:token).and_return("TTTTTTTTTT")
19
+ end
20
+
21
+ it "initializes" do
22
+ uri = @srv.endPoint('/')
23
+ expect(uri.to_s).to eq("https://bizapi.hosted.exosite.io/api:1/solution/XYZ/")
24
+ end
25
+
26
+ it "gets info" do
27
+ body = {
28
+ :id=>"XYZ",
29
+ :label=>nil,
30
+ :domain=>"ugdemo.apps.exosite.io",
31
+ :biz_id=>"ABCDEFG",
32
+ :cors=> "{\"origin\":true,\"methods\":[\"HEAD\",\"GET\",\"POST\",\"PUT\",\"DELETE\",\"OPTIONS\",\"PATCH\"],\"headers\":[\"Content-Type\",\"Cookie\",\"Authorization\"],\"credentials\":true}"
33
+ }
34
+
35
+ stub_request(:get, "https://bizapi.hosted.exosite.io/api:1/solution/XYZ").
36
+ with(:headers=>{'Authorization'=>'token TTTTTTTTTT',
37
+ 'Content-Type'=>'application/json'}).
38
+ to_return(body: body.to_json)
39
+
40
+ ret = @srv.info()
41
+ expect(ret).to eq(body)
42
+ end
43
+
44
+ it "lists" do
45
+ body = {
46
+ :id=>"XYZ",
47
+ :label=>nil,
48
+ :domain=>"ugdemo.apps.exosite.io",
49
+ :biz_id=>"ABCDEFG",
50
+ :cors=> "{\"origin\":true,\"methods\":[\"HEAD\",\"GET\",\"POST\",\"PUT\",\"DELETE\",\"OPTIONS\",\"PATCH\"],\"headers\":[\"Content-Type\",\"Cookie\",\"Authorization\"],\"credentials\":true}"
51
+ }
52
+
53
+ stub_request(:get, "https://bizapi.hosted.exosite.io/api:1/solution/XYZ/").
54
+ with(:headers=>{'Authorization'=>'token TTTTTTTTTT',
55
+ 'Content-Type'=>'application/json'}).
56
+ to_return(body: body.to_json)
57
+
58
+ ret = @srv.list()
59
+ expect(ret).to eq(body)
60
+ end
61
+
62
+ it "Gets version" do
63
+ body = {:min_cli_version=>"0.10"}
64
+ stub_request(:get, "https://bizapi.hosted.exosite.io/api:1/solution/XYZ/version").
65
+ with(:headers=>{'Authorization'=>'token TTTTTTTTTT',
66
+ 'Content-Type'=>'application/json'}).
67
+ to_return(body: body.to_json)
68
+
69
+ ret = @srv.version()
70
+ expect(ret).to eq(body)
71
+ end
72
+
73
+ it "Gets logs" do
74
+ body = {:p=>[
75
+ {:type=>"error",
76
+ :timestamp=>1481746755,
77
+ :subject=>"service call failed",
78
+ :data=>{:service_alias=>"user", :function_call=>"assignUser"}
79
+ }, ],
80
+ :total=>1}
81
+ stub_request(:get, "https://bizapi.hosted.exosite.io/api:1/solution/XYZ/logs").
82
+ with(:headers=>{'Authorization'=>'token TTTTTTTTTT',
83
+ 'Content-Type'=>'application/json'}).
84
+ to_return(body: body.to_json)
85
+
86
+ ret = @srv.log()
87
+ expect(ret).to eq(body)
88
+ end
89
+
90
+ end
91
+
92
+ # vim: set ai et sw=2 ts=2 :
@@ -0,0 +1,83 @@
1
+ require 'MrMurano/version'
2
+ require 'MrMurano/Config'
3
+ require 'MrMurano/ProjectFile'
4
+ require 'MrMurano/SyncUpDown'
5
+ require '_workspace'
6
+
7
+ RSpec.describe MrMurano::SyncRoot do
8
+ include_context "WORKSPACE"
9
+
10
+ after(:example) do
11
+ MrMurano::SyncRoot.reset
12
+ end
13
+
14
+ before(:example) do
15
+ MrMurano::SyncRoot.reset # also creates @@syncset
16
+ class User
17
+ end
18
+ class Role
19
+ end
20
+ MrMurano::SyncRoot.add('user', User, 'U', "describe user", true)
21
+ MrMurano::SyncRoot.add('role', Role, 'R', "describe role")
22
+
23
+ # This must happen after all syncables have been added.
24
+ $cfg = MrMurano::Config.new
25
+ $cfg.load
26
+ $project = MrMurano::ProjectFile.new
27
+ $project.load
28
+
29
+ @options = {}
30
+ @options.define_singleton_method(:method_missing) do |mid,*args|
31
+ if mid.to_s.match(/^(.+)=$/) then
32
+ self[$1.to_sym] = args.first
33
+ else
34
+ self[mid]
35
+ end
36
+ end
37
+ end
38
+
39
+ it "has defaults" do
40
+ ret = MrMurano::SyncRoot.bydefault
41
+ expect(ret).to eq(['user'])
42
+ end
43
+
44
+ it "iterates on each" do
45
+ ret=[]
46
+ MrMurano::SyncRoot.each{|a,b,c| ret << a}
47
+ expect(ret).to eq(["user", "role"])
48
+ end
49
+
50
+ it "iterates only on selected" do
51
+ @options.role = true
52
+ ret=[]
53
+ MrMurano::SyncRoot.each_filtered(@options) {|a,b,c| ret << a}
54
+ expect(ret).to eq(["role"])
55
+ end
56
+
57
+ it "selects all" do
58
+ @options.all = true
59
+ MrMurano::SyncRoot.checkSAME(@options)
60
+ expect(@options).to eq({:all=>true, :user=>true, :role=>true})
61
+ end
62
+
63
+ it "selects defaults when none" do
64
+ MrMurano::SyncRoot.checkSAME(@options)
65
+ expect(@options).to eq({:user=>true})
66
+ end
67
+
68
+ it "selects custom defaults when none" do
69
+ $cfg['sync.bydefault'] = 'role'
70
+ MrMurano::SyncRoot.checkSAME(@options)
71
+ expect(@options).to eq({:role=>true})
72
+ end
73
+
74
+ it "builds option params" do
75
+ ret=[]
76
+ MrMurano::SyncRoot.each_option do |s,l,d|
77
+ ret << [s,l,d]
78
+ end
79
+ expect(ret).to eq([["-u", "--[no-]user", "describe user"], ["-r", "--[no-]role", "describe role"]])
80
+ end
81
+
82
+ end
83
+ # vim: set ai et sw=2 ts=2 :
@@ -0,0 +1,495 @@
1
+ require 'MrMurano/version'
2
+ require 'MrMurano/verbosing'
3
+ require 'MrMurano/Config'
4
+ require 'MrMurano/SyncUpDown'
5
+ require '_workspace'
6
+
7
+ class TSUD
8
+ include MrMurano::Verbose
9
+ include MrMurano::SyncUpDown
10
+ def initialize
11
+ @itemkey = :name
12
+ @project_section = :routes
13
+ end
14
+ def fetch(id)
15
+ end
16
+ end
17
+
18
+ RSpec::Matchers.define :pathname_globs do |glob|
19
+ match do |pthnm|
20
+ pthnm.fnmatch(glob)
21
+ end
22
+ end
23
+
24
+ RSpec.describe MrMurano::SyncUpDown do
25
+ include_context "WORKSPACE"
26
+ before(:example) do
27
+ MrMurano::SyncRoot.reset
28
+ $cfg = MrMurano::Config.new
29
+ $cfg.load
30
+ $project = MrMurano::ProjectFile.new
31
+ $project.load
32
+ $project['routes.location'] = 'tsud'
33
+ $project['routes.include'] = ['*.lua', '*/*.lua']
34
+ $cfg['net.host'] = 'bizapi.hosted.exosite.io'
35
+ $cfg['solution.id'] = 'XYZ'
36
+ end
37
+
38
+ context "status" do
39
+ it "warns with missing directory" do
40
+ t = TSUD.new
41
+ expect(t).to receive(:warning).once.with(/Skipping missing location.*/)
42
+ ret = t.status
43
+ expect(ret).to eq({:toadd=>[], :todel=>[], :tomod=>[], :unchg=>[]})
44
+ end
45
+
46
+ it "finds nothing in empty directory" do
47
+ FileUtils.mkpath(@projectDir + '/tsud')
48
+ t = TSUD.new
49
+ ret = t.status
50
+ expect(ret).to eq({:toadd=>[], :todel=>[], :tomod=>[], :unchg=>[]})
51
+ end
52
+
53
+ it "finds things there but not here" do
54
+ FileUtils.mkpath(@projectDir + '/tsud')
55
+ t = TSUD.new
56
+ expect(t).to receive(:list).once.and_return([
57
+ {:name=>1},{:name=>2},{:name=>3}
58
+ ])
59
+ ret = t.status
60
+ expect(ret).to eq({
61
+ :toadd=>[],
62
+ :todel=>[{:name=>1, :synckey=>1}, {:name=>2, :synckey=>2}, {:name=>3, :synckey=>3}],
63
+ :tomod=>[],
64
+ :unchg=>[]})
65
+ end
66
+
67
+ it "finds things there but not here; asdown" do
68
+ FileUtils.mkpath(@projectDir + '/tsud')
69
+ t = TSUD.new
70
+ expect(t).to receive(:list).once.and_return([
71
+ {:name=>1},{:name=>2},{:name=>3}
72
+ ])
73
+ ret = t.status({:asdown=>true})
74
+ expect(ret).to eq({
75
+ :todel=>[],
76
+ :toadd=>[{:name=>1, :synckey=>1}, {:name=>2, :synckey=>2}, {:name=>3, :synckey=>3}],
77
+ :tomod=>[],
78
+ :unchg=>[]})
79
+ end
80
+
81
+ it "finds things here but not there" do
82
+ FileUtils.mkpath(@projectDir + '/tsud')
83
+ FileUtils.touch(@projectDir + '/tsud/one.lua')
84
+ FileUtils.touch(@projectDir + '/tsud/two.lua')
85
+ t = TSUD.new
86
+ expect(t).to receive(:toRemoteItem).and_return(
87
+ {:name=>'one.lua'},{:name=>'two.lua'}
88
+ )
89
+ ret = t.status
90
+ expect(ret).to match({
91
+ :toadd=>[
92
+ {:name=>'one.lua', :synckey=>'one.lua',
93
+ :local_path=>an_instance_of(Pathname)},
94
+ {:name=>'two.lua', :synckey=>'two.lua',
95
+ :local_path=>an_instance_of(Pathname)},
96
+ ],
97
+ :todel=>[],
98
+ :tomod=>[],
99
+ :unchg=>[]})
100
+ end
101
+
102
+ it "finds things here and there" do
103
+ FileUtils.mkpath(@projectDir + '/tsud')
104
+ FileUtils.touch(@projectDir + '/tsud/one.lua')
105
+ FileUtils.touch(@projectDir + '/tsud/two.lua')
106
+ t = TSUD.new
107
+ expect(t).to receive(:list).once.and_return([
108
+ {:name=>'one.lua'},{:name=>'two.lua'}
109
+ ])
110
+ expect(t).to receive(:toRemoteItem).and_return(
111
+ {:name=>'one.lua'},{:name=>'two.lua'}
112
+ )
113
+ ret = t.status
114
+ expect(ret).to match({
115
+ :tomod=>[
116
+ {:name=>'one.lua', :synckey=>'one.lua',
117
+ :local_path=>an_instance_of(Pathname)},
118
+ {:name=>'two.lua', :synckey=>'two.lua',
119
+ :local_path=>an_instance_of(Pathname)},
120
+ ],
121
+ :todel=>[],
122
+ :toadd=>[],
123
+ :unchg=>[]})
124
+ end
125
+ it "finds things here and there; but they're the same" do
126
+ FileUtils.mkpath(@projectDir + '/tsud')
127
+ FileUtils.touch(@projectDir + '/tsud/one.lua')
128
+ FileUtils.touch(@projectDir + '/tsud/two.lua')
129
+ t = TSUD.new
130
+ expect(t).to receive(:list).once.and_return([
131
+ {:name=>'one.lua'},{:name=>'two.lua'}
132
+ ])
133
+ expect(t).to receive(:toRemoteItem).and_return(
134
+ {:name=>'one.lua'},{:name=>'two.lua'}
135
+ )
136
+ expect(t).to receive(:docmp).twice.and_return(false)
137
+ ret = t.status
138
+ expect(ret).to match({
139
+ :unchg=>[
140
+ {:name=>'one.lua', :synckey=>'one.lua',
141
+ :local_path=>an_instance_of(Pathname)},
142
+ {:name=>'two.lua', :synckey=>'two.lua',
143
+ :local_path=>an_instance_of(Pathname)},
144
+ ],
145
+ :todel=>[],
146
+ :toadd=>[],
147
+ :tomod=>[]})
148
+ end
149
+
150
+ it "calls diff" do
151
+ FileUtils.mkpath(@projectDir + '/tsud')
152
+ FileUtils.touch(@projectDir + '/tsud/one.lua')
153
+ t = TSUD.new
154
+ expect(t).to receive(:list).once.and_return([
155
+ {:name=>'one.lua'}
156
+ ])
157
+ expect(t).to receive(:toRemoteItem).and_return(
158
+ {:name=>'one.lua'}
159
+ )
160
+ expect(t).to receive(:dodiff).once.and_return("diffed output")
161
+ ret = t.status({:diff=>true})
162
+ expect(ret).to match({
163
+ :tomod=>[
164
+ {:name=>'one.lua', :synckey=>'one.lua',
165
+ :local_path=>an_instance_of(Pathname),
166
+ :diff=>"diffed output"},
167
+ ],
168
+ :todel=>[],
169
+ :toadd=>[],
170
+ :unchg=>[]})
171
+ end
172
+
173
+ context "Filtering" do
174
+ before(:example) do
175
+ FileUtils.mkpath(@projectDir + '/tsud/ga')
176
+ FileUtils.mkpath(@projectDir + '/tsud/gb')
177
+ FileUtils.touch(@projectDir + '/tsud/one.lua') # tomod
178
+ FileUtils.touch(@projectDir + '/tsud/ga/two.lua') # tomod
179
+ FileUtils.touch(@projectDir + '/tsud/three.lua') # unchg
180
+ FileUtils.touch(@projectDir + '/tsud/gb/four.lua') # unchg
181
+ FileUtils.touch(@projectDir + '/tsud/five.lua') # toadd
182
+ FileUtils.touch(@projectDir + '/tsud/ga/six.lua') # toadd
183
+ @t = TSUD.new
184
+ expect(@t).to receive(:list).once.and_return([
185
+ {:name=>'one.lua'},{:name=>'two.lua'}, # tomod
186
+ {:name=>'three.lua'},{:name=>'four.lua'}, # unchg
187
+ {:name=>'seven.lua'},{:name=>'eight.lua'}, # todel
188
+ ])
189
+ expect(@t).to receive(:toRemoteItem).
190
+ with(anything(), pathname_globs('**/one.lua')).
191
+ and_return({:name=>'one.lua'})
192
+ expect(@t).to receive(:toRemoteItem).
193
+ with(anything(), pathname_globs('**/two.lua')).
194
+ and_return({:name=>'two.lua'})
195
+ expect(@t).to receive(:toRemoteItem).
196
+ with(anything(), pathname_globs('**/three.lua')).
197
+ and_return({:name=>'three.lua'})
198
+ expect(@t).to receive(:toRemoteItem).
199
+ with(anything(), pathname_globs('**/four.lua')).
200
+ and_return({:name=>'four.lua'})
201
+ expect(@t).to receive(:toRemoteItem).
202
+ with(anything(), pathname_globs('**/five.lua')).
203
+ and_return({:name=>'five.lua'})
204
+ expect(@t).to receive(:toRemoteItem).
205
+ with(anything(), pathname_globs('**/six.lua')).
206
+ and_return({:name=>'six.lua'})
207
+
208
+ expect(@t).to receive(:docmp).with(include({:name=>'one.lua'}),anything()).and_return(true)
209
+ expect(@t).to receive(:docmp).with(include({:name=>'two.lua'}),anything()).and_return(true)
210
+ expect(@t).to receive(:docmp).with(include({:name=>'three.lua'}),anything()).and_return(false)
211
+ expect(@t).to receive(:docmp).with(include({:name=>'four.lua'}),anything()).and_return(false)
212
+ end
213
+
214
+ it "Returns all with no filter" do
215
+ ret = @t.status
216
+ expect(ret).to match({
217
+ :unchg=>[
218
+ {:name=>'three.lua', :synckey=>'three.lua',
219
+ :local_path=> pathname_globs('**/three.lua')},
220
+ {:name=>'four.lua', :synckey=>'four.lua',
221
+ :local_path=>pathname_globs('**/four.lua')},
222
+ ],
223
+ :toadd=>[
224
+ {:name=>'five.lua', :synckey=>'five.lua',
225
+ :local_path=>pathname_globs('**/five.lua')},
226
+ {:name=>'six.lua', :synckey=>'six.lua',
227
+ :local_path=>pathname_globs('**/six.lua')},
228
+ ],
229
+ :todel=>[
230
+ {:name=>'seven.lua', :synckey=>'seven.lua'},
231
+ {:name=>'eight.lua', :synckey=>'eight.lua'},
232
+ ],
233
+ :tomod=>[
234
+ {:name=>'one.lua', :synckey=>'one.lua',
235
+ :local_path=>pathname_globs('**/one.lua')},
236
+ {:name=>'two.lua', :synckey=>'two.lua',
237
+ :local_path=>pathname_globs('**/two.lua')},
238
+ ]})
239
+ end
240
+
241
+ it "Finds local path globs" do
242
+ ret = @t.status({}, ['**/ga/*.lua'])
243
+ expect(ret).to match({
244
+ :unchg=>[ ],
245
+ :toadd=>[
246
+ {:name=>'six.lua', :synckey=>'six.lua',
247
+ :local_path=>an_instance_of(Pathname)},
248
+ ],
249
+ :todel=>[ ],
250
+ :tomod=>[
251
+ {:name=>'two.lua', :synckey=>'two.lua',
252
+ :local_path=>an_instance_of(Pathname)},
253
+ ]})
254
+ end
255
+
256
+ it "Finds nothing with specific matcher" do
257
+ ret = @t.status({}, ['#foo'])
258
+ expect(ret).to match({
259
+ :unchg=>[ ],
260
+ :toadd=>[ ],
261
+ :todel=>[ ],
262
+ :tomod=>[ ]})
263
+ end
264
+
265
+ it "gets all the details" do
266
+ ret = @t.status({:unselected=>true})
267
+ expect(ret).to match({
268
+ :unchg=>[
269
+ {:name=>'three.lua', :synckey=>'three.lua', :selected=>true,
270
+ :local_path=> pathname_globs('**/three.lua')},
271
+ {:name=>'four.lua', :synckey=>'four.lua', :selected=>true,
272
+ :local_path=>pathname_globs('**/four.lua')},
273
+ ],
274
+ :toadd=>[
275
+ {:name=>'five.lua', :synckey=>'five.lua', :selected=>true,
276
+ :local_path=>pathname_globs('**/five.lua')},
277
+ {:name=>'six.lua', :synckey=>'six.lua', :selected=>true,
278
+ :local_path=>pathname_globs('**/six.lua')},
279
+ ],
280
+ :todel=>[
281
+ {:name=>'seven.lua', :selected=>true, :synckey=>'seven.lua'},
282
+ {:name=>'eight.lua', :selected=>true, :synckey=>'eight.lua'},
283
+ ],
284
+ :tomod=>[
285
+ {:name=>'one.lua', :synckey=>'one.lua', :selected=>true,
286
+ :local_path=>pathname_globs('**/one.lua')},
287
+ {:name=>'two.lua', :synckey=>'two.lua', :selected=>true,
288
+ :local_path=>pathname_globs('**/two.lua')},
289
+ ]})
290
+ end
291
+ end
292
+ end
293
+
294
+ context "localitems" do
295
+ before(:example) do
296
+ FileUtils.mkpath('tsud')
297
+ FileUtils.touch('tsud/one.lua')
298
+ FileUtils.touch('tsud/two.lua')
299
+ @t = TSUD.new
300
+ end
301
+ it "finds local items" do
302
+ expect(@t).to receive(:toRemoteItem).and_return(
303
+ {:name=>'one.lua'},{:name=>'two.lua'}
304
+ )
305
+ ret = @t.localitems(Pathname.new(@projectDir + '/tsud').realpath)
306
+ expect(ret).to match([
307
+ {:name=>'one.lua',
308
+ :local_path=>an_instance_of(Pathname)},
309
+ {:name=>'two.lua',
310
+ :local_path=>an_instance_of(Pathname)},
311
+ ])
312
+ end
313
+
314
+ it "takes an array from toRemoteItem" do
315
+ expect(@t).to receive(:toRemoteItem).and_return(
316
+ [{:name=>'one:1'},{:name=>'one:2'}],
317
+ [{:name=>'two:1'},{:name=>'two:2'}]
318
+ )
319
+ ret = @t.localitems(Pathname.new(@projectDir + '/tsud').realpath)
320
+ expect(ret).to match([
321
+ {:name=>'one:1',
322
+ :local_path=>an_instance_of(Pathname)},
323
+ {:name=>'one:2',
324
+ :local_path=>an_instance_of(Pathname)},
325
+ {:name=>'two:1',
326
+ :local_path=>an_instance_of(Pathname)},
327
+ {:name=>'two:2',
328
+ :local_path=>an_instance_of(Pathname)},
329
+ ])
330
+ end
331
+ end
332
+
333
+ context "download" do
334
+ it "defaults to :id if @itemkey missing" do
335
+ FileUtils.mkpath(@projectDir + '/tsud')
336
+ FileUtils.touch(@projectDir + '/tsud/one.lua')
337
+ lp = Pathname.new(@projectDir + '/tsud/one.lua').realpath
338
+ t = TSUD.new
339
+ expect(t).to receive(:fetch).once.with(1).and_yield("foo")
340
+ t.download(lp, {:id=>1})
341
+ end
342
+ end
343
+
344
+ context "doing diffs" do
345
+ before(:example) do
346
+ FileUtils.mkpath(@projectDir + '/tsud')
347
+ @t = TSUD.new
348
+ @scpt = Pathname.new(@projectDir + '/tsud/one.lua')
349
+ @scpt.open('w'){|io| io << %{-- fake lua\nreturn 0\n}}
350
+ @scpt = @scpt.realpath
351
+ end
352
+
353
+ it "nothing when same." do
354
+ expect(@t).to receive(:fetch).and_yield(%{-- fake lua\nreturn 0\n})
355
+ ret = @t.dodiff({:name=>'one.lua', :local_path=>@scpt})
356
+ if Gem.win_platform? then
357
+ expect(ret).to match(/FC: no differences encountered/)
358
+ else
359
+ expect(ret).to eq('')
360
+ end
361
+ end
362
+
363
+ it "something when different." do
364
+ expect(@t).to receive(:fetch).and_yield(%{-- fake lua\nreturn 2\n})
365
+ ret = @t.dodiff({:name=>'one.lua', :local_path=>@scpt})
366
+ expect(ret).not_to eq('')
367
+ end
368
+
369
+ it "uses script in item" do
370
+ script = %{-- fake lua\nreturn 2\n}
371
+ expect(@t).to receive(:fetch).and_yield(script)
372
+ ret = @t.dodiff({:name=>'one.lua', :local_path=>@scpt, :script=>script})
373
+ if Gem.win_platform? then
374
+ expect(ret).to match(/FC: no differences encountered/)
375
+ else
376
+ expect(ret).to eq('')
377
+ end
378
+ end
379
+ end
380
+
381
+ context "syncup" do
382
+ before(:example) do
383
+ FileUtils.mkpath(@projectDir + '/tsud')
384
+ @t = TSUD.new
385
+ end
386
+
387
+ it "removes" do
388
+ expect(@t).to receive(:list).once.and_return([
389
+ {:name=>1},{:name=>2},{:name=>3}
390
+ ])
391
+ expect(@t).to receive(:remove).exactly(3).times
392
+ @t.syncup({:delete=>true})
393
+ end
394
+
395
+ it "creates" do
396
+ FileUtils.touch(@projectDir + '/tsud/one.lua')
397
+ FileUtils.touch(@projectDir + '/tsud/two.lua')
398
+
399
+ expect(@t).to receive(:upload).twice.with(kind_of(Pathname), kind_of(Hash), false)
400
+ @t.syncup({:create=>true})
401
+ end
402
+
403
+ it "updates" do
404
+ FileUtils.touch(@projectDir + '/tsud/one.lua')
405
+ FileUtils.touch(@projectDir + '/tsud/two.lua')
406
+ expect(@t).to receive(:list).once.and_return([
407
+ {:name=>'one.lua'},{:name=>'two.lua'}
408
+ ])
409
+
410
+ expect(@t).to receive(:upload).twice.with(kind_of(Pathname), kind_of(Hash), true)
411
+ expect(@t).to receive(:toRemoteItem).and_return(
412
+ {:name=>'one.lua'},{:name=>'two.lua'}
413
+ )
414
+ @t.syncup({:update=>true})
415
+ end
416
+ end
417
+
418
+ context "syncdown" do
419
+ before(:example) do
420
+ FileUtils.mkpath(@projectDir + '/tsud')
421
+ @t = TSUD.new
422
+ end
423
+
424
+ it "removes" do
425
+ FileUtils.touch(@projectDir + '/tsud/one.lua')
426
+ FileUtils.touch(@projectDir + '/tsud/two.lua')
427
+
428
+ @t.syncdown({:delete=>true})
429
+ expect(FileTest.exist?(@projectDir + '/tsud/one.lua')).to be false
430
+ expect(FileTest.exist?(@projectDir + '/tsud/two.lua')).to be false
431
+ end
432
+
433
+ it "creates" do
434
+ expect(@t).to receive(:list).once.and_return([
435
+ {:name=>'one.lua'},{:name=>'two.lua'}
436
+ ])
437
+
438
+ expect(@t).to receive(:fetch).twice.and_yield("--foo\n")
439
+ @t.syncdown({:create=>true})
440
+ expect(FileTest.exist?(@projectDir + '/tsud/one.lua')).to be true
441
+ expect(FileTest.exist?(@projectDir + '/tsud/two.lua')).to be true
442
+ end
443
+
444
+ it "updates" do
445
+ FileUtils.touch(@projectDir + '/tsud/one.lua')
446
+ FileUtils.touch(@projectDir + '/tsud/two.lua')
447
+ expect(@t).to receive(:list).once.and_return([
448
+ {:name=>'one.lua'},{:name=>'two.lua'}
449
+ ])
450
+
451
+ expect(@t).to receive(:fetch).twice.and_yield("--foo\n")
452
+ expect(@t).to receive(:toRemoteItem).and_return(
453
+ {:name=>'one.lua'},{:name=>'two.lua'}
454
+ )
455
+ @t.syncdown({:update=>true})
456
+ expect(FileTest.exist?(@projectDir + '/tsud/one.lua')).to be true
457
+ expect(FileTest.exist?(@projectDir + '/tsud/two.lua')).to be true
458
+ end
459
+ end
460
+
461
+ context "bundles" do
462
+ before(:example) do
463
+ FileUtils.mkpath(@projectDir + '/tsud')
464
+ FileUtils.mkpath(@projectDir + '/bundles/mybun/tsud')
465
+ @t = TSUD.new
466
+ end
467
+
468
+ # it "finds items in bundles." do
469
+ # FileUtils.touch(@projectDir + '/tsud/one.lua')
470
+ # FileUtils.touch(@projectDir + '/bundles/mybun/tsud/two.lua')
471
+ #
472
+ # expect(@t).to receive(:toRemoteItem).and_return(
473
+ # {:name=>'two.lua'},{:name=>'one.lua'}
474
+ # )
475
+ # ret = @t.locallist
476
+ # expect(ret).to match([
477
+ # {:name=>'two.lua',
478
+ # :bundled=>true,
479
+ # :local_path=>an_instance_of(Pathname)},
480
+ # {:name=>'one.lua',
481
+ # :local_path=>an_instance_of(Pathname)},
482
+ # ])
483
+ # end
484
+
485
+ it "Doesn't download a bundled item" do
486
+ FileUtils.touch(@projectDir + '/tsud/one.lua')
487
+ lp = Pathname.new(@projectDir + '/tsud/one.lua').realpath
488
+
489
+ expect(@t).to receive(:warning).once.with(/Not downloading into bundled item.*/)
490
+
491
+ @t.download(lp, {:bundled=>true, :name=>'one.lua'})
492
+ end
493
+ end
494
+ end
495
+ # vim: set ai et sw=2 ts=2 :