blue_state_digital 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.rspec +1 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +3 -0
  7. data/Gemfile +2 -0
  8. data/Guardfile +5 -0
  9. data/LICENSE +24 -0
  10. data/README.md +123 -0
  11. data/Rakefile +11 -0
  12. data/blue_state_digital.gemspec +37 -0
  13. data/lib/blue_state_digital.rb +32 -0
  14. data/lib/blue_state_digital/address.rb +28 -0
  15. data/lib/blue_state_digital/api_data_model.rb +31 -0
  16. data/lib/blue_state_digital/collection_resource.rb +14 -0
  17. data/lib/blue_state_digital/connection.rb +119 -0
  18. data/lib/blue_state_digital/constituent.rb +178 -0
  19. data/lib/blue_state_digital/constituent_group.rb +151 -0
  20. data/lib/blue_state_digital/contribution.rb +73 -0
  21. data/lib/blue_state_digital/dataset.rb +139 -0
  22. data/lib/blue_state_digital/dataset_map.rb +138 -0
  23. data/lib/blue_state_digital/email.rb +22 -0
  24. data/lib/blue_state_digital/email_unsubscribe.rb +11 -0
  25. data/lib/blue_state_digital/error_middleware.rb +29 -0
  26. data/lib/blue_state_digital/event.rb +46 -0
  27. data/lib/blue_state_digital/event_rsvp.rb +17 -0
  28. data/lib/blue_state_digital/event_type.rb +19 -0
  29. data/lib/blue_state_digital/phone.rb +20 -0
  30. data/lib/blue_state_digital/version.rb +3 -0
  31. data/spec/blue_state_digital/address_spec.rb +25 -0
  32. data/spec/blue_state_digital/api_data_model_spec.rb +13 -0
  33. data/spec/blue_state_digital/connection_spec.rb +153 -0
  34. data/spec/blue_state_digital/constituent_group_spec.rb +269 -0
  35. data/spec/blue_state_digital/constituent_spec.rb +422 -0
  36. data/spec/blue_state_digital/contribution_spec.rb +132 -0
  37. data/spec/blue_state_digital/dataset_map_spec.rb +137 -0
  38. data/spec/blue_state_digital/dataset_spec.rb +177 -0
  39. data/spec/blue_state_digital/email_spec.rb +16 -0
  40. data/spec/blue_state_digital/error_middleware_spec.rb +15 -0
  41. data/spec/blue_state_digital/event_rsvp_spec.rb +17 -0
  42. data/spec/blue_state_digital/event_spec.rb +70 -0
  43. data/spec/blue_state_digital/event_type_spec.rb +51 -0
  44. data/spec/blue_state_digital/phone_spec.rb +16 -0
  45. data/spec/fixtures/multiple_event_types.json +234 -0
  46. data/spec/fixtures/single_event_type.json +117 -0
  47. data/spec/spec_helper.rb +21 -0
  48. data/spec/support/matchers/fields.rb +23 -0
  49. metadata +334 -0
@@ -0,0 +1,269 @@
1
+ require 'spec_helper'
2
+
3
+ describe BlueStateDigital::ConstituentGroup do
4
+ before(:each) do
5
+ @empty_response = <<-xml_string
6
+ <?xml version="1.0" encoding="utf-8"?>
7
+ <api>
8
+ </api>
9
+ xml_string
10
+ @empty_response.strip!
11
+
12
+ @multiple_cons_groups = <<-xml_string
13
+ <?xml version="1.0" encoding="utf-8"?>
14
+ <api>
15
+ <cons_group id='12' modified_dt="1171861200">
16
+ <name>First Quarter Donors</name>
17
+ <slug>q1donors</slug>
18
+ <description>People who donated in Q1 2007</description>
19
+ <is_banned>0</is_banned>
20
+ <create_dt>1168146000</create_dt>
21
+ <group_type>manual</group_type>
22
+ <members>162</members>
23
+ <unique_emails>164</unique_emails>
24
+ <unique_emails_subscribed>109</unique_emails_subscribed>
25
+ <count_dt>1213861583</count_dt>
26
+ </cons_group>
27
+ <cons_group id='13' modified_dt="1171861200">
28
+ <name>Second Quarter Donors</name>
29
+ <slug>q2donors</slug>
30
+ <description>People who donated in Q1 2007</description>
31
+ <is_banned>0</is_banned>
32
+ <create_dt>1168146000</create_dt>
33
+ <group_type>manual</group_type>
34
+ <members>162</members>
35
+ <unique_emails>164</unique_emails>
36
+ <unique_emails_subscribed>109</unique_emails_subscribed>
37
+ <count_dt>1213861583</count_dt>
38
+ </cons_group>
39
+ </api>
40
+ xml_string
41
+
42
+ @single_cons_groups = <<-xml_string
43
+ <?xml version="1.0" encoding="utf-8"?>
44
+ <api>
45
+ <cons_group id='13' modified_dt="1171861200">
46
+ <name>First Quarter Donors</name>
47
+ <slug>q1donors</slug>
48
+ <description>People who donated in Q1 2007</description>
49
+ <is_banned>0</is_banned>
50
+ <create_dt>1168146000</create_dt>
51
+ <group_type>manual</group_type>
52
+ <members>162</members>
53
+ <unique_emails>164</unique_emails>
54
+ <unique_emails_subscribed>109</unique_emails_subscribed>
55
+ <count_dt>1213861583</count_dt>
56
+ </cons_group>
57
+ </api>
58
+ xml_string
59
+
60
+ end
61
+
62
+ let(:connection) { BlueStateDigital::Connection.new({}) }
63
+
64
+ describe ".list_constituent_groups" do
65
+ it "should return a list of groups" do
66
+ expect(connection).to receive(:perform_request).with('/cons_group/list_constituent_groups', {}, "GET").and_return(@multiple_cons_groups)
67
+ groups = connection.constituent_groups.list_constituent_groups
68
+ expect(groups).to be_a(Array)
69
+ expect(groups.length).to eq(2)
70
+ end
71
+ end
72
+
73
+ describe ".find_by_id" do
74
+ it "should do a list comprehension to find a group in the list by id" do
75
+ expect(connection).to receive(:perform_request).with('/cons_group/get_constituent_group', {cons_group_id: 13}, "GET").and_return(@single_cons_groups)
76
+ group = connection.constituent_groups.find_by_id(13)
77
+ expect(group).to be_a(BlueStateDigital::ConstituentGroup)
78
+ expect(group.id).to eq('13')
79
+ end
80
+
81
+ it "should handle an empty result" do
82
+ expect(connection).to receive(:perform_request).with('/cons_group/get_constituent_group', {cons_group_id: 13}, "GET").and_return(@empty_response)
83
+ group = connection.constituent_groups.find_by_id(13)
84
+ expect(group).to be_nil
85
+ end
86
+ end
87
+
88
+ describe ".delete_constituent_groups" do
89
+ it "should handle an array of integers" do
90
+ expect(connection).to receive(:perform_request).with('/cons_group/delete_constituent_groups', {:cons_group_ids=>"2,3"}, "POST").and_return("deferred_id")
91
+ expect(connection).to receive(:perform_request).with('/get_deferred_results', {deferred_id: "deferred_id"}, "GET").and_return(true)
92
+ connection.constituent_groups.delete_constituent_groups([2,3])
93
+ end
94
+
95
+ it "should handle a single integer" do
96
+ expect(connection).to receive(:perform_request).with('/cons_group/delete_constituent_groups', {:cons_group_ids=>"2"}, "POST").and_return("deferred_id")
97
+ expect(connection).to receive(:perform_request).with('/get_deferred_results', {deferred_id: "deferred_id"}, "GET").and_return(true)
98
+
99
+ connection.constituent_groups.delete_constituent_groups(2)
100
+ end
101
+ end
102
+
103
+ describe ".find_or_create" do
104
+ before(:all) do
105
+ @timestamp = Time.now.to_i
106
+
107
+ @new_group_xml = <<-xml_string
108
+ <?xml version="1.0" encoding="utf-8"?>
109
+ <api>
110
+ <cons_group>
111
+ <name>Environment</name>
112
+ <slug>environment</slug>
113
+ <description>Environment Group</description>
114
+ <group_type>manual</group_type>
115
+ <create_dt>#{@timestamp}</create_dt>
116
+ </cons_group>
117
+ </api>
118
+ xml_string
119
+ @new_group_xml.gsub!(/\n/, "")
120
+
121
+ @exists_response = <<-xml_string
122
+ <?xml version="1.0" encoding="utf-8"?>
123
+ <api>
124
+ <cons_group id='12'>
125
+ </cons_group>
126
+ </api>
127
+ xml_string
128
+ @exists_response.strip!
129
+ end
130
+
131
+ it "should create a new group" do
132
+ attrs = { name: "Environment", slug: "environment", description: "Environment Group", group_type: "manual", create_dt: @timestamp }
133
+
134
+
135
+ expect(connection).to receive(:perform_request).with('/cons_group/get_constituent_group_by_slug', {slug:attrs[:slug]}, "GET") { @empty_response }
136
+ expect(connection).to receive(:perform_request).with('/cons_group/add_constituent_groups', {}, "POST", @new_group_xml) { @exists_response }
137
+
138
+ cons_group = connection.constituent_groups.find_or_create(attrs)
139
+ expect(cons_group.id).to eq('12')
140
+ end
141
+
142
+
143
+ it "should not create group if it already exists" do
144
+ attrs = { name: "Environment", slug: "environment", description: "Environment Group", group_type: "manual", create_dt: @timestamp }
145
+
146
+ expect(connection).to receive(:perform_request).with('/cons_group/get_constituent_group_by_slug', {slug:attrs[:slug]}, "GET") { @exists_response }
147
+ expect(connection).not_to receive(:perform_request).with('/cons_group/add_constituent_groups', {}, "POST", @new_group_xml)
148
+
149
+ cons_group = connection.constituent_groups.find_or_create(attrs)
150
+ expect(cons_group.id).to eq('12')
151
+ end
152
+ end
153
+
154
+ describe ".from_response" do
155
+ describe "a single group" do
156
+ before(:each) do
157
+ @response = <<-xml_string
158
+ <?xml version="1.0" encoding="utf-8"?>
159
+ <api>
160
+ <cons_group id='12' modified_dt="1171861200">
161
+ <name>First Quarter Donors</name>
162
+ <slug>q1donors</slug>
163
+ <description>People who donated in Q1 2007</description>
164
+ <is_banned>0</is_banned>
165
+ <create_dt>1168146000</create_dt>
166
+ <group_type>manual</group_type>
167
+ <members>162</members>
168
+ <unique_emails>164</unique_emails>
169
+ <unique_emails_subscribed>109</unique_emails_subscribed>
170
+ <count_dt>1213861583</count_dt>
171
+ </cons_group>
172
+ </api>
173
+ xml_string
174
+ end
175
+
176
+ it "should create a group from an xml string" do
177
+ response = connection.constituent_groups.send(:from_response, @response)
178
+ expect(response.id).to eq("12")
179
+ expect(response.slug).to eq('q1donors')
180
+ end
181
+ end
182
+
183
+ describe "multiple groups" do
184
+ it "should create an array of groups from an xml string" do
185
+ response = connection.constituent_groups.send(:from_response, @multiple_cons_groups)
186
+ expect(response).to be_a(Array)
187
+ first = response.first
188
+ expect(first.id).to eq("12")
189
+ expect(first.slug).to eq('q1donors')
190
+ end
191
+ end
192
+ end
193
+
194
+ [[:add, 'add_cons_ids_to_group'], [:remove, 'remove_cons_ids_from_group']].each do |(operation, method)|
195
+ # operation, method = method_under_test.key, method_under_test.value
196
+
197
+ it "should #{operation} constituent ids to group" do
198
+ cons_group_id = "12"
199
+ cons_ids = ["1", "2"]
200
+ post_params = { cons_group_id: cons_group_id, cons_ids: "1,2" }
201
+
202
+ expect(connection).to receive(:perform_request).with("/cons_group/#{method}", post_params, "POST").and_return("deferred_id")
203
+ expect(connection).to receive(:perform_request).with('/get_deferred_results', {deferred_id: "deferred_id"}, "GET").and_return(true)
204
+
205
+ connection.constituent_groups.send(method.to_sym, cons_group_id, cons_ids)
206
+ end
207
+
208
+ it "should batch on #{operation} constituent ids to group" do
209
+ stub_const('BlueStateDigital::ConstituentGroups::CONSTITUENTS_BATCH_SIZE', 2)
210
+
211
+ cons_group_id = "12"
212
+ cons_ids = ["1", "2", "3", "4"]
213
+
214
+ expect(connection).to receive(:perform_request).with("/cons_group/#{method}", { cons_group_id: cons_group_id, cons_ids: "1,2" }, "POST").and_return("deferred_id")
215
+ expect(connection).to receive(:perform_request).with("/cons_group/#{method}", { cons_group_id: cons_group_id, cons_ids: "3,4" }, "POST").and_return("deferred_id")
216
+ expect(connection).to receive(:perform_request).with('/get_deferred_results', {deferred_id: "deferred_id"}, "GET").twice.and_return(true)
217
+
218
+ connection.constituent_groups.send(method.to_sym, cons_group_id, cons_ids)
219
+ end
220
+
221
+ it "should not wait for the result if told not to" do
222
+ cons_group_id = "12"
223
+ cons_ids = ["1", "2"]
224
+ post_params = { cons_group_id: cons_group_id, cons_ids: "1,2" }
225
+
226
+ expect(connection).to receive(:perform_request).with("/cons_group/#{method}", post_params, "POST").and_return("deferred_id")
227
+ expect(connection).not_to receive(:perform_request).with('/get_deferred_results', {deferred_id: "deferred_id"}, "GET")
228
+
229
+ connection.constituent_groups.send(method.to_sym, cons_group_id, cons_ids, {wait_for_result: false})
230
+ end
231
+
232
+ it "should #{operation} a single constituent id to a group" do
233
+ cons_group_id = "12"
234
+ cons_ids = ["1"]
235
+ post_params = { cons_group_id: cons_group_id, cons_ids: "1" }
236
+
237
+ expect(connection).to receive(:perform_request).with("/cons_group/#{method}", post_params, "POST").and_return("deferred_id")
238
+ expect(connection).to receive(:perform_request).with('/get_deferred_results', {deferred_id: "deferred_id"}, "GET").and_return(true)
239
+
240
+ connection.constituent_groups.send(method.to_sym, cons_group_id, cons_ids)
241
+ end
242
+ end
243
+
244
+
245
+ it "should rename the constituent group" do
246
+ expect(connection).to receive(:perform_request).with('/cons_group/rename_group', {cons_group_id: "1", new_name: "foo"}, "POST").and_return("")
247
+ connection.constituent_groups.rename_group("1", "foo")
248
+ end
249
+
250
+ it "should allow replace_constituent_group!" do
251
+ old_cons_group_id = 15
252
+ new_cons_group_id = 1
253
+ attrs = { name: "Environment", slug: "environment", description: "Environment Group", group_type: "manual", create_dt: @timestamp }
254
+ new_group = double
255
+ allow(new_group).to receive(:id).and_return(new_cons_group_id)
256
+
257
+ old_group = double
258
+ allow(old_group).to receive(:id).and_return(old_cons_group_id)
259
+
260
+
261
+ expect(connection.constituent_groups).to receive(:get_constituent_group).with(old_cons_group_id).and_return( old_group )
262
+ expect(connection.constituent_groups).to receive(:find_or_create).with(attrs).and_return( new_group )
263
+ expect(connection.constituent_groups).to receive(:get_cons_ids_for_group).with(old_cons_group_id).and_return( [1, 2, 3] )
264
+ expect(connection.constituent_groups).to receive(:add_cons_ids_to_group).with(new_cons_group_id, [1, 2, 3] )
265
+ expect(connection.constituent_groups).to receive(:delete_constituent_groups).with( old_cons_group_id )
266
+
267
+ expect(connection.constituent_groups.replace_constituent_group!(old_cons_group_id, attrs)).to eq(new_group)
268
+ end
269
+ end
@@ -0,0 +1,422 @@
1
+ require 'spec_helper'
2
+
3
+ describe BlueStateDigital::Constituent do
4
+ describe ".to_xml" do
5
+ before(:each) do
6
+ @cons = BlueStateDigital::Constituent.new({})
7
+ end
8
+
9
+ context "with addresses" do
10
+ let(:expected_result) do
11
+ <<-xml_string.split("\n").map(&:strip).join
12
+ <?xml version="1.0" encoding="utf-8"?>
13
+ <api>
14
+ <cons>
15
+ <cons_addr>
16
+ <addr1>one</addr1>
17
+ </cons_addr>
18
+ </cons>
19
+ </api>
20
+ xml_string
21
+ end
22
+ it "should allow constituent addresses entries as hashes" do
23
+ constituent = BlueStateDigital::Constituent.new addresses: [{addr1: 'one'}]
24
+ expect(constituent.to_xml).to eq(expected_result)
25
+ end
26
+ it "should allow constituent addresses entries as BlueStateDigital::Addresses" do
27
+ constituent = BlueStateDigital::Constituent.new addresses: [BlueStateDigital::Address.new(addr1: 'one')]
28
+ expect(constituent.to_xml).to eq(expected_result)
29
+ end
30
+
31
+ end
32
+
33
+ context "with emails" do
34
+ let(:expected_result) do
35
+ <<-xml_string.split("\n").map(&:strip).join
36
+ <?xml version="1.0" encoding="utf-8"?>
37
+ <api>
38
+ <cons>
39
+ <cons_email>
40
+ <email>one@two.com</email>
41
+ </cons_email>
42
+ </cons>
43
+ </api>
44
+ xml_string
45
+ end
46
+ it "should allow constituent addresses entries as hashes" do
47
+ constituent = BlueStateDigital::Constituent.new emails: [{email: 'one@two.com'}]
48
+ expect(constituent.to_xml).to eq(expected_result)
49
+ end
50
+ it "should allow constituent addresses entries as BlueStateDigital::Addresses" do
51
+ constituent = BlueStateDigital::Constituent.new emails: [BlueStateDigital::Email.new(email: 'one@two.com')]
52
+ expect(constituent.to_xml).to eq(expected_result)
53
+ end
54
+
55
+ end
56
+
57
+ context "with phone numbers" do
58
+ let(:expected_result) do
59
+ <<-xml_string.split("\n").map(&:strip).join
60
+ <?xml version="1.0" encoding="utf-8"?>
61
+ <api>
62
+ <cons>
63
+ <cons_phone>
64
+ <phone>123321123</phone>
65
+ </cons_phone>
66
+ </cons>
67
+ </api>
68
+ xml_string
69
+ end
70
+ it "should allow constituent addresses entries as hashes" do
71
+ constituent = BlueStateDigital::Constituent.new phones: [{phone: '123321123'}]
72
+ expect(constituent.to_xml).to eq(expected_result)
73
+ end
74
+ it "should allow constituent addresses entries as BlueStateDigital::Addresses" do
75
+ constituent = BlueStateDigital::Constituent.new phones: [BlueStateDigital::Phone.new(phone: '123321123')]
76
+ expect(constituent.to_xml).to eq(expected_result)
77
+ end
78
+
79
+ end
80
+
81
+ [:firstname,:lastname,:is_banned,:create_dt,:birth_dt,:gender].each do |param|
82
+ describe "with #{param}" do
83
+ let (:expected_result) do
84
+ <<-xml_string.split("\n").map(&:strip).join
85
+ <?xml version="1.0" encoding="utf-8"?>
86
+ <api>
87
+ <cons>
88
+ <#{param.to_s}>#{param.to_s}_value</#{param.to_s}>
89
+ </cons>
90
+ </api>
91
+ xml_string
92
+ end
93
+ it "should be present as #{param.to_s} tag in cons tag" do
94
+ constituent = BlueStateDigital::Constituent.new
95
+ eval("constituent.#{param.to_s}='#{param.to_s}_value'")
96
+ expect(constituent.to_xml).to eq(expected_result)
97
+ end
98
+
99
+ end
100
+ end
101
+ end
102
+
103
+ let(:connection) { BlueStateDigital::Connection.new({}) }
104
+
105
+ describe "Get Constituents" do
106
+ before(:each) do
107
+ @single_constituent = <<-xml_string
108
+ <?xml version="1.0" encoding="utf-8"?>
109
+ <api>
110
+ <cons id="4382" modified_dt="1171861200">
111
+ <guid>ygdFPkyEdomzBhWEFZGREys</guid>
112
+ <firstname>Bob</firstname>
113
+ <middlename>Reginald</middlename>
114
+ <lastname>Smith</lastname>
115
+ <has_account>1</has_account>
116
+ <is_banned>0</is_banned>
117
+ <create_dt>1168146000</create_dt>
118
+ <suffix>III</suffix>
119
+ <prefix>Mr</prefix>
120
+ <gender>M</gender>
121
+ </cons>
122
+ </api>
123
+ xml_string
124
+
125
+ @constituent_with_group = <<-xml_string
126
+ <?xml version="1.0" encoding="utf-8"?>
127
+ <api>
128
+ <cons id="4382" modified_dt="1171861200">
129
+ <guid>ygdFPkyEdomzBhWEFZGREys</guid>
130
+ <firstname>Bob</firstname>
131
+ <lastname>Smith</lastname>
132
+ <has_account>1</has_account>
133
+ <is_banned>0</is_banned>
134
+ <create_dt>1168146000</create_dt>
135
+
136
+ <cons_group id="41" modified_dt="1163196031" />
137
+ </cons>
138
+ </api>
139
+ xml_string
140
+
141
+ @constituent_with_addr = <<-xml_string
142
+ <?xml version="1.0" encoding="utf-8"?>
143
+ <api>
144
+ <cons id="4382" modified_dt="1171861200">
145
+ <guid>ygdFPkyEdomzBhWEFZGREys</guid>
146
+ <firstname>Bob</firstname>
147
+ <lastname>Smith</lastname>
148
+ <has_account>1</has_account>
149
+ <is_banned>0</is_banned>
150
+ <create_dt>1168146000</create_dt>
151
+ <cons_addr id="43" modified_dt="1355800948">
152
+ <addr1>yyy2</addr1>
153
+ <addr2>yyy3</addr2>
154
+ <city>here</city>
155
+ <state_cd>2323</state_cd>
156
+ <zip>00323</zip>
157
+ <country></country>
158
+ <latitude>0.000000</latitude>
159
+ <longitude>0.000000</longitude>
160
+ <is_primary>0</is_primary>
161
+ <cons_addr_type_id>0</cons_addr_type_id>
162
+ </cons_addr>
163
+ <cons_addr id="42" modified_dt="1355800946">
164
+ <addr1>xxx1</addr1>
165
+ <addr2>xxx2</addr2>
166
+ <city>Helsinki</city>
167
+ <state_cd></state_cd>
168
+ <zip>12345</zip>
169
+ <country>AM</country>
170
+ <latitude>42.810059</latitude>
171
+ <longitude>-73.951050</longitude>
172
+ <is_primary>1</is_primary>
173
+ <cons_addr_type_id>0</cons_addr_type_id>
174
+ </cons_addr>
175
+ </cons>
176
+ </api>
177
+ xml_string
178
+
179
+ @constituent_with_emails = <<-xml_string
180
+ <?xml version="1.0" encoding="utf-8"?>
181
+ <api>
182
+ <cons id="4382" modified_dt="1171861200">
183
+ <guid>ygdFPkyEdomzBhWEFZGREys</guid>
184
+ <firstname>Bob</firstname>
185
+ <lastname>Smith</lastname>
186
+ <has_account>1</has_account>
187
+ <is_banned>0</is_banned>
188
+ <create_dt>1168146000</create_dt>
189
+
190
+ <cons_email id="35" modified_dt="1355796381">
191
+ <email>gil+punky1@thoughtworks.com</email>
192
+ <email_type>personal</email_type>
193
+ <is_subscribed>1</is_subscribed>
194
+ <is_primary>1</is_primary>
195
+ </cons_email>
196
+ <cons_email id="36" modified_dt="1355796381">
197
+ <email>fred@thoughtworks.com</email>
198
+ <email_type>internal</email_type>
199
+ <is_subscribed>0</is_subscribed>
200
+ <is_primary>0</is_primary>
201
+ </cons_email>
202
+ </cons>
203
+ </api>
204
+ xml_string
205
+
206
+ @constituent_with_groups = <<-xml_string
207
+ <?xml version="1.0" encoding="utf-8"?>
208
+ <api>
209
+ <cons id="4382" modified_dt="1171861200">
210
+ <guid>ygdFPkyEdomzBhWEFZGREys</guid>
211
+ <firstname>Bob</firstname>
212
+ <lastname>Smith</lastname>
213
+ <has_account>1</has_account>
214
+ <is_banned>0</is_banned>
215
+ <create_dt>1168146000</create_dt>
216
+
217
+ <cons_group id="17" modified_dt="1168146011"/>
218
+ <cons_group id="41" modified_dt="1163196031" />
219
+ </cons>
220
+ </api>
221
+ xml_string
222
+
223
+ @multiple_constituents = <<-xml_string
224
+ <?xml version="1.0" encoding="utf-8"?>
225
+ <api>
226
+ <cons id="4382" modified_dt="1171861200">
227
+ <guid>ygdFPkyEdomzBhWEFZGREys</guid>
228
+ <firstname>Bob</firstname>
229
+ <middlename>Reginald</middlename>
230
+ <lastname>Smith</lastname>
231
+ <has_account>1</has_account>
232
+ <is_banned>0</is_banned>
233
+ <create_dt>1168146000</create_dt>
234
+ <suffix>III</suffix>
235
+ <prefix>Mr</prefix>
236
+ <gender>M</gender>
237
+ </cons>
238
+
239
+ <cons id="4381" modified_dt="1171861200">
240
+ <guid>ygdFPkyEdomzBhWEFZGREys</guid>
241
+ <firstname>Susan</firstname>
242
+ <middlename>Reginald</middlename>
243
+ <lastname>Smith</lastname>
244
+ <has_account>1</has_account>
245
+ <is_banned>0</is_banned>
246
+ <create_dt>1168146000</create_dt>
247
+ <suffix></suffix>
248
+ <prefix>Mrs</prefix>
249
+ <gender>F</gender>
250
+ </cons>
251
+ </api>
252
+ xml_string
253
+ end
254
+
255
+ describe ".get_constituents_by_email" do
256
+ it "should make a filtered constituents query" do
257
+ expect(connection).to receive(:perform_request).with('/cons/get_constituents', {:filter=>"email=george@washington.com", :bundles => 'cons_group'}, "GET").and_return("deferred_id")
258
+ expect(connection).to receive(:perform_request).with('/get_deferred_results', {deferred_id: "deferred_id"}, "GET").and_return(@single_constituent)
259
+ response = connection.constituents.get_constituents_by_email("george@washington.com").first
260
+ expect(response.id).to eq("4382")
261
+ expect(response.firstname).to eq('Bob')
262
+ end
263
+
264
+ it "should return constituents' details based on bundles" do
265
+ bundles = 'cons_addr'
266
+ expect(connection).to receive(:perform_request).with('/cons/get_constituents', {:filter=>"email=george@washington.com", :bundles => bundles}, "GET").and_return("deferred_id")
267
+ expect(connection).to receive(:perform_request).with('/get_deferred_results', {deferred_id: "deferred_id"}, "GET").and_return(@constituent_with_addr)
268
+ response = connection.constituents.get_constituents_by_email("george@washington.com", ['cons_addr']).first
269
+ response.addresses[0].addr1 == "aaa1"
270
+ response.addresses[0].addr2 == "aaa2"
271
+ end
272
+ end
273
+
274
+ describe ".get_constituents_by_id" do
275
+ it "should return a constituent" do
276
+ expect(connection).to receive(:perform_request).with('/cons/get_constituents_by_id', {:cons_ids=>"23", :bundles => 'cons_group'}, "GET").and_return(@single_constituent)
277
+ response = connection.constituents.get_constituents_by_id("23").first
278
+ expect(response.id).to eq("4382")
279
+ expect(response.firstname).to eq('Bob')
280
+ end
281
+ end
282
+
283
+ describe ".from_response" do
284
+ it "should set connection in generated constituents" do
285
+ response = connection.constituents.send(:from_response, @single_constituent)
286
+ expect(response).to be_a(Array)
287
+ expect(response.size).to eq(1)
288
+ expect(response.first.connection).to eq(connection)
289
+ end
290
+
291
+ it "should create an array of constituents from a response that contains multiple constituents" do
292
+ response = connection.constituents.send(:from_response, @multiple_constituents)
293
+ expect(response).to be_a(Array)
294
+ first = response.first
295
+ expect(first.id).to eq("4382")
296
+ expect(first.firstname).to eq('Bob')
297
+ end
298
+
299
+ it "should create an array of single constituent when only one is supplied" do
300
+ response = connection.constituents.send(:from_response, @single_constituent)
301
+ expect(response).to be_a(Array)
302
+ expect(response.size).to eq(1)
303
+ expect(response.first.id).to eq("4382")
304
+ expect(response.first.firstname).to eq('Bob')
305
+ expect(response.first.gender).to eq('M')
306
+ end
307
+
308
+ it "should handle constituent group membership" do
309
+ response = connection.constituents.send(:from_response, @constituent_with_groups).first
310
+ expect(response.id).to eq('4382')
311
+ expect(response.group_ids).to eq(["17", "41"])
312
+ end
313
+
314
+ it "should handle single constituent group membership" do
315
+ response = connection.constituents.send(:from_response, @constituent_with_group).first
316
+ expect(response.id).to eq('4382')
317
+ expect(response.group_ids).to eq(["41"])
318
+ end
319
+
320
+ it "Should handle constituent addresses" do
321
+ response = connection.constituents.send(:from_response, @constituent_with_addr).first
322
+ expect(response.addresses.size).to eq(2)
323
+ expect(response.addresses[0]).to be_a BlueStateDigital::Address
324
+ expect(response.addresses[0].addr1).to eq("yyy2")
325
+ expect(response.addresses[0].addr2).to eq("yyy3")
326
+
327
+ expect(response.addresses[1]).to be_a BlueStateDigital::Address
328
+ expect(response.addresses[1].addr1).to eq("xxx1")
329
+ expect(response.addresses[1].addr2).to eq("xxx2")
330
+ end
331
+
332
+ it "Should handle constituent email addresses" do
333
+ response = connection.constituents.send(:from_response, @constituent_with_emails).first
334
+ expect(response.emails.size).to eq(2)
335
+ expect(response.emails[0]).to be_a BlueStateDigital::Email
336
+ expect(response.emails[0].email).to eq("gil+punky1@thoughtworks.com")
337
+ expect(response.emails[0].email_type).to eq("personal")
338
+ expect(response.emails[0].is_subscribed).to eq("1")
339
+ expect(response.emails[0].is_primary).to eq("1")
340
+
341
+ expect(response.emails[1]).to be_a BlueStateDigital::Email
342
+ expect(response.emails[1].email).to eq("fred@thoughtworks.com")
343
+ expect(response.emails[1].email_type).to eq("internal")
344
+ expect(response.emails[1].is_subscribed).to eq("0")
345
+ expect(response.emails[1].is_primary).to eq("0")
346
+ end
347
+
348
+ end
349
+ end
350
+
351
+ describe "delete_constituents_by_id" do
352
+ it "should handle an array of integers" do
353
+ expect(connection).to receive(:perform_request).with('/cons/delete_constituents_by_id', {:cons_ids=>"2,3"}, "POST")
354
+ connection.constituents.delete_constituents_by_id([2,3])
355
+ end
356
+
357
+ it "should handle a single integer" do
358
+ expect(connection).to receive(:perform_request).with('/cons/delete_constituents_by_id', {:cons_ids=>"2"}, "POST")
359
+ connection.constituents.delete_constituents_by_id(2)
360
+ end
361
+ end
362
+ it "should set constituent data" do
363
+ timestamp = Time.now.to_i
364
+
365
+ data = {
366
+ id: 'id',
367
+ firstname: 'First',
368
+ lastname: 'Last',
369
+ is_banned: 0,
370
+ create_dt: timestamp,
371
+ emails: [{ email: "email@email.com", email_type: "work", is_subscribed: 1, is_primary: 1 }],
372
+ groups: [3, 5],
373
+ connection: connection
374
+ }
375
+
376
+ input = %q{<?xml version="1.0" encoding="utf-8"?>}
377
+ input << "<api>"
378
+ input << "<cons id=\"id\">"
379
+ input << "<firstname>First</firstname>"
380
+ input << "<lastname>Last</lastname>"
381
+ input << "<is_banned>0</is_banned>"
382
+ input << "<create_dt>#{timestamp}</create_dt>"
383
+ input << "<cons_email>"
384
+ input << "<email>email@email.com</email>"
385
+ input << "<email_type>work</email_type>"
386
+ input << "<is_subscribed>1</is_subscribed>"
387
+ input << "<is_primary>1</is_primary>"
388
+ input << "</cons_email>"
389
+ input << "<cons_group id=\"3\"/>"
390
+ input << "<cons_group id=\"5\"/>"
391
+ input << "</cons>"
392
+ input << "</api>"
393
+
394
+ output = %q{<?xml version="1.0" encoding="utf-8"?>}
395
+ output << "<api>"
396
+ output << "<cons is_new='1' id='329'>"
397
+ output << "</cons>"
398
+ output << "</api>"
399
+
400
+ expect(connection).to receive(:perform_request).with('/cons/set_constituent_data', {}, "POST", input) { output }
401
+
402
+ cons_data = BlueStateDigital::Constituent.new(data)
403
+ cons_data.save
404
+ expect(cons_data.id).to eq('329')
405
+ expect(cons_data.is_new).to eq('1')
406
+ expect(cons_data.is_new?).to be_truthy
407
+ end
408
+
409
+ describe "#to_xml" do
410
+ it "should convert a constituent hash to xml" do
411
+ cons = BlueStateDigital::Constituent.new ({
412
+ firstname: 'George',
413
+ lastname: 'Washington',
414
+ create_dt: Time.now.to_i,
415
+ emails: [{ email: 'george@washington.com', is_subscribed: 1}],
416
+ addresses: [{ country: 'US', zip: '20001', is_primary: 1}],
417
+ phones: [{phone: '123456789', phone_type: 'unknown'}]
418
+ })
419
+ expect(cons.to_xml).not_to be_nil
420
+ end
421
+ end
422
+ end