restforce-db 2.5.0 → 2.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (19) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +16 -0
  3. data/lib/restforce/db/associator.rb +21 -9
  4. data/lib/restforce/db/initializer.rb +4 -4
  5. data/lib/restforce/db/model.rb +52 -0
  6. data/lib/restforce/db/synchronizer.rb +12 -12
  7. data/lib/restforce/db/timestamp_cache.rb +1 -1
  8. data/lib/restforce/db/version.rb +1 -1
  9. data/lib/restforce/db/worker.rb +21 -22
  10. data/test/cassettes/Restforce_DB_Associator/_run/given_a_BelongsTo_association/given_another_record_for_association/when_the_Salesforce_association_is_out_of_date/updates_the_association_ID_in_Salesforce.yml +188 -112
  11. data/test/cassettes/Restforce_DB_Associator/_run/given_a_BelongsTo_association/given_another_record_for_association/when_the_database_association_is_out_of_date/updates_the_associated_record_in_the_database.yml +175 -99
  12. data/test/cassettes/Restforce_DB_Model/given_a_database_model_which_includes_the_module/_force_sync_/given_a_previously-synchronized_record_for_a_mapped_model/force-updates_both_synchronized_records.yml +288 -0
  13. data/test/cassettes/Restforce_DB_Model/given_a_database_model_which_includes_the_module/_force_sync_/given_an_unsynchronized_record_for_a_mapped_model/creates_a_matching_record_in_Salesforce.yml +251 -0
  14. data/test/cassettes/Restforce_DB_Worker/a_race_condition_during_synchronization/does_not_change_the_user-entered_name_on_the_database_record.yml +512 -0
  15. data/test/cassettes/Restforce_DB_Worker/a_race_condition_during_synchronization/overrides_the_stale-but-more-recent_name_on_the_Salesforce.yml +551 -0
  16. data/test/lib/restforce/db/model_test.rb +86 -14
  17. data/test/lib/restforce/db/timestamp_cache_test.rb +5 -4
  18. data/test/lib/restforce/db/worker_test.rb +63 -0
  19. metadata +7 -2
@@ -4,25 +4,97 @@ describe Restforce::DB::Model do
4
4
 
5
5
  configure!
6
6
 
7
- let(:database_model) { CustomObject }
8
- let(:salesforce_model) { "CustomObject__c" }
7
+ describe "given a database model which includes the module" do
8
+ let(:database_model) { CustomObject }
9
+ let(:salesforce_model) { "CustomObject__c" }
9
10
 
10
- before do
11
- database_model.send(:include, Restforce::DB::Model)
12
- end
13
-
14
- describe ".sync_with" do
15
11
  before do
16
- database_model.sync_with(salesforce_model) do
17
- maps(
18
- name: "Name",
19
- example: "Example_Field__c",
20
- )
12
+ database_model.send(:include, Restforce::DB::Model)
13
+ end
14
+
15
+ describe ".sync_with" do
16
+ before do
17
+ database_model.sync_with(salesforce_model) do
18
+ maps(
19
+ name: "Name",
20
+ example: "Example_Field__c",
21
+ )
22
+ end
23
+ end
24
+
25
+ it "adds a mapping to the global Restforce::DB::Registry" do
26
+ expect(Restforce::DB::Registry[database_model]).to_not_be :empty?
21
27
  end
22
28
  end
23
29
 
24
- it "adds a mapping to the global Restforce::DB::Registry" do
25
- expect(Restforce::DB::Registry[database_model]).to_not_be :empty?
30
+ describe "#force_sync!", :vcr do
31
+ let(:mapping) { Restforce::DB::Registry[database_model].first }
32
+
33
+ before do
34
+ database_model.sync_with(salesforce_model) do
35
+ maps(
36
+ name: "Name",
37
+ example: "Example_Field__c",
38
+ )
39
+ end
40
+ end
41
+
42
+ describe "given an unpersisted record for a mapped model" do
43
+ let(:record) { database_model.new }
44
+
45
+ it "does nothing" do
46
+ expect(record.force_sync!).to_equal false
47
+ end
48
+ end
49
+
50
+ describe "given an unsynchronized record for a mapped model" do
51
+ let(:record) { database_model.create!(attributes) }
52
+ let(:attributes) do
53
+ {
54
+ name: "Frederick's Flip-flop",
55
+ example: "Yes, we only have the left one.",
56
+ }
57
+ end
58
+
59
+ before do
60
+ expect(record.force_sync!).to_equal true
61
+ Salesforce.records << [salesforce_model, record.salesforce_id]
62
+ end
63
+
64
+ it "creates a matching record in Salesforce" do
65
+ salesforce_record = mapping.salesforce_record_type.find(
66
+ record.salesforce_id,
67
+ ).record
68
+
69
+ expect(salesforce_record.Name).to_equal attributes[:name]
70
+ expect(salesforce_record.Example_Field__c).to_equal attributes[:example]
71
+ end
72
+ end
73
+
74
+ describe "given a previously-synchronized record for a mapped model" do
75
+ let(:attributes) do
76
+ {
77
+ name: "Sally's Seashells",
78
+ example: "She sells them down by the seashore.",
79
+ }
80
+ end
81
+ let(:salesforce_id) do
82
+ Salesforce.create!(
83
+ salesforce_model,
84
+ "Name" => attributes[:name],
85
+ "Example_Field__c" => attributes[:example],
86
+ )
87
+ end
88
+ let(:record) { database_model.create!(attributes.merge(salesforce_id: salesforce_id)) }
89
+
90
+ it "force-updates both synchronized records" do
91
+ record.update!(name: "Sarah's Seagulls")
92
+ expect(record.force_sync!).to_equal true
93
+
94
+ salesforce_record = mapping.salesforce_record_type.find(salesforce_id).record
95
+ expect(salesforce_record.Name).to_equal record.name
96
+ end
97
+ end
26
98
  end
27
99
  end
28
100
 
@@ -8,8 +8,9 @@ describe Restforce::DB::TimestampCache do
8
8
 
9
9
  let(:timestamp) { Time.now }
10
10
  let(:id) { "some-id" }
11
- let(:instance_class) { Struct.new(:id, :last_update) }
12
- let(:instance) { instance_class.new(id, timestamp) }
11
+ let(:record_type) { "CustomObject__c" }
12
+ let(:instance_class) { Struct.new(:id, :record_type, :last_update) }
13
+ let(:instance) { instance_class.new(id, record_type, timestamp) }
13
14
 
14
15
  describe "#cache_timestamp" do
15
16
  before { cache.cache_timestamp instance }
@@ -20,7 +21,7 @@ describe Restforce::DB::TimestampCache do
20
21
  end
21
22
 
22
23
  describe "#changed?" do
23
- let(:new_instance) { instance_class.new(id, timestamp) }
24
+ let(:new_instance) { instance_class.new(id, record_type, timestamp) }
24
25
 
25
26
  describe "when the passed instance was not internally updated" do
26
27
  before do
@@ -57,7 +58,7 @@ describe Restforce::DB::TimestampCache do
57
58
  end
58
59
 
59
60
  describe "and a stale timestamp is cached" do
60
- let(:new_instance) { instance_class.new(id, timestamp + 1) }
61
+ let(:new_instance) { instance_class.new(id, record_type, timestamp + 1) }
61
62
  before { cache.cache_timestamp instance }
62
63
 
63
64
  it "returns true" do
@@ -0,0 +1,63 @@
1
+ require_relative "../../../test_helper"
2
+
3
+ describe Restforce::DB::Worker do
4
+
5
+ configure!
6
+ mappings!
7
+
8
+ let(:worker) { Restforce::DB::Worker.new(delay: 0) }
9
+ let(:runner) { worker.send(:runner) }
10
+
11
+ describe "a race condition during synchronization", vcr: { match_requests_on: [:method, VCR.request_matchers.uri_without_param(:q)] } do
12
+ let(:database_record) { mapping.database_model.create! }
13
+ let(:new_name) { "A New User-Entered Name" }
14
+
15
+ before do
16
+ # 0. A record is added to the database.
17
+ database_record
18
+
19
+ # 1. The first loop runs
20
+
21
+ ## 1b. The record is synced to Salesforce.
22
+ worker.send :reset!
23
+ worker.send :propagate, mapping
24
+ expect(database_record.reload).to_be :salesforce_id?
25
+ Salesforce.records << [salesforce_model, database_record.salesforce_id]
26
+
27
+ ## 1c. The database record is updated (externally) by a user.
28
+ database_record.update! name: new_name
29
+
30
+ # We stub `last_update` to get around issues with VCR's cached timestamp;
31
+ # we need the Salesforce record timestamp to be contemporary with this
32
+ # test run.
33
+ Restforce::DB::Instances::Salesforce.stub_any_instance(:last_update, Time.now) do
34
+
35
+ ## 1d. The record in Salesforce is touched by another mapping.
36
+ salesforce_instance = mapping.salesforce_record_type.find(
37
+ database_record.salesforce_id,
38
+ )
39
+ salesforce_instance.update! "Name" => "A Stale Synchronized Name"
40
+ runner.cache_timestamp salesforce_instance
41
+
42
+ # 2. The second loop runs.
43
+ # We sleep here to ensure we pick up our manual changes.
44
+ sleep 1 if VCR.current_cassette.recording?
45
+ worker.send :reset!
46
+ worker.send :collect, mapping
47
+ worker.send :synchronize, mapping
48
+ end
49
+ end
50
+
51
+ it "does not change the user-entered name on the database record" do
52
+ expect(database_record.reload.name).to_equal new_name
53
+ end
54
+
55
+ it "overrides the stale-but-more-recent name on the Salesforce" do
56
+ salesforce_instance = mapping.salesforce_record_type.find(
57
+ database_record.salesforce_id,
58
+ )
59
+
60
+ expect(salesforce_instance.record.Name).to_equal new_name
61
+ end
62
+ end
63
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: restforce-db
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.5.0
4
+ version: 2.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Horner
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-06-15 00:00:00.000000000 Z
11
+ date: 2015-06-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -298,6 +298,8 @@ files:
298
298
  - test/cassettes/Restforce_DB_Instances_Salesforce/_update_/updates_the_record_in_Salesforce_with_the_passed_attributes.yml
299
299
  - test/cassettes/Restforce_DB_Instances_Salesforce/_updated_internally_/when_another_user_made_the_last_change/returns_false.yml
300
300
  - test/cassettes/Restforce_DB_Instances_Salesforce/_updated_internally_/when_our_client_made_the_last_change/returns_true.yml
301
+ - test/cassettes/Restforce_DB_Model/given_a_database_model_which_includes_the_module/_force_sync_/given_a_previously-synchronized_record_for_a_mapped_model/force-updates_both_synchronized_records.yml
302
+ - test/cassettes/Restforce_DB_Model/given_a_database_model_which_includes_the_module/_force_sync_/given_an_unsynchronized_record_for_a_mapped_model/creates_a_matching_record_in_Salesforce.yml
301
303
  - test/cassettes/Restforce_DB_RecordTypes_Salesforce/_all/returns_a_list_of_the_existing_records_in_Salesforce.yml
302
304
  - test/cassettes/Restforce_DB_RecordTypes_Salesforce/_create_/creates_a_record_in_Salesforce_from_the_passed_database_record_s_attributes.yml
303
305
  - test/cassettes/Restforce_DB_RecordTypes_Salesforce/_create_/updates_the_database_record_with_the_Salesforce_record_s_ID.yml
@@ -317,6 +319,8 @@ files:
317
319
  - test/cassettes/Restforce_DB_Synchronizer/_run/given_a_Salesforce_record_with_an_associated_database_record/when_the_changes_are_current/updates_the_database_record.yml
318
320
  - test/cassettes/Restforce_DB_Synchronizer/_run/given_a_Salesforce_record_with_an_associated_database_record/when_the_changes_are_current/updates_the_salesforce_record.yml
319
321
  - test/cassettes/Restforce_DB_Synchronizer/_run/given_a_Salesforce_record_with_no_associated_database_record/does_nothing_for_this_specific_mapping.yml
322
+ - test/cassettes/Restforce_DB_Worker/a_race_condition_during_synchronization/does_not_change_the_user-entered_name_on_the_database_record.yml
323
+ - test/cassettes/Restforce_DB_Worker/a_race_condition_during_synchronization/overrides_the_stale-but-more-recent_name_on_the_Salesforce.yml
320
324
  - test/lib/restforce/db/accumulator_test.rb
321
325
  - test/lib/restforce/db/adapter_test.rb
322
326
  - test/lib/restforce/db/association_cache_test.rb
@@ -347,6 +351,7 @@ files:
347
351
  - test/lib/restforce/db/synchronizer_test.rb
348
352
  - test/lib/restforce/db/timestamp_cache_test.rb
349
353
  - test/lib/restforce/db/tracker_test.rb
354
+ - test/lib/restforce/db/worker_test.rb
350
355
  - test/lib/restforce/db_test.rb
351
356
  - test/support/active_record.rb
352
357
  - test/support/database_cleaner.rb