rhoconnect 4.0.4 → 5.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -13
- data/CHANGELOG.md +54 -5
- data/CREDITS +219 -219
- data/Gemfile +2 -2
- data/Gemfile.lock +68 -79
- data/Rakefile +1 -2
- data/bench/benchapp/spec/models/ruby/mock_adapter_spec.rb +17 -17
- data/bench/benchapp/spec/models/ruby/queue_mock_adapter_spec.rb +17 -17
- data/bench/benchapp/spec/spec_helper.rb +3 -3
- data/bench/blobapp/spec/models/ruby/blob_adapter_spec.rb +17 -17
- data/bench/blobapp/spec/spec_helper.rb +3 -3
- data/bench/lib/bench/runner.rb +1 -69
- data/bench/lib/bench.rb +18 -18
- data/bench/spec/mock_adapter_spec.rb +54 -55
- data/bench/spec/mock_client_spec.rb +47 -48
- data/bench/spec/result_spec.rb +41 -44
- data/bench/spec/utils_spec.rb +24 -25
- data/commands/generators/app.rb +7 -5
- data/commands/generators/controller.rb +7 -5
- data/commands/generators/model.rb +7 -5
- data/commands/generators/source.rb +7 -5
- data/commands/parser.rb +1 -1
- data/commands/redis/redis_download.rb +1 -1
- data/doc/async-jobs.txt +9 -9
- data/doc/supported-platforms.txt +0 -2
- data/generators/rhoconnect.rb +92 -212
- data/generators/templates/application/rcgemfile +3 -3
- data/generators/templates/application/spec/application_controller_spec.rb +14 -16
- data/generators/templates/application/spec/js_spec.rb +20 -20
- data/generators/templates/application/spec/spec_helper.rb +1 -1
- data/generators/templates/source/controllers/ruby/controller_spec.rb +18 -19
- data/generators/templates/source/models/ruby/model_spec.rb +17 -17
- data/install.sh +10 -21
- data/installer/unix-like/rho_connect_install_constants.rb +5 -5
- data/installer/unix-like/rho_connect_install_installers.rb +4 -4
- data/installer/utils/constants.rb +6 -6
- data/installer/utils/nix_install_test.rb +29 -29
- data/installer/utils/package_upload/repos.rake +16 -26
- data/js-adapters/node.rb +4 -4
- data/js-adapters/node_channel.rb +4 -8
- data/lib/rhoconnect/db_adapter.rb +13 -13
- data/lib/rhoconnect/handler/changes/engine.rb +1 -1
- data/lib/rhoconnect/jobs/bulk_data_job.rb +29 -29
- data/lib/rhoconnect/license.rb +7 -7
- data/lib/rhoconnect/model/helpers/find_duplicates_on_update.rb +13 -13
- data/lib/rhoconnect/ping/apple.rb +4 -4
- data/lib/rhoconnect/server.rb +2 -2
- data/lib/rhoconnect/source.rb +2 -2
- data/lib/rhoconnect/store.rb +12 -6
- data/lib/rhoconnect/utilities.rb +2 -2
- data/lib/rhoconnect/version.rb +1 -1
- data/lib/rhoconnect.rb +6 -4
- data/rhoconnect.gemspec +5 -6
- data/spec/api/api_helper.rb +1 -1
- data/spec/api/app/fast_delete_spec.rb +22 -22
- data/spec/api/app/fast_insert_spec.rb +23 -23
- data/spec/api/app/fast_update_spec.rb +63 -63
- data/spec/api/app/push_deletes_spec.rb +11 -13
- data/spec/api/app/push_objects_spec.rb +39 -39
- data/spec/api/client/client_get_db_doc_spec.rb +29 -29
- data/spec/api/client/client_set_db_doc_spec.rb +11 -11
- data/spec/api/client/get_client_params_spec.rb +29 -29
- data/spec/api/client/list_client_docs_spec.rb +32 -34
- data/spec/api/client/reset_spec.rb +30 -30
- data/spec/api/readstate/set_refresh_time_spec.rb +43 -43
- data/spec/api/source/get_source_params_spec.rb +32 -34
- data/spec/api/source/list_sources_spec.rb +13 -13
- data/spec/api/source/update_source_params_spec.rb +19 -19
- data/spec/api/store/get_db_doc_spec.rb +27 -27
- data/spec/api/store/set_db_doc_spec.rb +38 -38
- data/spec/api/system/adapter_spec.rb +27 -29
- data/spec/api/system/get_license_info_spec.rb +11 -11
- data/spec/api/system/login_spec.rb +37 -37
- data/spec/api/system/reset_spec.rb +15 -15
- data/spec/api/system/stats_spec.rb +70 -71
- data/spec/api/user/create_user_spec.rb +37 -37
- data/spec/api/user/delete_client_spec.rb +7 -7
- data/spec/api/user/delete_user_spec.rb +62 -62
- data/spec/api/user/list_clients_spec.rb +24 -24
- data/spec/api/user/list_source_docs_spec.rb +29 -29
- data/spec/api/user/list_users_spec.rb +22 -22
- data/spec/api/user/ping_spec.rb +18 -18
- data/spec/api/user/show_user_spec.rb +10 -10
- data/spec/api/user/update_user_spec.rb +43 -43
- data/spec/api/user/user_get_db_doc_spec.rb +12 -12
- data/spec/api/user/user_set_db_doc_spec.rb +37 -37
- data/spec/api_token_spec.rb +8 -8
- data/spec/app_spec.rb +18 -17
- data/spec/apps/jstestapp/settings/settings.yml +2 -0
- data/spec/async_spec.rb +9 -11
- data/spec/bulk_data/bulk_data_spec.rb +120 -120
- data/spec/cli/cli_spec.rb +50 -53
- data/spec/client_spec.rb +105 -105
- data/spec/client_sync_spec.rb +529 -528
- data/spec/controllers/js_base_spec.rb +147 -141
- data/spec/doc/doc_spec.rb +51 -52
- data/spec/document_spec.rb +58 -58
- data/spec/dynamic_adapter_spec.rb +33 -36
- data/spec/generator/generator_spec.rb +76 -42
- data/spec/jobs/bulk_data_job_spec.rb +101 -102
- data/spec/jobs/ping_job_spec.rb +176 -177
- data/spec/jobs/source_job_spec.rb +24 -25
- data/spec/license_spec.rb +54 -55
- data/spec/models/js_base_spec.rb +121 -120
- data/spec/perf/bulk_data_perf_spec.rb +23 -24
- data/spec/perf/perf_spec_helper.rb +7 -7
- data/spec/perf/store_perf_spec.rb +139 -140
- data/spec/ping/apple_spec.rb +65 -65
- data/spec/ping/gcm_spec.rb +83 -84
- data/spec/ping/rhoconnect_push_spec.rb +52 -53
- data/spec/predefined_adapters/rho_internal_bench_adapter_controller_js_spec.rb +100 -101
- data/spec/predefined_adapters/rho_internal_js_bench_adapter_js_spec.rb +29 -31
- data/spec/read_state_spec.rb +24 -25
- data/spec/rhoconnect_spec.rb +7 -7
- data/spec/server/server_spec.rb +664 -662
- data/spec/server/stats_spec.rb +12 -12
- data/spec/source_adapter_spec.rb +124 -125
- data/spec/source_spec.rb +148 -149
- data/spec/source_sync_spec.rb +736 -736
- data/spec/spec_helper.rb +4 -5
- data/spec/stats/record_spec.rb +22 -21
- data/spec/store_orm_spec.rb +48 -48
- data/spec/store_spec.rb +428 -426
- data/spec/support/shared_examples.rb +5 -7
- data/spec/sync_states_spec.rb +67 -67
- data/spec/test_methods_spec.rb +121 -123
- data/spec/testdata/10000-data.txt +0 -0
- data/spec/testdata/5000-data.txt +0 -0
- data/spec/user_spec.rb +102 -102
- data/tasks/redis.rake +3 -3
- metadata +154 -195
- data/bench/benchapp/tmp/pids/passenger.9292.pid.lock +0 -0
- data/bench/benchapp/tmp/restart.txt +0 -0
- data/bench/blobapp/settings/license.key.bak +0 -2
- data/bench/blobapp/tmp/restart.txt +0 -0
- data/bench/lib/testdata/1-data.txt +0 -0
- data/bench/lib/testdata/10-data.txt +0 -0
- data/bench/lib/testdata/2-data.txt +0 -0
- data/bench/lib/testdata/250-data.txt +0 -0
- data/bench/lib/testdata/5-blob_data.txt +0 -0
- data/bench/lib/testdata/5-data.txt +0 -0
- data/bench/lib/testdata/50-data.txt +0 -0
- data/bench/lib/testdata/500-data.txt +0 -0
- data/doc/protocol.html +0 -1993
- data/spec/coverage/rcov/assets/0.2.3/jquery-1.3.2.min.js +0 -19
- data/spec/coverage/rcov/assets/0.2.3/jquery.tablesorter.min.js +0 -15
- data/spec/coverage/rcov/assets/0.2.3/print.css +0 -12
- data/spec/coverage/rcov/assets/0.2.3/rcov.js +0 -42
- data/spec/coverage/rcov/assets/0.2.3/screen.css +0 -270
- data/spec/coverage/rcov/index.html +0 -88
- data/spec/generator/generator_spec_helper.rb +0 -9
data/spec/source_sync_spec.rb
CHANGED
@@ -1,819 +1,819 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__),'spec_helper')
|
2
2
|
|
3
3
|
describe "SourceSync" do
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
include_examples "SharedRhoconnectHelper", :rhoconnect_data => true
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
@s = Source.load(@s_fields[:name],@s_params)
|
8
|
+
@model = Rhoconnect::Model::Base.create(@s)
|
9
|
+
end
|
10
|
+
|
11
|
+
let(:mock_schema) { {"property" => { "name" => "string", "brand" => "string" }, "version" => "1.0"} }
|
12
|
+
|
13
|
+
describe "SourceSync query methods" do
|
14
|
+
before (:each) do
|
15
|
+
rh = lambda { @model.query(params[:query])}
|
7
16
|
@model = Rhoconnect::Model::Base.create(@s)
|
17
|
+
@ssq = Rhoconnect::Handler::Query::Engine.new(@model, rh, {})
|
8
18
|
end
|
9
19
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
before (:each) do
|
14
|
-
rh = lambda { @model.query(params[:query])}
|
15
|
-
@model = Rhoconnect::Model::Base.create(@s)
|
16
|
-
@ssq = Rhoconnect::Handler::Query::Engine.new(@model, rh, {})
|
17
|
-
end
|
20
|
+
it "should create Rhoconnect::Handler::Query::Engine" do
|
21
|
+
@ssq.model.is_a?(SampleAdapter).should == true
|
22
|
+
end
|
18
23
|
|
19
|
-
|
20
|
-
|
21
|
-
|
24
|
+
it "should fail to create Rhoconnect::Handler::Query::Engine with InvalidArgumentError without source" do
|
25
|
+
lambda { Rhoconnect::Handler::Query::Engine.new(nil, nil, {}) }.should raise_error(ArgumentError, 'Unknown source')
|
26
|
+
end
|
22
27
|
|
23
|
-
|
24
|
-
|
25
|
-
|
28
|
+
it "should fail to create Rhoconnect::Handler::Query::Engine with InvalidArgumentError without proc handler" do
|
29
|
+
lambda { Rhoconnect::Handler::Query::Engine.new(@model, nil) }.should raise_error(ArgumentError, 'Invalid sync handler')
|
30
|
+
end
|
26
31
|
|
27
|
-
|
28
|
-
|
29
|
-
|
32
|
+
it "should raise LoginException if login fails" do
|
33
|
+
msg = "Error logging in"
|
34
|
+
@u.login = nil
|
35
|
+
@ssq.should_receive(:log).with("Model raised login exception: #{msg}")
|
36
|
+
@ssq.should_receive(:log).with(anything)
|
37
|
+
@ssq.do_sync
|
38
|
+
verify_doc_result(@s, :errors => {'login-error'=>{'message'=>msg}})
|
39
|
+
end
|
30
40
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
41
|
+
it "should raise LogoffException if logoff fails" do
|
42
|
+
msg = "Error logging off"
|
43
|
+
@ssq.should_receive(:log).with("Model raised logoff exception: #{msg}")
|
44
|
+
@ssq.should_receive(:log).with(anything)
|
45
|
+
set_test_data('test_db_storage',{},msg,'logoff error')
|
46
|
+
@ssq.do_sync
|
47
|
+
verify_doc_result(@s, :errors => {'logoff-error'=>{'message'=>msg}})
|
48
|
+
end
|
39
49
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
50
|
+
it "should hold on read on subsequent call of process if default poll interval is not exprired" do
|
51
|
+
expected = {'1'=>@product1}
|
52
|
+
set_state('test_db_storage' => expected)
|
53
|
+
@ssq.do_sync
|
54
|
+
set_state('test_db_storage' => {'2'=>@product2})
|
55
|
+
@ssq.do_sync
|
56
|
+
verify_doc_result(@s, :md => expected)
|
57
|
+
end
|
48
58
|
|
49
|
-
|
50
|
-
|
59
|
+
it "should read on every subsequent call of process if poll interval is set to 0" do
|
60
|
+
expected = {'2'=>@product2}
|
61
|
+
@s.poll_interval = 0
|
62
|
+
set_state('test_db_storage' => {'1'=>@product1})
|
63
|
+
@ssq.do_sync
|
64
|
+
set_state('test_db_storage' => expected)
|
65
|
+
@ssq.do_sync
|
66
|
+
verify_doc_result(@s, :md => expected)
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should never call read on any call of process if poll interval is set to -1" do
|
70
|
+
@s.poll_interval = -1
|
71
|
+
set_state('test_db_storage' => {'1'=>@product1})
|
72
|
+
@ssq.do_sync
|
73
|
+
verify_doc_result(@s, :md => {})
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
it "should process model metadata" do
|
78
|
+
mock_metadata_method([SampleAdapter, SimpleAdapter]) do
|
79
|
+
expected = {'1'=>@product1,'2'=>@product2}
|
51
80
|
set_state('test_db_storage' => expected)
|
52
81
|
@ssq.do_sync
|
53
|
-
|
54
|
-
|
55
|
-
|
82
|
+
verify_doc_result(@s, {:md => expected,
|
83
|
+
:metadata => "{\"foo\":\"bar\"}",
|
84
|
+
:metadata_sha1 => "a5e744d0164540d33b1d7ea616c28f2fa97e754a"})
|
56
85
|
end
|
86
|
+
end
|
57
87
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
set_state('test_db_storage' => {'1'=>@product1})
|
62
|
-
@ssq.do_sync
|
88
|
+
it "should process model schema" do
|
89
|
+
mock_schema_method([SampleAdapter]) do
|
90
|
+
expected = {'1'=>@product1,'2'=>@product2}
|
63
91
|
set_state('test_db_storage' => expected)
|
64
92
|
@ssq.do_sync
|
65
|
-
verify_doc_result(@s, :md => expected)
|
93
|
+
verify_doc_result(@s, :md => expected)
|
94
|
+
JSON.parse(@s.get_value(:schema)).should == mock_schema
|
95
|
+
@s.get_value(:schema_sha1).should == get_sha1(mock_schema['version'])
|
66
96
|
end
|
97
|
+
end
|
67
98
|
|
68
|
-
|
69
|
-
|
70
|
-
|
99
|
+
it "should raise exception if model schema has no version key/value pair" do
|
100
|
+
mock_schema_no_version_method([SampleAdapter]) do
|
101
|
+
expected = {'1'=>@product1,'2'=>@product2}
|
102
|
+
set_state('test_db_storage' => expected)
|
71
103
|
@ssq.do_sync
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
it "should process model metadata" do
|
77
|
-
mock_metadata_method([SampleAdapter, SimpleAdapter]) do
|
78
|
-
expected = {'1'=>@product1,'2'=>@product2}
|
79
|
-
set_state('test_db_storage' => expected)
|
80
|
-
@ssq.do_sync
|
81
|
-
verify_doc_result(@s, {:md => expected,
|
82
|
-
:metadata => "{\"foo\":\"bar\"}",
|
83
|
-
:metadata_sha1 => "a5e744d0164540d33b1d7ea616c28f2fa97e754a"})
|
84
|
-
end
|
104
|
+
errors = {}
|
105
|
+
@s.lock(:errors) { errors = @s.get_data(:errors) }
|
106
|
+
errors.empty?().should == false
|
107
|
+
errors["query-error"]["message"].should == "Mandatory version key is not defined in model schema method"
|
85
108
|
end
|
109
|
+
end
|
86
110
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
end
|
111
|
+
it "should process model with stash" do
|
112
|
+
expected = {'1'=>@product1,'2'=>@product2}
|
113
|
+
set_state('test_db_storage' => expected)
|
114
|
+
@ssq.params[:query] = {'stash_result' => true}
|
115
|
+
@ssq.do_sync
|
116
|
+
#@ssq.model.should_receive(:stash_result).once
|
117
|
+
verify_doc_result(@s, {:md => expected,
|
118
|
+
:md_size => expected.size.to_s})
|
119
|
+
end
|
97
120
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
end
|
108
|
-
end
|
121
|
+
it "should process model with pass_through set" do
|
122
|
+
expected = {'1'=>@product1,'2'=>@product2}
|
123
|
+
set_state('test_db_storage' => expected)
|
124
|
+
@s.pass_through = 'true'
|
125
|
+
@ssq.do_sync.should == expected
|
126
|
+
verify_doc_result(@s, {:md => {},
|
127
|
+
:md_size => nil})
|
128
|
+
@s.pass_through = nil
|
129
|
+
end
|
109
130
|
|
110
|
-
|
131
|
+
it "should call methods in model" do
|
132
|
+
mock_metadata_method([SampleAdapter, SimpleAdapter]) do
|
111
133
|
expected = {'1'=>@product1,'2'=>@product2}
|
112
|
-
|
113
|
-
@ssq.
|
134
|
+
metadata = "{\"foo\":\"bar\"}"
|
135
|
+
@ssq.model.should_receive(:login).once.with(no_args()).and_return(true)
|
136
|
+
@ssq.model.should_receive(:metadata).once.with(no_args()).and_return(metadata)
|
137
|
+
@ssq.model.should_receive(:query).once.with(nil).and_return(expected)
|
138
|
+
@ssq.model.should_receive(:sync).once.with(no_args()).and_return(true)
|
139
|
+
@ssq.model.should_receive(:logoff).once.with(no_args()).and_return(nil)
|
114
140
|
@ssq.do_sync
|
115
|
-
#@ssq.model.should_receive(:stash_result).once
|
116
|
-
verify_doc_result(@s, {:md => expected,
|
117
|
-
:md_size => expected.size.to_s})
|
118
|
-
end
|
119
|
-
|
120
|
-
it "should process model with pass_through set" do
|
121
|
-
expected = {'1'=>@product1,'2'=>@product2}
|
122
|
-
set_state('test_db_storage' => expected)
|
123
|
-
@s.pass_through = 'true'
|
124
|
-
@ssq.do_sync.should == expected
|
125
|
-
verify_doc_result(@s, {:md => {},
|
126
|
-
:md_size => nil})
|
127
|
-
@s.pass_through = nil
|
128
141
|
end
|
142
|
+
end
|
129
143
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
metadata = "{\"foo\":\"bar\"}"
|
134
|
-
@ssq.model.should_receive(:login).once.with(no_args()).and_return(true)
|
135
|
-
@ssq.model.should_receive(:metadata).once.with(no_args()).and_return(metadata)
|
136
|
-
@ssq.model.should_receive(:query).once.with(nil).and_return(expected)
|
137
|
-
@ssq.model.should_receive(:sync).once.with(no_args()).and_return(true)
|
138
|
-
@ssq.model.should_receive(:logoff).once.with(no_args()).and_return(nil)
|
139
|
-
@ssq.do_sync
|
140
|
-
end
|
141
|
-
end
|
144
|
+
it "should do query with no exception" do
|
145
|
+
verify_read_operation('query')
|
146
|
+
end
|
142
147
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
it "should do query with no exception pass through" do
|
148
|
-
verify_read_operation_pass_through('query')
|
149
|
-
end
|
148
|
+
it "should do query with no exception pass through" do
|
149
|
+
verify_read_operation_pass_through('query')
|
150
|
+
end
|
150
151
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
it "should do query with exception raised and update refresh time only after retries limit is exceeded" do
|
156
|
-
@s.retry_limit = 1
|
157
|
-
msg = "Error during query"
|
158
|
-
set_test_data('test_db_storage',{},msg,"query error")
|
159
|
-
res = @ssq.do_sync
|
160
|
-
verify_doc_result(@s, {:md => {},
|
161
|
-
:errors => {'query-error'=>{'message'=>msg}}})
|
162
|
-
# 1) if retry_limit is set to N - then, first N retries should not update refresh_time
|
163
|
-
@s.read_state.retry_counter.should == 1
|
164
|
-
@s.read_state.refresh_time.should <= Time.now.to_i
|
165
|
-
|
166
|
-
# try once more and fail again
|
167
|
-
set_test_data('test_db_storage',{},msg,"query error")
|
168
|
-
res = @ssq.do_sync
|
169
|
-
verify_doc_result(@s, {:md => {},
|
170
|
-
:errors => {'query-error'=>{'message'=>msg}}})
|
152
|
+
it "should do query with exception raised" do
|
153
|
+
verify_read_operation_with_error('query')
|
154
|
+
end
|
171
155
|
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
156
|
+
it "should do query with exception raised and update refresh time only after retries limit is exceeded" do
|
157
|
+
@s.retry_limit = 1
|
158
|
+
msg = "Error during query"
|
159
|
+
set_test_data('test_db_storage',{},msg,"query error")
|
160
|
+
res = @ssq.do_sync
|
161
|
+
verify_doc_result(@s, {:md => {},
|
162
|
+
:errors => {'query-error'=>{'message'=>msg}}})
|
163
|
+
# 1) if retry_limit is set to N - then, first N retries should not update refresh_time
|
164
|
+
@s.read_state.retry_counter.should == 1
|
165
|
+
@s.read_state.refresh_time.should <= Time.now.to_i
|
166
|
+
|
167
|
+
# try once more and fail again
|
168
|
+
set_test_data('test_db_storage',{},msg,"query error")
|
169
|
+
res = @ssq.do_sync
|
170
|
+
verify_doc_result(@s, {:md => {},
|
171
|
+
:errors => {'query-error'=>{'message'=>msg}}})
|
172
|
+
|
173
|
+
# 2) if retry_limit is set to N and number of retries exceeded it - update refresh_time
|
174
|
+
@s.read_state.retry_counter.should == 0
|
175
|
+
@s.read_state.refresh_time.should > Time.now.to_i
|
176
|
+
end
|
176
177
|
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
178
|
+
it "should do query with exception raised and restore state with succesfull retry" do
|
179
|
+
@s.retry_limit = 1
|
180
|
+
msg = "Error during query"
|
181
|
+
set_test_data('test_db_storage',{},msg,"query error")
|
182
|
+
res = @ssq.do_sync
|
183
|
+
verify_doc_result(@s, {:md => {},
|
184
|
+
:errors => {'query-error'=>{'message'=>msg}}})
|
185
|
+
# 1) if retry_limit is set to N - then, first N retries should not update refresh_time
|
186
|
+
@s.read_state.retry_counter.should == 1
|
187
|
+
@s.read_state.refresh_time.should <= Time.now.to_i
|
188
|
+
|
189
|
+
# try once more (with success)
|
190
|
+
expected = {'1'=>@product1,'2'=>@product2}
|
191
|
+
set_test_data('test_db_storage',expected)
|
192
|
+
@ssq.do_sync
|
193
|
+
verify_doc_result(@s, {:md => expected,
|
194
|
+
:errors => {}})
|
195
|
+
@s.read_state.retry_counter.should == 0
|
196
|
+
@s.read_state.refresh_time.should > Time.now.to_i
|
197
|
+
end
|
197
198
|
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
199
|
+
it "should reset the retry counter if prev_refresh_time was set more than poll_interval secs ago" do
|
200
|
+
@s.retry_limit = 3
|
201
|
+
@s.poll_interval = 2
|
202
|
+
msg = "Error during query"
|
203
|
+
set_test_data('test_db_storage',{},msg,"query error")
|
204
|
+
res = @ssq.do_sync
|
205
|
+
verify_doc_result(@s, {:md => {},
|
206
|
+
:errors => {'query-error'=>{'message'=>msg}}})
|
207
|
+
# 1) if retry_limit is set to N - then, first N retries should not update refresh_time
|
208
|
+
@s.read_state.retry_counter.should == 1
|
209
|
+
@s.read_state.refresh_time.should <= Time.now.to_i
|
210
|
+
|
211
|
+
# 2) Make another error - results are the same
|
212
|
+
set_test_data('test_db_storage',{},msg,"query error")
|
213
|
+
res = @ssq.do_sync
|
214
|
+
verify_doc_result(@s, {:md => {},
|
215
|
+
:errors => {'query-error'=>{'message'=>msg}}})
|
216
|
+
@s.read_state.retry_counter.should == 2
|
217
|
+
@s.read_state.refresh_time.should <= Time.now.to_i
|
218
|
+
|
219
|
+
# wait until time interval exprires and prev_refresh_time is too old -
|
220
|
+
# this should reset the counter on next request with error
|
221
|
+
# and do not update refresh_time
|
222
|
+
sleep(3)
|
223
|
+
set_test_data('test_db_storage',{},msg,"query error")
|
224
|
+
res = @ssq.do_sync
|
225
|
+
verify_doc_result(@s, {:md => {},
|
205
226
|
:errors => {'query-error'=>{'message'=>msg}}})
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
# 2) Make another error - results are the same
|
211
|
-
set_test_data('test_db_storage',{},msg,"query error")
|
212
|
-
res = @ssq.do_sync
|
213
|
-
verify_doc_result(@s, {:md => {},
|
214
|
-
:errors => {'query-error'=>{'message'=>msg}}})
|
215
|
-
@s.read_state.retry_counter.should == 2
|
216
|
-
@s.read_state.refresh_time.should <= Time.now.to_i
|
217
|
-
|
218
|
-
# wait until time interval exprires and prev_refresh_time is too old -
|
219
|
-
# this should reset the counter on next request with error
|
220
|
-
# and do not update refresh_time
|
221
|
-
sleep(3)
|
222
|
-
set_test_data('test_db_storage',{},msg,"query error")
|
223
|
-
res = @ssq.do_sync
|
224
|
-
verify_doc_result(@s, {:md => {},
|
225
|
-
:errors => {'query-error'=>{'message'=>msg}}})
|
226
|
-
@s.read_state.retry_counter.should == 1
|
227
|
-
@s.read_state.refresh_time.should <= Time.now.to_i
|
228
|
-
end
|
229
|
-
|
230
|
-
it "should do query with exception raised and update refresh time if retry_limit is 0" do
|
231
|
-
@s.retry_limit = 0
|
232
|
-
msg = "Error during query"
|
233
|
-
set_test_data('test_db_storage',{},msg,"query error")
|
234
|
-
res = @ssq.do_sync
|
235
|
-
verify_doc_result(@s, {:md => {},
|
236
|
-
:errors => {'query-error'=>{'message'=>msg}}})
|
237
|
-
# if poll_interval is set to 0 - refresh time should be updated
|
238
|
-
@s.read_state.retry_counter.should == 0
|
239
|
-
@s.read_state.refresh_time.should > Time.now.to_i
|
240
|
-
end
|
227
|
+
@s.read_state.retry_counter.should == 1
|
228
|
+
@s.read_state.refresh_time.should <= Time.now.to_i
|
229
|
+
end
|
241
230
|
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
:errors => {'query-error'=>{'message'=>msg}}})
|
253
|
-
# if poll_interval is set to 0 - refresh time should be updated
|
254
|
-
@s.read_state.retry_counter.should == 0
|
255
|
-
@s.read_state.refresh_time.should > prev_refresh_time
|
256
|
-
end
|
231
|
+
it "should do query with exception raised and update refresh time if retry_limit is 0" do
|
232
|
+
@s.retry_limit = 0
|
233
|
+
msg = "Error during query"
|
234
|
+
set_test_data('test_db_storage',{},msg,"query error")
|
235
|
+
res = @ssq.do_sync
|
236
|
+
verify_doc_result(@s, {:md => {},
|
237
|
+
:errors => {'query-error'=>{'message'=>msg}}})
|
238
|
+
# if poll_interval is set to 0 - refresh time should be updated
|
239
|
+
@s.read_state.retry_counter.should == 0
|
240
|
+
@s.read_state.refresh_time.should > Time.now.to_i
|
257
241
|
end
|
258
|
-
|
259
|
-
describe "push_notify" do
|
260
|
-
it "should do push_notify for source after push_objects if enabled" do
|
261
|
-
@s.push_notify = 'true'
|
262
|
-
data = {'1' => @product1, '2' => @product2, '3' => @product3}
|
263
|
-
ping_params = {'user_id'=>["testuser"], 'sources'=>["SampleAdapter"]}
|
264
|
-
PingJob.should_receive(:perform).once.with(ping_params)
|
265
|
-
po_handler = lambda { @model.push_objects(params) }
|
266
|
-
push_objects_handler = Rhoconnect::Handler::PluginCallbacks::Runner.new(po_handler, @model, {'objects' => data})
|
267
|
-
push_objects_handler.run
|
268
|
-
end
|
269
|
-
|
270
|
-
it "should do push_notify for source after push_deletes if enabled" do
|
271
|
-
@s.push_notify = 'true'
|
272
|
-
u2 = User.create(:login => 'user2')
|
273
|
-
@a.users << u2.id
|
274
|
-
data = {'1' => @product1, '2' => @product2, '3' => @product3}
|
275
|
-
ping_params = {'user_id'=>["testuser"], 'sources'=>["SampleAdapter"]}
|
276
|
-
PingJob.should_receive(:perform).once.with(ping_params)
|
277
|
-
pd_handler = lambda { @model.push_deletes(params) }
|
278
|
-
push_deletes_handler = Rhoconnect::Handler::PluginCallbacks::Runner.new(pd_handler, @model, {'objects' => data})
|
279
|
-
push_deletes_handler.run
|
280
|
-
end
|
281
|
-
|
282
|
-
it "should not do push_notify for user source if not enabled (default)" do
|
283
|
-
u2 = User.create(:login => 'user2')
|
284
|
-
@a.users << u2.id
|
285
|
-
data = {'1' => @product1}
|
286
|
-
PingJob.should_receive(:perform).never
|
287
|
-
po_handler = lambda { @model.push_objects(params) }
|
288
|
-
push_objects_handler = Rhoconnect::Handler::PluginCallbacks::Runner.new(po_handler, @model, {'objects' => data})
|
289
|
-
push_objects_handler.run
|
290
|
-
end
|
291
242
|
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
243
|
+
it "should do query with exception raised and update refresh time if poll_interval == 0" do
|
244
|
+
@s.retry_limit = 1
|
245
|
+
@s.poll_interval = 0
|
246
|
+
msg = "Error during query"
|
247
|
+
set_test_data('test_db_storage',{},msg,"query error")
|
248
|
+
prev_refresh_time = @s.read_state.refresh_time
|
249
|
+
# make sure refresh time is expired
|
250
|
+
sleep(1)
|
251
|
+
res = @ssq.do_sync
|
252
|
+
verify_doc_result(@s, {:md => {},
|
253
|
+
:errors => {'query-error'=>{'message'=>msg}}})
|
254
|
+
# if poll_interval is set to 0 - refresh time should be updated
|
255
|
+
@s.read_state.retry_counter.should == 0
|
256
|
+
@s.read_state.refresh_time.should > prev_refresh_time
|
302
257
|
end
|
258
|
+
end
|
303
259
|
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
:device_pin => 'efgh',
|
315
|
-
:device_port => '4444',
|
316
|
-
:user_id => @u2.id,
|
317
|
-
:app_id => @a.id
|
318
|
-
}
|
319
|
-
@c2 = Client.create(@c2_fields,{:source_name => @s_fields[:name]})
|
320
|
-
@a.users << @u2.id
|
321
|
-
end
|
260
|
+
describe "push_notify" do
|
261
|
+
it "should do push_notify for source after push_objects if enabled" do
|
262
|
+
@s.push_notify = 'true'
|
263
|
+
data = {'1' => @product1, '2' => @product2, '3' => @product3}
|
264
|
+
ping_params = {'user_id'=>["testuser"], 'sources'=>["SampleAdapter"]}
|
265
|
+
PingJob.should_receive(:perform).once.with(ping_params)
|
266
|
+
po_handler = lambda { @model.push_objects(params) }
|
267
|
+
push_objects_handler = Rhoconnect::Handler::PluginCallbacks::Runner.new(po_handler, @model, {'objects' => data})
|
268
|
+
push_objects_handler.run
|
269
|
+
end
|
322
270
|
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
271
|
+
it "should do push_notify for source after push_deletes if enabled" do
|
272
|
+
@s.push_notify = 'true'
|
273
|
+
u2 = User.create(:login => 'user2')
|
274
|
+
@a.users << u2.id
|
275
|
+
data = {'1' => @product1, '2' => @product2, '3' => @product3}
|
276
|
+
ping_params = {'user_id'=>["testuser"], 'sources'=>["SampleAdapter"]}
|
277
|
+
PingJob.should_receive(:perform).once.with(ping_params)
|
278
|
+
pd_handler = lambda { @model.push_deletes(params) }
|
279
|
+
push_deletes_handler = Rhoconnect::Handler::PluginCallbacks::Runner.new(pd_handler, @model, {'objects' => data})
|
280
|
+
push_deletes_handler.run
|
281
|
+
end
|
330
282
|
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
283
|
+
it "should not do push_notify for user source if not enabled (default)" do
|
284
|
+
u2 = User.create(:login => 'user2')
|
285
|
+
@a.users << u2.id
|
286
|
+
data = {'1' => @product1}
|
287
|
+
PingJob.should_receive(:perform).never
|
288
|
+
po_handler = lambda { @model.push_objects(params) }
|
289
|
+
push_objects_handler = Rhoconnect::Handler::PluginCallbacks::Runner.new(po_handler, @model, {'objects' => data})
|
290
|
+
push_objects_handler.run
|
291
|
+
end
|
339
292
|
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
end
|
293
|
+
it "should not do push_notify for app source" do
|
294
|
+
@s.partition = :app
|
295
|
+
u2 = User.create(:login => 'user2')
|
296
|
+
@a.users << u2.id
|
297
|
+
data = {'1' => @product1}
|
298
|
+
PingJob.should_receive(:perform).never
|
299
|
+
po_handler = lambda { @model.push_objects(params) }
|
300
|
+
push_objects_handler = Rhoconnect::Handler::PluginCallbacks::Runner.new(po_handler, @model, {'objects' => data})
|
301
|
+
push_objects_handler.run
|
302
|
+
end
|
303
|
+
end
|
352
304
|
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
:create_links => {'temp_id2'=>{'l'=>'backend_id'}}})
|
371
|
-
end
|
305
|
+
describe "create" do
|
306
|
+
before (:each) do
|
307
|
+
rh = lambda { @model.create(params[:create_object])}
|
308
|
+
@ssc = Rhoconnect::Handler::Changes::Engine.new(['create'], @model, rh, {})
|
309
|
+
@queue_name = "create"
|
310
|
+
@u2_fields = {:login => 'anotheruser'}
|
311
|
+
@u2 = User.create(@u2_fields)
|
312
|
+
@u2.password = 'testpass'
|
313
|
+
@c2_fields = {
|
314
|
+
:device_type => 'Android',
|
315
|
+
:device_pin => 'efgh',
|
316
|
+
:device_port => '4444',
|
317
|
+
:user_id => @u2.id,
|
318
|
+
:app_id => @a.id
|
319
|
+
}
|
320
|
+
@c2 = Client.create(@c2_fields,{:source_name => @s_fields[:name]})
|
321
|
+
@a.users << @u2.id
|
372
322
|
end
|
373
323
|
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
324
|
+
it "should do create where adapter.create returns nil" do
|
325
|
+
set_source_queue_state(@s, {@queue_name => [[@s.name, [['2', @product2]]]]}, @c.id, true)
|
326
|
+
@ssc.create
|
327
|
+
verify_source_queue_data(@s, @queue_name => [])
|
328
|
+
verify_doc_result(@c, {:create_errors => {},
|
329
|
+
:create_links => {}})
|
330
|
+
end
|
380
331
|
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
332
|
+
it "should do create where adapter.create returns object link" do
|
333
|
+
@product4['link'] = 'test link'
|
334
|
+
set_source_queue_state(@s, {@queue_name => [[@s.name, [['4', @product4]]]]},@c.id,true)
|
335
|
+
@ssc.create
|
336
|
+
verify_source_queue_data(@s, @queue_name => [])
|
337
|
+
verify_doc_result(@c, {:create_errors => {},
|
338
|
+
:create_links => {'4'=>{'l'=>'backend_id'}}})
|
339
|
+
end
|
387
340
|
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
)
|
400
|
-
@ssu.update
|
401
|
-
update_data,client_ids = @s.get_queue(@queue_name)
|
402
|
-
update_data.should == [[[@s.name, [['4', { 'price' => '199.99'}]]]]]
|
403
|
-
client_ids.should == [@c.id]
|
404
|
-
verify_doc_result(@c, {:update_errors =>
|
405
|
-
{"#{ERROR}-error"=>{"message"=>msg}, ERROR=>data[ERROR]},
|
406
|
-
:update_rollback => {ERROR=>{"price"=>"99.99"}}})
|
407
|
-
end
|
341
|
+
it "should raise exception on adapter.create" do
|
342
|
+
msg = "Error creating record"
|
343
|
+
data = add_error_object({'4'=>@product4,'2'=>@product2},msg)
|
344
|
+
source_queue_data = []
|
345
|
+
data.each do |key, value|
|
346
|
+
source_queue_data << [key, value]
|
347
|
+
end
|
348
|
+
set_source_queue_state(@s, {@queue_name => [[@s.name, source_queue_data]]},@c.id, true)
|
349
|
+
@ssc.create
|
350
|
+
verify_doc_result(@c, :create_errors =>
|
351
|
+
{"#{ERROR}-error"=>{"message"=>msg},ERROR=>data[ERROR]})
|
408
352
|
end
|
409
353
|
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
354
|
+
it "should properly process creates for 2 users using same queue" do
|
355
|
+
@product3['link'] = 'test link'
|
356
|
+
@product4['link'] = 'test link'
|
357
|
+
set_source_queue_state(@s, {@queue_name => [[@s.name, [['temp_id1', @product3]]]]},@c.id,true)
|
358
|
+
set_source_queue_state(@s, {@queue_name => [[@s.name, [['temp_id2', @product4]]]]},@c2.id,true)
|
359
|
+
@s.queue_docname(:create).should == "source:application:#{@s.name}:create"
|
360
|
+
@ssc.create
|
361
|
+
verify_source_queue_data(@s, @queue_name => [])
|
362
|
+
creates_source1 = Source.load(@s.name,
|
363
|
+
{:user_id => @u.id,:app_id => @a.id})
|
364
|
+
creates_source2 = Source.load(@s.name,
|
365
|
+
{:user_id => @u2.id,:app_id => @a.id})
|
366
|
+
verify_doc_result(creates_source1, {:md => {'backend_id' => @product3}})
|
367
|
+
verify_doc_result(creates_source2, {:md => {'backend_id' => @product4}})
|
368
|
+
verify_doc_result(@c, {:create_errors => {},
|
369
|
+
:create_links => {'temp_id1'=>{'l'=>'backend_id'}}})
|
370
|
+
verify_doc_result(@c2, {:create_errors => {},
|
371
|
+
:create_links => {'temp_id2'=>{'l'=>'backend_id'}}})
|
372
|
+
end
|
373
|
+
end
|
428
374
|
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
verify_doc_result(@c, :delete_errors => {})
|
436
|
-
verify_doc_result(@s, :md => {'3'=>@product3})
|
437
|
-
verify_doc_result(@c, :cd => {'3'=>@product3})
|
438
|
-
end
|
375
|
+
describe "update" do
|
376
|
+
before (:each) do
|
377
|
+
rh = lambda { @model.update(params[:update_object])}
|
378
|
+
@ssu = Rhoconnect::Handler::Changes::Engine.new(['update'], @model, rh, {})
|
379
|
+
@queue_name = "update"
|
380
|
+
end
|
439
381
|
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
end
|
447
|
-
# this one will be after the error and should remain in the queue
|
448
|
-
source_queue_data << ['2', @product2]
|
449
|
-
set_source_queue_state(@s, {@queue_name => [[@s.name, source_queue_data]]}, @c.id, true)
|
450
|
-
@ssd.delete
|
451
|
-
verify_doc_result(@c, :delete_errors => {"#{ERROR}-error"=>{"message"=>msg}, ERROR=>data[ERROR]})
|
452
|
-
verify_source_queue_data(@s, @queue_name => [[[@s.name, [['2', @product2]]]]])
|
453
|
-
end
|
382
|
+
it "should do update with no errors" do
|
383
|
+
set_source_queue_state(@s, {@queue_name => [[@s.name, [['4', { 'price' => '199.99' }]]]]},@c.id,true)
|
384
|
+
@ssu.update
|
385
|
+
verify_source_queue_data(@s, @queue_name => [])
|
386
|
+
verify_doc_result(@c, :update_errors => {})
|
387
|
+
end
|
454
388
|
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
verify_doc_result(@c, :cd => {'2'=>@product2,'3'=>@product3})
|
475
|
-
verify_doc_result(@c2, :cd => {'4'=>@product4, '1'=>@product1})
|
476
|
-
end
|
389
|
+
it "should do update with errors" do
|
390
|
+
msg = "Error updating record"
|
391
|
+
data = add_error_object({},msg)
|
392
|
+
source_queue_data = []
|
393
|
+
data.each do |key, value|
|
394
|
+
source_queue_data << [key, value]
|
395
|
+
end
|
396
|
+
# this one will be after the error record - and should remain in the queue
|
397
|
+
source_queue_data << ['4', { 'price' => '199.99' }]
|
398
|
+
set_source_queue_state(@s, {@queue_name => [[@s.name, source_queue_data]]},@c.id,true)
|
399
|
+
set_doc_state(@c, :cd => { ERROR => { 'price' => '99.99' } }
|
400
|
+
)
|
401
|
+
@ssu.update
|
402
|
+
update_data,client_ids = @s.get_queue(@queue_name)
|
403
|
+
update_data.should == [[[@s.name, [['4', { 'price' => '199.99'}]]]]]
|
404
|
+
client_ids.should == [@c.id]
|
405
|
+
verify_doc_result(@c, {:update_errors =>
|
406
|
+
{"#{ERROR}-error"=>{"message"=>msg}, ERROR=>data[ERROR]},
|
407
|
+
:update_rollback => {ERROR=>{"price"=>"99.99"}}})
|
477
408
|
end
|
409
|
+
end
|
478
410
|
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
411
|
+
describe "delete" do
|
412
|
+
before (:each) do
|
413
|
+
rh = lambda { @model.update(params[:delete_object])}
|
414
|
+
@ssd = Rhoconnect::Handler::Changes::Engine.new(['delete'], @model, rh, {})
|
415
|
+
@queue_name = "delete"
|
416
|
+
@u2_fields = {:login => 'anotheruser'}
|
417
|
+
@u2 = User.create(@u2_fields)
|
418
|
+
@u2.password = 'testpass'
|
419
|
+
@c2_fields = {
|
420
|
+
:device_type => 'Android',
|
421
|
+
:device_pin => 'efgh',
|
422
|
+
:device_port => '4444',
|
423
|
+
:user_id => @u2.id,
|
424
|
+
:app_id => @a.id
|
425
|
+
}
|
426
|
+
@c2 = Client.create(@c2_fields,{:source_name => @s_fields[:name]})
|
427
|
+
@a.users << @u2.id
|
428
|
+
end
|
484
429
|
|
485
|
-
|
486
|
-
|
487
|
-
|
430
|
+
it "should do delete with no errors" do
|
431
|
+
set_source_queue_state(@s, {@queue_name => [[@s.name, [['4', @product4]]]]}, @c.id, true)
|
432
|
+
set_doc_state(@s, :md => {'4'=>@product4,'3'=>@product3})
|
433
|
+
set_doc_state(@c, :cd => {'4'=>@product4,'3'=>@product3})
|
434
|
+
@ssd.delete
|
435
|
+
verify_source_queue_data(@s, @queue_name => [])
|
436
|
+
verify_doc_result(@c, :delete_errors => {})
|
437
|
+
verify_doc_result(@s, :md => {'3'=>@product3})
|
438
|
+
verify_doc_result(@c, :cd => {'3'=>@product3})
|
439
|
+
end
|
488
440
|
|
489
|
-
|
490
|
-
|
491
|
-
|
441
|
+
it "should do delete with errors" do
|
442
|
+
msg = "Error delete record"
|
443
|
+
data = add_error_object({},msg)
|
444
|
+
source_queue_data = []
|
445
|
+
data.each do |key, value|
|
446
|
+
source_queue_data << [key, value]
|
447
|
+
end
|
448
|
+
# this one will be after the error and should remain in the queue
|
449
|
+
source_queue_data << ['2', @product2]
|
450
|
+
set_source_queue_state(@s, {@queue_name => [[@s.name, source_queue_data]]}, @c.id, true)
|
451
|
+
@ssd.delete
|
452
|
+
verify_doc_result(@c, :delete_errors => {"#{ERROR}-error"=>{"message"=>msg}, ERROR=>data[ERROR]})
|
453
|
+
verify_source_queue_data(@s, @queue_name => [[[@s.name, [['2', @product2]]]]])
|
454
|
+
end
|
492
455
|
|
493
|
-
|
494
|
-
|
495
|
-
|
456
|
+
it "should properly process deletes for 2 users using same queue" do
|
457
|
+
deletes_source1 = Source.load(@s.name,
|
458
|
+
{:user_id => @u.id,:app_id => @a.id})
|
459
|
+
deletes_source2 = Source.load(@s.name,
|
460
|
+
{:user_id => @u2.id,:app_id => @a.id})
|
461
|
+
set_source_queue_state(deletes_source1, {@queue_name => [[@s.name, [['4', @product4]]]]},@c.id,true)
|
462
|
+
set_source_queue_state(deletes_source2, {@queue_name => [[@s.name, [['3', @product3]]]]},@c2.id,true)
|
463
|
+
deletes_source1.queue_docname(:delete).should == "source:application:#{@s.name}:delete"
|
464
|
+
deletes_source1.queue_docname(:delete).should == deletes_source2.queue_docname(:delete)
|
465
|
+
set_doc_state(deletes_source1, :md => {'4'=>@product4,'2'=>@product2,'3'=>@product3})
|
466
|
+
set_doc_state(deletes_source2, :md => {'4'=>@product4,'3'=>@product3, '1'=>@product1})
|
467
|
+
set_doc_state(@c, :cd => {'4'=>@product4,'2'=>@product2,'3'=>@product3})
|
468
|
+
set_doc_state(@c2, :cd => {'4'=>@product4,'3'=>@product3, '1'=>@product1})
|
469
|
+
@ssd.delete
|
470
|
+
verify_source_queue_data(@s, @queue_name => [])
|
471
|
+
verify_doc_result(@c, :delete_errors => {})
|
472
|
+
verify_doc_result(@c2, :delete_errors => {})
|
473
|
+
verify_doc_result(deletes_source1, :md => {'2'=>@product2,'3'=>@product3})
|
474
|
+
verify_doc_result(deletes_source2, :md => {'4'=>@product4, '1'=>@product1})
|
475
|
+
verify_doc_result(@c, :cd => {'2'=>@product2,'3'=>@product3})
|
476
|
+
verify_doc_result(@c2, :cd => {'4'=>@product4, '1'=>@product1})
|
477
|
+
end
|
478
|
+
end
|
496
479
|
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
@u2 = User.create(@u2_fields)
|
502
|
-
@u2.password = 'testpass'
|
503
|
-
@c2_fields = {
|
504
|
-
:device_type => 'Android',
|
505
|
-
:device_pin => 'efgh',
|
506
|
-
:device_port => '4444',
|
507
|
-
:user_id => @u2.id,
|
508
|
-
:app_id => @a.id
|
509
|
-
}
|
510
|
-
@c2 = Client.create(@c2_fields,{:source_name => @s_fields[:name]})
|
511
|
-
@a.users << @u2.id
|
512
|
-
create_doc1 = { 'name' => 'abc', 'link' => '1', 'an_attribute' => "My attrib 2" }
|
513
|
-
create_doc2 = { 'name' => 'name0', 'link' => '1' }
|
514
|
-
create_doc3 = { 'name' => 'name7', 'link' => '7' }
|
515
|
-
set_source_queue_state(@s, {@create_queue_name => [[@s.name, [['4', create_doc1]]]]},@c.id,true)
|
516
|
-
set_source_queue_state(@s, {@create_queue_name => [[@s.name, [['5', create_doc2]]]]},@c2.id,true)
|
517
|
-
set_source_queue_state(@s, {@create_queue_name => [[@s.name, [['6', create_doc3]]]]},@c.id,true)
|
518
|
-
set_doc_state(@c, :cd => {'10'=> {'name' => 'Apple'}})
|
519
|
-
set_source_queue_state(@s, {@update_queue_name => [[@s.name, [['10', { 'name' => 'Android' }]]]]},@c.id,true)
|
520
|
-
# should receive login/logoff pair twice for create and once for update
|
521
|
-
@sscud.should_receive(:auth_method).exactly(6).times.and_return(true)
|
522
|
-
@sscud.should_receive(:_process_create).exactly(3).times
|
523
|
-
@sscud.should_receive(:_process_update).once
|
524
|
-
@sscud.should_not_receive(:_process_delete)
|
525
|
-
@sscud.do_cud
|
526
|
-
end
|
480
|
+
describe "cud" do
|
481
|
+
before (:each) do
|
482
|
+
rh = lambda { @model.send(params[:operation].to_sym, params["#{params[:operation]}_object".to_sym]) }
|
483
|
+
@sscud = Rhoconnect::Handler::Changes::Engine.new(['create', 'update', 'delete'], @model, rh, {})
|
527
484
|
end
|
528
|
-
|
529
|
-
describe "cud conflicts" do
|
530
|
-
before (:each) do
|
531
|
-
@create_queue_name = :create
|
532
|
-
@update_queue_name = :update
|
533
|
-
@delete_queue_name = :delete
|
534
|
-
rh = lambda { @model.send(params[:operation].to_sym, params["#{params[:operation]}_object".to_sym]) }
|
535
|
-
@sscud = Rhoconnect::Handler::Changes::Engine.new(['create', 'update', 'delete'], @model, rh, {})
|
536
|
-
@u2_fields = {:login => 'anotheruser'}
|
537
|
-
@u2 = User.create(@u2_fields)
|
538
|
-
@u2.password = 'testpass'
|
539
|
-
@c2_fields = {
|
540
|
-
:device_type => 'Android',
|
541
|
-
:device_pin => 'efgh',
|
542
|
-
:device_port => '4444',
|
543
|
-
:user_id => @u2.id,
|
544
|
-
:app_id => @a.id
|
545
|
-
}
|
546
|
-
@c2 = Client.create(@c2_fields,{:source_name => @s_fields[:name]})
|
547
|
-
@a.users << @u2.id
|
548
|
-
end
|
549
485
|
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
@sscud.do_cud
|
554
|
-
|
555
|
-
verify_source_queue_data(@s, @create_queue_name => [])
|
556
|
-
verify_doc_result(@c, :create_errors => {})
|
557
|
-
verify_doc_result(@c, :create_links => {'4'=> { 'l' => 'backend_id' }, '5' => { 'l' => 'backend_id'}})
|
558
|
-
end
|
559
|
-
|
560
|
-
it "should detect create conflict and skip the duplicate record creation, but properly update the errors page" do
|
561
|
-
create_doc1 = { 'name' => 'wrongname', 'link' => '1', 'an_attribute' => "Create Sample Adapter Error" }
|
562
|
-
create_doc2 = { 'name' => 'wrongname', 'link' => '1', 'duplicate_of_cid' => @c.id, 'duplicate_of_entry_index' => '0', 'duplicate_of_queue_index' => '0'}
|
563
|
-
set_source_queue_state(@s, {@create_queue_name => [[@s.name, [['4', create_doc1]]]]},@c.id,true)
|
564
|
-
set_source_queue_state(@s, {@create_queue_name => [[@s.name, [['5', create_doc2]]]]},@c.id,true)
|
565
|
-
@sscud.do_cud
|
566
|
-
|
567
|
-
verify_source_queue_data(@s, @create_queue_name => [])
|
568
|
-
verify_doc_result(@c, :create_errors => {"4-error"=>{"message"=>"Create Sample Adapter Error"},
|
569
|
-
'4' => create_doc1,
|
570
|
-
"5-error"=>{"message"=>"Create Sample Adapter Error"},
|
571
|
-
'5' => create_doc2})
|
572
|
-
verify_doc_result(@c, :create_links => {})
|
573
|
-
end
|
574
|
-
|
575
|
-
it "should detect create conflict and force and error" do
|
576
|
-
set_source_queue_state(@s, {@create_queue_name => [[@s.name, [['4', { 'name' => 'Android', 'link' => true }]]]]},@c.id,true)
|
577
|
-
set_source_queue_state(@s, {@create_queue_name => [[@s.name, [['5', { 'name' => 'Android', 'link' => '1', 'force_duplicate_error' => '1' }]]]]},@c.id,true)
|
578
|
-
@sscud.do_cud
|
579
|
-
verify_source_queue_data(@s, @create_queue_name => [])
|
580
|
-
verify_doc_result(@c, :create_errors => {"5-error"=>{"message"=>"Error during create: object confict detected"}, "5"=>{"name"=>"Android", "link"=>"1", 'force_duplicate_error' => '1'}} )
|
581
|
-
end
|
486
|
+
it "should fail to create Rhoconnect::Handler::Changes::Engine with InvalidArgumentError without source" do
|
487
|
+
lambda { Rhoconnect::Handler::Changes::Engine.new(['create', 'update', 'delete'], nil, nil, {}) }.should raise_error(ArgumentError, 'Unknown source')
|
488
|
+
end
|
582
489
|
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
@sscud.do_cud
|
587
|
-
|
588
|
-
verify_source_queue_data(@s, @create_queue_name => [])
|
589
|
-
verify_doc_result(@c, :create_errors => {})
|
590
|
-
verify_doc_result(@c, :create_links => {'4'=> { 'l' => 'backend_id' }, '5'=> { 'l' => 'backend_id' }, '6' => { 'l' => 'backend_id' }, '7'=> { 'l' => 'backend_id' }})
|
591
|
-
end
|
592
|
-
|
593
|
-
it "should detect update conflict and skip the duplicate record update" do
|
594
|
-
set_doc_state(@c, :cd => {'4'=> {'name' => 'Apple'}})
|
595
|
-
set_source_queue_state(@s, {@update_queue_name => [[@s.name, [['4', { 'name' => 'Android' }]]]]},@c.id,true)
|
596
|
-
set_source_queue_state(@s, {@update_queue_name => [[@s.name, [['4', { 'name' => 'InvalidName', 'duplicate_of_cid' => @c.id, 'duplicate_of_entry_index' => '0', 'duplicate_of_queue_index' => '0'}]]]]},@c.id,true)
|
597
|
-
@sscud.do_cud
|
598
|
-
|
599
|
-
verify_source_queue_data(@s, @update_queue_name => [])
|
600
|
-
verify_doc_result(@c, :update_errors => {})
|
601
|
-
verify_doc_result(@c, :update_rollback => {})
|
602
|
-
end
|
603
|
-
|
604
|
-
it "should detect update conflict and force an error on duplicate record update" do
|
605
|
-
set_doc_state(@c, :cd => {'4'=> {'name' => 'Apple'}})
|
606
|
-
set_source_queue_state(@s, {@update_queue_name => [[@s.name, [['4', { 'name' => 'Android' }]]]]},@c.id,true)
|
607
|
-
set_source_queue_state(@s, {@update_queue_name => [[@s.name, [['4', { 'name' => 'ErrorName', 'force_duplicate_error' => '1' }]]]]},@c.id,true)
|
608
|
-
@sscud.do_cud
|
609
|
-
|
610
|
-
verify_source_queue_data(@s, @update_queue_name => [])
|
611
|
-
verify_doc_result(@c, :update_errors => {"4-error"=>{"message"=>"Error during update: object confict detected"}, "4"=>{"name"=>"ErrorName", 'force_duplicate_error' => '1'}})
|
612
|
-
verify_doc_result(@c, :update_rollback => {'4'=> {'name' => 'Apple'}})
|
613
|
-
end
|
490
|
+
it "should fail to create Rhoconnect::Handler::Changes::Engine with InvalidArgumentError without proc handler" do
|
491
|
+
lambda { Rhoconnect::Handler::Changes::Engine.new(['create', 'update', 'delete'], @model, nil) }.should raise_error(ArgumentError, 'Invalid CUD handler')
|
492
|
+
end
|
614
493
|
|
615
|
-
|
616
|
-
|
617
|
-
set_doc_state(@c, :cd => {'4'=> {'name' => 'Apple'}})
|
618
|
-
set_doc_state(@c2, :cd => {'4'=> {'name' => 'Apple'}})
|
619
|
-
update_source1 = Source.load(@s.name,
|
620
|
-
{:user_id => @u.id,:app_id => @a.id})
|
621
|
-
update_source2 = Source.load(@s.name,
|
622
|
-
{:user_id => @u2.id,:app_id => @a.id})
|
623
|
-
set_source_queue_state(update_source1, {@update_queue_name => [[@s.name, [['4', { 'name' => 'Android' }]]]]},@c.id,true)
|
624
|
-
set_source_queue_state(update_source2, {@update_queue_name => [[@s.name, [['4', { 'name' => 'Android' }]]]]},@c2.id, true)
|
625
|
-
operation_data,client_ids = update_source2.get_queue(:update)
|
626
|
-
invalid_meta = @sscud.model.run_validators(:update,operation_data,client_ids)
|
627
|
-
invalid_meta.should == { 1=> {@s.name => { 0 => { :duplicate_of=>true }}},
|
628
|
-
0=>{@s.name => { 0 => { :duplicates=>[{:client_id=>@c2.id, :key=>"4", :value=>{"name"=>"Android"}}]}}}}
|
629
|
-
|
630
|
-
@sscud.model.should_receive(:find_duplicates_on_update).once.and_return(invalid_meta)
|
631
|
-
@sscud.do_cud
|
632
|
-
|
633
|
-
verify_source_queue_data(@s, @update_queue_name => [])
|
634
|
-
verify_doc_result(@c, :update_errors => {})
|
635
|
-
verify_doc_result(@c, :update_rollback => {})
|
636
|
-
SampleAdapter.validators.delete(:find_duplicates_on_update)
|
637
|
-
end
|
638
|
-
|
639
|
-
it "should detect update conflict and force an error on duplicate record update" do
|
640
|
-
set_doc_state(@c, :cd => {'4'=> {'name' => 'Apple'}})
|
641
|
-
set_source_queue_state(@s, {@update_queue_name => [[@s.name, [['4', { 'name' => 'Android' }]]]]},@c.id,true)
|
642
|
-
set_source_queue_state(@s, {@update_queue_name => [[@s.name, [['4', { 'name' => 'ErrorName', 'force_duplicate_error' => '1' }]]]]},@c.id,true)
|
643
|
-
@sscud.do_cud
|
644
|
-
|
645
|
-
verify_source_queue_data(@s, @update_queue_name => [])
|
646
|
-
verify_doc_result(@c, :update_errors => {"4-error"=>{"message"=>"Error during update: object confict detected"}, "4"=>{"name"=>"ErrorName", 'force_duplicate_error' => '1'}})
|
647
|
-
verify_doc_result(@c, :update_rollback => {'4'=> {'name' => 'Apple'}})
|
648
|
-
end
|
649
|
-
|
650
|
-
it "should install find_duplicates_on_update , detect equal objects conflict and raise a custom error" do
|
651
|
-
SampleAdapter.enable :find_duplicates_on_update, :raise_error => true do |options, invalid_meta, operation, operation_data, client_ids|
|
652
|
-
invalid_meta.each do |index, index_data|
|
653
|
-
index_data.each do |source_id, objindex_data|
|
654
|
-
objindex_data.each do |objindex, objmeta|
|
655
|
-
if objmeta.has_key?(:error)
|
656
|
-
objmeta[:error] = "My custom error"
|
657
|
-
end
|
658
|
-
end if objindex_data
|
659
|
-
end if index_data
|
660
|
-
end if invalid_meta
|
661
|
-
invalid_meta
|
662
|
-
end
|
663
|
-
set_doc_state(@c, :cd => {'4'=> {'name' => 'Apple'}})
|
664
|
-
set_doc_state(@c2, :cd => {'4'=> {'name' => 'Apple'}})
|
665
|
-
update_source1 = Source.load(@s.name,
|
666
|
-
{:user_id => @u.id,:app_id => @a.id})
|
667
|
-
update_source2 = Source.load(@s.name,
|
668
|
-
{:user_id => @u2.id,:app_id => @a.id})
|
669
|
-
set_source_queue_state(update_source1, {@update_queue_name => [[@s.name, [['4', { 'name' => 'Android' }]]]]},@c.id,true)
|
670
|
-
set_source_queue_state(update_source2, {@update_queue_name => [[@s.name, [['4', { 'name' => 'Android' }]]]]},@c2.id, true)
|
671
|
-
operation_data,client_ids = update_source2.get_queue(:update)
|
672
|
-
invalid_meta = @sscud.model.run_validators(:update,operation_data,client_ids)
|
673
|
-
invalid_meta.should == {1=>{@s.name=>{0=>{:error=>"My custom error"}}}}
|
674
|
-
|
675
|
-
@sscud.do_cud
|
676
|
-
verify_source_queue_data(@s, @update_queue_name => [])
|
677
|
-
verify_doc_result(@c, :update_errors => {})
|
678
|
-
verify_doc_result(@c, :update_rollback => {})
|
679
|
-
verify_doc_result(@c2, :update_errors => {"4-error"=>{"message"=>"My custom error"}, "4"=>{"name"=>"Android"}})
|
680
|
-
verify_doc_result(@c2, :update_rollback => {'4'=> {'name' => 'Apple'}})
|
681
|
-
SampleAdapter.validators.delete(:find_duplicates_on_update)
|
682
|
-
end
|
683
|
-
|
684
|
-
|
685
|
-
it "should detect delete conflict and skip the duplicate record delete" do
|
686
|
-
set_doc_state(@c, :cd => {'4'=> {'name' => 'Apple'}})
|
687
|
-
set_doc_state(@c, :cd_size => 1)
|
688
|
-
set_source_queue_state(@s, {@delete_queue_name => [[@s.name, [['4', { 'name' => 'Apple' }]]]]},@c.id,true)
|
689
|
-
set_source_queue_state(@s, {@delete_queue_name => [[@s.name, [['4', { 'name' => 'Apple', 'duplicate_of_cid' => @c.id, 'duplicate_of_entry_index' => '0', 'duplicate_of_queue_index' => '0'}]]]]},@c.id,true)
|
690
|
-
@sscud.do_cud
|
691
|
-
|
692
|
-
verify_source_queue_data(@s, @delete_queue_name => [])
|
693
|
-
verify_doc_result(@c, :cd => {})
|
694
|
-
verify_doc_result(@c, :cd_size => '0')
|
695
|
-
verify_doc_result(@c, :delete_errors => {})
|
696
|
-
end
|
697
|
-
|
698
|
-
it "should detect delete conflict and force an error on duplicate record delete" do
|
699
|
-
set_doc_state(@c, :cd => {'4'=> {'name' => 'Apple'}})
|
700
|
-
set_doc_state(@c, :cd_size => 1)
|
701
|
-
set_source_queue_state(@s, {@delete_queue_name => [[@s.name, [['4', { 'name' => 'Apple' }]]]]},@c.id,true)
|
702
|
-
set_source_queue_state(@s, {@delete_queue_name => [[@s.name, [['4', { 'name' => 'Apple', 'force_duplicate_error' => '1'}]]]]},@c.id,true)
|
703
|
-
@sscud.do_cud
|
704
|
-
|
705
|
-
verify_source_queue_data(@s, @delete_queue_name => [])
|
706
|
-
verify_doc_result(@c, :delete_errors => {"4-error"=>{"message"=>"Error during delete: object confict detected"}, "4"=>{"name"=>"Apple", 'force_duplicate_error' => '1'}})
|
707
|
-
end
|
494
|
+
it "should create Rhoconnect::Handler::Changes::Engine" do
|
495
|
+
@sscud.model.is_a?(SampleAdapter).should == true
|
708
496
|
end
|
709
497
|
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
498
|
+
it "should do process_cud" do
|
499
|
+
@create_queue_name = :create
|
500
|
+
@update_queue_name = :update
|
501
|
+
@u2_fields = {:login => 'anotheruser'}
|
502
|
+
@u2 = User.create(@u2_fields)
|
503
|
+
@u2.password = 'testpass'
|
504
|
+
@c2_fields = {
|
505
|
+
:device_type => 'Android',
|
506
|
+
:device_pin => 'efgh',
|
507
|
+
:device_port => '4444',
|
508
|
+
:user_id => @u2.id,
|
509
|
+
:app_id => @a.id
|
510
|
+
}
|
511
|
+
@c2 = Client.create(@c2_fields,{:source_name => @s_fields[:name]})
|
512
|
+
@a.users << @u2.id
|
513
|
+
create_doc1 = { 'name' => 'abc', 'link' => '1', 'an_attribute' => "My attrib 2" }
|
514
|
+
create_doc2 = { 'name' => 'name0', 'link' => '1' }
|
515
|
+
create_doc3 = { 'name' => 'name7', 'link' => '7' }
|
516
|
+
set_source_queue_state(@s, {@create_queue_name => [[@s.name, [['4', create_doc1]]]]},@c.id,true)
|
517
|
+
set_source_queue_state(@s, {@create_queue_name => [[@s.name, [['5', create_doc2]]]]},@c2.id,true)
|
518
|
+
set_source_queue_state(@s, {@create_queue_name => [[@s.name, [['6', create_doc3]]]]},@c.id,true)
|
519
|
+
set_doc_state(@c, :cd => {'10'=> {'name' => 'Apple'}})
|
520
|
+
set_source_queue_state(@s, {@update_queue_name => [[@s.name, [['10', { 'name' => 'Android' }]]]]},@c.id,true)
|
521
|
+
# should receive login/logoff pair twice for create and once for update
|
522
|
+
@sscud.should_receive(:auth_method).exactly(6).times.and_return(true)
|
523
|
+
@sscud.should_receive(:_process_create).exactly(3).times
|
524
|
+
@sscud.should_receive(:_process_update).once
|
525
|
+
@sscud.should_not_receive(:_process_delete)
|
526
|
+
@sscud.do_cud
|
527
|
+
end
|
528
|
+
end
|
715
529
|
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
530
|
+
describe "cud conflicts" do
|
531
|
+
before (:each) do
|
532
|
+
@create_queue_name = :create
|
533
|
+
@update_queue_name = :update
|
534
|
+
@delete_queue_name = :delete
|
535
|
+
rh = lambda { @model.send(params[:operation].to_sym, params["#{params[:operation]}_object".to_sym]) }
|
536
|
+
@sscud = Rhoconnect::Handler::Changes::Engine.new(['create', 'update', 'delete'], @model, rh, {})
|
537
|
+
@u2_fields = {:login => 'anotheruser'}
|
538
|
+
@u2 = User.create(@u2_fields)
|
539
|
+
@u2.password = 'testpass'
|
540
|
+
@c2_fields = {
|
541
|
+
:device_type => 'Android',
|
542
|
+
:device_pin => 'efgh',
|
543
|
+
:device_port => '4444',
|
544
|
+
:user_id => @u2.id,
|
545
|
+
:app_id => @a.id
|
546
|
+
}
|
547
|
+
@c2 = Client.create(@c2_fields,{:source_name => @s_fields[:name]})
|
548
|
+
@a.users << @u2.id
|
549
|
+
end
|
723
550
|
|
724
|
-
|
725
|
-
|
726
|
-
|
551
|
+
it "should detect create conflict and skip the duplicate record creation, but properly update the links" do
|
552
|
+
set_source_queue_state(@s, {@create_queue_name => [[@s.name, [['4', { 'name' => 'Android', 'link' => '1' }]]]]},@c.id,true)
|
553
|
+
set_source_queue_state(@s, {@create_queue_name => [[@s.name, [['5', { 'name' => 'Android', 'link' => '1', 'duplicate_of_cid' => @c.id, 'duplicate_of_entry_index' => '0', 'duplicate_of_queue_index' => '0' }]]]]},@c.id,true)
|
554
|
+
@sscud.do_cud
|
555
|
+
|
556
|
+
verify_source_queue_data(@s, @create_queue_name => [])
|
557
|
+
verify_doc_result(@c, :create_errors => {})
|
558
|
+
verify_doc_result(@c, :create_links => {'4'=> { 'l' => 'backend_id' }, '5' => { 'l' => 'backend_id'}})
|
727
559
|
end
|
728
560
|
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
"read_state:#{test_app_name}:__shared__:SampleAdapter:prev_refresh_time",
|
743
|
-
"read_state:#{test_app_name}:__shared__:SampleAdapter:rho__id",
|
744
|
-
"read_state:#{test_app_name}:__shared__:SampleAdapter:retry_counter"].sort
|
745
|
-
end
|
561
|
+
it "should detect create conflict and skip the duplicate record creation, but properly update the errors page" do
|
562
|
+
create_doc1 = { 'name' => 'wrongname', 'link' => '1', 'an_attribute' => "Create Sample Adapter Error" }
|
563
|
+
create_doc2 = { 'name' => 'wrongname', 'link' => '1', 'duplicate_of_cid' => @c.id, 'duplicate_of_entry_index' => '0', 'duplicate_of_queue_index' => '0'}
|
564
|
+
set_source_queue_state(@s, {@create_queue_name => [[@s.name, [['4', create_doc1]]]]},@c.id,true)
|
565
|
+
set_source_queue_state(@s, {@create_queue_name => [[@s.name, [['5', create_doc2]]]]},@c.id,true)
|
566
|
+
@sscud.do_cud
|
567
|
+
|
568
|
+
verify_source_queue_data(@s, @create_queue_name => [])
|
569
|
+
verify_doc_result(@c, :create_errors => {"4-error"=>{"message"=>"Create Sample Adapter Error"},
|
570
|
+
'4' => create_doc1,
|
571
|
+
"5-error"=>{"message"=>"Create Sample Adapter Error"},
|
572
|
+
'5' => create_doc2})
|
573
|
+
verify_doc_result(@c, :create_links => {})
|
746
574
|
end
|
747
575
|
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
@
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
576
|
+
it "should detect create conflict and force and error" do
|
577
|
+
set_source_queue_state(@s, {@create_queue_name => [[@s.name, [['4', { 'name' => 'Android', 'link' => true }]]]]},@c.id,true)
|
578
|
+
set_source_queue_state(@s, {@create_queue_name => [[@s.name, [['5', { 'name' => 'Android', 'link' => '1', 'force_duplicate_error' => '1' }]]]]},@c.id,true)
|
579
|
+
@sscud.do_cud
|
580
|
+
verify_source_queue_data(@s, @create_queue_name => [])
|
581
|
+
verify_doc_result(@c, :create_errors => {"5-error"=>{"message"=>"Error during create: object confict detected"}, "5"=>{"name"=>"Android", "link"=>"1", 'force_duplicate_error' => '1'}} )
|
582
|
+
end
|
583
|
+
|
584
|
+
it "should detect create conflict in the intermediate state create and skip the duplicate record create" do
|
585
|
+
set_source_queue_state(@s, {@create_queue_name => [[@s.name, [['5', { 'name' => 'InvalidName', 'duplicate_of_cid' => @c.id, 'duplicate_of_entry_index' => '1', 'duplicate_of_queue_index' => '0'}], ['4', { 'name' => 'Android' , 'link' => true}]]]]},@c.id,true)
|
586
|
+
set_source_queue_state(@s, {@create_queue_name => [[@s.name, [['6', { 'name' => 'iPhone', 'link' => true }], ['7', { 'name' => 'InvalidName', 'duplicate_of_cid' => @c.id, 'duplicate_of_entry_index' => '1', 'duplicate_of_queue_index' => '0'}]]]]},@c.id,true)
|
587
|
+
@sscud.do_cud
|
588
|
+
|
589
|
+
verify_source_queue_data(@s, @create_queue_name => [])
|
590
|
+
verify_doc_result(@c, :create_errors => {})
|
591
|
+
verify_doc_result(@c, :create_links => {'4'=> { 'l' => 'backend_id' }, '5'=> { 'l' => 'backend_id' }, '6' => { 'l' => 'backend_id' }, '7'=> { 'l' => 'backend_id' }})
|
592
|
+
end
|
593
|
+
|
594
|
+
it "should detect update conflict and skip the duplicate record update" do
|
595
|
+
set_doc_state(@c, :cd => {'4'=> {'name' => 'Apple'}})
|
596
|
+
set_source_queue_state(@s, {@update_queue_name => [[@s.name, [['4', { 'name' => 'Android' }]]]]},@c.id,true)
|
597
|
+
set_source_queue_state(@s, {@update_queue_name => [[@s.name, [['4', { 'name' => 'InvalidName', 'duplicate_of_cid' => @c.id, 'duplicate_of_entry_index' => '0', 'duplicate_of_queue_index' => '0'}]]]]},@c.id,true)
|
598
|
+
@sscud.do_cud
|
599
|
+
|
600
|
+
verify_source_queue_data(@s, @update_queue_name => [])
|
601
|
+
verify_doc_result(@c, :update_errors => {})
|
602
|
+
verify_doc_result(@c, :update_rollback => {})
|
603
|
+
end
|
604
|
+
|
605
|
+
it "should detect update conflict and force an error on duplicate record update" do
|
606
|
+
set_doc_state(@c, :cd => {'4'=> {'name' => 'Apple'}})
|
607
|
+
set_source_queue_state(@s, {@update_queue_name => [[@s.name, [['4', { 'name' => 'Android' }]]]]},@c.id,true)
|
608
|
+
set_source_queue_state(@s, {@update_queue_name => [[@s.name, [['4', { 'name' => 'ErrorName', 'force_duplicate_error' => '1' }]]]]},@c.id,true)
|
609
|
+
@sscud.do_cud
|
610
|
+
|
611
|
+
verify_source_queue_data(@s, @update_queue_name => [])
|
612
|
+
verify_doc_result(@c, :update_errors => {"4-error"=>{"message"=>"Error during update: object confict detected"}, "4"=>{"name"=>"ErrorName", 'force_duplicate_error' => '1'}})
|
613
|
+
verify_doc_result(@c, :update_rollback => {'4'=> {'name' => 'Apple'}})
|
614
|
+
end
|
615
|
+
|
616
|
+
it "should install find_duplicates_on_update , detect equal objects conflict and skip the duplicate record update" do
|
617
|
+
SampleAdapter.enable :find_duplicates_on_update
|
618
|
+
set_doc_state(@c, :cd => {'4'=> {'name' => 'Apple'}})
|
619
|
+
set_doc_state(@c2, :cd => {'4'=> {'name' => 'Apple'}})
|
620
|
+
update_source1 = Source.load(@s.name,
|
621
|
+
{:user_id => @u.id,:app_id => @a.id})
|
622
|
+
update_source2 = Source.load(@s.name,
|
623
|
+
{:user_id => @u2.id,:app_id => @a.id})
|
624
|
+
set_source_queue_state(update_source1, {@update_queue_name => [[@s.name, [['4', { 'name' => 'Android' }]]]]},@c.id,true)
|
625
|
+
set_source_queue_state(update_source2, {@update_queue_name => [[@s.name, [['4', { 'name' => 'Android' }]]]]},@c2.id, true)
|
626
|
+
operation_data,client_ids = update_source2.get_queue(:update)
|
627
|
+
invalid_meta = @sscud.model.run_validators(:update,operation_data,client_ids)
|
628
|
+
invalid_meta.should == { 1=> {@s.name => { 0 => { :duplicate_of=>true }}},
|
629
|
+
0=>{@s.name => { 0 => { :duplicates=>[{:client_id=>@c2.id, :key=>"4", :value=>{"name"=>"Android"}}]}}}}
|
630
|
+
|
631
|
+
@sscud.model.should_receive(:find_duplicates_on_update).once.and_return(invalid_meta)
|
632
|
+
@sscud.do_cud
|
633
|
+
|
634
|
+
verify_source_queue_data(@s, @update_queue_name => [])
|
635
|
+
verify_doc_result(@c, :update_errors => {})
|
636
|
+
verify_doc_result(@c, :update_rollback => {})
|
637
|
+
SampleAdapter.validators.delete(:find_duplicates_on_update)
|
638
|
+
end
|
639
|
+
|
640
|
+
it "should detect update conflict and force an error on duplicate record update" do
|
641
|
+
set_doc_state(@c, :cd => {'4'=> {'name' => 'Apple'}})
|
642
|
+
set_source_queue_state(@s, {@update_queue_name => [[@s.name, [['4', { 'name' => 'Android' }]]]]},@c.id,true)
|
643
|
+
set_source_queue_state(@s, {@update_queue_name => [[@s.name, [['4', { 'name' => 'ErrorName', 'force_duplicate_error' => '1' }]]]]},@c.id,true)
|
644
|
+
@sscud.do_cud
|
645
|
+
|
646
|
+
verify_source_queue_data(@s, @update_queue_name => [])
|
647
|
+
verify_doc_result(@c, :update_errors => {"4-error"=>{"message"=>"Error during update: object confict detected"}, "4"=>{"name"=>"ErrorName", 'force_duplicate_error' => '1'}})
|
648
|
+
verify_doc_result(@c, :update_rollback => {'4'=> {'name' => 'Apple'}})
|
649
|
+
end
|
650
|
+
|
651
|
+
it "should install find_duplicates_on_update , detect equal objects conflict and raise a custom error" do
|
652
|
+
SampleAdapter.enable :find_duplicates_on_update, :raise_error => true do |options, invalid_meta, operation, operation_data, client_ids|
|
653
|
+
invalid_meta.each do |index, index_data|
|
654
|
+
index_data.each do |source_id, objindex_data|
|
655
|
+
objindex_data.each do |objindex, objmeta|
|
656
|
+
if objmeta.has_key?(:error)
|
657
|
+
objmeta[:error] = "My custom error"
|
658
|
+
end
|
659
|
+
end if objindex_data
|
660
|
+
end if index_data
|
661
|
+
end if invalid_meta
|
662
|
+
invalid_meta
|
663
|
+
end
|
664
|
+
set_doc_state(@c, :cd => {'4'=> {'name' => 'Apple'}})
|
665
|
+
set_doc_state(@c2, :cd => {'4'=> {'name' => 'Apple'}})
|
666
|
+
update_source1 = Source.load(@s.name,
|
667
|
+
{:user_id => @u.id,:app_id => @a.id})
|
668
|
+
update_source2 = Source.load(@s.name,
|
669
|
+
{:user_id => @u2.id,:app_id => @a.id})
|
670
|
+
set_source_queue_state(update_source1, {@update_queue_name => [[@s.name, [['4', { 'name' => 'Android' }]]]]},@c.id,true)
|
671
|
+
set_source_queue_state(update_source2, {@update_queue_name => [[@s.name, [['4', { 'name' => 'Android' }]]]]},@c2.id, true)
|
672
|
+
operation_data,client_ids = update_source2.get_queue(:update)
|
673
|
+
invalid_meta = @sscud.model.run_validators(:update,operation_data,client_ids)
|
674
|
+
invalid_meta.should == {1=>{@s.name=>{0=>{:error=>"My custom error"}}}}
|
675
|
+
|
676
|
+
@sscud.do_cud
|
677
|
+
verify_source_queue_data(@s, @update_queue_name => [])
|
678
|
+
verify_doc_result(@c, :update_errors => {})
|
679
|
+
verify_doc_result(@c, :update_rollback => {})
|
680
|
+
verify_doc_result(@c2, :update_errors => {"4-error"=>{"message"=>"My custom error"}, "4"=>{"name"=>"Android"}})
|
681
|
+
verify_doc_result(@c2, :update_rollback => {'4'=> {'name' => 'Apple'}})
|
682
|
+
SampleAdapter.validators.delete(:find_duplicates_on_update)
|
683
|
+
end
|
684
|
+
|
685
|
+
|
686
|
+
it "should detect delete conflict and skip the duplicate record delete" do
|
687
|
+
set_doc_state(@c, :cd => {'4'=> {'name' => 'Apple'}})
|
688
|
+
set_doc_state(@c, :cd_size => 1)
|
689
|
+
set_source_queue_state(@s, {@delete_queue_name => [[@s.name, [['4', { 'name' => 'Apple' }]]]]},@c.id,true)
|
690
|
+
set_source_queue_state(@s, {@delete_queue_name => [[@s.name, [['4', { 'name' => 'Apple', 'duplicate_of_cid' => @c.id, 'duplicate_of_entry_index' => '0', 'duplicate_of_queue_index' => '0'}]]]]},@c.id,true)
|
691
|
+
@sscud.do_cud
|
692
|
+
|
693
|
+
verify_source_queue_data(@s, @delete_queue_name => [])
|
694
|
+
verify_doc_result(@c, :cd => {})
|
695
|
+
verify_doc_result(@c, :cd_size => '0')
|
696
|
+
verify_doc_result(@c, :delete_errors => {})
|
697
|
+
end
|
698
|
+
|
699
|
+
it "should detect delete conflict and force an error on duplicate record delete" do
|
700
|
+
set_doc_state(@c, :cd => {'4'=> {'name' => 'Apple'}})
|
701
|
+
set_doc_state(@c, :cd_size => 1)
|
702
|
+
set_source_queue_state(@s, {@delete_queue_name => [[@s.name, [['4', { 'name' => 'Apple' }]]]]},@c.id,true)
|
703
|
+
set_source_queue_state(@s, {@delete_queue_name => [[@s.name, [['4', { 'name' => 'Apple', 'force_duplicate_error' => '1'}]]]]},@c.id,true)
|
704
|
+
@sscud.do_cud
|
705
|
+
|
706
|
+
verify_source_queue_data(@s, @delete_queue_name => [])
|
707
|
+
verify_doc_result(@c, :delete_errors => {"4-error"=>{"message"=>"Error during delete: object confict detected"}, "4"=>{"name"=>"Apple", 'force_duplicate_error' => '1'}})
|
708
|
+
end
|
709
|
+
end
|
710
|
+
|
711
|
+
describe "search" do
|
712
|
+
before (:each) do
|
713
|
+
rh = lambda { @model.search(params[:search]) }
|
714
|
+
@sss = Rhoconnect::Handler::Search::Engine.new(@model, @c, rh, {})
|
715
|
+
end
|
716
|
+
|
717
|
+
it "should do search with no exception" do
|
718
|
+
verify_read_operation('search')
|
719
|
+
end
|
720
|
+
|
721
|
+
it "should do search with no exception pass through" do
|
722
|
+
verify_read_operation_pass_through('search')
|
761
723
|
end
|
724
|
+
|
725
|
+
it "should do search with exception raised" do
|
726
|
+
verify_read_operation_with_error('search')
|
762
727
|
end
|
763
|
-
|
764
|
-
|
728
|
+
end
|
729
|
+
|
730
|
+
describe "app-level partitioning" do
|
731
|
+
it "should create app-level masterdoc with '__shared__' docname" do
|
732
|
+
@s1 = Source.load(@s_fields[:name],@s_params)
|
733
|
+
@s1.partition = :app
|
734
|
+
rh = lambda { @model.query(params[:query]) }
|
735
|
+
@model1 = Rhoconnect::Model::Base.create(@s1)
|
736
|
+
@ssq = Rhoconnect::Handler::Query::Engine.new(@model1, rh, {})
|
765
737
|
expected = {'1'=>@product1,'2'=>@product2}
|
766
|
-
|
767
|
-
@
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
738
|
+
set_state('test_db_storage' => expected)
|
739
|
+
@ssq.do_sync
|
740
|
+
verify_doc_result(@s1, :md => expected)
|
741
|
+
Store.get_store(0).keys("read_state:#{test_app_name}:__shared__*").sort.should ==
|
742
|
+
[ "read_state:#{test_app_name}:__shared__:SampleAdapter:refresh_time",
|
743
|
+
"read_state:#{test_app_name}:__shared__:SampleAdapter:prev_refresh_time",
|
744
|
+
"read_state:#{test_app_name}:__shared__:SampleAdapter:rho__id",
|
745
|
+
"read_state:#{test_app_name}:__shared__:SampleAdapter:retry_counter"].sort
|
746
|
+
end
|
747
|
+
end
|
748
|
+
|
749
|
+
def verify_read_operation(operation)
|
750
|
+
expected = {'1'=>@product1,'2'=>@product2}
|
751
|
+
set_test_data('test_db_storage',expected)
|
752
|
+
@s.put_data(:errors,
|
753
|
+
{"#{operation}-error"=>{'message'=>'failed'}},true)
|
754
|
+
if operation == 'query'
|
755
|
+
@ssq.run_query.should == true
|
756
|
+
verify_doc_result(@s, {:md => expected,
|
773
757
|
:errors => {}})
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
end
|
758
|
+
else
|
759
|
+
@sss.run_search.should == true
|
760
|
+
verify_doc_result(@c, {:search => expected,
|
761
|
+
:search_errors => {}})
|
779
762
|
end
|
763
|
+
end
|
780
764
|
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
:search_errors => {'search-error'=>{'message'=>msg}}})
|
796
|
-
end
|
765
|
+
def verify_read_operation_pass_through(operation)
|
766
|
+
expected = {'1'=>@product1,'2'=>@product2}
|
767
|
+
set_test_data('test_db_storage',expected)
|
768
|
+
@s.put_data(:errors,
|
769
|
+
{"#{operation}-error"=>{'message'=>'failed'}},true)
|
770
|
+
@s.pass_through = 'true'
|
771
|
+
if operation == 'query'
|
772
|
+
@ssq.run_query.should == expected
|
773
|
+
verify_doc_result(@s, {:md => {},
|
774
|
+
:errors => {}})
|
775
|
+
else
|
776
|
+
@sss.run_search.should == expected
|
777
|
+
verify_doc_result(@c, {:search => {},
|
778
|
+
:search_errors => {}})
|
797
779
|
end
|
780
|
+
end
|
798
781
|
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
782
|
+
def verify_read_operation_with_error(operation)
|
783
|
+
msg = "Error during #{operation}"
|
784
|
+
set_test_data('test_db_storage',{},msg,"#{operation} error")
|
785
|
+
if operation == 'query'
|
786
|
+
@ssq.should_receive(:log).with("Model raised #{operation} exception: #{msg}")
|
787
|
+
@ssq.should_receive(:log).with(anything)
|
788
|
+
@ssq.run_query.should == true
|
789
|
+
verify_doc_result(@s, {:md => {},
|
790
|
+
:errors => {'query-error'=>{'message'=>msg}}})
|
791
|
+
else
|
792
|
+
@sss.should_receive(:log).with("Model raised #{operation} exception: #{msg}")
|
793
|
+
@sss.should_receive(:log).with(anything)
|
794
|
+
@sss.run_search.should == true
|
795
|
+
verify_doc_result(@c, {:search => {},
|
796
|
+
:search_errors => {'search-error'=>{'message'=>msg}}})
|
797
|
+
end
|
798
|
+
end
|
808
799
|
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
800
|
+
describe "Jobs" do
|
801
|
+
it "should enqueue process_cud SourceJob" do
|
802
|
+
@s.cud_queue = :cud
|
803
|
+
rh = lambda { @model.send(params[:operation].to_sym, params["#{params[:operation]}_object".to_sym]) }
|
804
|
+
@sscud = Rhoconnect::Handler::Changes::Engine.new(['create', 'update', 'delete'], @model, rh, {})
|
805
|
+
@sscud.do_cud
|
806
|
+
Resque.peek(:cud).should == {"args"=>
|
807
|
+
["cud", @s.name, @a.name, @u.login, nil], "class"=>"Rhoconnect::SourceJob"}
|
808
|
+
end
|
809
|
+
|
810
|
+
it "should enqueue process_query SourceJob" do
|
811
|
+
@s.query_queue = :abc
|
812
|
+
rh = lambda { @model.query(params[:query]) }
|
813
|
+
@ssq = Rhoconnect::Handler::Query::Engine.new(@model, rh, { :query => {'foo'=>'bar'} })
|
814
|
+
@ssq.do_sync
|
815
|
+
Resque.peek(:abc).should == {"args"=>
|
816
|
+
["query", @s.name, @a.name, @u.login, {'foo'=>'bar'}], "class"=>"Rhoconnect::SourceJob"}
|
817
817
|
end
|
818
818
|
end
|
819
819
|
end
|