Commit c9546a4719202fa3759de878c03586a87ca86a67
1 parent
26ecf5ed
Exists in
master
and in
1 other branch
Refactored nightly api test. Added api test for l/r probabilty
Showing
1 changed file
with
188 additions
and
109 deletions
Show diff stats
lib/tasks/test_api.rake
@@ -243,13 +243,98 @@ namespace :test_api do | @@ -243,13 +243,98 @@ namespace :test_api do | ||
243 | desc "Description here" | 243 | desc "Description here" |
244 | task(:question_vote_consistency => :environment) do | 244 | task(:question_vote_consistency => :environment) do |
245 | questions = Question.find(:all) | 245 | questions = Question.find(:all) |
246 | + errors = [] | ||
247 | + successes = [] | ||
246 | 248 | ||
247 | - error_msg = "" | ||
248 | - | ||
249 | - bad_choices = [] | ||
250 | - bad_votes = [] | ||
251 | questions.each do |question| | 249 | questions.each do |question| |
252 | 250 | ||
251 | + message, error_occurred = check_basic_balanced_stats(question) | ||
252 | + #hack for now, get around to doing this with block /yield to get rid of duplication | ||
253 | + if error_occurred | ||
254 | + errors << message | ||
255 | + else | ||
256 | + successes << message | ||
257 | + end | ||
258 | + | ||
259 | + | ||
260 | + message, error_occurred = check_each_choice_appears_within_n_stddevs(question) | ||
261 | + if error_occurred | ||
262 | + errors << message | ||
263 | + else | ||
264 | + successes << message | ||
265 | + end | ||
266 | + | ||
267 | + message, error_occurred = check_each_choice_equally_likely_to_appear_left_or_right(question) | ||
268 | + if error_occurred | ||
269 | + errors << message | ||
270 | + else | ||
271 | + successes << message | ||
272 | + end | ||
273 | + | ||
274 | + | ||
275 | + | ||
276 | + message, error_occurred = check_object_counter_cache_values_match_actual_values(question) | ||
277 | + if error_occurred | ||
278 | + errors << message | ||
279 | + else | ||
280 | + successes << message | ||
281 | + end | ||
282 | + | ||
283 | + | ||
284 | + #catchup specific | ||
285 | + if question.uses_catchup? | ||
286 | + message, error_occurred = check_prompt_cache_hit_rate(question) | ||
287 | + if error_occurred | ||
288 | + errors << message | ||
289 | + else | ||
290 | + successes << message | ||
291 | + end | ||
292 | + end | ||
293 | + end | ||
294 | + | ||
295 | + message, error_occurred = ensure_all_votes_and_skips_have_unique_appearance | ||
296 | + | ||
297 | + if error_occurred | ||
298 | + errors << message | ||
299 | + else | ||
300 | + successes << message | ||
301 | + end | ||
302 | + | ||
303 | + message, error_occurred = response_time_tests | ||
304 | + | ||
305 | + if error_occurred | ||
306 | + errors << message | ||
307 | + else | ||
308 | + successes << message | ||
309 | + end | ||
310 | + | ||
311 | + email_text = "Conducted the following tests on API data and found the following results\n" + | ||
312 | + "For each of the #{questions.length} questions in the database: \n" | ||
313 | + errors.each do |e| | ||
314 | + email_text += " Test FAILED: " + e + "\n" | ||
315 | + end | ||
316 | + | ||
317 | + successes.uniq.each do |s| | ||
318 | + s.split("\n").each do |m| # some successes have several lines | ||
319 | + email_text += " Test Passed: " + m + "\n" | ||
320 | + end | ||
321 | + end | ||
322 | + | ||
323 | + puts email_text | ||
324 | + | ||
325 | + if errors.empty? | ||
326 | + CronMailer.deliver_info_message(CRON_EMAIL, "Test of API Vote Consistency passed", email_text) | ||
327 | + else | ||
328 | + CronMailer.deliver_info_message("#{CRON_EMAIL},#{ERRORS_EMAIL}", "Error! Failure of API Vote Consistency " , email_text) | ||
329 | + end | ||
330 | + | ||
331 | + end | ||
332 | + def check_basic_balanced_stats(question) | ||
333 | + error_message = "" | ||
334 | + success_message = "2 x Total Wins = Total Votes\n" + | ||
335 | + "Total Votes (wins + losses) is Even\n" + | ||
336 | + "Total Votes (wins + losses) = 2 x the number of vote objects that belong to the question\n" + | ||
337 | + "Total generated prompts on left = Total generated prompts on right" | ||
253 | total_wins =0 | 338 | total_wins =0 |
254 | total_votes =0 | 339 | total_votes =0 |
255 | total_generated_prompts_on_left = 0 | 340 | total_generated_prompts_on_left = 0 |
@@ -257,6 +342,7 @@ namespace :test_api do | @@ -257,6 +342,7 @@ namespace :test_api do | ||
257 | total_scores_gte_fifty= 0 | 342 | total_scores_gte_fifty= 0 |
258 | total_scores_lte_fifty= 0 | 343 | total_scores_lte_fifty= 0 |
259 | error_bool = false | 344 | error_bool = false |
345 | + | ||
260 | question.choices.each do |choice| | 346 | question.choices.each do |choice| |
261 | 347 | ||
262 | if choice.wins | 348 | if choice.wins |
@@ -277,20 +363,19 @@ namespace :test_api do | @@ -277,20 +363,19 @@ namespace :test_api do | ||
277 | delta = 0.001 | 363 | delta = 0.001 |
278 | 364 | ||
279 | if (cached_score - generated_score).abs >= delta | 365 | if (cached_score - generated_score).abs >= delta |
280 | - error_msg += "Error! The cached_score is not equal to the calculated score for choice #{choice.id}" | 366 | + error_message += "Error! The cached_score is not equal to the calculated score for choice #{choice.id}" |
281 | 367 | ||
282 | print "This score is wrong! #{choice.id} , Question ID: #{question.id}, #{cached_score}, #{generated_score}, updated: #{choice.updated_at}\n" | 368 | print "This score is wrong! #{choice.id} , Question ID: #{question.id}, #{cached_score}, #{generated_score}, updated: #{choice.updated_at}\n" |
283 | 369 | ||
284 | - bad_choices << choice.id | ||
285 | 370 | ||
286 | end | 371 | end |
287 | 372 | ||
288 | if cached_score == 0.0 || cached_score == 100.0 || cached_score.nil? | 373 | if cached_score == 0.0 || cached_score == 100.0 || cached_score.nil? |
289 | - error_msg += "Error! The cached_score for choice #{choice.id} is exactly 0 or 100, the value: #{cached_score}" | 374 | + error_message += "Error! The cached_score for choice #{choice.id} is exactly 0 or 100, the value: #{cached_score}" |
290 | print "Either 0 or 100 This score is wrong! #{choice.id} , Question ID: #{question.id}, #{cached_score}, #{generated_score}, updated: #{choice.updated_at}\n" | 375 | print "Either 0 or 100 This score is wrong! #{choice.id} , Question ID: #{question.id}, #{cached_score}, #{generated_score}, updated: #{choice.updated_at}\n" |
291 | - bad_choices << choice.id | ||
292 | end | 376 | end |
293 | 377 | ||
378 | + | ||
294 | if cached_score >= 50 | 379 | if cached_score >= 50 |
295 | total_scores_gte_fifty +=1 | 380 | total_scores_gte_fifty +=1 |
296 | end | 381 | end |
@@ -302,31 +387,40 @@ namespace :test_api do | @@ -302,31 +387,40 @@ namespace :test_api do | ||
302 | end | 387 | end |
303 | 388 | ||
304 | if (2*total_wins != total_votes) | 389 | if (2*total_wins != total_votes) |
305 | - error_msg += "Error 1: 2 x Total Wins != Total votes" | 390 | + error_message += "Error 1: 2 x Total Wins != Total votes" |
306 | error_bool= true | 391 | error_bool= true |
307 | end | 392 | end |
308 | 393 | ||
309 | if(total_votes % 2 != 0) | 394 | if(total_votes % 2 != 0) |
310 | - error_msg += "Error 2: Total votes is not Even!" | 395 | + error_message += "Error 2: Total votes is not Even!" |
311 | error_bool= true | 396 | error_bool= true |
312 | end | 397 | end |
313 | 398 | ||
314 | if(total_votes != 2* question.votes_count) | 399 | if(total_votes != 2* question.votes_count) |
315 | - error_msg += "Error 3: Total votes != 2 x # vote objects" | 400 | + error_message += "Error 3: Total votes != 2 x # vote objects" |
316 | error_bool = true | 401 | error_bool = true |
317 | end | 402 | end |
318 | 403 | ||
319 | if(total_generated_prompts_on_right != total_generated_prompts_on_right) | 404 | if(total_generated_prompts_on_right != total_generated_prompts_on_right) |
320 | - error_msg += "Error 4: Total generated prompts on left != Total generated prompts on right" | 405 | + error_message += "Error 4: Total generated prompts on left != Total generated prompts on right" |
321 | error_bool = true | 406 | error_bool = true |
322 | end | 407 | end |
323 | 408 | ||
324 | if(total_scores_lte_fifty == question.choices.size || total_scores_gte_fifty == question.choices.size) && (total_scores_lte_fifty != total_scores_gte_fifty) | 409 | if(total_scores_lte_fifty == question.choices.size || total_scores_gte_fifty == question.choices.size) && (total_scores_lte_fifty != total_scores_gte_fifty) |
325 | - error_msg += "Error: The scores of all choices are either all above 50, or all below 50. This is probably wrong" | 410 | + error_message += "Error: The scores of all choices are either all above 50, or all below 50. This is probably wrong" |
326 | error_bool = true | 411 | error_bool = true |
327 | puts "Error score fifty: #{question.id}" | 412 | puts "Error score fifty: #{question.id}" |
328 | end | 413 | end |
329 | - | 414 | + |
415 | + if error_bool | ||
416 | + error_message += "Question #{question.id}: 2*wins = #{2*total_wins}, total votes = #{total_votes}, vote_count = #{question.votes_count}\n" | ||
417 | + end | ||
418 | + return error_message.blank? ? [success_message, false] : [error_message, true] | ||
419 | + end | ||
420 | + def check_each_choice_appears_within_n_stddevs(question) | ||
421 | + error_message ="" | ||
422 | + success_message = "Each choice has appeared n times, where n falls within 6 stddevs of the mean number of appearances for a question " + | ||
423 | + "(Note: this applies only to seed choices (not user submitted) and choices currently marked active)" | ||
330 | 424 | ||
331 | wins_by_choice_id = question.votes.active.count(:group => :choice_id) | 425 | wins_by_choice_id = question.votes.active.count(:group => :choice_id) |
332 | losses_by_choice_id= question.votes.active.count(:conditions => "loser_choice_id IS NOT NULL", :group => :loser_choice_id) | 426 | losses_by_choice_id= question.votes.active.count(:conditions => "loser_choice_id IS NOT NULL", :group => :loser_choice_id) |
@@ -339,6 +433,7 @@ namespace :test_api do | @@ -339,6 +433,7 @@ namespace :test_api do | ||
339 | losses_hash.merge!(losses_by_choice_id) | 433 | losses_hash.merge!(losses_by_choice_id) |
340 | 434 | ||
341 | 435 | ||
436 | + | ||
342 | appearances_by_choice_id = wins_hash.merge(losses_hash) do |key, oldval, newval| oldval + newval end | 437 | appearances_by_choice_id = wins_hash.merge(losses_hash) do |key, oldval, newval| oldval + newval end |
343 | 438 | ||
344 | sum = total_appearances = appearances_by_choice_id.values.inject(0) {|sum, x| sum +=x} | 439 | sum = total_appearances = appearances_by_choice_id.values.inject(0) {|sum, x| sum +=x} |
@@ -349,157 +444,141 @@ namespace :test_api do | @@ -349,157 +444,141 @@ namespace :test_api do | ||
349 | 444 | ||
350 | appearances_by_choice_id.each do |choice_id, n_i| | 445 | appearances_by_choice_id.each do |choice_id, n_i| |
351 | if (n_i < (mean - 6*stddev)) || (n_i > mean + 6 *stddev) | 446 | if (n_i < (mean - 6*stddev)) || (n_i > mean + 6 *stddev) |
352 | - error_msg += "Choice #{choice_id} in Question ##{question.id} has an irregular number of appearances: #{n_i}, as compared to the mean: #{mean} and stddev #{stddev} for this question" | ||
353 | - error_bool = true | 447 | + error_message += "Choice #{choice_id} in Question ##{question.id} has an irregular number of appearances: #{n_i}, as compared to the mean: #{mean} and stddev #{stddev} for this question" |
354 | end | 448 | end |
355 | end | 449 | end |
356 | end | 450 | end |
357 | - | 451 | + |
452 | + return error_message.blank? ? [success_message, false] : [error_message, true] | ||
453 | + end | ||
454 | + def check_each_choice_equally_likely_to_appear_left_or_right(question) | ||
455 | + error_message = "" | ||
456 | + success_message = "All choices have equal probability of appearing on left or right (within error params)" | ||
457 | + question.choices.each do |c| | ||
458 | + left_prompts_ids = c.prompts_on_the_left.ids_only | ||
459 | + right_prompts_ids = c.prompts_on_the_right.ids_only | ||
460 | + | ||
461 | + left_appearances = question.appearances.count(:conditions => {:prompt_id => left_prompts_ids}) | ||
462 | + right_appearances = question.appearances.count(:conditions => {:prompt_id => right_prompts_ids}) | ||
463 | + | ||
464 | + n = left_appearances + right_appearances | ||
465 | + | ||
466 | + if n == 0 | ||
467 | + next | ||
468 | + end | ||
469 | + est_p = right_appearances.to_f / n.to_f | ||
470 | + z = (est_p - 0.5).abs / Math.sqrt((0.5 * 0.5) / n.to_f) | ||
471 | + | ||
472 | + if z > 6 | ||
473 | + error_message += "Error: Choice ID #{c.id} seems to favor one side: Left Appearances #{left_appearances}, Right Appearances: #{right_appearances}, z = #{z}\n" | ||
474 | + end | ||
475 | + end | ||
476 | + return error_message.blank? ? [success_message, false] : [error_message, true] | ||
477 | + end | ||
478 | + def check_prompt_cache_hit_rate(question) | ||
479 | + error_message = "" | ||
480 | + success_message = "At least 90% of prompts on catchup algorithm questions were served from cache\n" | ||
481 | + | ||
482 | + misses = question.get_prompt_cache_misses(Date.yesterday).to_i | ||
483 | + hits = question.get_prompt_cache_hits(Date.yesterday).to_i | ||
484 | + | ||
485 | + question.expire_prompt_cache_tracking_keys(Date.yesterday) | ||
486 | + | ||
487 | + yesterday_votes = question.appearances.count(:conditions => ['date(created_at) = ?', Date.yesterday]) | ||
488 | + | ||
489 | + if misses + hits != yesterday_votes | ||
490 | + error_message += "Error! Question #{question.id} isn't tracking prompt cache hits and misses accurately! Expected #{yesterday_votes}, Actual: #{misses+hits}\n" | ||
491 | + end | ||
492 | + | ||
493 | + miss_rate = misses.to_f / yesterday_votes.to_f | ||
494 | + if miss_rate > 0.1 | ||
495 | + error_message += "Error! Question #{question.id} has less than 90% of appearances taken from a pre-generated cache! Expected <#{0.1}, Actual: #{miss_rate}\n" | ||
496 | + end | ||
497 | + return error_message.blank? ? [success_message, false] : [error_message, true] | ||
498 | + end | ||
499 | + | ||
500 | + def check_object_counter_cache_values_match_actual_values(question) | ||
501 | + error_message = "" | ||
502 | + success_message = "All cached object values match actual values within database" | ||
358 | # Checks that counter_cache is working as expected | 503 | # Checks that counter_cache is working as expected |
359 | cached_prompts_size = question.prompts.size | 504 | cached_prompts_size = question.prompts.size |
360 | actual_prompts_size = question.prompts.count | 505 | actual_prompts_size = question.prompts.count |
361 | 506 | ||
362 | if cached_prompts_size != actual_prompts_size | 507 | if cached_prompts_size != actual_prompts_size |
363 | - error_msg += "Error! Question #{question.id} has an inconsistent # of prompts! cached#: #{cached_prompts_size}, actual#: #{actual_prompts_size}\n" | 508 | + error_message += "Error! Question #{question.id} has an inconsistent # of prompts! cached#: #{cached_prompts_size}, actual#: #{actual_prompts_size}\n" |
364 | end | 509 | end |
365 | 510 | ||
366 | cached_votes_size = question.votes.size | 511 | cached_votes_size = question.votes.size |
367 | actual_votes_size = question.votes.count | 512 | actual_votes_size = question.votes.count |
368 | 513 | ||
369 | if cached_votes_size != actual_votes_size | 514 | if cached_votes_size != actual_votes_size |
370 | - error_msg += "Error! Question #{question.id} has an inconsistent # of votes! cached#: #{cached_votes_size}, actual#: #{actual_votes_size}\n" | 515 | + error_message += "Error! Question #{question.id} has an inconsistent # of votes! cached#: #{cached_votes_size}, actual#: #{actual_votes_size}\n" |
371 | end | 516 | end |
372 | 517 | ||
373 | cached_choices_size = question.choices.size | 518 | cached_choices_size = question.choices.size |
374 | actual_choices_size = question.choices.count | 519 | actual_choices_size = question.choices.count |
375 | 520 | ||
376 | if cached_choices_size != actual_choices_size | 521 | if cached_choices_size != actual_choices_size |
377 | - error_msg += "Error! Question #{question.id} has an inconsistent # of choices! cached#: #{cached_choices_size}, actual#: #{actual_choices_size}\n" | 522 | + error_message+= "Error! Question #{question.id} has an inconsistent # of choices! cached#: #{cached_choices_size}, actual#: #{actual_choices_size}\n" |
378 | end | 523 | end |
379 | 524 | ||
380 | if cached_prompts_size != question.choices.size **2 - question.choices.size | 525 | if cached_prompts_size != question.choices.size **2 - question.choices.size |
381 | - error_msg += "Error! Question #{question.id} has an incorrect number of prompts! Expected #{question.choices.size **2 - question.choices.size}, Actual: #{cached_prompts_size}\n" | 526 | + error_message += "Error! Question #{question.id} has an incorrect number of prompts! Expected #{question.choices.size **2 - question.choices.size}, Actual: #{cached_prompts_size}\n" |
382 | end | 527 | end |
383 | - | ||
384 | - | ||
385 | - #catchup specific | ||
386 | - if question.uses_catchup? | ||
387 | - misses = question.get_prompt_cache_misses(Date.yesterday).to_i | ||
388 | - hits = question.get_prompt_cache_hits(Date.yesterday).to_i | ||
389 | - | ||
390 | - question.expire_prompt_cache_tracking_keys(Date.yesterday) | ||
391 | - | ||
392 | - | ||
393 | - | ||
394 | - yesterday_votes = question.appearances.count(:conditions => ['date(created_at) = ?', Date.yesterday]) | ||
395 | - | ||
396 | - if misses + hits != yesterday_votes | ||
397 | - error_msg += "Error! Question #{question.id} isn't tracking prompt cache hits and misses accurately! Expected #{yesterday_votes}, Actual: #{misses+hits}\n" | ||
398 | - end | ||
399 | - | ||
400 | - miss_rate = misses.to_f / yesterday_votes.to_f | ||
401 | - if miss_rate > 0.1 | ||
402 | - error_msg += "Error! Question #{question.id} has less than 90% of appearances taken from a pre-generated cache! Expected <#{0.1}, Actual: #{miss_rate}\n" | ||
403 | - end | ||
404 | - end | ||
405 | - | ||
406 | - | ||
407 | - if error_bool | ||
408 | - error_msg += "Question #{question.id}: 2*wins = #{2*total_wins}, total votes = #{total_votes}, vote_count = #{question.votes_count}\n" | ||
409 | - end | ||
410 | - | ||
411 | - error_bool = false | ||
412 | - end | ||
413 | - | 528 | + return error_message.blank? ? [success_message, false] : [error_message, true] |
529 | + end | ||
530 | + | ||
531 | + def ensure_all_votes_and_skips_have_unique_appearance | ||
532 | + error_message = "" | ||
533 | + success_message = "All vote and skip objects have an associated appearance object" | ||
414 | votes_without_appearances= Vote.count(:conditions => {:appearance_id => nil}) | 534 | votes_without_appearances= Vote.count(:conditions => {:appearance_id => nil}) |
415 | if (votes_without_appearances > 0) | 535 | if (votes_without_appearances > 0) |
416 | - error_msg += "Error! There are #{votes_without_appearances} votes without associated appearance objects." | 536 | + error_message += "Error! There are #{votes_without_appearances} votes without associated appearance objects." |
417 | end | 537 | end |
418 | 538 | ||
419 | skips_without_appearances= Skip.count(:conditions => {:appearance_id => nil}) | 539 | skips_without_appearances= Skip.count(:conditions => {:appearance_id => nil}) |
420 | if (skips_without_appearances > 0) | 540 | if (skips_without_appearances > 0) |
421 | - error_msg += "Error! There are #{skips_without_appearances} skips without associated appearance objects." | 541 | + error_message += "Error! There are #{skips_without_appearances} skips without associated appearance objects." |
422 | end | 542 | end |
543 | + | ||
544 | + return error_message.blank? ? [success_message, false] : [error_message, true] | ||
545 | + end | ||
423 | 546 | ||
547 | + def response_time_tests | ||
548 | + error_message = "" | ||
549 | + success_message = "All Vote objects have an client response time < calculated server roundtrip time\n" | ||
424 | 550 | ||
425 | recording_client_time_start_date = Vote.find(:all, :conditions => 'time_viewed IS NOT NULL', :order => 'created_at', :limit => 1).first.created_at | 551 | recording_client_time_start_date = Vote.find(:all, :conditions => 'time_viewed IS NOT NULL', :order => 'created_at', :limit => 1).first.created_at |
426 | 552 | ||
427 | Vote.find_each(:batch_size => 1000, :include => :appearance) do |v| | 553 | Vote.find_each(:batch_size => 1000, :include => :appearance) do |v| |
428 | 554 | ||
429 | - | ||
430 | # Subtracting DateTime objects results in the difference in days | 555 | # Subtracting DateTime objects results in the difference in days |
431 | server_response_time = v.created_at.to_f - v.appearance.created_at.to_f | 556 | server_response_time = v.created_at.to_f - v.appearance.created_at.to_f |
432 | if server_response_time < 0 | 557 | if server_response_time < 0 |
433 | the_error_msg = "Error! Vote #{v.id} was created before the appearance associated with it: Appearance id: #{v.appearance.id}, Vote creation time: #{v.created_at.to_s}, Appearance creation time: #{v.appearance.created_at.to_s}\n" | 558 | the_error_msg = "Error! Vote #{v.id} was created before the appearance associated with it: Appearance id: #{v.appearance.id}, Vote creation time: #{v.created_at.to_s}, Appearance creation time: #{v.appearance.created_at.to_s}\n" |
434 | 559 | ||
435 | - | ||
436 | - error_msg += the_error_msg | ||
437 | - print the_error_msg | ||
438 | - | ||
439 | - print "Error!" | 560 | + error_message += the_error_msg |
561 | + print "Error!" + the_error_msg | ||
440 | end | 562 | end |
441 | 563 | ||
442 | if v.time_viewed && v.time_viewed/1000 > server_response_time | 564 | if v.time_viewed && v.time_viewed/1000 > server_response_time |
443 | the_error_msg = "Error! Vote #{v.id} with Appearance #{v.appearance.id}, has a longer client response time than is possible. Vote creation time: #{v.created_at.to_s}, Appearance creation time: #{v.appearance.created_at.to_s}, Client side response time: #{v.time_viewed}\n" | 565 | the_error_msg = "Error! Vote #{v.id} with Appearance #{v.appearance.id}, has a longer client response time than is possible. Vote creation time: #{v.created_at.to_s}, Appearance creation time: #{v.appearance.created_at.to_s}, Client side response time: #{v.time_viewed}\n" |
444 | 566 | ||
445 | - error_msg += the_error_msg | ||
446 | - print the_error_msg | ||
447 | - | ||
448 | - bad_votes << v.id | 567 | + error_message += the_error_msg |
568 | + print the_error_msg | ||
449 | 569 | ||
450 | elsif v.time_viewed.nil? | 570 | elsif v.time_viewed.nil? |
451 | if v.created_at > recording_client_time_start_date && v.missing_response_time_exp != 'invalid' | 571 | if v.created_at > recording_client_time_start_date && v.missing_response_time_exp != 'invalid' |
452 | the_error_msg = "Error! Vote #{v.id} with Appearance #{v.appearance.id}, does not have a client response, even though it should! Vote creation time: #{v.created_at.to_s}, Appearance creation time: #{v.appearance.created_at.to_s}, Client side response time: #{v.time_viewed}\n" | 572 | the_error_msg = "Error! Vote #{v.id} with Appearance #{v.appearance.id}, does not have a client response, even though it should! Vote creation time: #{v.created_at.to_s}, Appearance creation time: #{v.appearance.created_at.to_s}, Client side response time: #{v.time_viewed}\n" |
453 | - error_msg += the_error_msg | 573 | + error_message += the_error_msg |
454 | print the_error_msg | 574 | print the_error_msg |
455 | end | 575 | end |
456 | 576 | ||
457 | end | 577 | end |
458 | 578 | ||
459 | - | ||
460 | - end | ||
461 | - | ||
462 | - if error_msg.blank? | ||
463 | - | ||
464 | - success_msg = "Conducted the following tests on API data and found no inconsistencies.\n" + | ||
465 | - "For each of the #{questions.length} questions in the database: \n" + | ||
466 | - " 2 x Total Wins = Total Votes\n" + | ||
467 | - " Total Votes (wins + losses) is Even\n" + | ||
468 | - " Total Votes (wins + losses) = 2 x the number of vote objects that belong to the question\n" + | ||
469 | - " Total generated prompts on left = Total generated prompts on right\n" + | ||
470 | - " Each choice has appeared n times, where n falls within 6 stddevs of the mean number of appearances for a question\n" + | ||
471 | - " Note: this applies only to seed choices (not user submitted) and choices currently marked active\n" + | ||
472 | - " The cached score value matches the calculated score value for each choice\n" + | ||
473 | - " The cached vote count matches the actual number of votes for each question\n" + | ||
474 | - " The cached choices count matches the actual number of choices for each question\n" + | ||
475 | - " The cached prompt count matches the actual number of prompts for each question\n" + | ||
476 | - " The prompt count matches the expected number of prompts ( num_choices ^2 - num choices) for each question\n" + | ||
477 | - " All Vote objects have an associated appearance object\n" + | ||
478 | - " All Vote objects have an client response time < calculated server roundtrip time\n" | ||
479 | - " More than 90% of prompts on catchup algorithm questions were served from cache\n" | ||
480 | - | ||
481 | - print success_msg | ||
482 | - | ||
483 | - CronMailer.deliver_info_message(CRON_EMAIL, "Test of API Vote Consistency passed", success_msg) | ||
484 | - else | ||
485 | - CronMailer.deliver_info_message("#{CRON_EMAIL},#{ERRORS_EMAIL}", "Error! Failure of API Vote Consistency " , error_msg) | ||
486 | - | ||
487 | - puts "There were errors: " | ||
488 | - puts error_msg | ||
489 | - | ||
490 | - unless bad_choices.blank? | ||
491 | - | ||
492 | - puts "Here's a list of choice ids that you may want to modify: #{bad_choices.uniq.inspect}" | ||
493 | - | ||
494 | - end | ||
495 | - unless bad_votes.blank? | ||
496 | - | ||
497 | - puts "Here's a list of vote ids that you may want to modify: #{bad_votes.uniq.inspect}" | ||
498 | - | ||
499 | - end | ||
500 | - print error_msg | 579 | + end |
580 | + | ||
581 | + return error_message.blank? ? [success_message, false] : [error_message, true] | ||
501 | end | 582 | end |
502 | - | ||
503 | - end | ||
504 | end | 583 | end |
505 | 584 |