bulkforce 1.0.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.
@@ -0,0 +1,99 @@
1
+ #encoding: utf-8
2
+ require "spec_helper"
3
+
4
+ describe Bulkforce::Connection do
5
+ let(:subject) do
6
+ described_class.new(
7
+ session_id: session_id,
8
+ instance: instance,
9
+ api_version: api_version,
10
+ )
11
+ end
12
+
13
+ let(:session_id) { "org_id!session_id" }
14
+ let(:instance) { "eu2" }
15
+ let(:api_version) { "33.0" }
16
+
17
+ {
18
+ login: 0,
19
+ create_job: 4,
20
+ close_job: 1,
21
+ query_batch: 2,
22
+ query_batch_result_id: 2,
23
+ query_batch_result_data: 3,
24
+ }.each do |method_name, num_of_params|
25
+ describe "##{method_name}" do
26
+ it "delegates correctly to Http class" do
27
+ allow(Bulkforce::Helper).to receive(:parse_csv).and_return([])
28
+ expect(Bulkforce::Http)
29
+ .to receive(method_name)
30
+ .and_return({})
31
+ subject.send(method_name, *Array.new(num_of_params))
32
+ end
33
+ end
34
+ end
35
+
36
+ describe "#add_query" do
37
+ it "delegates correctly to Http class" do
38
+ expect(Bulkforce::Http).to receive(:add_batch)
39
+ .and_return({})
40
+ subject.add_query(nil, nil)
41
+ end
42
+ end
43
+
44
+ describe "#org_id" do
45
+ let(:org_id) { "00D50000000IehZ" }
46
+ let(:session_id) { "#{org_id}!AQcAQH0dMHZfz972Szmpkb58urFRkgeBGsxL_QJWwYMfAbUeeG7c1E6LYUfiDUkWe6H34r1AAwOR8B8fLEz6n04NPGRrq0FM" }
47
+
48
+ it "returns correct OrgId" do
49
+ expect(subject.org_id).to eq(org_id)
50
+ end
51
+ end
52
+
53
+ describe "#add_batch" do
54
+ it "delegates correctly to underlying classes" do
55
+ expect(Bulkforce::Http).to receive(:add_batch)
56
+ .and_return({})
57
+ expect(Bulkforce::Helper).to receive(:records_to_csv)
58
+ .and_return("My,Awesome,CSV")
59
+ subject.add_batch(nil, [{"non_emtpy" => "records"}])
60
+ end
61
+
62
+ it "returns -1 for nil input" do
63
+ return_code = subject.add_batch(nil, nil)
64
+ expect(return_code).to eq(-1)
65
+ end
66
+
67
+ it "returns -1 for empty input" do
68
+ return_code = subject.add_batch(nil, [])
69
+ expect(return_code).to eq(-1)
70
+ end
71
+ end
72
+
73
+ describe "#query_batch_result_id" do
74
+ let(:job_id) {"12345"}
75
+ let(:batch_id) {"67890"}
76
+
77
+ context "with a single page of results" do
78
+ let(:single_result) {{:result=>"M75200000001Vgt", :@xmlns=>"http://www.force.com/2009/06/asyncapi/dataload"}}
79
+ it "returns the result_id as a string" do
80
+ expect(Bulkforce::Http).to receive(:query_batch_result_id).
81
+ with(instance, session_id, job_id, batch_id, api_version).
82
+ and_return(single_result)
83
+
84
+ expect(subject.query_batch_result_id(job_id, batch_id)).to eq(single_result)
85
+ end
86
+ end
87
+
88
+ context "with an array of page of results" do
89
+ let(:multiple_result) {{:result=>["752M00000001Vgt", "752M00000001Vgy"], :@xmlns=>"http://www.force.com/2009/06/asyncapi/dataload"}}
90
+ it "returns the result_id as a string" do
91
+ expect(Bulkforce::Http).to receive(:query_batch_result_id).
92
+ with(instance, session_id, job_id, batch_id, api_version).
93
+ and_return(multiple_result)
94
+
95
+ expect(subject.query_batch_result_id(job_id, batch_id)).to eq(multiple_result)
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,249 @@
1
+ #encoding: utf-8
2
+ require "spec_helper"
3
+
4
+ describe Bulkforce::Helper do
5
+ describe ".records_to_csv" do
6
+ it "returns valid csv for single record" do
7
+ input = [
8
+ {"Title" => "Awesome Title", "Name" => "A name"},
9
+ ]
10
+
11
+ expected_csv = "\"Title\",\"Name\"\n" \
12
+ "\"Awesome Title\",\"A name\"\n"
13
+ expect(described_class.records_to_csv(input)).to eq(expected_csv)
14
+ end
15
+
16
+ it "returns valid csv for basic records" do
17
+ input = [
18
+ {"Title" => "Awesome Title", "Name" => "A name"},
19
+ {"Title" => "A second Title", "Name" => "A second name"},
20
+ ]
21
+
22
+ expected_csv = "\"Title\",\"Name\"\n" \
23
+ "\"Awesome Title\",\"A name\"\n" \
24
+ "\"A second Title\",\"A second name\"\n"
25
+ expect(described_class.records_to_csv(input)).to eq(expected_csv)
26
+ end
27
+
28
+ it "returns valid csv when first row misses a key" do
29
+ input = [
30
+ {"Title" => "Awesome Title", "Name" => "A name"},
31
+ {"Title" => "A second Title", "Name" => "A second name", "Something" => "Else"},
32
+ ]
33
+
34
+ expected_csv = "\"Title\",\"Name\",\"Something\"\n" \
35
+ "\"Awesome Title\",\"A name\",\"\"\n" \
36
+ "\"A second Title\",\"A second name\",\"Else\"\n"
37
+ expect(described_class.records_to_csv(input)).to eq(expected_csv)
38
+ end
39
+
40
+ it "correctly converts Array to Multi-Picklist" do
41
+ input = [
42
+ {"Title" => "Awesome Title", "Picklist" => ["Several", "Values"]},
43
+ {"Title" => "A second Title", "Picklist" => ["SingleValue"]},
44
+ ]
45
+
46
+ expected_csv = "\"Title\",\"Picklist\"\n" \
47
+ "\"Awesome Title\",\"Several;Values\"\n" \
48
+ "\"A second Title\",\"SingleValue\"\n"
49
+ expect(described_class.records_to_csv(input)).to eq(expected_csv)
50
+ end
51
+
52
+ it "returns valid csv when order of keys varies" do
53
+ input = [
54
+ {"Title" => "Awesome Title", "Name" => "A name"},
55
+ {"Name" => "A second name", "Title" => "A second Title"},
56
+ ]
57
+
58
+ expected_csv = "\"Title\",\"Name\"\n" \
59
+ "\"Awesome Title\",\"A name\"\n" \
60
+ "\"A second Title\",\"A second name\"\n"
61
+ expect(described_class.records_to_csv(input)).to eq(expected_csv)
62
+ end
63
+
64
+ it "correctly converts empty arrays, nil values and blank strings to #N/A" do
65
+ input = [
66
+ {"Title" => nil, "Picklist" => ["Several", "Values"]},
67
+ {"Title" => "", "Picklist" => []},
68
+ ]
69
+
70
+ expected_csv = "\"Title\",\"Picklist\"\n" \
71
+ "\"#N/A\",\"Several;Values\"\n" \
72
+ "\"#N/A\",\"#N/A\"\n"
73
+ expect(described_class.records_to_csv(input)).to eq(expected_csv)
74
+ end
75
+ end
76
+
77
+ describe ".fetch_instance_from_server_url" do
78
+ let(:basic_api_server_url) {
79
+ "https://cs7-api.salesforce.com/services/Soap/u/28.0/00CS00000095Y5b"
80
+ }
81
+
82
+ let(:basic_server_url) {
83
+ "https://eu1.salesforce.com/services/Soap/u/28.0/00EU00000096Y8c"
84
+ }
85
+
86
+ let(:named_server_url) {
87
+ "https://supercustomname.my.salesforce.com/services/Soap/u/28.0/00EH0000001jNQu"
88
+ }
89
+
90
+ it "returns correct instance for regular salesforce server url" do
91
+ expect(described_class.fetch_instance_from_server_url(basic_server_url))
92
+ .to eq("eu1")
93
+ end
94
+
95
+ it "returns correct instance for api salesforce server url" do
96
+ expect(described_class.fetch_instance_from_server_url(basic_api_server_url))
97
+ .to eq("cs7")
98
+ end
99
+
100
+ it "returns correct instance for named salesforce server url" do
101
+ expect(described_class.fetch_instance_from_server_url(named_server_url))
102
+ .to eq("supercustomname.my")
103
+ end
104
+ end
105
+
106
+ describe ".attachment_keys" do
107
+ let(:records_with_attachment) do
108
+ prefix = Dir.tmpdir
109
+ FileUtils.touch("#{prefix}/attachment.pdf")
110
+ [
111
+ {
112
+ "normal_key" => "normal_value1",
113
+ "attachment_key" => nil,
114
+ },
115
+ {
116
+ "normal_key" => "normal_value2",
117
+ "attachment_key" => File.new("#{prefix}/attachment.pdf"),
118
+ }
119
+ ]
120
+ end
121
+
122
+ let(:records_with_multiple_attachment) do
123
+ prefix = Dir.tmpdir
124
+ FileUtils.touch("#{prefix}/attachment1.pdf")
125
+ FileUtils.touch("#{prefix}/attachment2.pdf")
126
+ FileUtils.touch("#{prefix}/attachment3.pdf")
127
+ [
128
+ {
129
+ "normal_key" => "normal_value1",
130
+ "attachment_key" => File.new("#{prefix}/attachment1.pdf"),
131
+ "another_attachment_key" => File.new("#{prefix}/attachment2.pdf"),
132
+ },
133
+ {
134
+ "normal_key" => "normal_value2",
135
+ "attachment_key" => File.new("#{prefix}/attachment3.pdf"),
136
+ }
137
+ ]
138
+ end
139
+
140
+ let(:records_without_attachment) do
141
+ [
142
+ {
143
+ "normal_key" => "normal_value1",
144
+ "another_normal_key" => "another_normal_value1",
145
+ },
146
+ {
147
+ "normal_key" => "normal_value2",
148
+ "another_normal_key" => "another_normal_value2",
149
+ }
150
+ ]
151
+ end
152
+
153
+ it "returns correct keys for single attachment key" do
154
+ expect(described_class.attachment_keys(records_with_attachment))
155
+ .to eq(["attachment_key"])
156
+ end
157
+
158
+ it "returns correct keys for multiple attachment keys" do
159
+ expect(described_class.attachment_keys(records_with_multiple_attachment))
160
+ .to eq(["attachment_key", "another_attachment_key"])
161
+ end
162
+
163
+ it "returns false for no attachment" do
164
+ expect(described_class.attachment_keys(records_without_attachment))
165
+ .to eq([])
166
+ end
167
+ end
168
+
169
+ describe ".transform_values!" do
170
+ let(:records_with_attachment) do
171
+ prefix = Dir.tmpdir
172
+ FileUtils.touch("#{prefix}/attachment.pdf")
173
+ [
174
+ {
175
+ "normal_key" => "normal_value1",
176
+ "attachment_key" => nil,
177
+ },
178
+ {
179
+ "normal_key" => "normal_value2",
180
+ "attachment_key" => File.new("#{prefix}/attachment.pdf"),
181
+ }
182
+ ]
183
+ end
184
+
185
+ it "transforms values correctly" do
186
+ expect(File).to receive(:absolute_path).and_return("/an/absolute/path")
187
+ expected_output = [
188
+ {
189
+ "normal_key" => "normal_value1",
190
+ "attachment_key" => nil,
191
+ },
192
+ {
193
+ "normal_key" => "normal_value2",
194
+ "attachment_key" => "#an/absolute/path",
195
+ }
196
+ ]
197
+
198
+ input = records_with_attachment
199
+ described_class.transform_values!(input,["attachment_key"])
200
+ expect(input).to eq(expected_output)
201
+ end
202
+
203
+ it "yields absolute path" do
204
+ expect(File).to receive(:absolute_path).and_return("/an/absolute/path")
205
+ input = records_with_attachment
206
+ expect do |blk|
207
+ described_class.transform_values!(input,["attachment_key"], &blk)
208
+ end.to yield_with_args("/an/absolute/path")
209
+ end
210
+ end
211
+
212
+ describe ".absolute_to_relative_path" do
213
+ let(:unix_path) { "/a/unix/path" }
214
+ let(:windows_path_backslash) { "C:\\a\\backslash\\path" }
215
+ let(:windows_path_forwardslash) { "C:/a/forwardslash/path" }
216
+
217
+ it "strips unix path correctly" do
218
+ expect(described_class.absolute_to_relative_path(unix_path,"")).
219
+ to eq("a/unix/path")
220
+ end
221
+
222
+ it "strips windows path with backslash correctly" do
223
+ expect(described_class.absolute_to_relative_path(windows_path_backslash,"")).
224
+ to eq("a\\backslash\\path")
225
+ end
226
+
227
+ it "strips windows path with forwardslash correctly" do
228
+ expect(described_class.absolute_to_relative_path(windows_path_forwardslash,"")).
229
+ to eq("a/forwardslash/path")
230
+ end
231
+ end
232
+
233
+ describe ".parse_csv" do
234
+ let(:csv_string) {
235
+ "Id,my_external_id__c\n" \
236
+ "003M000057GH39aIAD,K-00J799\n" \
237
+ "003M001200KO82cIAD,K-015699"
238
+ }
239
+ let(:expected_result) {
240
+ [
241
+ { "Id" => "003M000057GH39aIAD", "my_external_id__c" => "K-00J799" },
242
+ { "Id" => "003M001200KO82cIAD", "my_external_id__c" => "K-015699" },
243
+ ]
244
+ }
245
+ it "correctly transforms csv string" do
246
+ expect(described_class.parse_csv(csv_string)).to eq(expected_result)
247
+ end
248
+ end
249
+ end
@@ -0,0 +1,375 @@
1
+ #encoding: utf-8
2
+ require "spec_helper"
3
+
4
+ describe Bulkforce::Http do
5
+ describe "#process_http_request" do
6
+ let(:post_request) do
7
+ Bulkforce::Http::Request.new(
8
+ :post,
9
+ "test.host",
10
+ "/",
11
+ "post body",
12
+ {"X-SFDC-Session" => "super_secret"})
13
+ end
14
+
15
+ let(:get_request) do
16
+ Bulkforce::Http::Request.new(:get, "test.host", "/", "", [])
17
+ end
18
+
19
+ it "returns a response object" do
20
+ expected_body = "correct result"
21
+ stub_request(:post, "https://test.host")
22
+ .with(
23
+ body: post_request.body,
24
+ headers: post_request.headers)
25
+ .to_return(:body => expected_body)
26
+ res = Bulkforce::Http.process_http_request(post_request)
27
+ expect(res).to eq(expected_body)
28
+ end
29
+ end
30
+
31
+ describe "#login" do
32
+ let(:login_error_message) do
33
+ "INVALID_LOGIN: Invalid username, password, security token;" \
34
+ "or user locked out."
35
+ end
36
+
37
+ let(:sf_instance) {"cs7"}
38
+
39
+ let(:login_success) do
40
+ %Q{<?xml version="1.0" encoding="UTF-8"?>
41
+ <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns="urn:partner.soap.sforce.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
42
+ <soapenv:Body>
43
+ <loginResponse>
44
+ <result>
45
+ <metadataServerUrl>https://#{sf_instance}-api.salesforce.com/services/Soap/m/27.0/00EU00000095Y5b</metadataServerUrl>
46
+ <passwordExpired>false</passwordExpired>
47
+ <sandbox>true</sandbox>
48
+ <serverUrl>https://#{sf_instance}-api.salesforce.com/services/Soap/u/27.0/00EU00000095Y5b</serverUrl>
49
+ <sessionId>00DM00000099X9a!AQ7AQJ9BjrYfF2h_G9_VERsCRVjTMAPaWjz2zuqqRBduYvKCmHexVbKdtxFMrgTnV9Xi.M80AhIkjnwuEVxI00ChG09._Q_X</sessionId>
50
+ <userId>005K001100243YPIAY</userId>
51
+ <userInfo>
52
+ <accessibilityMode>false</accessibilityMode>
53
+ <currencySymbol xsi:nil="true"/>
54
+ <orgAttachmentFileSizeLimit>2621440</orgAttachmentFileSizeLimit>
55
+ <orgDefaultCurrencyIsoCode xsi:nil="true"/>
56
+ <orgDisallowHtmlAttachments>false</orgDisallowHtmlAttachments>
57
+ <orgHasPersonAccounts>false</orgHasPersonAccounts>
58
+ <organizationId>00ID0000OrgFoo</organizationId>
59
+ <organizationMultiCurrency>true</organizationMultiCurrency>
60
+ <organizationName>Hinz &amp; Kunz</organizationName>
61
+ <profileId>00eA0000000nJEY</profileId>
62
+ <roleId xsi:nil="true"/>
63
+ <sessionSecondsValid>3600</sessionSecondsValid>
64
+ <userDefaultCurrencyIsoCode>EUR</userDefaultCurrencyIsoCode>
65
+ <userEmail>theadmin@example.com</userEmail>
66
+ <userFullName>John Doe</userFullName>
67
+ <userId>005D0000002b3SPIAY</userId>
68
+ <userLanguage>de</userLanguage>
69
+ <userLocale>de_DE_EURO</userLocale>
70
+ <userName>theadmin@exammple.com.euvconfig</userName>
71
+ <userTimeZone>Europe/Berlin</userTimeZone>
72
+ <userType>Standard</userType>
73
+ <userUiSkin>Theme42</userUiSkin>
74
+ </userInfo>
75
+ </result>
76
+ </loginResponse>
77
+ </soapenv:Body>
78
+ </soapenv:Envelope>}
79
+ end
80
+
81
+ let(:login_success_new) do
82
+ %Q{<?xml version="1.0" encoding="UTF-8"?>
83
+ <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns="urn:partner.soap.sforce.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
84
+ <soapenv:Body>
85
+ <loginResponse>
86
+ <result>
87
+ <metadataServerUrl>https://#{sf_instance}.salesforce.com/services/Soap/m/27.0/00EU00000095Y5b</metadataServerUrl>
88
+ <passwordExpired>false</passwordExpired>
89
+ <sandbox>true</sandbox>
90
+ <serverUrl>https://#{sf_instance}.salesforce.com/services/Soap/u/27.0/00EU00000095Y5b</serverUrl>
91
+ <sessionId>00DM00000099X9a!AQ7AQJ9BjrYfF2h_G9_VERsCRVjTMAPaWjz2zuqqRBduYvKCmHexVbKdtxFMrgTnV9Xi.M80AhIkjnwuEVxI00ChG09._Q_X</sessionId>
92
+ <userId>005K001100243YPIAY</userId>
93
+ <userInfo>
94
+ <accessibilityMode>false</accessibilityMode>
95
+ <currencySymbol xsi:nil="true"/>
96
+ <orgAttachmentFileSizeLimit>2621440</orgAttachmentFileSizeLimit>
97
+ <orgDefaultCurrencyIsoCode xsi:nil="true"/>
98
+ <orgDisallowHtmlAttachments>false</orgDisallowHtmlAttachments>
99
+ <orgHasPersonAccounts>false</orgHasPersonAccounts>
100
+ <organizationId>00ID0000OrgFoo</organizationId>
101
+ <organizationMultiCurrency>true</organizationMultiCurrency>
102
+ <organizationName>Hinz &amp; Kunz</organizationName>
103
+ <profileId>00eA0000000nJEY</profileId>
104
+ <roleId xsi:nil="true"/>
105
+ <sessionSecondsValid>3600</sessionSecondsValid>
106
+ <userDefaultCurrencyIsoCode>EUR</userDefaultCurrencyIsoCode>
107
+ <userEmail>theadmin@example.com</userEmail>
108
+ <userFullName>John Doe</userFullName>
109
+ <userId>005D0000002b3SPIAY</userId>
110
+ <userLanguage>de</userLanguage>
111
+ <userLocale>de_DE_EURO</userLocale>
112
+ <userName>theadmin@exammple.com.euvconfig</userName>
113
+ <userTimeZone>Europe/Berlin</userTimeZone>
114
+ <userType>Standard</userType>
115
+ <userUiSkin>Theme42</userUiSkin>
116
+ </userInfo>
117
+ </result>
118
+ </loginResponse>
119
+ </soapenv:Body>
120
+ </soapenv:Envelope>}
121
+ end
122
+
123
+ let(:login_error) do
124
+ %Q{<?xml version="1.0" encoding="UTF-8"?>
125
+ <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
126
+ xmlns:sf="urn:fault.partner.soap.sforce.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
127
+ <soapenv:Body>
128
+ <soapenv:Fault>
129
+ <faultcode>INVALID_LOGIN</faultcode>
130
+ <faultstring>#{login_error_message}</faultstring>
131
+ <detail>
132
+ <sf:LoginFault xsi:type="sf:LoginFault">
133
+ <sf:exceptionCode>INVALID_LOGIN</sf:exceptionCode>
134
+ <sf:exceptionMessage>Invalid username, password, │Your bundle is complete!security token; or user locked out.
135
+ </sf:exceptionMessage>
136
+ </sf:LoginFault>
137
+ </detail>
138
+ </soapenv:Fault>
139
+ </soapenv:Body>
140
+ </soapenv:Envelope>}
141
+ end
142
+
143
+ it "raises an error for faulty login" do
144
+ expect(Bulkforce::Http).to receive(:process_http_request)
145
+ .and_return(login_error)
146
+ expect{ Bulkforce::Http.login("a","b","c", "d") }
147
+ .to raise_error(RuntimeError, login_error_message)
148
+ end
149
+
150
+ it "returns hash for correct login" do
151
+ [login_success, login_success_new].each do |login_response|
152
+ expect(Bulkforce::Http).to receive(:process_http_request)
153
+ .and_return(login_response)
154
+ result = Bulkforce::Http.login("a","b","c", "d")
155
+ expect(result).to be_a(Hash)
156
+ expect(result).to have_key(:session_id)
157
+ expect(result).to have_key(:server_url)
158
+ expect(result[:instance]).to eq(sf_instance)
159
+ end
160
+ end
161
+ end
162
+
163
+ describe "#oauth_login" do
164
+ let(:login_error) do
165
+ %Q{<OAuth>
166
+ <error_description>invalid client credentials</error_description>
167
+ <error>invalid_client</error>
168
+ </OAuth>}
169
+ end
170
+
171
+ let(:login_error_message) { "invalid_client: invalid client credentials" }
172
+
173
+ let(:sf_instance) {"eu2"}
174
+
175
+ let(:login_success) do
176
+ %Q{<OAuth>
177
+ <id>https://login.salesforce.com/id/00Db0000000bHH3EAM/005b0000000K44BAAS</id>
178
+ <issued_at>1435621555861</issued_at>
179
+ <scope>full</scope>
180
+ <instance_url>https://eu2.salesforce.com</instance_url>
181
+ <token_type>Bearer</token_type>
182
+ <signature>mbfZerj8DRVXVF9WD28kM/p8j6d74fE29RRGleTxELE=</signature>
183
+ <access_token>00Db0000000bHH3!AREAQFjvfelEv7D6TM3QqoqhWAJ52sUPSw.yMuIJ0x4NExqxey7xwvezoTG6DgUypEp8.FW_l6LdyK.viB4kxZY.Tn63oRP5</access_token>
184
+ </OAuth>}
185
+ end
186
+
187
+ it "raises an error for faulty login" do
188
+ expect(Bulkforce::Http).to receive(:process_http_request)
189
+ .and_return(login_error)
190
+ expect{ Bulkforce::Http.oauth_login("a","b","c", "d") }
191
+ .to raise_error(RuntimeError, login_error_message)
192
+ end
193
+
194
+ it "returns hash for correct login" do
195
+ expect(Bulkforce::Http).to receive(:process_http_request).and_return(login_success)
196
+ result = Bulkforce::Http.oauth_login("a","b","c", "d")
197
+ expect(result).to be_a(Hash)
198
+ expect(result).to have_key(:session_id)
199
+ expect(result).to have_key(:server_url)
200
+ expect(result[:instance]).to eq(sf_instance)
201
+ end
202
+ end
203
+
204
+ describe "#create_job" do
205
+ let(:create_job_success) do
206
+ %Q{<?xml version="1.0" encoding="UTF-8"?>
207
+ <jobInfo xmlns="http://www.force.com/2009/06/asyncapi/dataload">
208
+ <id>750D0000000002lIAA</id>
209
+ <operation>upsert</operation>
210
+ <object>Contact</object>
211
+ <createdById>005D0000001ALVFIA4</createdById>
212
+ <createdDate>2013-04-10T15:52:02.000Z</createdDate>
213
+ <systemModstamp>2013-04-10T15:52:02.000Z</systemModstamp>
214
+ <state>Open</state>
215
+ <externalIdFieldName>my_external_field__c</externalIdFieldName>
216
+ <concurrencyMode>Parallel</concurrencyMode>
217
+ <contentType>CSV</contentType>
218
+ <numberBatchesQueued>0</numberBatchesQueued>
219
+ <numberBatchesInProgress>0</numberBatchesInProgress>
220
+ <numberBatchesCompleted>0</numberBatchesCompleted>
221
+ <numberBatchesFailed>0</numberBatchesFailed>
222
+ <numberBatchesTotal>0</numberBatchesTotal>
223
+ <numberRecordsProcessed>0</numberRecordsProcessed>
224
+ <numberRetries>0</numberRetries>
225
+ <apiVersion>27.0</apiVersion>
226
+ <numberRecordsFailed>0</numberRecordsFailed>
227
+ <totalProcessingTime>0</totalProcessingTime>
228
+ <apiActiveProcessingTime>0</apiActiveProcessingTime>
229
+ <apexProcessingTime>0</apexProcessingTime>
230
+ </jobInfo>}
231
+ end
232
+
233
+ it "returns hash for creating job" do
234
+ expect(Bulkforce::Http).to receive(:process_http_request)
235
+ .and_return(create_job_success)
236
+ result = Bulkforce::Http.create_job("a","b","c","d", "e", "f")
237
+ expect(result).to be_a(Hash)
238
+ expect(result).to have_key(:id)
239
+ expect(result).to have_key(:operation)
240
+ end
241
+ end
242
+
243
+ describe "#add_batch" do
244
+ let(:add_batch_success) do
245
+ %Q{
246
+ <?xml version="1.0" encoding="UTF-8"?>
247
+ <batchInfo xmlns="http://www.force.com/2009/06/asyncapi/dataload">
248
+ <id>750M0000000B1Z6IAL</id>
249
+ <jobId>751K00000009x71IAA</jobId>
250
+ <state>Queued</state>
251
+ <createdDate>2013-04-10T15:53:46.000Z</createdDate>
252
+ <systemModstamp>2013-04-10T15:53:46.000Z</systemModstamp>
253
+ <numberRecordsProcessed>0</numberRecordsProcessed>
254
+ <numberRecordsFailed>0</numberRecordsFailed>
255
+ <totalProcessingTime>0</totalProcessingTime>
256
+ <apiActiveProcessingTime>0</apiActiveProcessingTime>
257
+ <apexProcessingTime>0</apexProcessingTime>
258
+ </batchInfo>
259
+ }
260
+ end
261
+
262
+ it "returns hash for adding batch" do
263
+ expect(Bulkforce::Http).to receive(:process_http_request)
264
+ .and_return(add_batch_success)
265
+ result = Bulkforce::Http.add_batch(:post,"a","b","c","d")
266
+ expect(result).to be_a(Hash)
267
+ expect(result).to have_key(:id)
268
+ expect(result).to have_key(:job_id)
269
+ expect(result).to have_key(:state)
270
+ end
271
+ end
272
+
273
+ describe "#close_job" do
274
+ let(:close_job_success) do
275
+ %Q{<?xml version="1.0" encoding="UTF-8"?>
276
+ <jobInfo xmlns="http://www.force.com/2009/06/asyncapi/dataload">
277
+ <id>750D0000000002jIAA</id>
278
+ <operation>upsert</operation>
279
+ <object>Contact</object>
280
+ <createdById>005D0000002b3SPIAY</createdById>
281
+ <createdDate>2013-04-10T16:27:56.000Z</createdDate>
282
+ <systemModstamp>2013-04-10T16:27:56.000Z</systemModstamp>
283
+ <state>Closed</state>
284
+ <externalIdFieldName>my_external_id__c</externalIdFieldName>
285
+ <concurrencyMode>Parallel</concurrencyMode>
286
+ <contentType>CSV</contentType>
287
+ <numberBatchesQueued>0</numberBatchesQueued>
288
+ <numberBatchesInProgress>0</numberBatchesInProgress>
289
+ <numberBatchesCompleted>1</numberBatchesCompleted>
290
+ <numberBatchesFailed>0</numberBatchesFailed>
291
+ <numberBatchesTotal>1</numberBatchesTotal>
292
+ <numberRecordsProcessed>4</numberRecordsProcessed>
293
+ <numberRetries>0</numberRetries>
294
+ <apiVersion>27.0</apiVersion>
295
+ <numberRecordsFailed>0</numberRecordsFailed>
296
+ <totalProcessingTime>838</totalProcessingTime>
297
+ <apiActiveProcessingTime>355</apiActiveProcessingTime>
298
+ <apexProcessingTime>253</apexProcessingTime>
299
+ </jobInfo>}
300
+ end
301
+
302
+ it "returns hash for closing job" do
303
+ expect(Bulkforce::Http).to receive(:process_http_request)
304
+ .and_return(close_job_success)
305
+ result = Bulkforce::Http.close_job("a","b","c","d")
306
+ expect(result).to be_a(Hash)
307
+ expect(result).to have_key(:id)
308
+ expect(result).to have_key(:object)
309
+ expect(result).to have_key(:operation)
310
+ expect(result).to have_key(:state)
311
+ expect(result).to have_key(:content_type)
312
+ end
313
+ end
314
+
315
+ describe "#add_batch" do
316
+ let(:invalid_session_id) do
317
+ %Q{<?xml version="1.0" encoding="UTF-8"?>
318
+ <error xmlns="http://www.force.com/2009/06/asyncapi/dataload">
319
+ <exceptionCode>InvalidSessionId</exceptionCode>
320
+ <exceptionMessage>Invalid session id</exceptionMessage>
321
+ </error>}
322
+ end
323
+
324
+ it "raises an exception on faulty authorization" do
325
+ expect(Bulkforce::Http).to receive(:process_http_request)
326
+ .and_return(invalid_session_id)
327
+ expect{Bulkforce::Http.query_batch("a","b","c","d","e")}
328
+ .to raise_error(RuntimeError, "InvalidSessionId: Invalid session id")
329
+ end
330
+ end
331
+
332
+ describe "#query_batch_result_id" do
333
+ let(:batch_result_success) do
334
+ %Q{<result-list xmlns="http://www.force.com/2009/06/asyncapi/dataload">
335
+ <result>750D0000002jIAA</result>
336
+ </result-list>}
337
+ end
338
+
339
+ it "returns hash including the result id" do
340
+ expect(Bulkforce::Http).to receive(:process_http_request)
341
+ .and_return(batch_result_success)
342
+ result = Bulkforce::Http.query_batch_result_id("a","b","c","d","e")
343
+ expect(result).to be_a(Hash)
344
+ expect(result).to have_key(:result)
345
+ end
346
+ end
347
+
348
+ describe "#query_batch_result_data" do
349
+ let(:batch_result_data_success) do
350
+ %Q{"Id","my_external_id__c"
351
+ "003M000057GH39aIAD","K-00J799"
352
+ "003M001200KO82cIAD","K-015699"}
353
+ end
354
+
355
+ let(:batch_result_data_with_spaces_success) do
356
+ %Q{"Id","Name"
357
+ "003K000057GH39aIAD","Master of Disaster"
358
+ "003K001200KO82cIAD","King of the Hill"}
359
+ end
360
+
361
+ it "returns a correctly formatted CSV string" do
362
+ expect(Bulkforce::Http).to receive(:process_http_request)
363
+ .and_return(batch_result_data_success)
364
+ result = Bulkforce::Http.query_batch_result_data("a","b","c","d","e","f")
365
+ expect(result).to eq(batch_result_data_success.gsub(/^\s+/, ""))
366
+ end
367
+
368
+ it "returns a correctly formatted CSV string including spaces" do
369
+ expect(Bulkforce::Http).to receive(:process_http_request)
370
+ .and_return(batch_result_data_with_spaces_success)
371
+ result = Bulkforce::Http.query_batch_result_data("a","b","c","d","e","f")
372
+ expect(result).to eq(batch_result_data_with_spaces_success.gsub(/^\s+/, ""))
373
+ end
374
+ end
375
+ end