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.
- 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
|