rhosync 2.0.0.beta1

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 (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,44 @@
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 "ApiHelper", :shared => true do
10
+ it_should_behave_like "RhosyncDataHelper"
11
+
12
+ include Rack::Test::Methods
13
+ include Rhosync
14
+
15
+ before(:each) do
16
+ require File.join(get_testapp_path,@test_app_name)
17
+ Rhosync.bootstrap(get_testapp_path) do |rhosync|
18
+ rhosync.vendor_directory = File.join(rhosync.base_directory,'..','..','..','vendor')
19
+ end
20
+ Server.set(
21
+ :environment => :test,
22
+ :run => false,
23
+ :secret => "secure!"
24
+ )
25
+ @api_token = User.load('admin').token_id
26
+ end
27
+
28
+ def app
29
+ @app ||= Server.new
30
+ end
31
+
32
+ it_should_behave_like "DBObjectsHelper"
33
+ end
34
+
35
+ def compress(path)
36
+ path.sub!(%r[/$],'')
37
+ archive = File.join(path,File.basename(path))+'.zip'
38
+ FileUtils.rm archive, :force=>true
39
+ Zip::ZipFile.open(archive, 'w') do |zipfile|
40
+ Dir["#{path}/**/**"].reject{|f|f==archive}.each do |file|
41
+ zipfile.add(file.sub(path+'/',''),file)
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,13 @@
1
+ require File.join(File.dirname(__FILE__),'api_helper')
2
+
3
+ describe "RhosyncApiCreateUser" do
4
+ it_should_behave_like "ApiHelper"
5
+
6
+ it "should create client for a user" do
7
+ post "/api/create_client", {:app_name => @test_app_name, :api_token => @api_token, :user_id => @u_fields[:login]}
8
+ last_response.should be_ok
9
+ clients = User.load(@u_fields[:login]).clients.members
10
+ clients.size.should == 2
11
+ clients.include?(last_response.body).should == true
12
+ end
13
+ end
@@ -0,0 +1,16 @@
1
+ require File.join(File.dirname(__FILE__),'api_helper')
2
+
3
+ describe "RhosyncApiCreateUser" do
4
+ it_should_behave_like "ApiHelper"
5
+
6
+ it "should create user as admin" do
7
+ params = {:app_name => @test_app_name, :api_token => @api_token,
8
+ :attributes => {:login => 'testuser1', :password => 'testpass1'}}
9
+ post "/api/create_user", params
10
+ last_response.should be_ok
11
+ User.load(params[:attributes][:login]).login.should == params[:attributes][:login]
12
+ User.authenticate(params[:attributes][:login],
13
+ params[:attributes][:password]).login.should == params[:attributes][:login]
14
+ @a.users.members.sort.should == [@u.login, params[:attributes][:login]]
15
+ end
16
+ end
@@ -0,0 +1,13 @@
1
+ require File.join(File.dirname(__FILE__),'api_helper')
2
+
3
+ describe "RhosyncApiDeleteUser" do
4
+ it_should_behave_like "ApiHelper"
5
+
6
+ it "should delete client" do
7
+ post "/api/delete_client", {:app_name => @test_app_name, :api_token => @api_token,
8
+ :user_id => @u_fields[:login], :client_id => @c.id}
9
+ last_response.should be_ok
10
+ Client.is_exist?(@c.id).should == false
11
+ User.load(@u_fields[:login]).clients.members.should == []
12
+ end
13
+ end
@@ -0,0 +1,18 @@
1
+ require File.join(File.dirname(__FILE__),'api_helper')
2
+
3
+ describe "RhosyncApiDeleteUser" do
4
+ it_should_behave_like "ApiHelper"
5
+
6
+ it "should delete user" do
7
+ params = {:app_name => @test_app_name, :api_token => @api_token,
8
+ :attributes => {:login => 'testuser1', :password => 'testpass1'}}
9
+ post "/api/create_user", params
10
+ last_response.should be_ok
11
+ User.is_exist?(params[:attributes][:login]).should == true
12
+ post "/api/delete_user", {:app_name => @test_app_name, :api_token => @api_token,
13
+ :user_id => params[:attributes][:login]}
14
+ last_response.should be_ok
15
+ User.is_exist?(params[:attributes][:login]).should == false
16
+ App.load(@test_app_name).users.members.should == ["testuser"]
17
+ end
18
+ end
@@ -0,0 +1,25 @@
1
+ require File.join(File.dirname(__FILE__),'api_helper')
2
+
3
+ describe "RhosyncApiGetApiToken" do
4
+ it_should_behave_like "ApiHelper"
5
+
6
+ it "should get token string" do
7
+ post "/login", :login => 'admin',:password => ''
8
+ post "/api/get_api_token"
9
+ last_response.body.should == @api_token
10
+ end
11
+
12
+ it "should fail to get token if user is not admin" do
13
+ post "/login", :login => @u_fields[:login],:password => 'testpass'
14
+ post "/api/get_api_token"
15
+ last_response.status.should == 422
16
+ last_response.body.should == 'Invalid/missing API user'
17
+ end
18
+
19
+ it "should return 422 if no token provided" do
20
+ params = {:app_name => @test_app_name, :attributes =>
21
+ {:login => 'testuser1', :password => 'testpass1'}}
22
+ post "/api/create_user", params
23
+ last_response.status.should == 422
24
+ end
25
+ end
@@ -0,0 +1,18 @@
1
+ require File.join(File.dirname(__FILE__),'api_helper')
2
+
3
+ describe "RhosyncApiGetClientParams" do
4
+ it_should_behave_like "ApiHelper"
5
+
6
+ it "should list client attributes" do
7
+ post "/api/get_client_params", {:api_token => @api_token, :client_id =>@c.id}
8
+ res = JSON.parse(last_response.body)
9
+ res.delete_if { |attrib| attrib['name'] == 'rho__id' }
10
+ res.sort{|x,y| x['name']<=>y['name']}.should == [
11
+ {"name"=>"device_type", "value"=>"iPhone", "type"=>"string"},
12
+ {"name"=>"device_pin", "value"=>"abcd", "type"=>"string"},
13
+ {"name"=>"device_port", "value"=>"3333", "type"=>"string"},
14
+ {"name"=>"user_id", "value"=>"testuser", "type"=>"string"},
15
+ {"name"=>"app_id", "value"=>"application", "type"=>"string"}].sort{|x,y| x['name']<=>y['name']}
16
+ end
17
+
18
+ end
@@ -0,0 +1,21 @@
1
+ require File.join(File.dirname(__FILE__),'api_helper')
2
+
3
+ describe "RhosyncApiGetDbDoc" do
4
+ it_should_behave_like "ApiHelper"
5
+
6
+ it "should return db document by name" do
7
+ data = {'1' => {'foo' => 'bar'}}
8
+ set_state('abc:abc' => data)
9
+ post "/api/get_db_doc", :api_token => @api_token, :doc => 'abc:abc'
10
+ last_response.should be_ok
11
+ JSON.parse(last_response.body).should == data
12
+ end
13
+
14
+ it "should return db document by name and data_type" do
15
+ data = 'some string'
16
+ set_state('abc:abc' => data)
17
+ post "/api/get_db_doc", :api_token => @api_token, :doc => 'abc:abc', :data_type => :string
18
+ last_response.should be_ok
19
+ last_response.body.should == data
20
+ end
21
+ end
@@ -0,0 +1,16 @@
1
+ require File.join(File.dirname(__FILE__),'api_helper')
2
+
3
+ describe "RhosyncApiGetLicenseInfo" do
4
+ it_should_behave_like "ApiHelper"
5
+
6
+ it "should get license info" do
7
+ true.should == true
8
+ post "/api/get_license_info", {:api_token => @api_token}
9
+ JSON.parse(last_response.body).should == {
10
+ "available" => 9,
11
+ "issued" => "Fri Apr 23 17:20:13 -0700 2010",
12
+ "seats" => 10,
13
+ "rhosync_version" => "Version 1",
14
+ "licensee" => "Rhomobile" }
15
+ end
16
+ end
@@ -0,0 +1,26 @@
1
+ require File.join(File.dirname(__FILE__),'api_helper')
2
+
3
+ describe "RhosyncApiGetSourceParams" do
4
+ it_should_behave_like "ApiHelper"
5
+
6
+ it "should list source attributes" do
7
+ post "/api/get_source_params", {:app_name => @test_app_name, :api_token => @api_token, :source_id =>"SampleAdapter"}
8
+ JSON.parse(last_response.body).should == [
9
+ {"name"=>"rho__id", "value"=>"SampleAdapter", "type"=>"string"},
10
+ {"name"=>"source_id", "value"=>nil, "type"=>"integer"},
11
+ {"name"=>"name", "value"=>"SampleAdapter", "type"=>"string"},
12
+ {"name"=>"url", "value"=>"", "type"=>"string"},
13
+ {"name"=>"login", "value"=>"", "type"=>"string"},
14
+ {"name"=>"password", "value"=>"", "type"=>"string"},
15
+ {"name"=>"priority", "value"=>3, "type"=>"integer"},
16
+ {"name"=>"callback_url", "value"=>nil, "type"=>"string"},
17
+ {"name"=>"poll_interval", "value"=>300, "type"=>"integer"},
18
+ {"name"=>"partition_type", "value"=>"user", "type"=>"string"},
19
+ {"name"=>"sync_type", "value"=>"incremental", "type"=>"string"},
20
+ {"name"=>"queue", "value"=>nil, "type"=>"string"},
21
+ {"name"=>"query_queue", "value"=>nil, "type"=>"string"},
22
+ {"name"=>"cud_queue", "value"=>nil, "type"=>"string"}]
23
+ end
24
+
25
+ end
26
+
@@ -0,0 +1,33 @@
1
+ require File.join(File.dirname(__FILE__),'api_helper')
2
+
3
+ describe "RhosyncApiListClientDocs" do
4
+ it_should_behave_like "ApiHelper"
5
+
6
+ it "should list client documents" do
7
+ post "/api/list_client_docs", {:app_name => @test_app_name, :api_token => @api_token,
8
+ :source_id => "SimpleAdapter", :client_id => @c.id}
9
+ JSON.parse(last_response.body).should == {
10
+ "cd"=>"client:application:testuser:#{@c.id}:SimpleAdapter:cd",
11
+ "cd_size"=>"client:application:testuser:#{@c.id}:SimpleAdapter:cd_size",
12
+ "create"=>"client:application:testuser:#{@c.id}:SimpleAdapter:create",
13
+ "update"=>"client:application:testuser:#{@c.id}:SimpleAdapter:update",
14
+ "delete"=>"client:application:testuser:#{@c.id}:SimpleAdapter:delete",
15
+
16
+ "page"=>"client:application:testuser:#{@c.id}:SimpleAdapter:page",
17
+ "page_token"=>"client:application:testuser:#{@c.id}:SimpleAdapter:page_token",
18
+ "delete_page"=>"client:application:testuser:#{@c.id}:SimpleAdapter:delete_page",
19
+ "create_links"=>"client:application:testuser:#{@c.id}:SimpleAdapter:create_links",
20
+ "create_links_page"=>"client:application:testuser:#{@c.id}:SimpleAdapter:create_links_page",
21
+
22
+ "delete_errors"=>"client:application:testuser:#{@c.id}:SimpleAdapter:delete_errors",
23
+ "login_error"=>"client:application:testuser:#{@c.id}:SimpleAdapter:login_error",
24
+ "create_errors"=>"client:application:testuser:#{@c.id}:SimpleAdapter:create_errors",
25
+ "update_errors"=>"client:application:testuser:#{@c.id}:SimpleAdapter:update_errors",
26
+ "logoff_error"=>"client:application:testuser:#{@c.id}:SimpleAdapter:logoff_error",
27
+
28
+ "search"=>"client:application:testuser:#{@c.id}:SimpleAdapter:search",
29
+ "search_token"=>"client:application:testuser:#{@c.id}:SimpleAdapter:search_token",
30
+ "search_errors"=>"client:application:testuser:#{@c.id}:SimpleAdapter:search_errors"}
31
+ end
32
+
33
+ end
@@ -0,0 +1,23 @@
1
+ require File.join(File.dirname(__FILE__),'api_helper')
2
+
3
+ describe "RhosyncApiListUsers" do
4
+ it_should_behave_like "ApiHelper"
5
+
6
+ it "should list clients" do
7
+ post "/api/list_clients", {:app_name => @test_app_name, :api_token => @api_token,
8
+ :user_id => @u_fields[:login]}
9
+ res = JSON.parse(last_response.body)
10
+ res.is_a?(Array).should == true
11
+ res.size.should == 1
12
+ res[0].is_a?(String) == true
13
+ res[0].length.should == 32
14
+ end
15
+
16
+ it "should handle empty client's list" do
17
+ @u.clients.delete(@c.id)
18
+ post "/api/list_clients", {:app_name => @test_app_name, :api_token => @api_token,
19
+ :user_id => @u_fields[:login]}
20
+ JSON.parse(last_response.body).should == []
21
+ end
22
+
23
+ end
@@ -0,0 +1,26 @@
1
+ require File.join(File.dirname(__FILE__),'api_helper')
2
+
3
+ describe "RhosyncApiListSourceDocs" do
4
+ it_should_behave_like "ApiHelper"
5
+
6
+ it "should list of shared source documents" do
7
+ post "/api/list_source_docs", {:app_name => @test_app_name, :api_token => @api_token,
8
+ :source_id => "SimpleAdapter", :user_id => '*'}
9
+ JSON.parse(last_response.body).should == {
10
+ "md"=>"source:application:__shared__:SimpleAdapter:md",
11
+ "errors"=>"source:application:__shared__:SimpleAdapter:errors",
12
+ "md_size"=>"source:application:__shared__:SimpleAdapter:md_size",
13
+ "md_copy"=>"source:application:__shared__:SimpleAdapter:md_copy"}
14
+ end
15
+
16
+ it "should list user source documents" do
17
+ post "/api/list_source_docs", {:app_name => @test_app_name, :api_token => @api_token,
18
+ :source_id => "SampleAdapter", :user_id => @u.id}
19
+ JSON.parse(last_response.body).should == {
20
+ "md"=>"source:application:testuser:SampleAdapter:md",
21
+ "errors"=>"source:application:testuser:SampleAdapter:errors",
22
+ "md_size"=>"source:application:testuser:SampleAdapter:md_size",
23
+ "md_copy"=>"source:application:testuser:SampleAdapter:md_copy"}
24
+ end
25
+
26
+ end
@@ -0,0 +1,27 @@
1
+ require File.join(File.dirname(__FILE__),'api_helper')
2
+
3
+ describe "RhosyncApiListSources" do
4
+ it_should_behave_like "ApiHelper"
5
+
6
+ it "should list all application sources" do
7
+ post "/api/list_sources", {:app_name => @test_app_name, :api_token => @api_token}
8
+ JSON.parse(last_response.body).should == ["SimpleAdapter", "SampleAdapter"]
9
+ end
10
+
11
+ it "should list all application sources using partition_type param" do
12
+ post "/api/list_sources",
13
+ {:app_name => @test_app_name, :api_token => @api_token, :partition_type => 'all'}
14
+ JSON.parse(last_response.body).should == ["SimpleAdapter", "SampleAdapter"]
15
+ end
16
+
17
+ it "should list app partition sources" do
18
+ post "/api/list_sources", {:app_name => @test_app_name, :api_token => @api_token, :partition_type => :app}
19
+ JSON.parse(last_response.body).should == ["SimpleAdapter"]
20
+ end
21
+
22
+ it "should list user partition sources" do
23
+ post "/api/list_sources", {:app_name => @test_app_name, :api_token => @api_token, :partition_type => :user}
24
+ JSON.parse(last_response.body).should == ["SampleAdapter"]
25
+ end
26
+
27
+ end
@@ -0,0 +1,21 @@
1
+ require File.join(File.dirname(__FILE__),'api_helper')
2
+
3
+ describe "RhosyncApiListUsers" do
4
+ it_should_behave_like "ApiHelper"
5
+
6
+ it "should list users" do
7
+ params = {:app_name => @test_app_name, :api_token => @api_token,
8
+ :attributes => {:login => 'testuser1', :password => 'testpass1'}}
9
+ post "/api/create_user", params
10
+ last_response.should be_ok
11
+ post "/api/list_users", {:app_name => @test_app_name, :api_token => @api_token}
12
+ JSON.parse(last_response.body).should == ["testuser", "testuser1"]
13
+ end
14
+
15
+ it "should handle empty user's list" do
16
+ @a.delete; @a = App.create(@a_fields)
17
+ post "/api/list_users", {:app_name => @test_app_name, :api_token => @api_token}
18
+ JSON.parse(last_response.body).should == []
19
+ end
20
+
21
+ end
@@ -0,0 +1,24 @@
1
+ require File.join(File.dirname(__FILE__),'api_helper')
2
+
3
+ describe "RhosyncApiPing" do
4
+ it_should_behave_like "ApiHelper"
5
+
6
+ it "should do ping synchronously" do
7
+ params = {"user_id" => @u.id, "api_token" => @api_token,
8
+ "sources" => [@s.name], "message" => 'hello world',
9
+ "vibrate" => '5', "badge" => '5', "sound" => 'hello.mp3'}
10
+ PingJob.should_receive(:perform).once.with(params)
11
+ post "/api/ping", params
12
+ last_response.should be_ok
13
+ end
14
+
15
+ it "should do ping asynchronously" do
16
+ params = {"user_id" => @u.id, "api_token" => @api_token,
17
+ "async" => "true","sources" => [@s.name], "message" => 'hello world',
18
+ "vibrate" => '5', "badge" => '5', "sound" => 'hello.mp3'}
19
+ PingJob.should_receive(:enqueue).once.with(params)
20
+ post "/api/ping", params
21
+ last_response.should be_ok
22
+ end
23
+
24
+ end
@@ -0,0 +1,16 @@
1
+ require File.join(File.dirname(__FILE__),'api_helper')
2
+
3
+ describe "RhosyncApiPushDeletes" do
4
+ it_should_behave_like "ApiHelper"
5
+
6
+ it "should delete object from :md" do
7
+ data = {'1' => @product1, '2' => @product2, '3' => @product3}
8
+ set_state(@s.docname(:md) => data)
9
+ data.delete('2')
10
+ post "/api/push_deletes", :api_token => @api_token,
11
+ :user_id => @u.id, :source_id => @s_fields[:name], :objects => ['2']
12
+ last_response.should be_ok
13
+ verify_result(@s.docname(:md) => data,@s.docname(:md_size)=>'2')
14
+ end
15
+
16
+ end
@@ -0,0 +1,27 @@
1
+ require File.join(File.dirname(__FILE__),'api_helper')
2
+
3
+ describe "RhosyncApiPushObjects" do
4
+ it_should_behave_like "ApiHelper"
5
+
6
+ it "should push new objects to rhosync's :md" do
7
+ data = {'1' => @product1, '2' => @product2, '3' => @product3}
8
+ post "/api/push_objects", :api_token => @api_token,
9
+ :user_id => @u.id, :source_id => @s_fields[:name], :objects => data
10
+ last_response.should be_ok
11
+ verify_result(@s.docname(:md) => data,@s.docname(:md_size)=>'3')
12
+ end
13
+
14
+ it "should push updates to existing objects to rhosync's :md" do
15
+ data = {'1' => @product1, '2' => @product2, '3' => @product3}
16
+ update = {'price' => '0.99', 'new_field' => 'value'}
17
+ set_state(@s.docname(:md) => data)
18
+ update.each do |key,value|
19
+ data['2'][key] = value
20
+ end
21
+ post "/api/push_objects", :api_token => @api_token,
22
+ :user_id => @u.id, :source_id => @s_fields[:name], :objects => {'2'=>update}
23
+ last_response.should be_ok
24
+ verify_result(@s.docname(:md) => data,@s.docname(:md_size)=>'3')
25
+ end
26
+
27
+ end
@@ -0,0 +1,22 @@
1
+ require File.join(File.dirname(__FILE__),'api_helper')
2
+
3
+ describe "RhosyncApiReset" do
4
+ it_should_behave_like "ApiHelper"
5
+
6
+ it "should reset and re-create admin user with bootstrap" do
7
+ Store.put_data('somedoc',{'foo'=>'bar'})
8
+ post "/api/reset", :api_token => @api_token
9
+ App.is_exist?(@test_app_name).should == true
10
+ Store.get_data('somedoc').should == {}
11
+ User.authenticate('admin','').should_not be_nil
12
+ end
13
+
14
+ it "should reset and re-create admin user with initializer" do
15
+ Store.put_data('somedoc',{'foo'=>'bar'})
16
+ post "/api/reset", :api_token => @api_token
17
+ App.is_exist?(@test_app_name).should == true
18
+ Store.get_data('somedoc').should == {}
19
+ User.authenticate('admin','').should_not be_nil
20
+ load File.join(Rhosync.base_directory,@test_app_name+'.rb')
21
+ end
22
+ end
@@ -0,0 +1,20 @@
1
+ require File.join(File.dirname(__FILE__),'api_helper')
2
+
3
+ describe "RhosyncApiSetDbDoc" do
4
+ it_should_behave_like "ApiHelper"
5
+
6
+ it "should set db document by doc name and data" do
7
+ data = {'1' => {'foo' => 'bar'}}
8
+ post "/api/set_db_doc", :api_token => @api_token, :doc => 'abc:abc', :data => data
9
+ last_response.should be_ok
10
+ verify_result('abc:abc' => data)
11
+ end
12
+
13
+ it "should set db document by doc name, data type, and data" do
14
+ data = 'some string'
15
+ post "/api/set_db_doc", :api_token => @api_token, :doc => 'abc:abc:str', :data => data, :data_type => :string
16
+ last_response.should be_ok
17
+ verify_result('abc:abc:str' => data)
18
+ end
19
+
20
+ end
@@ -0,0 +1,43 @@
1
+ require File.join(File.dirname(__FILE__),'api_helper')
2
+
3
+ describe "RhosyncApiSetRefreshTime" do
4
+ it_should_behave_like "ApiHelper"
5
+
6
+ it "should set refresh time to 100s from 'now'" do
7
+ before = Time.now.to_i
8
+ post "/api/set_refresh_time", :api_token => @api_token, :source_name => @s_fields[:name],
9
+ :app_name => @a_fields[:name], :user_name => @u_fields[:login], :refresh_time => 100
10
+ after = Time.now.to_i
11
+ last_response.should be_ok
12
+ @s = Source.load(@s.id,@s_params)
13
+ @s.read_state.refresh_time.should >= before + 100
14
+ @s.read_state.refresh_time.should <= after + 100
15
+ end
16
+
17
+ it "should set refresh time to 'now' if no refresh_time provided" do
18
+ before = Time.now.to_i
19
+ post "/api/set_refresh_time", :api_token => @api_token, :source_name => @s_fields[:name],
20
+ :app_name => @a_fields[:name], :user_name => @u_fields[:login]
21
+ after = Time.now.to_i
22
+ last_response.should be_ok
23
+ @s = Source.load(@s.id,@s_params)
24
+ @s.read_state.refresh_time.should >= before
25
+ @s.read_state.refresh_time.should <= after
26
+ end
27
+
28
+ it "should set poll interval" do
29
+ post "/api/set_refresh_time", :api_token => @api_token, :source_name => @s_fields[:name],
30
+ :app_name => @a_fields[:name], :user_name => @u_fields[:login], :poll_interval => 100
31
+ last_response.should be_ok
32
+ @s = Source.load(@s.id,@s_params)
33
+ @s.poll_interval.should == 100
34
+ end
35
+
36
+ it "should should not set nil poll interval" do
37
+ post "/api/set_refresh_time", :api_token => @api_token, :source_name => @s_fields[:name],
38
+ :app_name => @a_fields[:name], :user_name => @u_fields[:login], :poll_interval => nil
39
+ last_response.should be_ok
40
+ @s = Source.load(@s.id,@s_params)
41
+ @s.poll_interval.should == 300
42
+ end
43
+ end
@@ -0,0 +1,31 @@
1
+ require File.join(File.dirname(__FILE__),'api_helper')
2
+
3
+ describe "RhosyncApiUpdateUser" do
4
+ it_should_behave_like "ApiHelper"
5
+
6
+ it "should update user successfully" do
7
+ post "/api/update_user", :api_token => @api_token,
8
+ :attributes => {:new_password => '123'}
9
+ last_response.should be_ok
10
+ user = User.authenticate('admin','123')
11
+ user.login.should == 'admin'
12
+ user.admin.should == 1
13
+ end
14
+
15
+ it "should fail to update user with wrong attributes" do
16
+ post "/api/update_user", :api_token => @api_token,
17
+ :attributes => {:missingattrib => '123'}
18
+ last_response.status.should == 500
19
+ last_response.body.match('undefined method').should_not be_nil
20
+ end
21
+
22
+ it "should not update login attribute for user" do
23
+ post "/api/update_user", :api_token => @api_token,
24
+ :attributes => {:new_password => '123', :login => 'admin1'}
25
+ last_response.should be_ok
26
+ user = User.authenticate('admin','123')
27
+ user.login.should == 'admin'
28
+ user.admin.should == 1
29
+ User.is_exist?('admin1').should == false
30
+ end
31
+ end
@@ -0,0 +1,26 @@
1
+ require File.join(File.dirname(__FILE__),'api_helper')
2
+
3
+ describe "RhosyncApiUploadFile" do
4
+ it_should_behave_like "ApiHelper"
5
+
6
+ it "should upload and unzip file" do
7
+ file = File.join(File.dirname(__FILE__),'..','testdata','compressed')
8
+ compress(file)
9
+ zipfile = File.join(file,"compressed.zip")
10
+ post "/api/upload_file", :app_name => @test_app_name, :api_token => @api_token,
11
+ :upload_file => Rack::Test::UploadedFile.new(zipfile, "application/octet-stream")
12
+ FileUtils.rm zipfile
13
+ expected = File.join(Rhosync.app_directory,'compress-data.txt')
14
+ File.exists?(expected).should == true
15
+ File.read(expected).should == 'some compressed text'
16
+ FileUtils.rm expected
17
+ end
18
+
19
+ it "should fail to upload a non-zip file" do
20
+ file = File.join(File.dirname(__FILE__),'..','testdata','compressed','compress-data.txt')
21
+ post "/api/upload_file", :app_name => @test_app_name, :api_token => @api_token,
22
+ :upload_file => Rack::Test::UploadedFile.new(file, "application/octet-stream")
23
+ last_response.status.should == 500
24
+ File.exists?(File.join(Rhosync.app_directory,'compress-data.txt')).should == false
25
+ end
26
+ end
@@ -0,0 +1,13 @@
1
+ require File.join(File.dirname(__FILE__),'spec_helper')
2
+
3
+ describe "ApiToken" do
4
+ it_should_behave_like "SpecBootstrapHelper"
5
+ it_should_behave_like "SourceAdapterHelper"
6
+
7
+ it "should generate api token with user" do
8
+ token = ApiToken.create(:user_id => @u.id)
9
+ token.value.length.should == 32
10
+ token.user_id.should == @u.id
11
+ token.user.login.should == @u.login
12
+ end
13
+ end
data/spec/app_spec.rb ADDED
@@ -0,0 +1,20 @@
1
+ require File.join(File.dirname(__FILE__),'spec_helper')
2
+
3
+ describe "App" do
4
+ it_should_behave_like "SpecBootstrapHelper"
5
+ it_should_behave_like "SourceAdapterHelper"
6
+
7
+ it "should create app with fields" do
8
+ @a.id.should == @a_fields[:name]
9
+ @a1 = App.load(@a_fields[:name])
10
+ @a1.id.should == @a.id
11
+ @a1.name.should == @a_fields[:name]
12
+ end
13
+
14
+ it "should add source adapters" do
15
+ @a.sources << "SimpleAdapter"
16
+ @a.sources << "SampleAdapter"
17
+ @a1 = App.load(@a_fields[:name])
18
+ @a1.sources.members.sort.should == ["SampleAdapter", "SimpleAdapter"]
19
+ end
20
+ end
@@ -0,0 +1 @@
1
+ # Dummy Rakefile for testing...
@@ -0,0 +1,16 @@
1
+ class Application < Rhosync::Base
2
+ class << self
3
+ def authenticate(username,password,session)
4
+ session[:auth] = "delegated"
5
+ password == 'wrongpass' ? false : true
6
+ end
7
+
8
+ # Add hooks for application startup here
9
+ # Don't forget to call super at the end!
10
+ def initializer(path)
11
+ super
12
+ end
13
+ end
14
+ end
15
+
16
+ Application.initializer(File.dirname(__FILE__))
@@ -0,0 +1 @@
1
+ # Dummy config.ru for testing...
@@ -0,0 +1 @@
1
+ This is fake!
@@ -0,0 +1 @@
1
+ 858fc60fadfde40273d0ac505906969318aa4931d1a2c4aeb24d98393c74379f60e226651601969874257f7f1fbda9b099ecd551a641519aa46819947fda0191