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.
- data/CHANGELOG +5 -0
- data/LICENSE +674 -0
- data/README.md +26 -0
- data/Rakefile +109 -0
- data/bench/bench +6 -0
- data/bench/benchapp/Rakefile +14 -0
- data/bench/benchapp/application.rb +13 -0
- data/bench/benchapp/config.ru +32 -0
- data/bench/benchapp/settings/license.key +1 -0
- data/bench/benchapp/settings/settings.yml +18 -0
- data/bench/benchapp/sources/mock_adapter.rb +55 -0
- data/bench/benchapp/sources/queue_mock_adapter.rb +2 -0
- data/bench/benchapp/vendor/rhosync/lib/rhosync.rb +7 -0
- data/bench/lib/bench/cli.rb +16 -0
- data/bench/lib/bench/logging.rb +18 -0
- data/bench/lib/bench/mock_client.rb +41 -0
- data/bench/lib/bench/result.rb +50 -0
- data/bench/lib/bench/runner.rb +44 -0
- data/bench/lib/bench/session.rb +65 -0
- data/bench/lib/bench/statistics.rb +56 -0
- data/bench/lib/bench/test_data.rb +55 -0
- data/bench/lib/bench/timer.rb +10 -0
- data/bench/lib/bench/utils.rb +49 -0
- data/bench/lib/bench.rb +128 -0
- data/bench/lib/testdata/100-data.txt +148 -0
- data/bench/lib/testdata/5-data.txt +11 -0
- data/bench/scripts/cud_script.rb +77 -0
- data/bench/scripts/helpers.rb +101 -0
- data/bench/scripts/query_md_script.rb +46 -0
- data/bench/scripts/query_script.rb +46 -0
- data/bench/spec/bench_spec_helper.rb +65 -0
- data/bench/spec/logging_spec.rb +19 -0
- data/bench/spec/mock_adapter_spec.rb +61 -0
- data/bench/spec/mock_client_spec.rb +64 -0
- data/bench/spec/result_spec.rb +59 -0
- data/bench/spec/utils_spec.rb +35 -0
- data/bin/rhosync +34 -0
- data/doc/protocol.html +1901 -0
- data/doc/public/css/print.css +29 -0
- data/doc/public/css/screen.css +257 -0
- data/doc/public/css/style.css +20 -0
- data/examples/simple/application.rb +13 -0
- data/examples/simple/sources/sample_adapter.rb +5 -0
- data/examples/simple/sources/simple_adapter.rb +5 -0
- data/examples/simple/vendor/rhosync/lib/rhosync.rb +7 -0
- data/generators/rhosync.rb +98 -0
- data/generators/templates/application/Rakefile +19 -0
- data/generators/templates/application/application.rb +27 -0
- data/generators/templates/application/config.ru +33 -0
- data/generators/templates/application/settings/license.key +1 -0
- data/generators/templates/application/settings/settings.yml +14 -0
- data/generators/templates/source/source_adapter.rb +49 -0
- data/lib/rhosync/api/create_client.rb +3 -0
- data/lib/rhosync/api/create_user.rb +7 -0
- data/lib/rhosync/api/delete_client.rb +5 -0
- data/lib/rhosync/api/delete_user.rb +5 -0
- data/lib/rhosync/api/get_api_token.rb +7 -0
- data/lib/rhosync/api/get_client_params.rb +3 -0
- data/lib/rhosync/api/get_db_doc.rb +7 -0
- data/lib/rhosync/api/get_license_info.rb +7 -0
- data/lib/rhosync/api/get_source_params.rb +3 -0
- data/lib/rhosync/api/list_client_docs.rb +12 -0
- data/lib/rhosync/api/list_clients.rb +3 -0
- data/lib/rhosync/api/list_source_docs.rb +10 -0
- data/lib/rhosync/api/list_sources.rb +15 -0
- data/lib/rhosync/api/list_users.rb +3 -0
- data/lib/rhosync/api/ping.rb +7 -0
- data/lib/rhosync/api/push_deletes.rb +6 -0
- data/lib/rhosync/api/push_objects.rb +6 -0
- data/lib/rhosync/api/reset.rb +10 -0
- data/lib/rhosync/api/set_db_doc.rb +8 -0
- data/lib/rhosync/api/set_refresh_time.rb +8 -0
- data/lib/rhosync/api/update_user.rb +4 -0
- data/lib/rhosync/api/upload_file.rb +4 -0
- data/lib/rhosync/api_token.rb +19 -0
- data/lib/rhosync/app.rb +69 -0
- data/lib/rhosync/bulk_data/bulk_data.rb +75 -0
- data/lib/rhosync/bulk_data/syncdb.index.schema +3 -0
- data/lib/rhosync/bulk_data/syncdb.schema +37 -0
- data/lib/rhosync/bulk_data.rb +2 -0
- data/lib/rhosync/client.rb +74 -0
- data/lib/rhosync/client_sync.rb +296 -0
- data/lib/rhosync/console/app/helpers/auth_helper.rb +18 -0
- data/lib/rhosync/console/app/helpers/extensions.rb +19 -0
- data/lib/rhosync/console/app/helpers/helpers.rb +52 -0
- data/lib/rhosync/console/app/public/main.css +7 -0
- data/lib/rhosync/console/app/public/text.txt +0 -0
- data/lib/rhosync/console/app/routes/auth.rb +29 -0
- data/lib/rhosync/console/app/routes/client.rb +32 -0
- data/lib/rhosync/console/app/routes/docs.rb +84 -0
- data/lib/rhosync/console/app/routes/home.rb +22 -0
- data/lib/rhosync/console/app/routes/user.rb +63 -0
- data/lib/rhosync/console/app/views/client.erb +30 -0
- data/lib/rhosync/console/app/views/doc.erb +56 -0
- data/lib/rhosync/console/app/views/docs.erb +29 -0
- data/lib/rhosync/console/app/views/index.erb +50 -0
- data/lib/rhosync/console/app/views/layout.erb +12 -0
- data/lib/rhosync/console/app/views/newuser.erb +17 -0
- data/lib/rhosync/console/app/views/ping.erb +28 -0
- data/lib/rhosync/console/app/views/result.erb +11 -0
- data/lib/rhosync/console/app/views/user.erb +32 -0
- data/lib/rhosync/console/app/views/users.erb +14 -0
- data/lib/rhosync/console/rhosync_api.rb +102 -0
- data/lib/rhosync/console/server.rb +27 -0
- data/lib/rhosync/credential.rb +9 -0
- data/lib/rhosync/document.rb +43 -0
- data/lib/rhosync/indifferent_access.rb +132 -0
- data/lib/rhosync/jobs/bulk_data_job.rb +104 -0
- data/lib/rhosync/jobs/ping_job.rb +19 -0
- data/lib/rhosync/jobs/source_job.rb +16 -0
- data/lib/rhosync/license.rb +79 -0
- data/lib/rhosync/lock_ops.rb +11 -0
- data/lib/rhosync/model.rb +410 -0
- data/lib/rhosync/ping/blackberry.rb +55 -0
- data/lib/rhosync/ping/iphone.rb +44 -0
- data/lib/rhosync/ping.rb +2 -0
- data/lib/rhosync/read_state.rb +27 -0
- data/lib/rhosync/server/views/index.erb +12 -0
- data/lib/rhosync/server.rb +242 -0
- data/lib/rhosync/source.rb +112 -0
- data/lib/rhosync/source_adapter.rb +95 -0
- data/lib/rhosync/source_sync.rb +245 -0
- data/lib/rhosync/store.rb +199 -0
- data/lib/rhosync/tasks.rb +151 -0
- data/lib/rhosync/user.rb +83 -0
- data/lib/rhosync/version.rb +3 -0
- data/lib/rhosync.rb +251 -0
- data/spec/api/api_helper.rb +44 -0
- data/spec/api/create_client_spec.rb +13 -0
- data/spec/api/create_user_spec.rb +16 -0
- data/spec/api/delete_client_spec.rb +13 -0
- data/spec/api/delete_user_spec.rb +18 -0
- data/spec/api/get_api_token_spec.rb +25 -0
- data/spec/api/get_client_params_spec.rb +18 -0
- data/spec/api/get_db_doc_spec.rb +21 -0
- data/spec/api/get_license_info_spec.rb +16 -0
- data/spec/api/get_source_params_spec.rb +26 -0
- data/spec/api/list_client_docs_spec.rb +33 -0
- data/spec/api/list_clients_spec.rb +23 -0
- data/spec/api/list_source_docs_spec.rb +26 -0
- data/spec/api/list_sources_spec.rb +27 -0
- data/spec/api/list_users_spec.rb +21 -0
- data/spec/api/ping_spec.rb +24 -0
- data/spec/api/push_deletes_spec.rb +16 -0
- data/spec/api/push_objects_spec.rb +27 -0
- data/spec/api/reset_spec.rb +22 -0
- data/spec/api/set_db_doc_spec.rb +20 -0
- data/spec/api/set_refresh_time_spec.rb +43 -0
- data/spec/api/update_user_spec.rb +31 -0
- data/spec/api/upload_file_spec.rb +26 -0
- data/spec/api_token_spec.rb +13 -0
- data/spec/app_spec.rb +20 -0
- data/spec/apps/rhotestapp/Rakefile +1 -0
- data/spec/apps/rhotestapp/application.rb +16 -0
- data/spec/apps/rhotestapp/config.ru +1 -0
- data/spec/apps/rhotestapp/settings/apple_fake_cert.pem +1 -0
- data/spec/apps/rhotestapp/settings/license.key +1 -0
- data/spec/apps/rhotestapp/settings/settings.yml +23 -0
- data/spec/apps/rhotestapp/sources/base_adapter.rb +9 -0
- data/spec/apps/rhotestapp/sources/sample_adapter.rb +66 -0
- data/spec/apps/rhotestapp/sources/simple_adapter.rb +39 -0
- data/spec/apps/rhotestapp/sources/sub_adapter.rb +7 -0
- data/spec/apps/rhotestapp/vendor/mygem-0.1.0/lib/mygem/mygem.rb +8 -0
- data/spec/apps/rhotestapp/vendor/mygem-0.1.0/lib/mygem.rb +1 -0
- data/spec/bulk_data/bulk_data_spec.rb +79 -0
- data/spec/client_spec.rb +58 -0
- data/spec/client_sync_spec.rb +377 -0
- data/spec/doc/base.html +72 -0
- data/spec/doc/doc_spec.rb +303 -0
- data/spec/doc/footer.html +4 -0
- data/spec/doc/header.html +30 -0
- data/spec/document_spec.rb +27 -0
- data/spec/generator/generator_spec.rb +53 -0
- data/spec/generator/generator_spec_helper.rb +8 -0
- data/spec/jobs/bulk_data_job_spec.rb +76 -0
- data/spec/jobs/ping_job_spec.rb +26 -0
- data/spec/jobs/source_job_spec.rb +25 -0
- data/spec/license_spec.rb +48 -0
- data/spec/model_spec.rb +269 -0
- data/spec/perf/bulk_data_perf_spec.rb +33 -0
- data/spec/perf/perf_spec_helper.rb +51 -0
- data/spec/perf/store_perf_spec.rb +28 -0
- data/spec/ping/blackberry_spec.rb +62 -0
- data/spec/ping/iphone_spec.rb +50 -0
- data/spec/read_state_spec.rb +25 -0
- data/spec/rhosync_spec.rb +43 -0
- data/spec/server/server_spec.rb +341 -0
- data/spec/source_adapter_spec.rb +114 -0
- data/spec/source_spec.rb +77 -0
- data/spec/source_sync_spec.rb +248 -0
- data/spec/spec_helper.rb +240 -0
- data/spec/store_spec.rb +149 -0
- data/spec/sync_states_spec.rb +101 -0
- data/spec/testdata/1000-data.txt +1414 -0
- data/spec/testdata/compressed/compress-data.txt +1 -0
- data/spec/testdata/upload1.txt +1 -0
- data/spec/testdata/upload2.txt +1 -0
- data/spec/user_spec.rb +79 -0
- data/tasks/redis.rake +134 -0
- 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
|
data/spec/source_spec.rb
ADDED
|
@@ -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
|