restforce-db 2.5.0 → 2.6.0
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.
- checksums.yaml +4 -4
- data/README.md +16 -0
- data/lib/restforce/db/associator.rb +21 -9
- data/lib/restforce/db/initializer.rb +4 -4
- data/lib/restforce/db/model.rb +52 -0
- data/lib/restforce/db/synchronizer.rb +12 -12
- data/lib/restforce/db/timestamp_cache.rb +1 -1
- data/lib/restforce/db/version.rb +1 -1
- data/lib/restforce/db/worker.rb +21 -22
- 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
- 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
- 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
- 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
- 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
- data/test/cassettes/Restforce_DB_Worker/a_race_condition_during_synchronization/overrides_the_stale-but-more-recent_name_on_the_Salesforce.yml +551 -0
- data/test/lib/restforce/db/model_test.rb +86 -14
- data/test/lib/restforce/db/timestamp_cache_test.rb +5 -4
- data/test/lib/restforce/db/worker_test.rb +63 -0
- metadata +7 -2
@@ -4,25 +4,97 @@ describe Restforce::DB::Model do
|
|
4
4
|
|
5
5
|
configure!
|
6
6
|
|
7
|
-
|
8
|
-
|
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.
|
17
|
-
|
18
|
-
|
19
|
-
|
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
|
-
|
25
|
-
|
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(:
|
12
|
-
let(:
|
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.
|
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-
|
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
|