rhosync 2.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (200) hide show
  1. data/CHANGELOG +5 -0
  2. data/LICENSE +674 -0
  3. data/README.md +26 -0
  4. data/Rakefile +109 -0
  5. data/bench/bench +6 -0
  6. data/bench/benchapp/Rakefile +14 -0
  7. data/bench/benchapp/application.rb +13 -0
  8. data/bench/benchapp/config.ru +32 -0
  9. data/bench/benchapp/settings/license.key +1 -0
  10. data/bench/benchapp/settings/settings.yml +18 -0
  11. data/bench/benchapp/sources/mock_adapter.rb +55 -0
  12. data/bench/benchapp/sources/queue_mock_adapter.rb +2 -0
  13. data/bench/benchapp/vendor/rhosync/lib/rhosync.rb +7 -0
  14. data/bench/lib/bench/cli.rb +16 -0
  15. data/bench/lib/bench/logging.rb +18 -0
  16. data/bench/lib/bench/mock_client.rb +41 -0
  17. data/bench/lib/bench/result.rb +50 -0
  18. data/bench/lib/bench/runner.rb +44 -0
  19. data/bench/lib/bench/session.rb +65 -0
  20. data/bench/lib/bench/statistics.rb +56 -0
  21. data/bench/lib/bench/test_data.rb +55 -0
  22. data/bench/lib/bench/timer.rb +10 -0
  23. data/bench/lib/bench/utils.rb +49 -0
  24. data/bench/lib/bench.rb +128 -0
  25. data/bench/lib/testdata/100-data.txt +148 -0
  26. data/bench/lib/testdata/5-data.txt +11 -0
  27. data/bench/scripts/cud_script.rb +77 -0
  28. data/bench/scripts/helpers.rb +101 -0
  29. data/bench/scripts/query_md_script.rb +46 -0
  30. data/bench/scripts/query_script.rb +46 -0
  31. data/bench/spec/bench_spec_helper.rb +65 -0
  32. data/bench/spec/logging_spec.rb +19 -0
  33. data/bench/spec/mock_adapter_spec.rb +61 -0
  34. data/bench/spec/mock_client_spec.rb +64 -0
  35. data/bench/spec/result_spec.rb +59 -0
  36. data/bench/spec/utils_spec.rb +35 -0
  37. data/bin/rhosync +34 -0
  38. data/doc/protocol.html +1901 -0
  39. data/doc/public/css/print.css +29 -0
  40. data/doc/public/css/screen.css +257 -0
  41. data/doc/public/css/style.css +20 -0
  42. data/examples/simple/application.rb +13 -0
  43. data/examples/simple/sources/sample_adapter.rb +5 -0
  44. data/examples/simple/sources/simple_adapter.rb +5 -0
  45. data/examples/simple/vendor/rhosync/lib/rhosync.rb +7 -0
  46. data/generators/rhosync.rb +98 -0
  47. data/generators/templates/application/Rakefile +19 -0
  48. data/generators/templates/application/application.rb +27 -0
  49. data/generators/templates/application/config.ru +33 -0
  50. data/generators/templates/application/settings/license.key +1 -0
  51. data/generators/templates/application/settings/settings.yml +14 -0
  52. data/generators/templates/source/source_adapter.rb +49 -0
  53. data/lib/rhosync/api/create_client.rb +3 -0
  54. data/lib/rhosync/api/create_user.rb +7 -0
  55. data/lib/rhosync/api/delete_client.rb +5 -0
  56. data/lib/rhosync/api/delete_user.rb +5 -0
  57. data/lib/rhosync/api/get_api_token.rb +7 -0
  58. data/lib/rhosync/api/get_client_params.rb +3 -0
  59. data/lib/rhosync/api/get_db_doc.rb +7 -0
  60. data/lib/rhosync/api/get_license_info.rb +7 -0
  61. data/lib/rhosync/api/get_source_params.rb +3 -0
  62. data/lib/rhosync/api/list_client_docs.rb +12 -0
  63. data/lib/rhosync/api/list_clients.rb +3 -0
  64. data/lib/rhosync/api/list_source_docs.rb +10 -0
  65. data/lib/rhosync/api/list_sources.rb +15 -0
  66. data/lib/rhosync/api/list_users.rb +3 -0
  67. data/lib/rhosync/api/ping.rb +7 -0
  68. data/lib/rhosync/api/push_deletes.rb +6 -0
  69. data/lib/rhosync/api/push_objects.rb +6 -0
  70. data/lib/rhosync/api/reset.rb +10 -0
  71. data/lib/rhosync/api/set_db_doc.rb +8 -0
  72. data/lib/rhosync/api/set_refresh_time.rb +8 -0
  73. data/lib/rhosync/api/update_user.rb +4 -0
  74. data/lib/rhosync/api/upload_file.rb +4 -0
  75. data/lib/rhosync/api_token.rb +19 -0
  76. data/lib/rhosync/app.rb +69 -0
  77. data/lib/rhosync/bulk_data/bulk_data.rb +75 -0
  78. data/lib/rhosync/bulk_data/syncdb.index.schema +3 -0
  79. data/lib/rhosync/bulk_data/syncdb.schema +37 -0
  80. data/lib/rhosync/bulk_data.rb +2 -0
  81. data/lib/rhosync/client.rb +74 -0
  82. data/lib/rhosync/client_sync.rb +296 -0
  83. data/lib/rhosync/console/app/helpers/auth_helper.rb +18 -0
  84. data/lib/rhosync/console/app/helpers/extensions.rb +19 -0
  85. data/lib/rhosync/console/app/helpers/helpers.rb +52 -0
  86. data/lib/rhosync/console/app/public/main.css +7 -0
  87. data/lib/rhosync/console/app/public/text.txt +0 -0
  88. data/lib/rhosync/console/app/routes/auth.rb +29 -0
  89. data/lib/rhosync/console/app/routes/client.rb +32 -0
  90. data/lib/rhosync/console/app/routes/docs.rb +84 -0
  91. data/lib/rhosync/console/app/routes/home.rb +22 -0
  92. data/lib/rhosync/console/app/routes/user.rb +63 -0
  93. data/lib/rhosync/console/app/views/client.erb +30 -0
  94. data/lib/rhosync/console/app/views/doc.erb +56 -0
  95. data/lib/rhosync/console/app/views/docs.erb +29 -0
  96. data/lib/rhosync/console/app/views/index.erb +50 -0
  97. data/lib/rhosync/console/app/views/layout.erb +12 -0
  98. data/lib/rhosync/console/app/views/newuser.erb +17 -0
  99. data/lib/rhosync/console/app/views/ping.erb +28 -0
  100. data/lib/rhosync/console/app/views/result.erb +11 -0
  101. data/lib/rhosync/console/app/views/user.erb +32 -0
  102. data/lib/rhosync/console/app/views/users.erb +14 -0
  103. data/lib/rhosync/console/rhosync_api.rb +102 -0
  104. data/lib/rhosync/console/server.rb +27 -0
  105. data/lib/rhosync/credential.rb +9 -0
  106. data/lib/rhosync/document.rb +43 -0
  107. data/lib/rhosync/indifferent_access.rb +132 -0
  108. data/lib/rhosync/jobs/bulk_data_job.rb +104 -0
  109. data/lib/rhosync/jobs/ping_job.rb +19 -0
  110. data/lib/rhosync/jobs/source_job.rb +16 -0
  111. data/lib/rhosync/license.rb +79 -0
  112. data/lib/rhosync/lock_ops.rb +11 -0
  113. data/lib/rhosync/model.rb +410 -0
  114. data/lib/rhosync/ping/blackberry.rb +55 -0
  115. data/lib/rhosync/ping/iphone.rb +44 -0
  116. data/lib/rhosync/ping.rb +2 -0
  117. data/lib/rhosync/read_state.rb +27 -0
  118. data/lib/rhosync/server/views/index.erb +12 -0
  119. data/lib/rhosync/server.rb +242 -0
  120. data/lib/rhosync/source.rb +112 -0
  121. data/lib/rhosync/source_adapter.rb +95 -0
  122. data/lib/rhosync/source_sync.rb +245 -0
  123. data/lib/rhosync/store.rb +199 -0
  124. data/lib/rhosync/tasks.rb +151 -0
  125. data/lib/rhosync/user.rb +83 -0
  126. data/lib/rhosync/version.rb +3 -0
  127. data/lib/rhosync.rb +251 -0
  128. data/spec/api/api_helper.rb +44 -0
  129. data/spec/api/create_client_spec.rb +13 -0
  130. data/spec/api/create_user_spec.rb +16 -0
  131. data/spec/api/delete_client_spec.rb +13 -0
  132. data/spec/api/delete_user_spec.rb +18 -0
  133. data/spec/api/get_api_token_spec.rb +25 -0
  134. data/spec/api/get_client_params_spec.rb +18 -0
  135. data/spec/api/get_db_doc_spec.rb +21 -0
  136. data/spec/api/get_license_info_spec.rb +16 -0
  137. data/spec/api/get_source_params_spec.rb +26 -0
  138. data/spec/api/list_client_docs_spec.rb +33 -0
  139. data/spec/api/list_clients_spec.rb +23 -0
  140. data/spec/api/list_source_docs_spec.rb +26 -0
  141. data/spec/api/list_sources_spec.rb +27 -0
  142. data/spec/api/list_users_spec.rb +21 -0
  143. data/spec/api/ping_spec.rb +24 -0
  144. data/spec/api/push_deletes_spec.rb +16 -0
  145. data/spec/api/push_objects_spec.rb +27 -0
  146. data/spec/api/reset_spec.rb +22 -0
  147. data/spec/api/set_db_doc_spec.rb +20 -0
  148. data/spec/api/set_refresh_time_spec.rb +43 -0
  149. data/spec/api/update_user_spec.rb +31 -0
  150. data/spec/api/upload_file_spec.rb +26 -0
  151. data/spec/api_token_spec.rb +13 -0
  152. data/spec/app_spec.rb +20 -0
  153. data/spec/apps/rhotestapp/Rakefile +1 -0
  154. data/spec/apps/rhotestapp/application.rb +16 -0
  155. data/spec/apps/rhotestapp/config.ru +1 -0
  156. data/spec/apps/rhotestapp/settings/apple_fake_cert.pem +1 -0
  157. data/spec/apps/rhotestapp/settings/license.key +1 -0
  158. data/spec/apps/rhotestapp/settings/settings.yml +23 -0
  159. data/spec/apps/rhotestapp/sources/base_adapter.rb +9 -0
  160. data/spec/apps/rhotestapp/sources/sample_adapter.rb +66 -0
  161. data/spec/apps/rhotestapp/sources/simple_adapter.rb +39 -0
  162. data/spec/apps/rhotestapp/sources/sub_adapter.rb +7 -0
  163. data/spec/apps/rhotestapp/vendor/mygem-0.1.0/lib/mygem/mygem.rb +8 -0
  164. data/spec/apps/rhotestapp/vendor/mygem-0.1.0/lib/mygem.rb +1 -0
  165. data/spec/bulk_data/bulk_data_spec.rb +79 -0
  166. data/spec/client_spec.rb +58 -0
  167. data/spec/client_sync_spec.rb +377 -0
  168. data/spec/doc/base.html +72 -0
  169. data/spec/doc/doc_spec.rb +303 -0
  170. data/spec/doc/footer.html +4 -0
  171. data/spec/doc/header.html +30 -0
  172. data/spec/document_spec.rb +27 -0
  173. data/spec/generator/generator_spec.rb +53 -0
  174. data/spec/generator/generator_spec_helper.rb +8 -0
  175. data/spec/jobs/bulk_data_job_spec.rb +76 -0
  176. data/spec/jobs/ping_job_spec.rb +26 -0
  177. data/spec/jobs/source_job_spec.rb +25 -0
  178. data/spec/license_spec.rb +48 -0
  179. data/spec/model_spec.rb +269 -0
  180. data/spec/perf/bulk_data_perf_spec.rb +33 -0
  181. data/spec/perf/perf_spec_helper.rb +51 -0
  182. data/spec/perf/store_perf_spec.rb +28 -0
  183. data/spec/ping/blackberry_spec.rb +62 -0
  184. data/spec/ping/iphone_spec.rb +50 -0
  185. data/spec/read_state_spec.rb +25 -0
  186. data/spec/rhosync_spec.rb +43 -0
  187. data/spec/server/server_spec.rb +341 -0
  188. data/spec/source_adapter_spec.rb +114 -0
  189. data/spec/source_spec.rb +77 -0
  190. data/spec/source_sync_spec.rb +248 -0
  191. data/spec/spec_helper.rb +240 -0
  192. data/spec/store_spec.rb +149 -0
  193. data/spec/sync_states_spec.rb +101 -0
  194. data/spec/testdata/1000-data.txt +1414 -0
  195. data/spec/testdata/compressed/compress-data.txt +1 -0
  196. data/spec/testdata/upload1.txt +1 -0
  197. data/spec/testdata/upload2.txt +1 -0
  198. data/spec/user_spec.rb +79 -0
  199. data/tasks/redis.rake +134 -0
  200. metadata +545 -0
@@ -0,0 +1,341 @@
1
+ require File.join(File.dirname(__FILE__),'..','spec_helper')
2
+ require 'rack/test'
3
+ require 'spec'
4
+ require 'spec/autorun'
5
+ require 'spec/interop/test'
6
+
7
+ require File.join(File.dirname(__FILE__),'..','..','lib','rhosync','server.rb')
8
+
9
+ describe "Server" do
10
+ it_should_behave_like "RhosyncDataHelper"
11
+ it_should_behave_like "TestappHelper"
12
+
13
+ include Rack::Test::Methods
14
+ include Rhosync
15
+
16
+ before(:each) do
17
+ require File.join(get_testapp_path,@test_app_name)
18
+ Rhosync.bootstrap(get_testapp_path) do |rhosync|
19
+ rhosync.vendor_directory = File.join(rhosync.base_directory,'..','..','..','vendor')
20
+ end
21
+ Server.set(
22
+ :environment => :test,
23
+ :run => false,
24
+ :secret => "secure!"
25
+ )
26
+ Server.use Rack::Static, :urls => ["/data"],
27
+ :root => File.join(File.dirname(__FILE__),'..','apps','rhotestapp')
28
+ end
29
+
30
+ def app
31
+ @app ||= Server.new
32
+ end
33
+
34
+ it_should_behave_like "DBObjectsHelper"
35
+
36
+ it "should show status page" do
37
+ get '/'
38
+ last_response.body.match(Rhosync::VERSION)[0].should == Rhosync::VERSION
39
+ end
40
+
41
+ it "should login without app_name" do
42
+ post "/login", "login" => @u_fields[:login], "password" => 'testpass'
43
+ last_response.should be_ok
44
+ end
45
+
46
+ it "should respond with 401 to /:app_name" do
47
+ get "/application"
48
+ last_response.status.should == 401
49
+ end
50
+
51
+ it "should have default session secret" do
52
+ Server.secret.should == "secure!"
53
+ end
54
+
55
+ it "should update session secret to default" do
56
+ Server.set :secret, "<changeme>"
57
+ Server.secret.should == "<changeme>"
58
+ Server.should_receive(:log).any_number_of_times.with(any_args())
59
+ check_default_secret!("<changeme>")
60
+ Server.set :secret, "secure!"
61
+ end
62
+
63
+ it "should complain about hsqldata.jar missing" do
64
+ Rhosync.vendor_directory = 'missing'
65
+ Server.should_receive(:log).any_number_of_times.with(any_args())
66
+ check_hsql_lib!
67
+ end
68
+
69
+ describe "helpers" do
70
+ before(:each) do
71
+ do_post "/application/clientlogin", "login" => @u.login, "password" => 'testpass'
72
+ end
73
+
74
+ it "should return nil if params[:source_name] is missing" do
75
+ get "/application"
76
+ last_response.status.should == 500
77
+ end
78
+ end
79
+
80
+ describe "auth routes" do
81
+ it "should login user with correct username,password" do
82
+ do_post "/application/clientlogin", "login" => @u.login, "password" => 'testpass'
83
+ last_response.should be_ok
84
+ end
85
+
86
+ it "should respond 401 for incorrect username or password" do
87
+ do_post "/application/clientlogin", "login" => @u.login, "password" => 'wrongpass'
88
+ last_response.status.should == 401
89
+ end
90
+
91
+ it "should create unknown user through delegated authentication" do
92
+ do_post "/application/clientlogin", "login" => 'newuser', "password" => 'testpass'
93
+ User.is_exist?('newuser').should == true
94
+ @a.users.members.sort.should == ['newuser','testuser']
95
+ end
96
+ end
97
+
98
+ describe "client management routes" do
99
+ before(:each) do
100
+ do_post "/application/clientlogin", "login" => @u.login, "password" => 'testpass'
101
+ @source_config = {"sources"=>{"SampleAdapter"=>{"poll_interval"=>300},
102
+ "SimpleAdapter"=>{"partition_type"=>"app","poll_interval"=>600}}}
103
+ end
104
+
105
+ it "should respond to clientcreate" do
106
+ get "/application/clientcreate?device_type=blackberry"
107
+ last_response.should be_ok
108
+ last_response.content_type.should == 'application/json'
109
+ id = JSON.parse(last_response.body)['client']['client_id']
110
+ id.length.should == 32
111
+ JSON.parse(last_response.body).should ==
112
+ {"client"=>{"client_id"=>id}}.merge!(@source_config)
113
+ c = Client.load(id,{:source_name => '*'})
114
+ c.user_id.should == 'testuser'
115
+ c.device_type.should == 'blackberry'
116
+ end
117
+
118
+ it "should respond to clientregister" do
119
+ do_post "/application/clientregister",
120
+ "device_type" => "iPhone", "device_pin" => 'abcd', "client_id" => @c.id
121
+ last_response.should be_ok
122
+ JSON.parse(last_response.body).should == @source_config
123
+ @c.device_type.should == 'iPhone'
124
+ @c.device_pin.should == 'abcd'
125
+ @c.id.length.should == 32
126
+ end
127
+
128
+ it "should respond to clientreset" do
129
+ set_state(@c.docname(:cd) => @data)
130
+ get "/application/clientreset", :client_id => @c.id,:version => ClientSync::VERSION
131
+ JSON.parse(last_response.body).should == @source_config
132
+ verify_result(@c.docname(:cd) => {})
133
+ end
134
+ end
135
+
136
+ describe "source routes" do
137
+ before(:each) do
138
+ do_post "/application/clientlogin", "login" => @u.login, "password" => 'testpass'
139
+ end
140
+
141
+ it "should return 404 message with version < 3" do
142
+ get "/application",:source_name => @s.name,:version => 2
143
+ last_response.status.should == 404
144
+ last_response.body.should == "Server supports version 3 or higher of the protocol."
145
+ end
146
+
147
+ it "should post records for create" do
148
+ @product1['_id'] = '1'
149
+ params = {'create'=>{'1'=>@product1},:client_id => @c.id,:source_name => @s.name,
150
+ :version => ClientSync::VERSION}
151
+ do_post "/application", params
152
+ last_response.should be_ok
153
+ last_response.body.should == ''
154
+ verify_result("test_create_storage" => {'1'=>@product1})
155
+ end
156
+
157
+ it "should post records for update" do
158
+ params = {'update'=>{'1'=>@product1},:client_id => @c.id,:source_name => @s.name,
159
+ :version => ClientSync::VERSION}
160
+ do_post "/application", params
161
+ last_response.should be_ok
162
+ last_response.body.should == ''
163
+ verify_result("test_update_storage" => {'1'=>@product1})
164
+ end
165
+
166
+ it "should post records for delete" do
167
+ params = {'delete'=>{'1'=>@product1},:client_id => @c.id,:source_name => @s.name,
168
+ :version => ClientSync::VERSION}
169
+ do_post "/application", params
170
+ last_response.should be_ok
171
+ last_response.body.should == ''
172
+ verify_result("test_delete_storage" => {'1'=>@product1})
173
+ end
174
+
175
+ it "should get inserts json" do
176
+ cs = ClientSync.new(@s,@c,1)
177
+ data = {'1'=>@product1,'2'=>@product2}
178
+ set_test_data('test_db_storage',data)
179
+ get "/application",:client_id => @c.id,:source_name => @s.name,:version => ClientSync::VERSION
180
+ last_response.should be_ok
181
+ last_response.content_type.should == 'application/json'
182
+ token = @c.get_value(:page_token)
183
+ JSON.parse(last_response.body).should == [{"version"=>ClientSync::VERSION},{"token"=>token},
184
+ {"count"=>2}, {"progress_count"=>0},{"total_count"=>2},{'insert'=>data}]
185
+ end
186
+
187
+ it "should get inserts json and confirm token" do
188
+ cs = ClientSync.new(@s,@c,1)
189
+ data = {'1'=>@product1,'2'=>@product2}
190
+ set_test_data('test_db_storage',data)
191
+ get "/application",:client_id => @c.id,:source_name => @s.name,:version => ClientSync::VERSION
192
+ last_response.should be_ok
193
+ token = @c.get_value(:page_token)
194
+ JSON.parse(last_response.body).should == [{"version"=>ClientSync::VERSION},{"token"=>token},
195
+ {"count"=>2}, {"progress_count"=>0}, {"total_count"=>2},{'insert'=>data}]
196
+ get "/application",:client_id => @c.id,:source_name => @s.name,:token => token,
197
+ :version => ClientSync::VERSION
198
+ last_response.should be_ok
199
+ JSON.parse(last_response.body).should == [{"version"=>ClientSync::VERSION},{"token"=>''},
200
+ {"count"=>0}, {"progress_count"=>2}, {"total_count"=>2},{}]
201
+ end
202
+
203
+ it "should get deletes json" do
204
+ cs = ClientSync.new(@s,@c,1)
205
+ data = {'1'=>@product1,'2'=>@product2}
206
+ set_test_data('test_db_storage',data)
207
+
208
+ get "/application",:client_id => @c.id,:source_name => @s.name,:version => ClientSync::VERSION
209
+ last_response.should be_ok
210
+ token = @c.get_value(:page_token)
211
+ JSON.parse(last_response.body).should == [{"version"=>ClientSync::VERSION},{"token"=>token},
212
+ {"count"=>2}, {"progress_count"=>0}, {"total_count"=>2},{'insert'=>data}]
213
+
214
+ Store.flash_data('test_db_storage')
215
+ @s.read_state.refresh_time = Time.now.to_i
216
+
217
+ get "/application",:client_id => @c.id,:source_name => @s.name,:token => token,
218
+ :version => ClientSync::VERSION
219
+ last_response.should be_ok
220
+ token = @c.get_value(:page_token)
221
+ JSON.parse(last_response.body).should == [{"version"=>ClientSync::VERSION},{"token"=>token},
222
+ {"count"=>2}, {"progress_count"=>0}, {"total_count"=>0},{'delete'=>data}]
223
+ end
224
+
225
+ it "should get search results" do
226
+ sources = ['SampleAdapter']
227
+ cs = ClientSync.new(@s,@c,1)
228
+ Store.put_data('test_db_storage',@data)
229
+ params = {:client_id => @c.id,:sources => sources,:search => {'name' => 'iPhone'},
230
+ :version => ClientSync::VERSION}
231
+ get "/application/search",params
232
+ last_response.content_type.should == 'application/json'
233
+ token = @c.get_value(:search_token)
234
+ JSON.parse(last_response.body).should == [[{'version'=>ClientSync::VERSION},{'search_token'=>token},
235
+ {'source'=>sources[0]},{'count'=>1},{'insert'=>{'1'=>@product1}}]]
236
+ end
237
+
238
+ it "should get search results with error" do
239
+ sources = ['SampleAdapter']
240
+ msg = "Error during search"
241
+ error = set_test_data('test_db_storage',@data,msg,'search error')
242
+ params = {:client_id => @c.id,:sources => sources,:search => {'name' => 'iPhone'},
243
+ :version => ClientSync::VERSION}
244
+ get "/application/search",params
245
+ JSON.parse(last_response.body).should == [[{'version'=>ClientSync::VERSION},
246
+ {'source'=>sources[0]},{'search-error'=>{'search-error'=>{'message'=>msg}}}]]
247
+ end
248
+
249
+ it "should get multiple source search results" do
250
+ @s_fields[:name] = 'SimpleAdapter'
251
+ @s1 = Source.load(@s_fields,@s_params)
252
+ Store.put_data('test_db_storage',@data)
253
+ sources = ['SimpleAdapter','SampleAdapter']
254
+ params = {:client_id => @c.id,:sources => sources,:search => {'search' => 'bar'},
255
+ :version => ClientSync::VERSION}
256
+ get "/application/search",params
257
+ @c.source_name = 'SimpleAdapter'
258
+ token1 = @c.get_value(:search_token)
259
+ @c.source_name = 'SampleAdapter'
260
+ token = @c.get_value(:search_token)
261
+ JSON.parse(last_response.body).should == [
262
+ [{"version"=>ClientSync::VERSION},{'search_token'=>token1},{"source"=>"SimpleAdapter"},
263
+ {"count"=>1}, {"insert"=>{'obj'=>{'foo'=>'bar'}}}],
264
+ [{"version"=>ClientSync::VERSION},{'search_token'=>token},{"source"=>"SampleAdapter"},
265
+ {"count"=>1}, {"insert"=>{'1'=>@product1}}]]
266
+ end
267
+ end
268
+
269
+ describe "bulk data routes" do
270
+ before(:each) do
271
+ do_post "/application/clientlogin", "login" => @u.login, "password" => 'testpass'
272
+ end
273
+
274
+ after(:each) do
275
+ delete_data_directory
276
+ end
277
+
278
+ it "should make initial bulk data request and receive wait" do
279
+ set_state('test_db_storage' => @data)
280
+ get "/application/bulk_data", :partition => :user, :client_id => @c.id
281
+ last_response.should be_ok
282
+ last_response.body.should == {:result => :wait}.to_json
283
+ end
284
+
285
+ it "should receive url when bulk data is available" do
286
+ set_state('test_db_storage' => @data)
287
+ get "/application/bulk_data", :partition => :user, :client_id => @c.id
288
+ BulkDataJob.perform("data_name" => bulk_data_docname(@a.id,@u.id))
289
+ get "/application/bulk_data", :partition => :user, :client_id => @c.id
290
+ last_response.should be_ok
291
+ last_response.body.should == {:result => :url,
292
+ :url => BulkData.load(bulk_data_docname(@a.id,@u.id)).dbfile}.to_json
293
+ validate_db_by_name(JSON.parse(last_response.body)["url"],@data)
294
+ end
295
+
296
+ it "should download bulk data file" do
297
+ set_state('test_db_storage' => @data)
298
+ get "/application/bulk_data", :partition => :user, :client_id => @c.id
299
+ BulkDataJob.perform("data_name" => bulk_data_docname(@a.id,@u.id))
300
+ get "/application/bulk_data", :partition => :user, :client_id => @c.id
301
+ get "/data/application/#{@u.id}/#{JSON.parse(last_response.body)["url"].split('/').last}"
302
+ last_response.should be_ok
303
+ File.open('test.data','wb') {|f| f.puts last_response.body}
304
+ validate_db_by_name('test.data',@data)
305
+ File.delete('test.data')
306
+ end
307
+
308
+ it "should receive nop when no sources are available for partition" do
309
+ set_state('test_db_storage' => @data)
310
+ Source.load('SimpleAdapter',@s_params).partition = :user
311
+ get "/application/bulk_data", :partition => :app, :client_id => @c.id
312
+ last_response.should be_ok
313
+ last_response.body.should == {:result => :nop}.to_json
314
+ end
315
+ end
316
+
317
+ describe "blob sync" do
318
+ before(:each) do
319
+ do_post "/application/clientlogin", "login" => @u.login, "password" => 'testpass'
320
+ end
321
+ it "should upload blob in multipart post" do
322
+ file1,file2 = 'upload1.txt','upload2.txt'
323
+ @product1['txtfile-rhoblob'] = file1
324
+ @product1['_id'] = 'tempobj1'
325
+ @product2['txtfile-rhoblob'] = file2
326
+ @product2['_id'] = 'tempobj2'
327
+ cud = {'create'=>{'1'=>@product1,'2'=>@product2},
328
+ :client_id => @c.id,:source_name => @s.name,
329
+ :version => ClientSync::VERSION,
330
+ :blob_fields => ['txtfile-rhoblob']}.to_json
331
+ post "/application",
332
+ {:cud => cud,'txtfile-rhoblob-1' =>
333
+ Rack::Test::UploadedFile.new(File.join(File.dirname(__FILE__),'..','testdata',file1), "application/octet-stream"),
334
+ 'txtfile-rhoblob-2' =>
335
+ Rack::Test::UploadedFile.new(File.join(File.dirname(__FILE__),'..','testdata',file2), "application/octet-stream")}
336
+ Store.get_data('test_create_storage').each do |id,obj|
337
+ File.exists?(obj['txtfile-rhoblob']).should == true
338
+ end
339
+ end
340
+ end
341
+ end
@@ -0,0 +1,114 @@
1
+ require File.join(File.dirname(__FILE__),'spec_helper')
2
+
3
+ class Rhosync::SourceAdapter
4
+ def inject_result(result)
5
+ @result = result
6
+ end
7
+ end
8
+
9
+ describe "SourceAdapter" do
10
+ it_should_behave_like "SpecBootstrapHelper"
11
+ it_should_behave_like "SourceAdapterHelper"
12
+
13
+ before(:each) do
14
+ @s.name = 'SimpleAdapter'
15
+ @sa = SourceAdapter.create(@s,nil)
16
+ end
17
+
18
+ it "should create SourceAdapter with source" do
19
+ @sa.class.name.should == @s.name
20
+ end
21
+
22
+ it "should create and execute SubAdapter that extends BaseAdapter" do
23
+ @s.name = 'SubAdapter'
24
+ @sa = SourceAdapter.create(@s,nil)
25
+ @sa.class.name.should == 'SubAdapter'
26
+ expected = {'1'=>@product1,'2'=>@product2}
27
+ @sa.inject_result expected
28
+ @sa.query.should == expected
29
+ end
30
+
31
+ it "should fail to create SourceAdapter" do
32
+ @s_fields[:name] = 'Broken'
33
+ broken_source = Source.create(@s_fields,@s_params)
34
+ lambda { SourceAdapter.create(broken_source) }.should raise_error(Exception)
35
+ end
36
+
37
+ it "should create SourceAdapter with trailing spaces" do
38
+ @s.name = 'SimpleAdapter '
39
+ SourceAdapter.create(@s,nil).is_a?(SimpleAdapter).should be_true
40
+ end
41
+
42
+ describe "SourceAdapter methods" do
43
+ it "should execute SourceAdapter login method with source vars" do
44
+ @sa.login.should == true
45
+ end
46
+
47
+ it "should execute SourceAdapter query method" do
48
+ expected = {'1'=>@product1,'2'=>@product2}
49
+ @sa.inject_result expected
50
+ @sa.query.should == expected
51
+ end
52
+
53
+ it "should execute SourceAdapter query method" do
54
+ expected = {'1'=>@product1,'2'=>@product2}
55
+ @sa.inject_result expected
56
+ @sa.query.should == expected
57
+ end
58
+
59
+ it "should execute SourceAdapter search method and modify params" do
60
+ params = {:hello => 'world'}
61
+ expected = {'1'=>@product1,'2'=>@product2}
62
+ @sa.inject_result expected
63
+ @sa.search(params).should == expected
64
+ params.should == {:hello => 'world', :foo => 'bar'}
65
+ end
66
+
67
+ it "should execute SourceAdapter login with current_user" do
68
+ @sa.should_receive(:current_user).with(no_args()).and_return(@u)
69
+ @sa.login
70
+ end
71
+
72
+ it "should execute SourceAdapter sync method" do
73
+ expected = {'1'=>@product1,'2'=>@product2}
74
+ @sa.inject_result expected
75
+ @sa.query.should == expected
76
+ @sa.sync
77
+ Store.get_data(@s.docname(:md)).should == expected
78
+ Store.get_value(@s.docname(:md_size)).to_i.should == 2
79
+ end
80
+
81
+ it "should fail gracefully if @result is missing" do
82
+ @sa.inject_result nil
83
+ lambda { @sa.query }.should_not raise_error
84
+ end
85
+
86
+ it "should reset count if @result is empty" do
87
+ @sa.inject_result({'1'=>@product1,'2'=>@product2})
88
+ @sa.query; @sa.sync
89
+ Store.get_value(@s.docname(:md_size)).to_i.should == 2
90
+ @sa.inject_result({})
91
+ @sa.query; @sa.sync
92
+ Store.get_value(@s.docname(:md_size)).to_i.should == 0
93
+ end
94
+
95
+ it "should execute SourceAdapter create method" do
96
+ @sa.create(@product4).should == 'obj4'
97
+ end
98
+
99
+ it "should log warning if @result is missing" do
100
+ @sa.should_receive(:log).with(SourceAdapter::MSG_NIL_RESULT_ATTRIB)
101
+ @sa.inject_result nil
102
+ @sa.sync
103
+ end
104
+
105
+ describe "SourceAdapter metadata method" do
106
+
107
+ it "should execute SourceAdapter metadata method" do
108
+ mock_metadata_method([SimpleAdapter]) do
109
+ @sa.metadata.should == "{\"foo\":\"bar\"}"
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,77 @@
1
+ require File.join(File.dirname(__FILE__),'spec_helper')
2
+
3
+ describe "Source" do
4
+ it_should_behave_like "SpecBootstrapHelper"
5
+ it_should_behave_like "SourceAdapterHelper"
6
+
7
+ it "should create and load source with @s_fields and @s_params" do
8
+ @s.name.should == @s_fields[:name]
9
+ @s.url.should == @s_fields[:url]
10
+ @s.login.should == @s_fields[:login]
11
+ @s.app.name.should == @a_fields[:name]
12
+ @s.priority.should == 3
13
+ @s.callback_url.should be_nil
14
+ @s.queue.should be_nil
15
+ @s.query_queue.should be_nil
16
+ @s.cud_queue.should be_nil
17
+ @s.app_id.should == @s_params[:app_id]
18
+ @s.user_id.should == @s_params[:user_id]
19
+ @s.sync_type.should == :incremental
20
+ @s.partition_type.should == :user
21
+ @s.poll_interval.should == 300
22
+
23
+ @s1 = Source.load(@s.id,@s_params)
24
+ @s1.name.should == @s_fields[:name]
25
+ @s1.url.should == @s_fields[:url]
26
+ @s1.login.should == @s_fields[:login]
27
+ @s1.app.name.should == @a_fields[:name]
28
+ @s1.priority.should == 3
29
+ @s1.callback_url.should be_nil
30
+ @s1.poll_interval.should == 300
31
+ @s1.app_id.should == @s_params[:app_id]
32
+ @s1.user_id.should == @s_params[:user_id]
33
+ end
34
+
35
+ it "should create source with user" do
36
+ @s.user.login.should == @u_fields[:login]
37
+ end
38
+
39
+ it "should create source with app and document" do
40
+ @s.app.name.should == @a_fields[:name]
41
+ @s.docname(:md).should == "source:#{@s.app.id}:#{@u.id}:#{@s_fields[:name]}:md"
42
+ end
43
+
44
+ it "should delete source" do
45
+ @s.delete
46
+ Source.is_exist?(@s_fields[:name]).should == false
47
+ end
48
+
49
+ it "should delete master and all documents associated with source" do
50
+ set_state(@s.docname(:md) => @data)
51
+ @s.delete
52
+ verify_result(@s.docname(:md) => {})
53
+ Store.db.keys(@s.docname('*')).should == []
54
+ end
55
+
56
+ it "should create source with default partition user" do
57
+ @s1 = Source.load(@s_fields[:name],{:app_id => @a.id,:user_id => '*'})
58
+ @s1.partition.should == :user
59
+ end
60
+
61
+ it "should create correct docname based on partition scheme" do
62
+ @s.partition = :app
63
+ @s.docname(:md).should == "source:#{@s.app.id}:__shared__:#{@s_fields[:name]}:md"
64
+ end
65
+
66
+ it "should create source with default read/write queue" do
67
+ @s.delete
68
+ @s_fields[:queue] = :default
69
+ @s_fields[:query_queue] = :query
70
+ @s_fields[:cud_queue] = :cud
71
+ Source.create(@s_fields,@s_params)
72
+ s = Source.load(@s_fields[:name],@s_params)
73
+ s.queue.should == 'default'
74
+ s.query_queue.should == 'query'
75
+ s.cud_queue.should == 'cud'
76
+ end
77
+ end