question_spec.rb
11.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe Question do
it {should belong_to :creator}
it {should belong_to :site}
it {should have_many :choices}
it {should have_many :prompts}
it {should have_many :votes}
it {should have_many :densities}
it {should have_many :appearances}
it {should validate_presence_of :site}
it {should validate_presence_of :creator}
before(:each) do
@question = Factory.create(:aoi_question)
@aoi_clone = @question.site
end
it "should have 2 active choices" do
@question.choices.active.reload.size.should == 2
end
it "should create a new instance given valid attributes" do
# Factory.attributes_for does not return associations, this is a good enough substitute
Question.create!(Factory.build(:question).attributes.symbolize_keys)
end
it "should not create two default choices if none are provided" do
q = @aoi_clone.create_question("foobarbaz", {:name => 'foo'})
q.choices(true).size.should == 0
end
#it "should generate prompts after choices are added" do
#@question.prompts(true).size.should == 2
#end
it "should choose an active prompt randomly" do
prompt = @question.picked_prompt
prompt.active?.should == true
end
it "should choose an active prompt using catchup algorithm" do
prompt = @question.catchup_choose_prompt
prompt.active?.should == true
end
it "should raise runtime exception if there is no possible prompt to choose" do
@question.choices.first.deactivate!
@question.reload
lambda { @question.choose_prompt}.should raise_error(RuntimeError)
end
it "should return nil if optional parameters are empty" do
@question_optional_information = @question.get_optional_information(nil)
@question_optional_information.should be_empty
end
it "should return nil if optional parameters are nil" do
params = {"id" => '37'}
@question_optional_information = @question.get_optional_information(params)
@question_optional_information.should be_empty
end
it "should return a hash with an prompt id when optional parameters contains 'with_prompt'" do
params = {:id => 124, :with_prompt => true}
@question_optional_information = @question.get_optional_information(params)
@question_optional_information.should include(:picked_prompt_id)
@question_optional_information[:picked_prompt_id].should be_an_instance_of(Fixnum)
end
it "should return a hash with an appearance hash when optional parameters contains 'with_appearance'" do
params = {:id => 124, :with_prompt => true, :with_appearance=> true, :visitor_identifier => 'jim'}
@question_optional_information = @question.get_optional_information(params)
@question_optional_information.should include(:appearance_id)
@question_optional_information[:appearance_id].should be_an_instance_of(String)
end
it "should return a hash with two visitor stats when optional parameters contains 'with_visitor_stats'" do
params = {:id => 124, :with_visitor_stats=> true, :visitor_identifier => "jim"}
@question_optional_information = @question.get_optional_information(params)
@question_optional_information.should include(:visitor_votes)
@question_optional_information.should include(:visitor_ideas)
@question_optional_information[:visitor_votes].should be_an_instance_of(Fixnum)
@question_optional_information[:visitor_ideas].should be_an_instance_of(Fixnum)
end
it "should return a hash when optional parameters have more than one optional param " do
params = {:id => 124, :with_visitor_stats=> true, :visitor_identifier => "jim", :with_prompt => true, :with_appearance => true}
@question_optional_information = @question.get_optional_information(params)
@question_optional_information.should include(:visitor_votes)
@question_optional_information.should include(:visitor_ideas)
@question_optional_information[:visitor_votes].should be_an_instance_of(Fixnum)
@question_optional_information[:visitor_ideas].should be_an_instance_of(Fixnum)
@question_optional_information.should include(:picked_prompt_id)
@question_optional_information[:picked_prompt_id].should be_an_instance_of(Fixnum)
@question_optional_information.should include(:appearance_id)
@question_optional_information[:appearance_id].should be_an_instance_of(String)
end
it "should return the same appearance when a visitor requests two prompts without voting" do
params = {:id => 124, :with_visitor_stats=> true, :visitor_identifier => "jim", :with_prompt => true, :with_appearance => true}
@question_optional_information = @question.get_optional_information(params)
@question_optional_information[:appearance_id].should be_an_instance_of(String)
@question_optional_information[:picked_prompt_id].should be_an_instance_of(Fixnum)
saved_appearance_id = @question_optional_information[:appearance_id]
saved_prompt_id = @question_optional_information[:picked_prompt_id]
@question_optional_information = @question.get_optional_information(params)
@question_optional_information[:appearance_id].should == saved_appearance_id
@question_optional_information[:picked_prompt_id].should == saved_prompt_id
end
context "catchup algorithm" do
before(:all) do
@catchup_q = Factory.create(:aoi_question)
@catchup_q.it_should_autoactivate_ideas = true
@catchup_q.uses_catchup = true
@catchup_q.save!
# 2 ideas already exist, so this will make an even hundred
98.times.each do |num|
@catchup_q.site.create_choice("visitor identifier", @catchup_q, {:data => num.to_s, :local_identifier => "exmaple"})
end
@catchup_q.reload
end
it "should create a delayed job after requesting a prompt" do
proc { @catchup_q.choose_prompt}.should change(Delayed::Job, :count).by(1)
end
it "should choose an active prompt using catchup algorithm on a large number of choices" do
@catchup_q.reload
# Sanity check
@catchup_q.choices.size.should == 100
prompt = @catchup_q.catchup_choose_prompt
prompt.active?.should == true
end
it "should have a normalized vector of weights to support the catchup algorithm" do
weights = @catchup_q.catchup_prompts_weights
sum = 0
weights.each{|k,v| sum+=v}
(sum - 1.0).abs.should < 0.000001
end
it "should allow the prompt queue to be cleared" do
@catchup_q.add_prompt_to_queue
@catchup_q.clear_prompt_queue
@catchup_q.pop_prompt_queue.should == nil
end
it "should allow a prompt to be added to the prompt queue" do
@catchup_q.clear_prompt_queue
@catchup_q.pop_prompt_queue.should == nil
@catchup_q.add_prompt_to_queue
prompt = @catchup_q.pop_prompt_queue
prompt.should_not == nil
prompt.active?.should == true
end
it "should return prompts from the queue in FIFO order" do
@catchup_q.clear_prompt_queue
@catchup_q.pop_prompt_queue.should == nil
prompt1 = @catchup_q.add_prompt_to_queue
prompt2 = @catchup_q.add_prompt_to_queue
prompt3 = @catchup_q.add_prompt_to_queue
prompt_1 = @catchup_q.pop_prompt_queue
prompt_2 = @catchup_q.pop_prompt_queue
prompt_3 = @catchup_q.pop_prompt_queue
prompt_1.should == prompt1
prompt_2.should == prompt2
prompt_3.should == prompt3
# there is a small probability that the catchup algorithm
# choose two prompts that are indeed equal
prompt_1.should_not == prompt_2
prompt_1.should_not == prompt_3
prompt_2.should_not == prompt_3
@catchup_q.pop_prompt_queue.should == nil
end
end
context "exporting data" do
before(:all) do
@question = Factory.create(:aoi_question)
user = @question.site
@question.it_should_autoactivate_ideas = true
@question.save!
visitor = user.visitors.find_or_create_by_identifier('visitor identifier')
100.times.each do |num|
user.create_choice(visitor.identifier, @question, {:data => num.to_s, :local_identifier => "example creator"})
end
200.times.each do |num|
@p = @question.picked_prompt
@a = user.record_appearance(visitor, @p)
vote_options = {:visitor_identifier => visitor.identifier,
:appearance_lookup => @a.lookup,
:prompt => @p,
:time_viewed => rand(1000),
:direction => (rand(2) == 0) ? "left" : "right"}
skip_options = {:visitor_identifier => visitor.identifier,
:appearance_lookup => @a.lookup,
:prompt => @p,
:time_viewed => rand(1000),
:skip_reason => "some reason"}
choice = rand(3)
case choice
when 0
user.record_vote(vote_options)
when 1
user.record_skip(skip_options)
when 2
#this is an orphaned appearance, so do nothing
end
end
end
it "should export vote data to a csv file" do
filename = @question.export('votes')
filename.should_not be nil
filename.should match /.*ideamarketplace_#{@question.id}_votes[.]csv$/
File.exists?(filename).should be_true
# Not specifying exact file syntax, it's likely to change frequently
#
rows = FasterCSV.read(filename)
rows.first.should include("Vote ID")
rows.first.should_not include("Idea ID")
File.delete(filename).should be_true
end
it "should notify redis after completing an export, if redis option set" do
redis_key = "test_key123"
$redis.del(redis_key) # clear if key exists already
filename = @question.export('votes', :response_type => 'redis', :redis_key => redis_key)
filename.should_not be nil
filename.should match /.*ideamarketplace_#{@question.id}_votes[.]csv$/
File.exists?(filename).should be_true
$redis.lpop(redis_key).should == filename
$redis.del(redis_key) # clean up
File.delete(filename).should be_true
end
it "should email question owner after completing an export, if email option set" do
#TODO
end
it "should export non vote data to a csv file" do
filename = @question.export('non_votes')
filename.should_not be nil
filename.should match /.*ideamarketplace_#{@question.id}_non_votes[.]csv$/
File.exists?(filename).should be_true
# Not specifying exact file syntax, it's likely to change frequently
#
rows = FasterCSV.read(filename)
rows.first.should include("Record ID")
rows.first.should include("Record Type")
rows.first.should_not include("Idea ID")
puts filename
File.delete(filename).should_not be_nil
end
it "should export idea data to a csv file" do
filename = @question.export('ideas')
filename.should_not be nil
filename.should match /.*ideamarketplace_#{@question.id}_ideas[.]csv$/
File.exists?(filename).should be_true
# Not specifying exact file syntax, it's likely to change frequently
#
rows = FasterCSV.read(filename)
rows.first.should include("Idea ID")
rows.first.should_not include("Skip ID")
puts filename
File.delete(filename).should_not be_nil
end
it "should raise an error when given an unsupported export type" do
lambda { @question.export("blahblahblah") }.should raise_error
end
it "should export data and schedule a job to delete export after X days" do
Delayed::Job.delete_all
filename = @question.export_and_delete('votes', :delete_at => 2.days.from_now)
Delayed::Job.count.should == 1
Delayed::Job.delete_all
File.delete(filename).should_not be_nil
end
end
end