rhoconnect 4.0.4 → 5.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. checksums.yaml +5 -13
  2. data/CHANGELOG.md +54 -5
  3. data/CREDITS +219 -219
  4. data/Gemfile +2 -2
  5. data/Gemfile.lock +68 -79
  6. data/Rakefile +1 -2
  7. data/bench/benchapp/spec/models/ruby/mock_adapter_spec.rb +17 -17
  8. data/bench/benchapp/spec/models/ruby/queue_mock_adapter_spec.rb +17 -17
  9. data/bench/benchapp/spec/spec_helper.rb +3 -3
  10. data/bench/blobapp/spec/models/ruby/blob_adapter_spec.rb +17 -17
  11. data/bench/blobapp/spec/spec_helper.rb +3 -3
  12. data/bench/lib/bench/runner.rb +1 -69
  13. data/bench/lib/bench.rb +18 -18
  14. data/bench/spec/mock_adapter_spec.rb +54 -55
  15. data/bench/spec/mock_client_spec.rb +47 -48
  16. data/bench/spec/result_spec.rb +41 -44
  17. data/bench/spec/utils_spec.rb +24 -25
  18. data/commands/generators/app.rb +7 -5
  19. data/commands/generators/controller.rb +7 -5
  20. data/commands/generators/model.rb +7 -5
  21. data/commands/generators/source.rb +7 -5
  22. data/commands/parser.rb +1 -1
  23. data/commands/redis/redis_download.rb +1 -1
  24. data/doc/async-jobs.txt +9 -9
  25. data/doc/supported-platforms.txt +0 -2
  26. data/generators/rhoconnect.rb +92 -212
  27. data/generators/templates/application/rcgemfile +3 -3
  28. data/generators/templates/application/spec/application_controller_spec.rb +14 -16
  29. data/generators/templates/application/spec/js_spec.rb +20 -20
  30. data/generators/templates/application/spec/spec_helper.rb +1 -1
  31. data/generators/templates/source/controllers/ruby/controller_spec.rb +18 -19
  32. data/generators/templates/source/models/ruby/model_spec.rb +17 -17
  33. data/install.sh +10 -21
  34. data/installer/unix-like/rho_connect_install_constants.rb +5 -5
  35. data/installer/unix-like/rho_connect_install_installers.rb +4 -4
  36. data/installer/utils/constants.rb +6 -6
  37. data/installer/utils/nix_install_test.rb +29 -29
  38. data/installer/utils/package_upload/repos.rake +16 -26
  39. data/js-adapters/node.rb +4 -4
  40. data/js-adapters/node_channel.rb +4 -8
  41. data/lib/rhoconnect/db_adapter.rb +13 -13
  42. data/lib/rhoconnect/handler/changes/engine.rb +1 -1
  43. data/lib/rhoconnect/jobs/bulk_data_job.rb +29 -29
  44. data/lib/rhoconnect/license.rb +7 -7
  45. data/lib/rhoconnect/model/helpers/find_duplicates_on_update.rb +13 -13
  46. data/lib/rhoconnect/ping/apple.rb +4 -4
  47. data/lib/rhoconnect/server.rb +2 -2
  48. data/lib/rhoconnect/source.rb +2 -2
  49. data/lib/rhoconnect/store.rb +12 -6
  50. data/lib/rhoconnect/utilities.rb +2 -2
  51. data/lib/rhoconnect/version.rb +1 -1
  52. data/lib/rhoconnect.rb +6 -4
  53. data/rhoconnect.gemspec +5 -6
  54. data/spec/api/api_helper.rb +1 -1
  55. data/spec/api/app/fast_delete_spec.rb +22 -22
  56. data/spec/api/app/fast_insert_spec.rb +23 -23
  57. data/spec/api/app/fast_update_spec.rb +63 -63
  58. data/spec/api/app/push_deletes_spec.rb +11 -13
  59. data/spec/api/app/push_objects_spec.rb +39 -39
  60. data/spec/api/client/client_get_db_doc_spec.rb +29 -29
  61. data/spec/api/client/client_set_db_doc_spec.rb +11 -11
  62. data/spec/api/client/get_client_params_spec.rb +29 -29
  63. data/spec/api/client/list_client_docs_spec.rb +32 -34
  64. data/spec/api/client/reset_spec.rb +30 -30
  65. data/spec/api/readstate/set_refresh_time_spec.rb +43 -43
  66. data/spec/api/source/get_source_params_spec.rb +32 -34
  67. data/spec/api/source/list_sources_spec.rb +13 -13
  68. data/spec/api/source/update_source_params_spec.rb +19 -19
  69. data/spec/api/store/get_db_doc_spec.rb +27 -27
  70. data/spec/api/store/set_db_doc_spec.rb +38 -38
  71. data/spec/api/system/adapter_spec.rb +27 -29
  72. data/spec/api/system/get_license_info_spec.rb +11 -11
  73. data/spec/api/system/login_spec.rb +37 -37
  74. data/spec/api/system/reset_spec.rb +15 -15
  75. data/spec/api/system/stats_spec.rb +70 -71
  76. data/spec/api/user/create_user_spec.rb +37 -37
  77. data/spec/api/user/delete_client_spec.rb +7 -7
  78. data/spec/api/user/delete_user_spec.rb +62 -62
  79. data/spec/api/user/list_clients_spec.rb +24 -24
  80. data/spec/api/user/list_source_docs_spec.rb +29 -29
  81. data/spec/api/user/list_users_spec.rb +22 -22
  82. data/spec/api/user/ping_spec.rb +18 -18
  83. data/spec/api/user/show_user_spec.rb +10 -10
  84. data/spec/api/user/update_user_spec.rb +43 -43
  85. data/spec/api/user/user_get_db_doc_spec.rb +12 -12
  86. data/spec/api/user/user_set_db_doc_spec.rb +37 -37
  87. data/spec/api_token_spec.rb +8 -8
  88. data/spec/app_spec.rb +18 -17
  89. data/spec/apps/jstestapp/settings/settings.yml +2 -0
  90. data/spec/async_spec.rb +9 -11
  91. data/spec/bulk_data/bulk_data_spec.rb +120 -120
  92. data/spec/cli/cli_spec.rb +50 -53
  93. data/spec/client_spec.rb +105 -105
  94. data/spec/client_sync_spec.rb +529 -528
  95. data/spec/controllers/js_base_spec.rb +147 -141
  96. data/spec/doc/doc_spec.rb +51 -52
  97. data/spec/document_spec.rb +58 -58
  98. data/spec/dynamic_adapter_spec.rb +33 -36
  99. data/spec/generator/generator_spec.rb +76 -42
  100. data/spec/jobs/bulk_data_job_spec.rb +101 -102
  101. data/spec/jobs/ping_job_spec.rb +176 -177
  102. data/spec/jobs/source_job_spec.rb +24 -25
  103. data/spec/license_spec.rb +54 -55
  104. data/spec/models/js_base_spec.rb +121 -120
  105. data/spec/perf/bulk_data_perf_spec.rb +23 -24
  106. data/spec/perf/perf_spec_helper.rb +7 -7
  107. data/spec/perf/store_perf_spec.rb +139 -140
  108. data/spec/ping/apple_spec.rb +65 -65
  109. data/spec/ping/gcm_spec.rb +83 -84
  110. data/spec/ping/rhoconnect_push_spec.rb +52 -53
  111. data/spec/predefined_adapters/rho_internal_bench_adapter_controller_js_spec.rb +100 -101
  112. data/spec/predefined_adapters/rho_internal_js_bench_adapter_js_spec.rb +29 -31
  113. data/spec/read_state_spec.rb +24 -25
  114. data/spec/rhoconnect_spec.rb +7 -7
  115. data/spec/server/server_spec.rb +664 -662
  116. data/spec/server/stats_spec.rb +12 -12
  117. data/spec/source_adapter_spec.rb +124 -125
  118. data/spec/source_spec.rb +148 -149
  119. data/spec/source_sync_spec.rb +736 -736
  120. data/spec/spec_helper.rb +4 -5
  121. data/spec/stats/record_spec.rb +22 -21
  122. data/spec/store_orm_spec.rb +48 -48
  123. data/spec/store_spec.rb +428 -426
  124. data/spec/support/shared_examples.rb +5 -7
  125. data/spec/sync_states_spec.rb +67 -67
  126. data/spec/test_methods_spec.rb +121 -123
  127. data/spec/testdata/10000-data.txt +0 -0
  128. data/spec/testdata/5000-data.txt +0 -0
  129. data/spec/user_spec.rb +102 -102
  130. data/tasks/redis.rake +3 -3
  131. metadata +154 -195
  132. data/bench/benchapp/tmp/pids/passenger.9292.pid.lock +0 -0
  133. data/bench/benchapp/tmp/restart.txt +0 -0
  134. data/bench/blobapp/settings/license.key.bak +0 -2
  135. data/bench/blobapp/tmp/restart.txt +0 -0
  136. data/bench/lib/testdata/1-data.txt +0 -0
  137. data/bench/lib/testdata/10-data.txt +0 -0
  138. data/bench/lib/testdata/2-data.txt +0 -0
  139. data/bench/lib/testdata/250-data.txt +0 -0
  140. data/bench/lib/testdata/5-blob_data.txt +0 -0
  141. data/bench/lib/testdata/5-data.txt +0 -0
  142. data/bench/lib/testdata/50-data.txt +0 -0
  143. data/bench/lib/testdata/500-data.txt +0 -0
  144. data/doc/protocol.html +0 -1993
  145. data/spec/coverage/rcov/assets/0.2.3/jquery-1.3.2.min.js +0 -19
  146. data/spec/coverage/rcov/assets/0.2.3/jquery.tablesorter.min.js +0 -15
  147. data/spec/coverage/rcov/assets/0.2.3/print.css +0 -12
  148. data/spec/coverage/rcov/assets/0.2.3/rcov.js +0 -42
  149. data/spec/coverage/rcov/assets/0.2.3/screen.css +0 -270
  150. data/spec/coverage/rcov/index.html +0 -88
  151. data/spec/generator/generator_spec_helper.rb +0 -9
@@ -1,819 +1,819 @@
1
1
  require File.join(File.dirname(__FILE__),'spec_helper')
2
2
 
3
3
  describe "SourceSync" do
4
- it_behaves_like "SharedRhoconnectHelper", :rhoconnect_data => true do
5
- before(:each) do
6
- @s = Source.load(@s_fields[:name],@s_params)
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
- let(:mock_schema) { {"property" => { "name" => "string", "brand" => "string" }, "version" => "1.0"} }
11
-
12
- describe "SourceSync query methods" do
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
- it "should create Rhoconnect::Handler::Query::Engine" do
20
- @ssq.model.is_a?(SampleAdapter).should == true
21
- end
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
- it "should fail to create Rhoconnect::Handler::Query::Engine with InvalidArgumentError without source" do
24
- lambda { Rhoconnect::Handler::Query::Engine.new(nil, nil, {}) }.should raise_error(ArgumentError, 'Unknown source')
25
- end
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
- it "should fail to create Rhoconnect::Handler::Query::Engine with InvalidArgumentError without proc handler" do
28
- lambda { Rhoconnect::Handler::Query::Engine.new(@model, nil) }.should raise_error(ArgumentError, 'Invalid sync handler')
29
- end
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
- it "should raise LoginException if login fails" do
32
- msg = "Error logging in"
33
- @u.login = nil
34
- @ssq.should_receive(:log).with("Model raised login exception: #{msg}")
35
- @ssq.should_receive(:log).with(anything)
36
- @ssq.do_sync
37
- verify_doc_result(@s, :errors => {'login-error'=>{'message'=>msg}})
38
- end
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
- it "should raise LogoffException if logoff fails" do
41
- msg = "Error logging off"
42
- @ssq.should_receive(:log).with("Model raised logoff exception: #{msg}")
43
- @ssq.should_receive(:log).with(anything)
44
- set_test_data('test_db_storage',{},msg,'logoff error')
45
- @ssq.do_sync
46
- verify_doc_result(@s, :errors => {'logoff-error'=>{'message'=>msg}})
47
- end
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
- it "should hold on read on subsequent call of process if default poll interval is not exprired" do
50
- expected = {'1'=>@product1}
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
- set_state('test_db_storage' => {'2'=>@product2})
54
- @ssq.do_sync
55
- verify_doc_result(@s, :md => expected)
82
+ verify_doc_result(@s, {:md => expected,
83
+ :metadata => "{\"foo\":\"bar\"}",
84
+ :metadata_sha1 => "a5e744d0164540d33b1d7ea616c28f2fa97e754a"})
56
85
  end
86
+ end
57
87
 
58
- it "should read on every subsequent call of process if poll interval is set to 0" do
59
- expected = {'2'=>@product2}
60
- @s.poll_interval = 0
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
- it "should never call read on any call of process if poll interval is set to -1" do
69
- @s.poll_interval = -1
70
- set_state('test_db_storage' => {'1'=>@product1})
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
- verify_doc_result(@s, :md => {})
73
- end
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
- it "should process model schema" do
88
- mock_schema_method([SampleAdapter]) do
89
- expected = {'1'=>@product1,'2'=>@product2}
90
- set_state('test_db_storage' => expected)
91
- @ssq.do_sync
92
- verify_doc_result(@s, :md => expected)
93
- JSON.parse(@s.get_value(:schema)).should == mock_schema
94
- @s.get_value(:schema_sha1).should == get_sha1(mock_schema['version'])
95
- end
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
- it "should raise exception if model schema has no version key/value pair" do
99
- mock_schema_no_version_method([SampleAdapter]) do
100
- expected = {'1'=>@product1,'2'=>@product2}
101
- set_state('test_db_storage' => expected)
102
- @ssq.do_sync
103
- errors = {}
104
- @s.lock(:errors) { errors = @s.get_data(:errors) }
105
- errors.empty?().should == false
106
- errors["query-error"]["message"].should == "Mandatory version key is not defined in model schema method"
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
- it "should process model with stash" do
131
+ it "should call methods in model" do
132
+ mock_metadata_method([SampleAdapter, SimpleAdapter]) do
111
133
  expected = {'1'=>@product1,'2'=>@product2}
112
- set_state('test_db_storage' => expected)
113
- @ssq.params[:query] = {'stash_result' => true}
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
- it "should call methods in model" do
131
- mock_metadata_method([SampleAdapter, SimpleAdapter]) do
132
- expected = {'1'=>@product1,'2'=>@product2}
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
- it "should do query with no exception" do
144
- verify_read_operation('query')
145
- end
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
- it "should do query with exception raised" do
152
- verify_read_operation_with_error('query')
153
- end
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
- # 2) if retry_limit is set to N and number of retries exceeded it - update refresh_time
173
- @s.read_state.retry_counter.should == 0
174
- @s.read_state.refresh_time.should > Time.now.to_i
175
- end
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
- it "should do query with exception raised and restore state with succesfull retry" do
178
- @s.retry_limit = 1
179
- msg = "Error during query"
180
- set_test_data('test_db_storage',{},msg,"query error")
181
- res = @ssq.do_sync
182
- verify_doc_result(@s, {:md => {},
183
- :errors => {'query-error'=>{'message'=>msg}}})
184
- # 1) if retry_limit is set to N - then, first N retries should not update refresh_time
185
- @s.read_state.retry_counter.should == 1
186
- @s.read_state.refresh_time.should <= Time.now.to_i
187
-
188
- # try once more (with success)
189
- expected = {'1'=>@product1,'2'=>@product2}
190
- set_test_data('test_db_storage',expected)
191
- @ssq.do_sync
192
- verify_doc_result(@s, {:md => expected,
193
- :errors => {}})
194
- @s.read_state.retry_counter.should == 0
195
- @s.read_state.refresh_time.should > Time.now.to_i
196
- end
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
- it "should reset the retry counter if prev_refresh_time was set more than poll_interval secs ago" do
199
- @s.retry_limit = 3
200
- @s.poll_interval = 2
201
- msg = "Error during query"
202
- set_test_data('test_db_storage',{},msg,"query error")
203
- res = @ssq.do_sync
204
- verify_doc_result(@s, {:md => {},
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
- # 1) if retry_limit is set to N - then, first N retries should not update refresh_time
207
- @s.read_state.retry_counter.should == 1
208
- @s.read_state.refresh_time.should <= Time.now.to_i
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
- it "should do query with exception raised and update refresh time if poll_interval == 0" do
243
- @s.retry_limit = 1
244
- @s.poll_interval = 0
245
- msg = "Error during query"
246
- set_test_data('test_db_storage',{},msg,"query error")
247
- prev_refresh_time = @s.read_state.refresh_time
248
- # make sure refresh time is expired
249
- sleep(1)
250
- res = @ssq.do_sync
251
- verify_doc_result(@s, {:md => {},
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
- it "should not do push_notify for app source" do
293
- @s.partition = :app
294
- u2 = User.create(:login => 'user2')
295
- @a.users << u2.id
296
- data = {'1' => @product1}
297
- PingJob.should_receive(:perform).never
298
- po_handler = lambda { @model.push_objects(params) }
299
- push_objects_handler = Rhoconnect::Handler::PluginCallbacks::Runner.new(po_handler, @model, {'objects' => data})
300
- push_objects_handler.run
301
- end
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
- describe "create" do
305
- before (:each) do
306
- rh = lambda { @model.create(params[:create_object])}
307
- @ssc = Rhoconnect::Handler::Changes::Engine.new(['create'], @model, rh, {})
308
- @queue_name = "create"
309
- @u2_fields = {:login => 'anotheruser'}
310
- @u2 = User.create(@u2_fields)
311
- @u2.password = 'testpass'
312
- @c2_fields = {
313
- :device_type => 'Android',
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
- it "should do create where adapter.create returns nil" do
324
- set_source_queue_state(@s, {@queue_name => [[@s.name, [['2', @product2]]]]}, @c.id, true)
325
- @ssc.create
326
- verify_source_queue_data(@s, @queue_name => [])
327
- verify_doc_result(@c, {:create_errors => {},
328
- :create_links => {}})
329
- end
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
- it "should do create where adapter.create returns object link" do
332
- @product4['link'] = 'test link'
333
- set_source_queue_state(@s, {@queue_name => [[@s.name, [['4', @product4]]]]},@c.id,true)
334
- @ssc.create
335
- verify_source_queue_data(@s, @queue_name => [])
336
- verify_doc_result(@c, {:create_errors => {},
337
- :create_links => {'4'=>{'l'=>'backend_id'}}})
338
- end
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
- it "should raise exception on adapter.create" do
341
- msg = "Error creating record"
342
- data = add_error_object({'4'=>@product4,'2'=>@product2},msg)
343
- source_queue_data = []
344
- data.each do |key, value|
345
- source_queue_data << [key, value]
346
- end
347
- set_source_queue_state(@s, {@queue_name => [[@s.name, source_queue_data]]},@c.id, true)
348
- @ssc.create
349
- verify_doc_result(@c, :create_errors =>
350
- {"#{ERROR}-error"=>{"message"=>msg},ERROR=>data[ERROR]})
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
- it "should properly process creates for 2 users using same queue" do
354
- @product3['link'] = 'test link'
355
- @product4['link'] = 'test link'
356
- set_source_queue_state(@s, {@queue_name => [[@s.name, [['temp_id1', @product3]]]]},@c.id,true)
357
- set_source_queue_state(@s, {@queue_name => [[@s.name, [['temp_id2', @product4]]]]},@c2.id,true)
358
- @s.queue_docname(:create).should == "source:application:#{@s.name}:create"
359
- @ssc.create
360
- verify_source_queue_data(@s, @queue_name => [])
361
- creates_source1 = Source.load(@s.name,
362
- {:user_id => @u.id,:app_id => @a.id})
363
- creates_source2 = Source.load(@s.name,
364
- {:user_id => @u2.id,:app_id => @a.id})
365
- verify_doc_result(creates_source1, {:md => {'backend_id' => @product3}})
366
- verify_doc_result(creates_source2, {:md => {'backend_id' => @product4}})
367
- verify_doc_result(@c, {:create_errors => {},
368
- :create_links => {'temp_id1'=>{'l'=>'backend_id'}}})
369
- verify_doc_result(@c2, {:create_errors => {},
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
- describe "update" do
375
- before (:each) do
376
- rh = lambda { @model.update(params[:update_object])}
377
- @ssu = Rhoconnect::Handler::Changes::Engine.new(['update'], @model, rh, {})
378
- @queue_name = "update"
379
- end
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
- it "should do update with no errors" do
382
- set_source_queue_state(@s, {@queue_name => [[@s.name, [['4', { 'price' => '199.99' }]]]]},@c.id,true)
383
- @ssu.update
384
- verify_source_queue_data(@s, @queue_name => [])
385
- verify_doc_result(@c, :update_errors => {})
386
- end
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
- it "should do update with errors" do
389
- msg = "Error updating record"
390
- data = add_error_object({},msg)
391
- source_queue_data = []
392
- data.each do |key, value|
393
- source_queue_data << [key, value]
394
- end
395
- # this one will be after the error record - and should remain in the queue
396
- source_queue_data << ['4', { 'price' => '199.99' }]
397
- set_source_queue_state(@s, {@queue_name => [[@s.name, source_queue_data]]},@c.id,true)
398
- set_doc_state(@c, :cd => { ERROR => { 'price' => '99.99' } }
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
- describe "delete" do
411
- before (:each) do
412
- rh = lambda { @model.update(params[:delete_object])}
413
- @ssd = Rhoconnect::Handler::Changes::Engine.new(['delete'], @model, rh, {})
414
- @queue_name = "delete"
415
- @u2_fields = {:login => 'anotheruser'}
416
- @u2 = User.create(@u2_fields)
417
- @u2.password = 'testpass'
418
- @c2_fields = {
419
- :device_type => 'Android',
420
- :device_pin => 'efgh',
421
- :device_port => '4444',
422
- :user_id => @u2.id,
423
- :app_id => @a.id
424
- }
425
- @c2 = Client.create(@c2_fields,{:source_name => @s_fields[:name]})
426
- @a.users << @u2.id
427
- end
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
- it "should do delete with no errors" do
430
- set_source_queue_state(@s, {@queue_name => [[@s.name, [['4', @product4]]]]}, @c.id, true)
431
- set_doc_state(@s, :md => {'4'=>@product4,'3'=>@product3})
432
- set_doc_state(@c, :cd => {'4'=>@product4,'3'=>@product3})
433
- @ssd.delete
434
- verify_source_queue_data(@s, @queue_name => [])
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
- it "should do delete with errors" do
441
- msg = "Error delete record"
442
- data = add_error_object({},msg)
443
- source_queue_data = []
444
- data.each do |key, value|
445
- source_queue_data << [key, value]
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
- it "should properly process deletes for 2 users using same queue" do
456
- deletes_source1 = Source.load(@s.name,
457
- {:user_id => @u.id,:app_id => @a.id})
458
- deletes_source2 = Source.load(@s.name,
459
- {:user_id => @u2.id,:app_id => @a.id})
460
- set_source_queue_state(deletes_source1, {@queue_name => [[@s.name, [['4', @product4]]]]},@c.id,true)
461
- set_source_queue_state(deletes_source2, {@queue_name => [[@s.name, [['3', @product3]]]]},@c2.id,true)
462
- deletes_source1.queue_docname(:delete).should == "source:application:#{@s.name}:delete"
463
- deletes_source1.queue_docname(:delete).should == deletes_source2.queue_docname(:delete)
464
- set_doc_state(deletes_source1, :md => {'4'=>@product4,'2'=>@product2,'3'=>@product3})
465
- set_doc_state(deletes_source2, :md => {'4'=>@product4,'3'=>@product3, '1'=>@product1})
466
- set_doc_state(@c, :cd => {'4'=>@product4,'2'=>@product2,'3'=>@product3})
467
- set_doc_state(@c2, :cd => {'4'=>@product4,'3'=>@product3, '1'=>@product1})
468
- @ssd.delete
469
- verify_source_queue_data(@s, @queue_name => [])
470
- verify_doc_result(@c, :delete_errors => {})
471
- verify_doc_result(@c2, :delete_errors => {})
472
- verify_doc_result(deletes_source1, :md => {'2'=>@product2,'3'=>@product3})
473
- verify_doc_result(deletes_source2, :md => {'4'=>@product4, '1'=>@product1})
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
- describe "cud" do
480
- before (:each) do
481
- rh = lambda { @model.send(params[:operation].to_sym, params["#{params[:operation]}_object".to_sym]) }
482
- @sscud = Rhoconnect::Handler::Changes::Engine.new(['create', 'update', 'delete'], @model, rh, {})
483
- end
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
- it "should fail to create Rhoconnect::Handler::Changes::Engine with InvalidArgumentError without source" do
486
- lambda { Rhoconnect::Handler::Changes::Engine.new(['create', 'update', 'delete'], nil, nil, {}) }.should raise_error(ArgumentError, 'Unknown source')
487
- end
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
- it "should fail to create Rhoconnect::Handler::Changes::Engine with InvalidArgumentError without proc handler" do
490
- lambda { Rhoconnect::Handler::Changes::Engine.new(['create', 'update', 'delete'], @model, nil) }.should raise_error(ArgumentError, 'Invalid CUD handler')
491
- end
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
- it "should create Rhoconnect::Handler::Changes::Engine" do
494
- @sscud.model.is_a?(SampleAdapter).should == true
495
- end
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
- it "should do process_cud" do
498
- @create_queue_name = :create
499
- @update_queue_name = :update
500
- @u2_fields = {:login => 'anotheruser'}
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
- it "should detect create conflict and skip the duplicate record creation, but properly update the links" do
551
- set_source_queue_state(@s, {@create_queue_name => [[@s.name, [['4', { 'name' => 'Android', 'link' => '1' }]]]]},@c.id,true)
552
- 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)
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
- it "should detect create conflict in the intermediate state create and skip the duplicate record create" do
584
- 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)
585
- 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)
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
- it "should install find_duplicates_on_update , detect equal objects conflict and skip the duplicate record update" do
616
- SampleAdapter.enable :find_duplicates_on_update
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
- describe "search" do
711
- before (:each) do
712
- rh = lambda { @model.search(params[:search]) }
713
- @sss = Rhoconnect::Handler::Search::Engine.new(@model, @c, rh, {})
714
- end
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
- it "should do search with no exception" do
717
- verify_read_operation('search')
718
- end
719
-
720
- it "should do search with no exception pass through" do
721
- verify_read_operation_pass_through('search')
722
- end
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
- it "should do search with exception raised" do
725
- verify_read_operation_with_error('search')
726
- end
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
- describe "app-level partitioning" do
730
- it "should create app-level masterdoc with '__shared__' docname" do
731
- @s1 = Source.load(@s_fields[:name],@s_params)
732
- @s1.partition = :app
733
- rh = lambda { @model.query(params[:query]) }
734
- @model1 = Rhoconnect::Model::Base.create(@s1)
735
- @ssq = Rhoconnect::Handler::Query::Engine.new(@model1, rh, {})
736
- expected = {'1'=>@product1,'2'=>@product2}
737
- set_state('test_db_storage' => expected)
738
- @ssq.do_sync
739
- verify_doc_result(@s1, :md => expected)
740
- Store.get_store(0).keys("read_state:#{test_app_name}:__shared__*").sort.should ==
741
- [ "read_state:#{test_app_name}:__shared__:SampleAdapter:refresh_time",
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
- def verify_read_operation(operation)
749
- expected = {'1'=>@product1,'2'=>@product2}
750
- set_test_data('test_db_storage',expected)
751
- @s.put_data(:errors,
752
- {"#{operation}-error"=>{'message'=>'failed'}},true)
753
- if operation == 'query'
754
- @ssq.run_query.should == true
755
- verify_doc_result(@s, {:md => expected,
756
- :errors => {}})
757
- else
758
- @sss.run_search.should == true
759
- verify_doc_result(@c, {:search => expected,
760
- :search_errors => {}})
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
- def verify_read_operation_pass_through(operation)
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
- set_test_data('test_db_storage',expected)
767
- @s.put_data(:errors,
768
- {"#{operation}-error"=>{'message'=>'failed'}},true)
769
- @s.pass_through = 'true'
770
- if operation == 'query'
771
- @ssq.run_query.should == expected
772
- verify_doc_result(@s, {:md => {},
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
- else
775
- @sss.run_search.should == expected
776
- verify_doc_result(@c, {:search => {},
777
- :search_errors => {}})
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
- def verify_read_operation_with_error(operation)
782
- msg = "Error during #{operation}"
783
- set_test_data('test_db_storage',{},msg,"#{operation} error")
784
- if operation == 'query'
785
- @ssq.should_receive(:log).with("Model raised #{operation} exception: #{msg}")
786
- @ssq.should_receive(:log).with(anything)
787
- @ssq.run_query.should == true
788
- verify_doc_result(@s, {:md => {},
789
- :errors => {'query-error'=>{'message'=>msg}}})
790
- else
791
- @sss.should_receive(:log).with("Model raised #{operation} exception: #{msg}")
792
- @sss.should_receive(:log).with(anything)
793
- @sss.run_search.should == true
794
- verify_doc_result(@c, {:search => {},
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
- describe "Jobs" do
800
- it "should enqueue process_cud SourceJob" do
801
- @s.cud_queue = :cud
802
- rh = lambda { @model.send(params[:operation].to_sym, params["#{params[:operation]}_object".to_sym]) }
803
- @sscud = Rhoconnect::Handler::Changes::Engine.new(['create', 'update', 'delete'], @model, rh, {})
804
- @sscud.do_cud
805
- Resque.peek(:cud).should == {"args"=>
806
- ["cud", @s.name, @a.name, @u.login, nil], "class"=>"Rhoconnect::SourceJob"}
807
- end
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
- it "should enqueue process_query SourceJob" do
810
- @s.query_queue = :abc
811
- rh = lambda { @model.query(params[:query]) }
812
- @ssq = Rhoconnect::Handler::Query::Engine.new(@model, rh, { :query => {'foo'=>'bar'} })
813
- @ssq.do_sync
814
- Resque.peek(:abc).should == {"args"=>
815
- ["query", @s.name, @a.name, @u.login, {'foo'=>'bar'}], "class"=>"Rhoconnect::SourceJob"}
816
- end
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