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